From 8b3db9cb6f682841c6b511ba94408e92e72c6f08 Mon Sep 17 00:00:00 2001 From: evanbacon Date: Sun, 5 Nov 2023 14:32:36 -0600 Subject: [PATCH 1/5] support string module IDs --- .../metro-runtime/src/polyfills/require.js | 57 +++++++++---------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/packages/metro-runtime/src/polyfills/require.js b/packages/metro-runtime/src/polyfills/require.js index cd3b1db8af..fdb80e5fb1 100644 --- a/packages/metro-runtime/src/polyfills/require.js +++ b/packages/metro-runtime/src/polyfills/require.js @@ -47,7 +47,7 @@ type HotModuleReloadingData = { accept: (callback?: HotModuleReloadingCallback) => void, dispose: (callback?: HotModuleReloadingCallback) => void, }; -type ModuleID = number; +type ModuleID = number | string; type Module = { id?: ModuleID, exports: Exports, @@ -67,11 +67,7 @@ type ModuleDefinition = { publicModule: Module, verboseName?: string, }; -type ModuleList = { - [number]: ?ModuleDefinition, - __proto__: null, - ... -}; +type ModuleList = Map; export type RequireFn = (id: ModuleID | VerboseModuleNameForDev) => Exports; export type DefineFn = ( factory: FactoryFn, @@ -103,7 +99,7 @@ if (__DEV__) { } function clear(): ModuleList { - modules = (Object.create(null): ModuleList); + modules = (new Map(): ModuleList); // We return modules here so that we can assign an initial value to modules // when defining it. Otherwise, we would have to do "let modules = null", @@ -125,7 +121,7 @@ function define( moduleId: number, dependencyMap?: DependencyMap, ): void { - if (modules[moduleId] != null) { + if (modules.has(moduleId)) { if (__DEV__) { // (We take `inverseDependencies` from `arguments` to avoid an unused // named parameter in `define` in production. @@ -153,7 +149,7 @@ function define( publicModule: {exports: {}}, }; - modules[moduleId] = mod; + modules.set(moduleId, mod); if (__DEV__) { // HMR @@ -194,8 +190,8 @@ function metroRequire(moduleId: ModuleID | VerboseModuleNameForDev): Exports { if (initializingIndex !== -1) { const cycle = initializingModuleIds .slice(initializingIndex) - .map((id: number) => - modules[id] ? modules[id].verboseName : '[unknown]', + .map((id: number | string) => + modules.has(id) ? modules.get(id).verboseName : '[unknown]', ); if (shouldPrintRequireCycle(cycle)) { @@ -209,7 +205,7 @@ function metroRequire(moduleId: ModuleID | VerboseModuleNameForDev): Exports { } } - const module = modules[moduleIdReallyIsNumber]; + const module = modules.get(moduleIdReallyIsNumber); return module && module.isInitialized ? module.publicModule.exports @@ -244,18 +240,19 @@ function metroImportDefault( const moduleIdReallyIsNumber: number = moduleId; if ( - modules[moduleIdReallyIsNumber] && - modules[moduleIdReallyIsNumber].importedDefault !== EMPTY + modules.has(moduleIdReallyIsNumber) && + modules.get(moduleIdReallyIsNumber).importedDefault !== EMPTY ) { - return modules[moduleIdReallyIsNumber].importedDefault; + return modules.get(moduleIdReallyIsNumber).importedDefault; } const exports: Exports = metroRequire(moduleIdReallyIsNumber); const importedDefault: any | Exports = exports && exports.__esModule ? exports.default : exports; - // $FlowFixMe The metroRequire call above will throw if modules[id] is null - return (modules[moduleIdReallyIsNumber].importedDefault = importedDefault); + // $FlowFixMe The metroRequire call above will throw if modules.get(id) is null + return (modules.get(moduleIdReallyIsNumber).importedDefault = + importedDefault); } metroRequire.importDefault = metroImportDefault; @@ -271,10 +268,10 @@ function metroImportAll( const moduleIdReallyIsNumber: number = moduleId; if ( - modules[moduleIdReallyIsNumber] && - modules[moduleIdReallyIsNumber].importedAll !== EMPTY + modules.has(moduleIdReallyIsNumber) && + modules.get(moduleIdReallyIsNumber).importedAll !== EMPTY ) { - return modules[moduleIdReallyIsNumber].importedAll; + return modules.get(moduleIdReallyIsNumber).importedAll; } const exports: Exports = metroRequire(moduleIdReallyIsNumber); @@ -297,8 +294,8 @@ function metroImportAll( importedAll.default = exports; } - // $FlowFixMe The metroRequire call above will throw if modules[id] is null - return (modules[moduleIdReallyIsNumber].importedAll = importedAll); + // $FlowFixMe The metroRequire call above will throw if modules.get(id) is null + return (modules.get(moduleIdReallyIsNumber).importedAll = importedAll); } metroRequire.importAll = metroImportAll; @@ -394,7 +391,7 @@ function registerSegment( } if (moduleIds) { moduleIds.forEach(moduleId => { - if (!modules[moduleId] && !definingSegmentByModuleID.has(moduleId)) { + if (!modules.has(moduleId) && !definingSegmentByModuleID.has(moduleId)) { definingSegmentByModuleID.set(moduleId, segmentId); } }); @@ -410,7 +407,7 @@ function loadModuleImplementation( const definer = moduleDefinersBySegmentID[segmentId]; if (definer != null) { definer(moduleId); - module = modules[moduleId]; + module = modules.get(moduleId); definingSegmentByModuleID.delete(moduleId); } } @@ -419,7 +416,7 @@ function loadModuleImplementation( if (!module && nativeRequire) { const {segmentId, localId} = unpackModuleId(moduleId); nativeRequire(localId, segmentId); - module = modules[moduleId]; + module = modules.get(moduleId); } if (!module) { @@ -563,7 +560,7 @@ if (__DEV__) { dependencyMap: DependencyMap, inverseDependencies: InverseDependencyMap, ) { - const mod = modules[id]; + const mod = modules.get(id); if (!mod) { if (factory) { // New modules are going to be handled by the define() method. @@ -606,7 +603,7 @@ if (__DEV__) { updatedModuleIDs = topologicalSort( [id], // Start with the changed module and go upwards pendingID => { - const pendingModule = modules[pendingID]; + const pendingModule = modules.get(pendingID); if (pendingModule == null) { // Nothing to do. return []; @@ -677,7 +674,7 @@ if (__DEV__) { } seenModuleIDs.add(updatedID); - const updatedMod = modules[updatedID]; + const updatedMod = modules.get(updatedID); if (updatedMod == null) { throw new Error('[Refresh] Expected to find the updated module.'); } @@ -734,7 +731,7 @@ if (__DEV__) { // Schedule all parent refresh boundaries to re-run in this loop. for (let j = 0; j < parentIDs.length; j++) { const parentID = parentIDs[j]; - const parentMod = modules[parentID]; + const parentMod = modules.get(parentID); if (parentMod == null) { throw new Error('[Refresh] Expected to find parent module.'); } @@ -810,7 +807,7 @@ if (__DEV__) { factory?: FactoryFn, dependencyMap?: DependencyMap, ): boolean { - const mod = modules[id]; + const mod = modules.get(id); if (mod == null) { throw new Error('[Refresh] Expected to find the module.'); } From 1ecddbce3e4d72d768cdc3b7e9ac7348822496d5 Mon Sep 17 00:00:00 2001 From: evanbacon Date: Sun, 5 Nov 2023 14:42:04 -0600 Subject: [PATCH 2/5] improve types --- .../metro-runtime/src/polyfills/require.js | 63 +++++++------------ 1 file changed, 22 insertions(+), 41 deletions(-) diff --git a/packages/metro-runtime/src/polyfills/require.js b/packages/metro-runtime/src/polyfills/require.js index fdb80e5fb1..29cc249f95 100644 --- a/packages/metro-runtime/src/polyfills/require.js +++ b/packages/metro-runtime/src/polyfills/require.js @@ -71,7 +71,7 @@ type ModuleList = Map; export type RequireFn = (id: ModuleID | VerboseModuleNameForDev) => Exports; export type DefineFn = ( factory: FactoryFn, - moduleId: number, + moduleId: ModuleID, dependencyMap?: DependencyMap, verboseName?: string, inverseDependencies?: InverseDependencyMap, @@ -108,17 +108,13 @@ function clear(): ModuleList { } if (__DEV__) { - var verboseNamesToModuleIds: { - [key: string]: number, - __proto__: null, - ... - } = Object.create(null); - var initializingModuleIds: Array = []; + var verboseNamesToModuleIds = new Map(); + var initializingModuleIds: Array = []; } function define( factory: FactoryFn, - moduleId: number, + moduleId: ModuleID, dependencyMap?: DependencyMap, ): void { if (modules.has(moduleId)) { @@ -161,7 +157,7 @@ function define( const verboseName: string | void = arguments[3]; if (verboseName) { mod.verboseName = verboseName; - verboseNamesToModuleIds[verboseName] = moduleId; + verboseNamesToModuleIds.set(verboseName, moduleId); } } } @@ -169,7 +165,7 @@ function define( function metroRequire(moduleId: ModuleID | VerboseModuleNameForDev): Exports { if (__DEV__ && typeof moduleId === 'string') { const verboseName = moduleId; - moduleId = verboseNamesToModuleIds[verboseName]; + moduleId = verboseNamesToModuleIds.get(verboseName); if (moduleId == null) { throw new Error(`Unknown named module: "${verboseName}"`); } else { @@ -180,17 +176,12 @@ function metroRequire(moduleId: ModuleID | VerboseModuleNameForDev): Exports { } } - //$FlowFixMe: at this point we know that moduleId is a number - const moduleIdReallyIsNumber: number = moduleId; - if (__DEV__) { - const initializingIndex = initializingModuleIds.indexOf( - moduleIdReallyIsNumber, - ); + const initializingIndex = initializingModuleIds.indexOf(moduleId); if (initializingIndex !== -1) { const cycle = initializingModuleIds .slice(initializingIndex) - .map((id: number | string) => + .map((id: ModuleID) => modules.has(id) ? modules.get(id).verboseName : '[unknown]', ); @@ -205,11 +196,11 @@ function metroRequire(moduleId: ModuleID | VerboseModuleNameForDev): Exports { } } - const module = modules.get(moduleIdReallyIsNumber); + const module = modules.get(moduleId); return module && module.isInitialized ? module.publicModule.exports - : guardedLoadModule(moduleIdReallyIsNumber, module); + : guardedLoadModule(moduleId, module); } // We print require cycles unless they match a pattern in the @@ -233,48 +224,38 @@ function metroImportDefault( ): any | Exports { if (__DEV__ && typeof moduleId === 'string') { const verboseName = moduleId; - moduleId = verboseNamesToModuleIds[verboseName]; + moduleId = verboseNamesToModuleIds.get(verboseName); } - //$FlowFixMe: at this point we know that moduleId is a number - const moduleIdReallyIsNumber: number = moduleId; - if ( - modules.has(moduleIdReallyIsNumber) && - modules.get(moduleIdReallyIsNumber).importedDefault !== EMPTY + modules.has(moduleId) && + modules.get(moduleId).importedDefault !== EMPTY ) { - return modules.get(moduleIdReallyIsNumber).importedDefault; + return modules.get(moduleId).importedDefault; } - const exports: Exports = metroRequire(moduleIdReallyIsNumber); + const exports: Exports = metroRequire(moduleId); const importedDefault: any | Exports = exports && exports.__esModule ? exports.default : exports; // $FlowFixMe The metroRequire call above will throw if modules.get(id) is null - return (modules.get(moduleIdReallyIsNumber).importedDefault = - importedDefault); + return (modules.get(moduleId).importedDefault = importedDefault); } metroRequire.importDefault = metroImportDefault; function metroImportAll( - moduleId: ModuleID | VerboseModuleNameForDev | number, + moduleId: ModuleID | VerboseModuleNameForDev, ): any | Exports | {[string]: any} { if (__DEV__ && typeof moduleId === 'string') { const verboseName = moduleId; - moduleId = verboseNamesToModuleIds[verboseName]; + moduleId = verboseNamesToModuleIds.get(verboseName); } - //$FlowFixMe: at this point we know that moduleId is a number - const moduleIdReallyIsNumber: number = moduleId; - - if ( - modules.has(moduleIdReallyIsNumber) && - modules.get(moduleIdReallyIsNumber).importedAll !== EMPTY - ) { - return modules.get(moduleIdReallyIsNumber).importedAll; + if (modules.has(moduleId) && modules.get(moduleId).importedAll !== EMPTY) { + return modules.get(moduleId).importedAll; } - const exports: Exports = metroRequire(moduleIdReallyIsNumber); + const exports: Exports = metroRequire(moduleId); let importedAll: Exports | {[string]: any}; if (exports && exports.__esModule) { @@ -295,7 +276,7 @@ function metroImportAll( } // $FlowFixMe The metroRequire call above will throw if modules.get(id) is null - return (modules.get(moduleIdReallyIsNumber).importedAll = importedAll); + return (modules.get(moduleId).importedAll = importedAll); } metroRequire.importAll = metroImportAll; From 516b4bfe67a693fa4ab7dcaac0485018432c7b61 Mon Sep 17 00:00:00 2001 From: evanbacon Date: Sun, 5 Nov 2023 14:51:10 -0600 Subject: [PATCH 3/5] Support string in types for createModuleIdFactory --- packages/metro-config/src/configTypes.flow.js | 2 +- packages/metro-config/types/configTypes.d.ts | 2 +- packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js | 2 +- packages/metro/src/HmrServer.js | 4 ++-- packages/metro/src/Server.js | 2 +- packages/metro/src/lib/createModuleIdFactory.js | 2 +- packages/metro/src/shared/types.flow.js | 2 +- packages/metro/types/shared/types.d.ts | 4 ++-- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/metro-config/src/configTypes.flow.js b/packages/metro-config/src/configTypes.flow.js index 6832e52862..05bf760046 100644 --- a/packages/metro-config/src/configTypes.flow.js +++ b/packages/metro-config/src/configTypes.flow.js @@ -120,7 +120,7 @@ type ResolverConfigT = { }; type SerializerConfigT = { - createModuleIdFactory: () => (path: string) => number, + createModuleIdFactory: () => (path: string) => number | string, customSerializer: ?( entryPoint: string, preModules: $ReadOnlyArray>, diff --git a/packages/metro-config/types/configTypes.d.ts b/packages/metro-config/types/configTypes.d.ts index 2dc65b821d..57fa5e01b6 100644 --- a/packages/metro-config/types/configTypes.d.ts +++ b/packages/metro-config/types/configTypes.d.ts @@ -115,7 +115,7 @@ export interface ResolverConfigT { } export interface SerializerConfigT { - createModuleIdFactory: () => (path: string) => number; + createModuleIdFactory: () => (path: string) => number | string; customSerializer: | (( entryPoint: string, diff --git a/packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js b/packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js index 0e1d45666b..a1f0669fc8 100644 --- a/packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js +++ b/packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js @@ -23,7 +23,7 @@ const url = require('url'); type Options = $ReadOnly<{ clientUrl: EntryPointURL, - createModuleId: string => number, + createModuleId: string => number | string, includeAsyncPaths: boolean, projectRoot: string, serverRoot: string, diff --git a/packages/metro/src/HmrServer.js b/packages/metro/src/HmrServer.js index 94969cba51..870c77d545 100644 --- a/packages/metro/src/HmrServer.js +++ b/packages/metro/src/HmrServer.js @@ -69,12 +69,12 @@ function send(sendFns: Array<(string) => void>, message: HmrMessage): void { class HmrServer { _config: ConfigT; _bundler: IncrementalBundler; - _createModuleId: (path: string) => number; + _createModuleId: (path: string) => number | string; _clientGroups: Map; constructor( bundler: IncrementalBundler, - createModuleId: (path: string) => number, + createModuleId: (path: string) => number | string, config: ConfigT, ) { this._config = config; diff --git a/packages/metro/src/Server.js b/packages/metro/src/Server.js index a001ccb46e..a318a1df9b 100644 --- a/packages/metro/src/Server.js +++ b/packages/metro/src/Server.js @@ -134,7 +134,7 @@ const FILES_CHANGED_COUNT_HEADER = 'X-Metro-Files-Changed-Count'; class Server { _bundler: IncrementalBundler; _config: ConfigT; - _createModuleId: (path: string) => number; + _createModuleId: (path: string) => number | string; _isEnded: boolean; _logger: typeof Logger; _nextBundleBuildNumber: number; diff --git a/packages/metro/src/lib/createModuleIdFactory.js b/packages/metro/src/lib/createModuleIdFactory.js index 24e964fe04..aaeef40d87 100644 --- a/packages/metro/src/lib/createModuleIdFactory.js +++ b/packages/metro/src/lib/createModuleIdFactory.js @@ -11,7 +11,7 @@ 'use strict'; -function createModuleIdFactory(): (path: string) => number { +function createModuleIdFactory(): (path: string) => number | string { const fileToIdMap: Map = new Map(); let nextId = 0; return (path: string) => { diff --git a/packages/metro/src/shared/types.flow.js b/packages/metro/src/shared/types.flow.js index 2d4fd618b8..d4cd241a3a 100644 --- a/packages/metro/src/shared/types.flow.js +++ b/packages/metro/src/shared/types.flow.js @@ -137,7 +137,7 @@ export type RequestOptions = { dev?: boolean, minify: boolean, platform: string, - createModuleIdFactory?: () => (path: string) => number, + createModuleIdFactory?: () => (path: string) => number | string, onProgress?: (transformedFileCount: number, totalFileCount: number) => void, +customResolverOptions?: CustomResolverOptions, +customTransformOptions?: CustomTransformOptions, diff --git a/packages/metro/types/shared/types.d.ts b/packages/metro/types/shared/types.d.ts index 0430652453..dc991c1796 100644 --- a/packages/metro/types/shared/types.d.ts +++ b/packages/metro/types/shared/types.d.ts @@ -56,7 +56,7 @@ export interface BundleOptions { readonly shallow: boolean; sourceMapUrl?: string; sourceUrl?: string; - createModuleIdFactory?: () => (path: string) => number; + createModuleIdFactory?: () => (path: string) => number | string; readonly unstable_transformProfile: TransformProfile; } @@ -133,7 +133,7 @@ export interface RequestOptions { dev?: boolean; minify: boolean; platform: string; - createModuleIdFactory?: () => (path: string) => number; + createModuleIdFactory?: () => (path: string) => number | string; onProgress?: (transformedFileCount: number, totalFileCount: number) => void; } From 8beb6097a10fd645dd5d090a0e3a33e6f8f4a473 Mon Sep 17 00:00:00 2001 From: evanbacon Date: Sun, 5 Nov 2023 14:53:13 -0600 Subject: [PATCH 4/5] Update types.flow.js --- packages/metro/src/shared/types.flow.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/metro/src/shared/types.flow.js b/packages/metro/src/shared/types.flow.js index d4cd241a3a..f25b471b59 100644 --- a/packages/metro/src/shared/types.flow.js +++ b/packages/metro/src/shared/types.flow.js @@ -59,7 +59,7 @@ export type BundleOptions = { +shallow: boolean, sourceMapUrl: ?string, sourceUrl: ?string, - createModuleIdFactory?: () => (path: string) => number, + createModuleIdFactory?: () => (path: string) => number | string, +unstable_transformProfile: TransformProfile, }; From 218eee4d8587a2229dd884aa5bec69ba92e81121 Mon Sep 17 00:00:00 2001 From: evanbacon Date: Sun, 5 Nov 2023 15:11:18 -0600 Subject: [PATCH 5/5] drop verbose names support to prevent recursive loop --- .../metro-runtime/src/polyfills/require.js | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/packages/metro-runtime/src/polyfills/require.js b/packages/metro-runtime/src/polyfills/require.js index 29cc249f95..e80e3441a3 100644 --- a/packages/metro-runtime/src/polyfills/require.js +++ b/packages/metro-runtime/src/polyfills/require.js @@ -108,7 +108,6 @@ function clear(): ModuleList { } if (__DEV__) { - var verboseNamesToModuleIds = new Map(); var initializingModuleIds: Array = []; } @@ -157,25 +156,11 @@ function define( const verboseName: string | void = arguments[3]; if (verboseName) { mod.verboseName = verboseName; - verboseNamesToModuleIds.set(verboseName, moduleId); } } } function metroRequire(moduleId: ModuleID | VerboseModuleNameForDev): Exports { - if (__DEV__ && typeof moduleId === 'string') { - const verboseName = moduleId; - moduleId = verboseNamesToModuleIds.get(verboseName); - if (moduleId == null) { - throw new Error(`Unknown named module: "${verboseName}"`); - } else { - console.warn( - `Requiring module "${verboseName}" by name is only supported for ` + - 'debugging purposes and will BREAK IN PRODUCTION!', - ); - } - } - if (__DEV__) { const initializingIndex = initializingModuleIds.indexOf(moduleId); if (initializingIndex !== -1) { @@ -222,11 +207,6 @@ function shouldPrintRequireCycle(modules: $ReadOnlyArray): boolean { function metroImportDefault( moduleId: ModuleID | VerboseModuleNameForDev, ): any | Exports { - if (__DEV__ && typeof moduleId === 'string') { - const verboseName = moduleId; - moduleId = verboseNamesToModuleIds.get(verboseName); - } - if ( modules.has(moduleId) && modules.get(moduleId).importedDefault !== EMPTY @@ -246,11 +226,6 @@ metroRequire.importDefault = metroImportDefault; function metroImportAll( moduleId: ModuleID | VerboseModuleNameForDev, ): any | Exports | {[string]: any} { - if (__DEV__ && typeof moduleId === 'string') { - const verboseName = moduleId; - moduleId = verboseNamesToModuleIds.get(verboseName); - } - if (modules.has(moduleId) && modules.get(moduleId).importedAll !== EMPTY) { return modules.get(moduleId).importedAll; }