mirror of https://github.com/status-im/metro.git
Expose createModuleIdFactory as bundler option
Summary: **Summary** `createModuleIdFactory` is already used in `metro` internally, but is currently always a fixed function. This enables `metro.runBuild()` to be run with a custom module ID factory. One use case: building a base bundle, on top of which other application-specific bundles could reference modules in the base. The application-specific IDs need to not conflict with those in the base bundle, and all references to modules in the base must resolve to the correct ID in the base bundle. A custom ID factory can make all this possible. **Test plan** <!-- Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI. --> Using `metro.runBuild(...)` with these changes, I was able to substitute in a custom ID factory ```javascript const fs = require('fs') const metro = require('metro') const baseManifestFileContents = JSON.parse(fs.readFileSync('./baseManifest.json')) // baseManifest looks like: // { "modules": { ... // "react/index.js": { "id": 12 }, // "react/cjs/react.production.min.js": { "id": 13 }, ... } } const opts = { dev: false, entry: 'index.js', out: './out.bundle', platform: 'ios', projectRoots: ['.', 'node_modules'], config: { createModuleIdFactory: createModuleIdFactory(baseManifestFileContents) } } metro.runBuild(opts) // Creates a sample custom ID factory function createModuleIdFactory(manifestFileContents) { return function createModuleIdFactory() { const fileToIdMap = new Map() let nextId = manifestFileContents ? getNextIdAfterBaseManifest(manifestFileContents) : 0 return path => { const sourcePath = path .replace(process.cwd() + '/node_modules/', '') .replace(process.cwd(), '.') // If module is in the base manifest, return its ID if (manifestFileContents && manifestFileContents.modules[sourcePath]) { return manifestFileContents.modules[sourcePath].id } // Otherwise, get it from the map or create a new ID if (!fileToIdMap.has(path)) { fileToIdMap.set(path, nextId) nextId += 1 } return fileToIdMap.get(path) } } function getNextIdAfterBaseManifest(manifestFileContents) { return Object.keys(manifestFileContents.modules).reduce((id, key) => { if (manifestFileContents.modules[key].id > id) { return manifestFileContents.modules[key].id } return id }, 0) + 1 } } ``` With the sample module ID factory above, the output looks like the following, where defined module IDs start at a higher number to avoid the base module IDs, but may depend on modules in the base bundle (lower numbers). ```javascript ... __d(function(r,o,t,i,n){t.exports=r.ErrorUtils},551,[]); __d(function(n,t,o,r,u){'use strict';var e,c=t(u[0]);e=c.now?function(){return c.now()}:function(){return Date.now()},o.exports=e},552,[553]); ... __d(function(e,t,r,s,l){'use strict'; ...},564,[18,565,27]); ... ``` Closes https://github.com/facebook/metro/pull/100 Reviewed By: mjesun Differential Revision: D6508351 Pulled By: rafeca fbshipit-source-id: f2cfe5c373a6c83c8ae6c526435538633a7c9c2a
This commit is contained in:
parent
53290f5b9c
commit
f347e4ff47
|
@ -139,6 +139,11 @@ export type ConfigT = {
|
||||||
* contain the absolute path of each module.
|
* contain the absolute path of each module.
|
||||||
*/
|
*/
|
||||||
getModulesRunBeforeMainModule: (entryFilePath: string) => Array<string>,
|
getModulesRunBeforeMainModule: (entryFilePath: string) => Array<string>,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An optional custom module ID factory creator used by the bundler.
|
||||||
|
*/
|
||||||
|
createModuleIdFactory?: () => (path: string) => number,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT = ({
|
const DEFAULT = ({
|
||||||
|
|
|
@ -108,7 +108,7 @@ class DeltaTransformer extends EventEmitter {
|
||||||
// different bundles to share the same ids (on HMR, where we need to patch
|
// different bundles to share the same ids (on HMR, where we need to patch
|
||||||
// the correct module).
|
// the correct module).
|
||||||
this._getModuleId = this._bundleOptions.isolateModuleIDs
|
this._getModuleId = this._bundleOptions.isolateModuleIDs
|
||||||
? createModuleIdFactory()
|
? (bundleOptions.createModuleIdFactory || createModuleIdFactory)()
|
||||||
: globalCreateModuleId;
|
: globalCreateModuleId;
|
||||||
|
|
||||||
this._deltaCalculator.on('change', this._onFileChange);
|
this._deltaCalculator.on('change', this._onFileChange);
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* @emails oncall+js_foundation
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
jest
|
||||||
|
.mock('fs')
|
||||||
|
.mock('assert')
|
||||||
|
.mock('progress')
|
||||||
|
.mock('../DeltaCalculator')
|
||||||
|
.mock('../../JSTransformer')
|
||||||
|
.mock('/root/to/something.js', () => ({}), {virtual: true})
|
||||||
|
.mock('/path/to/transformer.js', () => ({}), {virtual: true});
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const Bundler = require('../../Bundler');
|
||||||
|
const Resolver = require('../../Resolver');
|
||||||
|
const DeltaTransformer = require('../DeltaTransformer');
|
||||||
|
|
||||||
|
const defaults = require('../../defaults');
|
||||||
|
|
||||||
|
const bundlerOptions = {
|
||||||
|
allowBundleUpdates: false,
|
||||||
|
assetExts: defaults.assetExts,
|
||||||
|
cacheVersion: 'smth',
|
||||||
|
enableBabelRCLookup: true,
|
||||||
|
extraNodeModules: {},
|
||||||
|
platforms: defaults.platforms,
|
||||||
|
resetCache: false,
|
||||||
|
sourceExts: defaults.sourceExts,
|
||||||
|
transformModulePath: '/path/to/transformer.js',
|
||||||
|
watch: false,
|
||||||
|
projectRoots: ['/root'],
|
||||||
|
assetServer: {
|
||||||
|
getAssetData: jest.fn(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('DeltaTransformer', () => {
|
||||||
|
let bundler;
|
||||||
|
beforeEach(() => {
|
||||||
|
Resolver.load = jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementation(opts => Promise.resolve(new Resolver(opts)));
|
||||||
|
|
||||||
|
fs.__setMockFilesystem({
|
||||||
|
path: {to: {'transformer.js': ''}},
|
||||||
|
root: {to: {'something.js': ''}},
|
||||||
|
});
|
||||||
|
|
||||||
|
fs.statSync.mockImplementation(function() {
|
||||||
|
return {
|
||||||
|
isDirectory: () => true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
bundler = new Bundler(bundlerOptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow setting a custom module ID factory', async () => {
|
||||||
|
const bundlerOptions = {
|
||||||
|
isolateModuleIDs: true,
|
||||||
|
createModuleIdFactory: createPlus10000ModuleIdFactory,
|
||||||
|
};
|
||||||
|
|
||||||
|
const deltaTransformer = await DeltaTransformer.create(
|
||||||
|
bundler,
|
||||||
|
{},
|
||||||
|
bundlerOptions,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(deltaTransformer._getModuleId('test/path')).toBe(10000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function createPlus10000ModuleIdFactory(): (path: string) => number {
|
||||||
|
const fileToIdMap: Map<string, number> = new Map();
|
||||||
|
let nextId = 10000;
|
||||||
|
return (path: string) => {
|
||||||
|
let id = fileToIdMap.get(path);
|
||||||
|
if (typeof id !== 'number') {
|
||||||
|
id = nextId++;
|
||||||
|
fileToIdMap.set(path, id);
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
}
|
|
@ -72,6 +72,7 @@ class Server {
|
||||||
assetExts: Array<string>,
|
assetExts: Array<string>,
|
||||||
blacklistRE: void | RegExp,
|
blacklistRE: void | RegExp,
|
||||||
cacheVersion: string,
|
cacheVersion: string,
|
||||||
|
createModuleIdFactory?: () => (path: string) => number,
|
||||||
enableBabelRCLookup: boolean,
|
enableBabelRCLookup: boolean,
|
||||||
extraNodeModules: {},
|
extraNodeModules: {},
|
||||||
getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>,
|
getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>,
|
||||||
|
@ -121,6 +122,7 @@ class Server {
|
||||||
assetRegistryPath: options.assetRegistryPath,
|
assetRegistryPath: options.assetRegistryPath,
|
||||||
blacklistRE: options.blacklistRE,
|
blacklistRE: options.blacklistRE,
|
||||||
cacheVersion: options.cacheVersion || '1.0',
|
cacheVersion: options.cacheVersion || '1.0',
|
||||||
|
createModuleIdFactory: options.createModuleIdFactory,
|
||||||
enableBabelRCLookup:
|
enableBabelRCLookup:
|
||||||
options.enableBabelRCLookup != null
|
options.enableBabelRCLookup != null
|
||||||
? options.enableBabelRCLookup
|
? options.enableBabelRCLookup
|
||||||
|
|
|
@ -94,6 +94,7 @@ async function runMetro({
|
||||||
assetExts: normalizedConfig.assetTransforms ? [] : assetExts,
|
assetExts: normalizedConfig.assetTransforms ? [] : assetExts,
|
||||||
assetRegistryPath: normalizedConfig.assetRegistryPath,
|
assetRegistryPath: normalizedConfig.assetRegistryPath,
|
||||||
blacklistRE: normalizedConfig.getBlacklistRE(),
|
blacklistRE: normalizedConfig.getBlacklistRE(),
|
||||||
|
createModuleIdFactory: normalizedConfig.createModuleIdFactory,
|
||||||
extraNodeModules: normalizedConfig.extraNodeModules,
|
extraNodeModules: normalizedConfig.extraNodeModules,
|
||||||
getPolyfills: normalizedConfig.getPolyfills,
|
getPolyfills: normalizedConfig.getPolyfills,
|
||||||
getModulesRunBeforeMainModule:
|
getModulesRunBeforeMainModule:
|
||||||
|
@ -239,6 +240,9 @@ exports.runBuild = async (options: RunBuildOptions) => {
|
||||||
minify: options.optimize || false,
|
minify: options.optimize || false,
|
||||||
platform: options.platform || `web`,
|
platform: options.platform || `web`,
|
||||||
sourceMapUrl: options.sourceMapUrl,
|
sourceMapUrl: options.sourceMapUrl,
|
||||||
|
createModuleIdFactory: options.config
|
||||||
|
? options.config.createModuleIdFactory
|
||||||
|
: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const metroBundle = await MetroBundler.build(metroServer, requestOptions);
|
const metroBundle = await MetroBundler.build(metroServer, requestOptions);
|
||||||
|
|
|
@ -46,6 +46,7 @@ export type BundleOptions = {
|
||||||
+runModule: boolean,
|
+runModule: boolean,
|
||||||
sourceMapUrl: ?string,
|
sourceMapUrl: ?string,
|
||||||
unbundle: boolean,
|
unbundle: boolean,
|
||||||
|
createModuleIdFactory?: () => (path: string) => number,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ModuleGroups = {|
|
export type ModuleGroups = {|
|
||||||
|
@ -69,6 +70,7 @@ export type Options = {|
|
||||||
+assetRegistryPath: string,
|
+assetRegistryPath: string,
|
||||||
blacklistRE?: RegExp,
|
blacklistRE?: RegExp,
|
||||||
cacheVersion?: string,
|
cacheVersion?: string,
|
||||||
|
createModuleIdFactory?: () => (path: string) => number,
|
||||||
enableBabelRCLookup?: boolean,
|
enableBabelRCLookup?: boolean,
|
||||||
extraNodeModules?: {},
|
extraNodeModules?: {},
|
||||||
getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>,
|
getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>,
|
||||||
|
@ -113,4 +115,5 @@ export type RequestOptions = {|
|
||||||
dev?: boolean,
|
dev?: boolean,
|
||||||
minify: boolean,
|
minify: boolean,
|
||||||
platform: string,
|
platform: string,
|
||||||
|
createModuleIdFactory?: () => (path: string) => number,
|
||||||
|};
|
|};
|
||||||
|
|
Loading…
Reference in New Issue