chore: use import defer
This commit is contained in:
parent
622df65cbf
commit
5956d19125
12 changed files with 181 additions and 35 deletions
|
|
@ -8,6 +8,7 @@ import pkg from '../package.json'
|
|||
import asRequire from './plugins/as-require'
|
||||
import hermesSwcPlugin from './plugins/hermes-swc'
|
||||
import hermesCPlugin from './plugins/hermesc'
|
||||
import importDefer from './plugins/import-defer'
|
||||
import shimAliases from './plugins/shim-aliases'
|
||||
|
||||
const ShimsDir = `${import.meta.dir}/../shims`
|
||||
|
|
@ -90,6 +91,7 @@ export default async function build(dev = Dev, log = true) {
|
|||
asRequire(),
|
||||
shimAliases(ShimsDir),
|
||||
importGlobPlugin(),
|
||||
importDefer(),
|
||||
hermesSwcPlugin(),
|
||||
hermesCPlugin({
|
||||
flags: [
|
||||
|
|
|
|||
127
scripts/plugins/import-defer.ts
Normal file
127
scripts/plugins/import-defer.ts
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import MagicString from 'magic-string'
|
||||
import type { RolldownPlugin } from 'rolldown'
|
||||
|
||||
// `import defer * as ns from './bar.js'`
|
||||
const DEFER_NAMESPACE_IMPORT_REGEX =
|
||||
/import\s+defer\s+(\*\s+as\s+\w+)\s+from\s+(['"])([^'"]+)\2\s*;?/g
|
||||
|
||||
/**
|
||||
* Really wacky support for `import defer` statements by transforming them to `require()`.
|
||||
*
|
||||
* `import defer` statements are transformed to provide on-demand/lazy loading.
|
||||
*
|
||||
* This is achieved by creating a scoped closure to cache the module.
|
||||
* - The deferred namespace import is converted to a function that triggers the `require()` when called.
|
||||
* - Usages like `ns.foo` are rewritten to `ns().foo` to execute the loader.
|
||||
*/
|
||||
export default function importDefer() {
|
||||
return {
|
||||
name: 'import-defer',
|
||||
async transform(code) {
|
||||
const s = new MagicString(code)
|
||||
let hasReplacements = false
|
||||
|
||||
const allBindingMatches = [
|
||||
...code.matchAll(DEFER_NAMESPACE_IMPORT_REGEX),
|
||||
]
|
||||
|
||||
for (const match of allBindingMatches) {
|
||||
const [fullMatch, bindingsStr, , modulePath] = match
|
||||
const bindings = parseBindings(bindingsStr)
|
||||
|
||||
if (bindings.length > 0) {
|
||||
const localName = bindings[0].local
|
||||
|
||||
// Replace the original import defer statement with the IIFE that creates the lazy function.
|
||||
s.overwrite(
|
||||
match.index,
|
||||
match.index + fullMatch.length,
|
||||
generateDeferReplacement(bindings, modulePath),
|
||||
)
|
||||
hasReplacements = true
|
||||
|
||||
// Find all usages of the imported identifier
|
||||
const usageRegex = new RegExp(`\\b${localName}\\b`, 'g');
|
||||
|
||||
for (const usageMatch of code.matchAll(usageRegex)) {
|
||||
// Original import declaration
|
||||
if (
|
||||
usageMatch.index >= match.index &&
|
||||
usageMatch.index < match.index + fullMatch.length
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
const startIndex = usageMatch.index
|
||||
const endIndex = startIndex + usageMatch[0].length
|
||||
|
||||
// Check for context to decide on the replacement by inspecting surrounding characters.
|
||||
// Look for the next non-whitespace character.
|
||||
let nextChar = ''
|
||||
let i = endIndex
|
||||
while (i < code.length) {
|
||||
if (!/\s/.test(code[i])) {
|
||||
nextChar = code[i]
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
// Skip object property key: `Identifier: ...`
|
||||
if (nextChar === ':') continue
|
||||
|
||||
let prevChar = ''
|
||||
i = startIndex - 1
|
||||
while (i >= 0) {
|
||||
if (!/\s/.test(code[i])) {
|
||||
prevChar = code[i]
|
||||
break
|
||||
}
|
||||
i--
|
||||
}
|
||||
|
||||
// Shorthand object property: { ..., Identifier, ... }
|
||||
const isShorthand =
|
||||
(prevChar === '{' || prevChar === ',') &&
|
||||
(nextChar === '}' || nextChar === ',')
|
||||
|
||||
if (isShorthand)
|
||||
s.overwrite(
|
||||
startIndex,
|
||||
endIndex,
|
||||
`${localName}: ${localName}()`,
|
||||
)
|
||||
// All other usages (value, property access). `doThing(Identifier)`, `Identifier.foo`
|
||||
else s.overwrite(startIndex, endIndex, `${localName}()`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasReplacements) return null
|
||||
|
||||
return {
|
||||
code: s.toString(),
|
||||
map: s.generateMap({ hires: true }),
|
||||
}
|
||||
},
|
||||
} satisfies RolldownPlugin
|
||||
}
|
||||
|
||||
type Bindings = { local: string; imported: string }[]
|
||||
|
||||
function parseBindings(bindingsStr: string): Bindings {
|
||||
const remainingStr = bindingsStr.trim()
|
||||
const namespaceMatch = remainingStr.match(/^\*\s+as\s+(\w+)/)
|
||||
if (namespaceMatch) return [{ local: namespaceMatch[1], imported: '*' }]
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
function generateDeferReplacement(bindings: Bindings, modulePath: string) {
|
||||
if (!bindings.length) return ''
|
||||
|
||||
const [{ local: localName }] = bindings
|
||||
const cacheVar = `_cache_${modulePath.replace(/[^a-zA-Z0-9]/g, '_')}`
|
||||
|
||||
return `var ${cacheVar};const ${localName}=()=>${cacheVar}??=require('${modulePath}')`
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import defer * as Components from '@revenge-mod/components'
|
||||
import { InternalPluginFlags, registerPlugin } from '@revenge-mod/plugins/_'
|
||||
import { PluginFlags } from '@revenge-mod/plugins/constants'
|
||||
import { defineLazyProperties } from '@revenge-mod/utils/object'
|
||||
|
|
@ -14,7 +15,7 @@ registerPlugin(
|
|||
init({ unscoped }) {
|
||||
defineLazyProperties(unscoped, {
|
||||
components: () => {
|
||||
return require('@revenge-mod/components')
|
||||
return Components
|
||||
},
|
||||
})
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,4 +1,10 @@
|
|||
import defer * as DiscordActions from '@revenge-mod/discord/actions'
|
||||
import defer * as DiscordCommon from '@revenge-mod/discord/common'
|
||||
import { Logger } from '@revenge-mod/discord/common'
|
||||
import defer * as DiscordDesign from '@revenge-mod/discord/design'
|
||||
import defer * as DiscordFlux from '@revenge-mod/discord/flux'
|
||||
import defer * as DiscordModulesMainTabsV2 from '@revenge-mod/discord/modules/main_tabs_v2'
|
||||
import defer * as DiscordNative from '@revenge-mod/discord/native'
|
||||
import { InternalPluginFlags, registerPlugin } from '@revenge-mod/plugins/_'
|
||||
import { PluginFlags } from '@revenge-mod/plugins/constants'
|
||||
import {
|
||||
|
|
@ -32,7 +38,7 @@ registerPlugin(
|
|||
{} as PluginApiDiscord.Modules,
|
||||
{
|
||||
mainTabsV2: () => {
|
||||
return require('@revenge-mod/discord/modules/main_tabs_v2')
|
||||
return DiscordModulesMainTabsV2
|
||||
},
|
||||
settings: () => ({
|
||||
...require('@revenge-mod/discord/modules/settings'),
|
||||
|
|
@ -43,19 +49,19 @@ registerPlugin(
|
|||
} as PluginApiDiscord),
|
||||
{
|
||||
actions: () => {
|
||||
return require('@revenge-mod/discord/actions')
|
||||
return DiscordActions
|
||||
},
|
||||
common: () => {
|
||||
return require('@revenge-mod/discord/common')
|
||||
return DiscordCommon
|
||||
},
|
||||
flux: () => {
|
||||
return require('@revenge-mod/discord/flux')
|
||||
return DiscordFlux
|
||||
},
|
||||
design: () => {
|
||||
return require('@revenge-mod/discord/design')
|
||||
return DiscordDesign
|
||||
},
|
||||
native: () => {
|
||||
return require('@revenge-mod/discord/native')
|
||||
return DiscordNative
|
||||
},
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
import defer * as Browserify from '@revenge-mod/externals/browserify'
|
||||
import defer * as ReactNativeClipboard from '@revenge-mod/externals/react-native-clipboard'
|
||||
import defer * as ReactNavigation from '@revenge-mod/externals/react-navigation'
|
||||
import defer * as Shopify from '@revenge-mod/externals/shopify'
|
||||
import { InternalPluginFlags, registerPlugin } from '@revenge-mod/plugins/_'
|
||||
import { PluginFlags } from '@revenge-mod/plugins/constants'
|
||||
import { defineLazyProperties } from '@revenge-mod/utils/object'
|
||||
|
|
@ -17,16 +21,16 @@ registerPlugin(
|
|||
(unscoped.externals = {} as PluginApiExternals),
|
||||
{
|
||||
Browserify: () => {
|
||||
return require('@revenge-mod/externals/browserify')
|
||||
return Browserify
|
||||
},
|
||||
ReactNativeClipboard: () => {
|
||||
return require('@revenge-mod/externals/react-native-clipboard')
|
||||
return ReactNativeClipboard
|
||||
},
|
||||
ReactNavigation: () => {
|
||||
return require('@revenge-mod/externals/react-navigation')
|
||||
return ReactNavigation
|
||||
},
|
||||
Shopify: () => {
|
||||
return require('@revenge-mod/externals/shopify')
|
||||
return Shopify
|
||||
},
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
import { InternalPluginFlags, registerPlugin } from '@revenge-mod/plugins/_'
|
||||
import { PluginFlags } from '@revenge-mod/plugins/constants'
|
||||
import defer * as UtilsCallback from '@revenge-mod/utils/callback'
|
||||
import defer * as UtilsDiscord from '@revenge-mod/utils/discord'
|
||||
import defer * as UtilsError from '@revenge-mod/utils/error'
|
||||
import defer * as UtilsObject from '@revenge-mod/utils/object'
|
||||
import defer * as UtilsPromise from '@revenge-mod/utils/promise'
|
||||
import defer * as UtilsProxy from '@revenge-mod/utils/proxy'
|
||||
import defer * as UtilsReact from '@revenge-mod/utils/react'
|
||||
import defer * as UtilsTree from '@revenge-mod/utils/tree'
|
||||
|
||||
registerPlugin(
|
||||
{
|
||||
|
|
@ -12,17 +20,17 @@ registerPlugin(
|
|||
{
|
||||
preInit({ unscoped }) {
|
||||
unscoped.utils = {
|
||||
callback: require('@revenge-mod/utils/callback'),
|
||||
error: require('@revenge-mod/utils/error'),
|
||||
object: require('@revenge-mod/utils/object'),
|
||||
promise: require('@revenge-mod/utils/promise'),
|
||||
proxy: require('@revenge-mod/utils/proxy'),
|
||||
tree: require('@revenge-mod/utils/tree'),
|
||||
callback: UtilsCallback,
|
||||
error: UtilsError,
|
||||
object: UtilsObject,
|
||||
promise: UtilsPromise,
|
||||
proxy: UtilsProxy,
|
||||
tree: UtilsTree,
|
||||
}
|
||||
},
|
||||
init({ unscoped: { utils } }) {
|
||||
utils.discord = require('@revenge-mod/utils/discord')
|
||||
utils.react = require('@revenge-mod/utils/react')
|
||||
utils.discord = UtilsDiscord
|
||||
utils.react = UtilsReact
|
||||
},
|
||||
},
|
||||
PluginFlags.Enabled,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import TableRowAssetIcon from '@revenge-mod/components/TableRowAssetIcon'
|
||||
import { RouteNames, Setting } from '../constants'
|
||||
import defer * as AssetBrowserSettingScreen from '../screens/AssetBrowserSettingScreen'
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const AssetBrowserSetting: SettingsItem = {
|
||||
|
|
@ -9,8 +10,7 @@ const AssetBrowserSetting: SettingsItem = {
|
|||
title: () => 'Asset Browser',
|
||||
screen: {
|
||||
route: RouteNames[Setting.AssetBrowser],
|
||||
getComponent: () =>
|
||||
require('../screens/AssetBrowserSettingScreen').default,
|
||||
getComponent: () => AssetBrowserSettingScreen.default,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import TableRowAssetIcon from '@revenge-mod/components/TableRowAssetIcon'
|
||||
import { RouteNames, Setting } from '../constants'
|
||||
import defer * as RevengeDeveloperSettingScreen from '../screens/RevengeDeveloperSettingScreen'
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const RevengeDeveloperSetting: SettingsItem = {
|
||||
|
|
@ -9,8 +10,7 @@ const RevengeDeveloperSetting: SettingsItem = {
|
|||
title: () => 'Developer',
|
||||
screen: {
|
||||
route: RouteNames[Setting.RevengeDeveloper],
|
||||
getComponent: () =>
|
||||
require('../screens/RevengeDeveloperSettingScreen').default,
|
||||
getComponent: () => RevengeDeveloperSettingScreen.default,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import TableRowAssetIcon from '@revenge-mod/components/TableRowAssetIcon'
|
||||
import { RouteNames, Setting } from '../constants'
|
||||
import defer * as TestErrorBoundarySettingScreen from '../screens/TestErrorBoundarySettingScreen'
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const TestErrorBoundarySetting: SettingsItem = {
|
||||
|
|
@ -12,8 +13,7 @@ const TestErrorBoundarySetting: SettingsItem = {
|
|||
title: () => 'Test ErrorBoundary',
|
||||
screen: {
|
||||
route: RouteNames[Setting.TestErrorBoundary],
|
||||
getComponent: () =>
|
||||
require('../screens/TestErrorBoundarySettingScreen').default,
|
||||
getComponent: () => TestErrorBoundarySettingScreen.default,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { PluginFlags } from '@revenge-mod/plugins/constants'
|
|||
import { useReRender } from '@revenge-mod/utils/react'
|
||||
import { useEffect } from 'react'
|
||||
import { RouteNames, Setting } from '../constants'
|
||||
import defer * as RevengePluginsSettingScreen from '../screens/RevengePluginsSettingScreen'
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
import type { AnyPlugin } from '@revenge-mod/plugins/_'
|
||||
|
||||
|
|
@ -18,8 +19,7 @@ const RevengePluginsSetting: SettingsItem = {
|
|||
useTrailing: () => `${useEnabledPluginCount()} enabled`,
|
||||
screen: {
|
||||
route: RouteNames[Setting.RevengePlugins],
|
||||
getComponent: () =>
|
||||
require('../screens/RevengePluginsSettingScreen').default,
|
||||
getComponent: () => RevengePluginsSettingScreen.default,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import TableRowAssetIcon from '@revenge-mod/components/TableRowAssetIcon'
|
|||
import { FullVersion } from '~/constants'
|
||||
import RevengeIcon from '~assets/RevengeIcon'
|
||||
import { RouteNames, Setting } from '../constants'
|
||||
import defer * as RevengeSettingScreen from '../screens/RevengeSettingScreen'
|
||||
import type { SettingsItem } from '@revenge-mod/discord/modules/settings'
|
||||
|
||||
const RevengeSetting: SettingsItem = {
|
||||
|
|
@ -12,7 +13,7 @@ const RevengeSetting: SettingsItem = {
|
|||
useTrailing: () => FullVersion,
|
||||
screen: {
|
||||
route: RouteNames[Setting.Revenge],
|
||||
getComponent: () => require('../screens/RevengeSettingScreen').default,
|
||||
getComponent: () => RevengeSettingScreen.default,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,19 +6,16 @@ import {
|
|||
import { pEmitter } from '@revenge-mod/plugins/_'
|
||||
import { proxify } from '@revenge-mod/utils/proxy'
|
||||
import { useLayoutEffect } from 'react'
|
||||
import defer * as NavigatorHeaderWithIcon_ from './components/NavigatorHeaderWithIcon'
|
||||
import { Setting } from './constants'
|
||||
import type { StackScreenProps } from '@react-navigation/stack'
|
||||
import type { ReactNavigationParamList } from '@revenge-mod/externals/react-navigation'
|
||||
import type { PluginApi } from '@revenge-mod/plugins/types'
|
||||
|
||||
type NavigatorHeaderWithIcon =
|
||||
typeof import('./components/NavigatorHeaderWithIcon')['default']
|
||||
type NavigatorHeaderWithIcon = (typeof NavigatorHeaderWithIcon_)['default']
|
||||
|
||||
let NavigatorHeaderWithIcon = proxify(
|
||||
(): NavigatorHeaderWithIcon =>
|
||||
(NavigatorHeaderWithIcon =
|
||||
require('./components/NavigatorHeaderWithIcon').default),
|
||||
{},
|
||||
let NavigatorHeaderWithIcon: NavigatorHeaderWithIcon = proxify(
|
||||
() => (NavigatorHeaderWithIcon = NavigatorHeaderWithIcon_.default),
|
||||
)
|
||||
|
||||
pEmitter.on('started', plugin => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue