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.
|
||||
*/
|
||||
getModulesRunBeforeMainModule: (entryFilePath: string) => Array<string>,
|
||||
|
||||
/**
|
||||
* An optional custom module ID factory creator used by the bundler.
|
||||
*/
|
||||
createModuleIdFactory?: () => (path: string) => number,
|
||||
};
|
||||
|
||||
const DEFAULT = ({
|
||||
|
|
|
@ -108,7 +108,7 @@ class DeltaTransformer extends EventEmitter {
|
|||
// different bundles to share the same ids (on HMR, where we need to patch
|
||||
// the correct module).
|
||||
this._getModuleId = this._bundleOptions.isolateModuleIDs
|
||||
? createModuleIdFactory()
|
||||
? (bundleOptions.createModuleIdFactory || createModuleIdFactory)()
|
||||
: globalCreateModuleId;
|
||||
|
||||
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>,
|
||||
blacklistRE: void | RegExp,
|
||||
cacheVersion: string,
|
||||
createModuleIdFactory?: () => (path: string) => number,
|
||||
enableBabelRCLookup: boolean,
|
||||
extraNodeModules: {},
|
||||
getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>,
|
||||
|
@ -121,6 +122,7 @@ class Server {
|
|||
assetRegistryPath: options.assetRegistryPath,
|
||||
blacklistRE: options.blacklistRE,
|
||||
cacheVersion: options.cacheVersion || '1.0',
|
||||
createModuleIdFactory: options.createModuleIdFactory,
|
||||
enableBabelRCLookup:
|
||||
options.enableBabelRCLookup != null
|
||||
? options.enableBabelRCLookup
|
||||
|
|
|
@ -94,6 +94,7 @@ async function runMetro({
|
|||
assetExts: normalizedConfig.assetTransforms ? [] : assetExts,
|
||||
assetRegistryPath: normalizedConfig.assetRegistryPath,
|
||||
blacklistRE: normalizedConfig.getBlacklistRE(),
|
||||
createModuleIdFactory: normalizedConfig.createModuleIdFactory,
|
||||
extraNodeModules: normalizedConfig.extraNodeModules,
|
||||
getPolyfills: normalizedConfig.getPolyfills,
|
||||
getModulesRunBeforeMainModule:
|
||||
|
@ -239,6 +240,9 @@ exports.runBuild = async (options: RunBuildOptions) => {
|
|||
minify: options.optimize || false,
|
||||
platform: options.platform || `web`,
|
||||
sourceMapUrl: options.sourceMapUrl,
|
||||
createModuleIdFactory: options.config
|
||||
? options.config.createModuleIdFactory
|
||||
: undefined,
|
||||
};
|
||||
|
||||
const metroBundle = await MetroBundler.build(metroServer, requestOptions);
|
||||
|
|
|
@ -46,6 +46,7 @@ export type BundleOptions = {
|
|||
+runModule: boolean,
|
||||
sourceMapUrl: ?string,
|
||||
unbundle: boolean,
|
||||
createModuleIdFactory?: () => (path: string) => number,
|
||||
};
|
||||
|
||||
export type ModuleGroups = {|
|
||||
|
@ -69,6 +70,7 @@ export type Options = {|
|
|||
+assetRegistryPath: string,
|
||||
blacklistRE?: RegExp,
|
||||
cacheVersion?: string,
|
||||
createModuleIdFactory?: () => (path: string) => number,
|
||||
enableBabelRCLookup?: boolean,
|
||||
extraNodeModules?: {},
|
||||
getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>,
|
||||
|
@ -113,4 +115,5 @@ export type RequestOptions = {|
|
|||
dev?: boolean,
|
||||
minify: boolean,
|
||||
platform: string,
|
||||
createModuleIdFactory?: () => (path: string) => number,
|
||||
|};
|
||||
|
|
Loading…
Reference in New Issue