Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(metro-runtime): support using strings as module IDs #1132

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/metro-config/src/configTypes.flow.js
Expand Up @@ -120,7 +120,7 @@ type ResolverConfigT = {
};

type SerializerConfigT = {
createModuleIdFactory: () => (path: string) => number,
createModuleIdFactory: () => (path: string) => number | string,
customSerializer: ?(
entryPoint: string,
preModules: $ReadOnlyArray<Module<>>,
Expand Down
2 changes: 1 addition & 1 deletion packages/metro-config/types/configTypes.d.ts
Expand Up @@ -115,7 +115,7 @@ export interface ResolverConfigT {
}

export interface SerializerConfigT {
createModuleIdFactory: () => (path: string) => number;
createModuleIdFactory: () => (path: string) => number | string;
customSerializer:
| ((
entryPoint: string,
Expand Down
113 changes: 33 additions & 80 deletions packages/metro-runtime/src/polyfills/require.js
Expand Up @@ -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,
Expand All @@ -67,15 +67,11 @@ type ModuleDefinition = {
publicModule: Module,
verboseName?: string,
};
type ModuleList = {
[number]: ?ModuleDefinition,
__proto__: null,
...
};
type ModuleList = Map<ModuleID, ModuleDefinition>;
export type RequireFn = (id: ModuleID | VerboseModuleNameForDev) => Exports;
export type DefineFn = (
factory: FactoryFn,
moduleId: number,
moduleId: ModuleID,
dependencyMap?: DependencyMap,
verboseName?: string,
inverseDependencies?: InverseDependencyMap,
Expand Down Expand Up @@ -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",
Expand All @@ -112,20 +108,15 @@ function clear(): ModuleList {
}

if (__DEV__) {
var verboseNamesToModuleIds: {
[key: string]: number,
__proto__: null,
...
} = Object.create(null);
var initializingModuleIds: Array<number> = [];
var initializingModuleIds: Array<ModuleID> = [];
}

function define(
factory: FactoryFn,
moduleId: number,
moduleId: ModuleID,
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.
Expand Down Expand Up @@ -153,7 +144,7 @@ function define(
publicModule: {exports: {}},
};

modules[moduleId] = mod;
modules.set(moduleId, mod);

if (__DEV__) {
// HMR
Expand All @@ -165,37 +156,18 @@ function define(
const verboseName: string | void = arguments[3];
if (verboseName) {
mod.verboseName = verboseName;
verboseNamesToModuleIds[verboseName] = moduleId;
}
}
}

function metroRequire(moduleId: ModuleID | VerboseModuleNameForDev): Exports {
if (__DEV__ && typeof moduleId === 'string') {
const verboseName = moduleId;
moduleId = verboseNamesToModuleIds[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!',
);
}
}

//$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) =>
modules[id] ? modules[id].verboseName : '[unknown]',
.map((id: ModuleID) =>
modules.has(id) ? modules.get(id).verboseName : '[unknown]',
);

if (shouldPrintRequireCycle(cycle)) {
Expand All @@ -209,11 +181,11 @@ function metroRequire(moduleId: ModuleID | VerboseModuleNameForDev): Exports {
}
}

const module = modules[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
Expand All @@ -235,49 +207,30 @@ function shouldPrintRequireCycle(modules: $ReadOnlyArray<?string>): boolean {
function metroImportDefault(
moduleId: ModuleID | VerboseModuleNameForDev,
): any | Exports {
if (__DEV__ && typeof moduleId === 'string') {
const verboseName = moduleId;
moduleId = verboseNamesToModuleIds[verboseName];
}

//$FlowFixMe: at this point we know that moduleId is a number
const moduleIdReallyIsNumber: number = moduleId;

if (
modules[moduleIdReallyIsNumber] &&
modules[moduleIdReallyIsNumber].importedDefault !== EMPTY
modules.has(moduleId) &&
modules.get(moduleId).importedDefault !== EMPTY
) {
return modules[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[id] is null
return (modules[moduleIdReallyIsNumber].importedDefault = importedDefault);
// $FlowFixMe The metroRequire call above will throw if modules.get(id) is null
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];
}

//$FlowFixMe: at this point we know that moduleId is a number
const moduleIdReallyIsNumber: number = moduleId;

if (
modules[moduleIdReallyIsNumber] &&
modules[moduleIdReallyIsNumber].importedAll !== EMPTY
) {
return modules[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) {
Expand All @@ -297,8 +250,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(moduleId).importedAll = importedAll);
}
metroRequire.importAll = metroImportAll;

Expand Down Expand Up @@ -394,7 +347,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);
}
});
Expand All @@ -410,7 +363,7 @@ function loadModuleImplementation(
const definer = moduleDefinersBySegmentID[segmentId];
if (definer != null) {
definer(moduleId);
module = modules[moduleId];
module = modules.get(moduleId);
definingSegmentByModuleID.delete(moduleId);
}
}
Expand All @@ -419,7 +372,7 @@ function loadModuleImplementation(
if (!module && nativeRequire) {
const {segmentId, localId} = unpackModuleId(moduleId);
nativeRequire(localId, segmentId);
module = modules[moduleId];
module = modules.get(moduleId);
}

if (!module) {
Expand Down Expand Up @@ -563,7 +516,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.
Expand Down Expand Up @@ -606,7 +559,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 [];
Expand Down Expand Up @@ -677,7 +630,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.');
}
Expand Down Expand Up @@ -734,7 +687,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.');
}
Expand Down Expand Up @@ -810,7 +763,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.');
}
Expand Down
2 changes: 1 addition & 1 deletion packages/metro/src/DeltaBundler/Serializers/hmrJSBundle.js
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions packages/metro/src/HmrServer.js
Expand Up @@ -69,12 +69,12 @@ function send(sendFns: Array<(string) => void>, message: HmrMessage): void {
class HmrServer<TClient: Client> {
_config: ConfigT;
_bundler: IncrementalBundler;
_createModuleId: (path: string) => number;
_createModuleId: (path: string) => number | string;
_clientGroups: Map<RevisionId, ClientGroup>;

constructor(
bundler: IncrementalBundler,
createModuleId: (path: string) => number,
createModuleId: (path: string) => number | string,
config: ConfigT,
) {
this._config = config;
Expand Down
2 changes: 1 addition & 1 deletion packages/metro/src/Server.js
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion packages/metro/src/lib/createModuleIdFactory.js
Expand Up @@ -11,7 +11,7 @@

'use strict';

function createModuleIdFactory(): (path: string) => number {
function createModuleIdFactory(): (path: string) => number | string {
const fileToIdMap: Map<string, number> = new Map();
let nextId = 0;
return (path: string) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/metro/src/shared/types.flow.js
Expand Up @@ -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,
};

Expand Down Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions packages/metro/types/shared/types.d.ts
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand Down