metro-bundler: ModuleGraph: add support for require.async() when collecting dependencies

Summary: This is the last step remaining before enabling the `import()` syntax to use `require.async()` (for now that function is just doing a simple `require()` in behind).

Reviewed By: davidaurelio

Differential Revision: D6147030

fbshipit-source-id: 5cd8ee6cc550816ae3cdea0b457dc2419c99e7a7
This commit is contained in:
Jean Lauliac 2017-10-26 02:13:35 -07:00 committed by Facebook Github Bot
parent d74685fd1d
commit d01f514fa0
2 changed files with 41 additions and 11 deletions

View File

@ -37,6 +37,20 @@ describe('dependency collection from ASTs:', () => {
]);
});
it('collects asynchronous dependencies', () => {
const ast = astFromCode(`
const a = require('b/lib/a');
if (!something) {
require.async("some/async/module").then(foo => {});
}
`);
expect(collectDependencies(ast).dependencies).toEqual([
'b/lib/a',
'some/async/module',
]);
});
it('supports template literals as arguments', () => {
const ast = astFromCode('require(`left-pad`)');
@ -101,11 +115,12 @@ describe('Dependency collection from optimized ASTs:', () => {
ast = astFromCode(`
const a = require(${dependencyMapName}[0], 'b/lib/a');
exports.do = () => require(${dependencyMapName}[1], "do");
require.async(${dependencyMapName}[2], 'some/async/module').then(foo => {});
if (!something) {
require(${dependencyMapName}[2], "setup/something");
require(${dependencyMapName}[3], "setup/something");
}
`);
names = ['b/lib/a', 'do', 'setup/something'];
names = ['b/lib/a', 'do', 'some/async/module', 'setup/something'];
});
it('passes the `dependencyMapName` through', () => {
@ -130,8 +145,9 @@ describe('Dependency collection from optimized ASTs:', () => {
comparableCode(`
const a = require(${dependencyMapName}[0]);
exports.do = () => require(${dependencyMapName}[1]);
require.async(${dependencyMapName}[2]).then(foo => {});
if (!something) {
require(${dependencyMapName}[2]);
require(${dependencyMapName}[3]);
}
`),
);

View File

@ -132,6 +132,16 @@ function createMapLookup(dependencyMapIdentifier, propertyIdentifier) {
function collectDependencies(ast, replacement, dependencyMapIdentifier) {
const visited = new WeakSet();
const traversalState = {dependencyMapIdentifier};
function processRequireCall(node, state, isAsync) {
const arg = replacement.getRequireCallArg(node);
const index = replacement.getIndex(arg);
node.arguments = replacement.makeArgs(
types.numericLiteral(index),
arg,
state.dependencyMapIdentifier,
);
visited.add(node);
}
traverse(
ast,
{
@ -148,14 +158,9 @@ function collectDependencies(ast, replacement, dependencyMapIdentifier) {
return;
}
if (isRequireCall(node.callee)) {
const arg = replacement.getRequireCallArg(node);
const index = replacement.getIndex(arg);
node.arguments = replacement.makeArgs(
types.numericLiteral(index),
arg,
state.dependencyMapIdentifier,
);
visited.add(node);
processRequireCall(node, state, false);
} else if (isAsyncRequireCall(node.callee)) {
processRequireCall(node, state, true);
}
},
},
@ -180,6 +185,15 @@ function isRequireCall(callee) {
return callee.type === 'Identifier' && callee.name === 'require';
}
function isAsyncRequireCall(callee) {
return (
callee.type === 'MemberExpression' &&
!callee.computed &&
callee.property.name === 'async' &&
isRequireCall(callee.object)
);
}
class InvalidRequireCallError extends Error {
constructor(message) {
super(message);