metro: collectDependencies: do not hardwire dependency on asyncRequire

Reviewed By: rafeca

Differential Revision: D6773002

fbshipit-source-id: a55b63f6bf20f467496ffe1d18c663b87f5d7ba6
This commit is contained in:
Jean Lauliac 2018-01-29 08:55:05 -08:00 committed by Facebook Github Bot
parent 47519ec56b
commit 4b7371d732
17 changed files with 99 additions and 18 deletions

View File

@ -81,6 +81,7 @@ export type PostProcessBundleSourcemap = ({
export type Options = {|
+assetExts: Array<string>,
+assetRegistryPath: string,
+asyncRequireModulePath: string,
+blacklistRE?: RegExp,
+cacheVersion: string,
+dynamicDepsInPackages: DynamicRequiresBehavior,
@ -120,6 +121,7 @@ class Bundler {
opts.projectRoots.forEach(verifyRootExists);
this._transformer = new Transformer({
asyncRequireModulePath: opts.asyncRequireModulePath,
maxWorkers: opts.maxWorkers,
reporters: {
stdoutChunk: chunk =>
@ -139,6 +141,7 @@ class Bundler {
extraNodeModules: opts.extraNodeModules,
getPolyfills: opts.getPolyfills,
getTransformCacheKey: getTransformCacheKeyFn({
asyncRequireModulePath: opts.asyncRequireModulePath,
cacheVersion: opts.cacheVersion,
dynamicDepsInPackages: opts.dynamicDepsInPackages,
projectRoots: opts.projectRoots,

View File

@ -27,6 +27,7 @@ describe('Transformer', function() {
const transformModulePath = __filename;
const opts = {
asyncRequireModulePath: 'asyncRequire',
maxWorkers: 4,
reporters: {},
transformModulePath,
@ -82,6 +83,7 @@ describe('Transformer', function() {
transformOptions,
[],
'',
'asyncRequire',
'reject',
);
});

View File

@ -38,16 +38,19 @@ type Reporters = {
module.exports = class Transformer {
_worker: WorkerInterface;
_transformModulePath: string;
_asyncRequireModulePath: string;
_dynamicDepsInPackages: DynamicRequiresBehavior;
constructor(options: {|
+maxWorkers: number,
+reporters: Reporters,
+transformModulePath: string,
+asyncRequireModulePath: string,
+dynamicDepsInPackages: DynamicRequiresBehavior,
+workerPath: ?string,
|}) {
this._transformModulePath = options.transformModulePath;
this._asyncRequireModulePath = options.asyncRequireModulePath;
this._dynamicDepsInPackages = options.dynamicDepsInPackages;
const {workerPath = require.resolve('./worker')} = options;
@ -108,6 +111,7 @@ module.exports = class Transformer {
options,
assetExts,
assetRegistryPath,
this._asyncRequireModulePath,
this._dynamicDepsInPackages,
);

View File

@ -34,6 +34,7 @@ describe('code transformation worker:', () => {
},
[],
'',
'asyncRequire',
'reject',
);
@ -61,6 +62,7 @@ describe('code transformation worker:', () => {
},
[],
'',
'asyncRequire',
'reject',
);
@ -93,6 +95,7 @@ describe('code transformation worker:', () => {
},
[],
'',
'asyncRequire',
'reject',
);
@ -133,6 +136,7 @@ describe('code transformation worker:', () => {
},
[],
'',
'asyncRequire',
'reject',
);
throw new Error('should not reach this');
@ -157,6 +161,7 @@ describe('code transformation worker:', () => {
},
[],
'',
'asyncRequire',
'throwAtRuntime',
);
});

View File

@ -98,6 +98,7 @@ function postTransform(
isScript: boolean,
options: Options,
transformFileStartLogEntry: LogEntry,
asyncRequireModulePath: string,
dynamicDepsInPackages: DynamicRequiresBehavior,
receivedAst: ?Ast,
): Data {
@ -128,6 +129,7 @@ function postTransform(
let dependencyMapName;
try {
const opts = {
asyncRequireModulePath,
dynamicRequires: getDynamicDepsBehavior(
dynamicDepsInPackages,
filename,
@ -197,6 +199,7 @@ function transformCode(
options: Options,
assetExts: $ReadOnlyArray<string>,
assetRegistryPath: string,
asyncRequireModulePath: string,
dynamicDepsInPackages: DynamicRequiresBehavior,
): Data | Promise<Data> {
const isJson = filename.endsWith('.json');
@ -243,6 +246,7 @@ function transformCode(
isScript,
options,
transformFileStartLogEntry,
asyncRequireModulePath,
dynamicDepsInPackages,
];

View File

@ -21,7 +21,10 @@ const {codeFromAst, comparableCode} = require('../../test-helpers');
const {any} = expect;
const {InvalidRequireCallError} = collectDependencies;
const opts = {dynamicRequires: 'reject'};
const opts = {
asyncRequireModulePath: 'asyncRequire',
dynamicRequires: 'reject',
};
it('collects unique dependency identifiers and transforms the AST', () => {
const ast = astFromCode(`
@ -193,7 +196,10 @@ describe('Evaluating static arguments', () => {
it('throws at runtime when requiring non-strings with special option', () => {
const ast = astFromCode('require(1)');
const opts = {dynamicRequires: 'throwAtRuntime'};
const opts = {
asyncRequireModulePath: 'asyncRequire',
dynamicRequires: 'throwAtRuntime',
};
const {dependencies} = collectDependencies(ast, opts);
expect(dependencies).toEqual([]);
expect(codeFromAst(ast)).toEqual(

View File

@ -27,6 +27,7 @@ const {objectContaining} = jasmine;
describe('optimizing JS modules', () => {
const filename = 'arbitrary/file.js';
const sourceExts = new Set(['js', 'json']);
const asyncRequireModulePath = 'asyncRequire';
const optimizationOptions = {
dev: false,
platform: 'android',
@ -43,7 +44,7 @@ describe('optimizing JS modules', () => {
let transformResult;
beforeAll(() => {
const trOpts = {filename, sourceExts, transformer};
const trOpts = {asyncRequireModulePath, filename, sourceExts, transformer};
const result = transformModule(originalCode, trOpts);
invariant(result.type === 'code', 'result must be code');
transformResult = new Buffer(

View File

@ -33,6 +33,7 @@ jest.mock('image-size', () => buffer => {
describe('transforming JS modules:', () => {
const filename = 'arbitrary.js';
const sourceExts = new Set(['js', 'json']);
const asyncRequireModulePath = 'asyncRequire';
let transformer;
@ -47,6 +48,7 @@ describe('transforming JS modules:', () => {
const {bodyAst, sourceCode, transformedCode} = createTestData();
const options = (variants?: TransformVariants) => ({
asyncRequireModulePath,
filename,
sourceExts,
transformer,

View File

@ -21,12 +21,16 @@ const prettyPrint = require('babel-generator').default;
import type {TransformResultDependency} from '../types.flow';
export type DynamicRequiresBehavior = 'throwAtRuntime' | 'reject';
type Options = {|+dynamicRequires: DynamicRequiresBehavior|};
type Options = {|
+dynamicRequires: DynamicRequiresBehavior,
+asyncRequireModulePath: string,
|};
type Context = {
nameToIndex: Map<string, number>,
dependencies: Array<{|+name: string, isAsync: boolean|}>,
+asyncRequireModulePath: string,
+dynamicRequires: DynamicRequiresBehavior,
dependencies: Array<{|+name: string, isAsync: boolean|}>,
nameToIndex: Map<string, number>,
};
type CollectedDependencies = {|
@ -47,8 +51,13 @@ function collectDependencies(
options: Options,
): CollectedDependencies {
const visited = new WeakSet();
const {dynamicRequires} = options;
const context = {nameToIndex: new Map(), dependencies: [], dynamicRequires};
const {asyncRequireModulePath, dynamicRequires} = options;
const context = {
asyncRequireModulePath,
dynamicRequires,
dependencies: [],
nameToIndex: new Map(),
};
const visitor = {
Program(path, state) {
state.dependencyMapIdentifier = path.scope.generateUidIdentifier(
@ -90,9 +99,10 @@ function processImportCall(context, path, node, depMapIdent) {
}
const index = assignDependencyIndex(context, name, 'import');
const mapLookup = createDepMapLookup(depMapIdent, index);
const {asyncRequireModulePath} = context;
const newImport = makeAsyncRequire({
MODULE_ID: mapLookup,
ASYNC_REQUIRE_PATH: {type: 'StringLiteral', value: 'asyncRequire'},
ASYNC_REQUIRE_PATH: {type: 'StringLiteral', value: asyncRequireModulePath},
});
path.replaceWith(newImport);
}

View File

@ -39,6 +39,7 @@ import type {
import type {Ast} from 'babel-core';
export type TransformOptions<ExtraOptions> = {|
+asyncRequireModulePath: string,
filename: string,
hasteImpl?: HasteImpl,
polyfill?: boolean,
@ -76,7 +77,7 @@ function transformModule(
return {type: 'unknown'};
}
const code = content.toString('utf8');
const sourceCode = content.toString('utf8');
const {filename, transformer, polyfill, variants = defaultVariants} = options;
const transformed: {[key: string]: TransformResult} = {};
@ -85,15 +86,22 @@ function transformModule(
filename,
localPath: filename,
options: {...defaultTransformOptions, ...variants[variantName]},
src: code,
src: sourceCode,
});
invariant(ast != null, 'ast required from the transform results');
transformed[variantName] = makeResult(ast, filename, code, polyfill);
const {asyncRequireModulePath} = options;
transformed[variantName] = makeResult({
ast,
asyncRequireModulePath,
filename,
isPolyfill: polyfill || false,
sourceCode,
});
}
let hasteID = null;
if (filename.indexOf(NODE_MODULES) === -1 && !polyfill) {
hasteID = docblock.parse(docblock.extract(code)).providesModule;
hasteID = docblock.parse(docblock.extract(sourceCode)).providesModule;
if (options.hasteImpl) {
if (options.hasteImpl.enforceHasteNameMatches) {
options.hasteImpl.enforceHasteNameMatches(filename, hasteID);
@ -182,17 +190,25 @@ function getAssetSize(
return {width, height};
}
function makeResult(ast: Ast, filename, sourceCode, isPolyfill = false) {
function makeResult(options: {|
+ast: Ast,
+asyncRequireModulePath: string,
+filename: string,
+isPolyfill: boolean,
+sourceCode: string,
|}) {
let dependencies, dependencyMapName, file;
if (isPolyfill) {
const {ast} = options;
if (options.isPolyfill) {
dependencies = [];
file = JsFileWrapping.wrapPolyfill(ast);
} else {
const opts = {dynamicRequires: 'reject'};
const {asyncRequireModulePath} = options;
const opts = {asyncRequireModulePath, dynamicRequires: 'reject'};
({dependencies, dependencyMapName} = collectDependencies(ast, opts));
file = JsFileWrapping.wrapModule(ast, dependencyMapName);
}
const {filename, sourceCode} = options;
const gen = generate(file, filename, sourceCode, false);
return {
code: gen.code,

View File

@ -168,6 +168,9 @@ class Server {
bundlerOpts.globalTransformCache = options.globalTransformCache;
bundlerOpts.watch = this._opts.watch;
bundlerOpts.reporter = reporter;
bundlerOpts.asyncRequireModulePath =
options.asyncRequireModulePath ||
'metro/src/lib/bundle-modules/asyncRequire';
this._bundler = new Bundler(bundlerOpts);
// changes to the haste map can affect resolution of files in the bundle

View File

@ -169,6 +169,7 @@ function toServerOptions(options: Options): ServerOptions {
assetTransforms: options.assetTransforms,
assetExts: options.assetExts,
assetRegistryPath: options.assetRegistryPath,
asyncRequireModulePath: options.asyncRequireModulePath,
blacklistRE: options.blacklistRE,
cacheVersion: options.cacheVersion,
dynamicDepsInPackages: options.dynamicDepsInPackages,

View File

@ -1,3 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`getTransformCacheKeyFn Should return always the same key for the same params 1`] = `"c4732dae08f17ef9c571cb8bcc24d249166862c6"`;
exports[`getTransformCacheKeyFn Should return always the same key for the same params 1`] = `"1b825aa92d266cc01c946596e777463bd7e68c82"`;

View File

@ -20,6 +20,7 @@ describe('getTransformCacheKeyFn', () => {
it('Should return always the same key for the same params', async () => {
expect(
getTransformCacheKeyFn({
asyncRequireModulePath: 'beep',
cacheVersion: '1.0',
dynamicDepsInPackages: 'arbitrary',
projectRoots: [__dirname],
@ -33,6 +34,7 @@ describe('getTransformCacheKeyFn', () => {
it('Should return a different key when the params change', async () => {
const baseParams = {
asyncRequireModulePath: 'beep',
cacheVersion: '1.0',
dynamicDepsInPackages: 'arbitrary',
projectRoots: [__dirname],

View File

@ -0,0 +1,19 @@
/**
* Copyright (c) 2016-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.
*
* @format
* @flow
*/
'use strict';
// eslint-disable-next-line flow-no-fixme
const dynamicRequire = (require: $FlowFixMe);
module.exports = function(moduleID: mixed): Promise<mixed> {
return Promise.resolve(dynamicRequire(moduleID));
};

View File

@ -23,6 +23,7 @@ const VERSION = require('../../package.json').version;
* passed transform options.
*/
function getTransformCacheKeyFn(opts: {|
+asyncRequireModulePath: string,
+cacheVersion: string,
+dynamicDepsInPackages: string,
+projectRoots: $ReadOnlyArray<string>,
@ -46,6 +47,7 @@ function getTransformCacheKeyFn(opts: {|
.split(path.sep)
.join('-'),
transformModuleHash,
opts.asyncRequireModulePath,
opts.dynamicDepsInPackages,
];

View File

@ -71,6 +71,7 @@ export type Options = {|
// TODO: Remove this option below (T23793920)
assetTransforms?: boolean,
assetExts?: Array<string>,
+asyncRequireModulePath?: string,
+assetRegistryPath: string,
blacklistRE?: RegExp,
cacheVersion?: string,