mirror of https://github.com/status-im/metro.git
Make collectDependencies smarter when evaluating the require argument
Reviewed By: BYK Differential Revision: D6592104 fbshipit-source-id: d90a93c51cb04e38cc172eb8eda932f64fce0075
This commit is contained in:
parent
57cfa19518
commit
3b497585f1
|
@ -1,5 +1,7 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`throws on tagged template literals 1`] = `"Calls to require() expect exactly 1 string literal argument, but this was found: \`require(tag\`left-pad\`)\`."`;
|
exports[`Evaluating static arguments throws on tagged template literals 1`] = `"Calls to require() expect exactly 1 string literal argument, but this was found: \`require(tag\`left-pad\`)\`."`;
|
||||||
|
|
||||||
exports[`throws on template literals with interpolations 1`] = `"Calls to require() expect exactly 1 string literal argument, but this was found: \`require(\`left\${\\"-\\"}pad\`)\`."`;
|
exports[`Evaluating static arguments throws template literals with dyncamic interpolations 1`] = `"Calls to require() expect exactly 1 string literal argument, but this was found: \`require(\`left\${foo}pad\`)\`."`;
|
||||||
|
|
||||||
|
exports[`Evaluating static arguments throws when requiring non-strings 1`] = `"Calls to require() expect exactly 1 string literal argument, but this was found: \`require(1)\`."`;
|
||||||
|
|
|
@ -101,6 +101,7 @@ it('collects mixed dependencies as being sync; reverse order', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Evaluating static arguments', () => {
|
||||||
it('supports template literals as arguments', () => {
|
it('supports template literals as arguments', () => {
|
||||||
const ast = astFromCode('require(`left-pad`)');
|
const ast = astFromCode('require(`left-pad`)');
|
||||||
const {dependencies, dependencyMapName} = collectDependencies(ast);
|
const {dependencies, dependencyMapName} = collectDependencies(ast);
|
||||||
|
@ -110,8 +111,17 @@ it('supports template literals as arguments', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws on template literals with interpolations', () => {
|
it('supports template literals with static interpolations', () => {
|
||||||
const ast = astFromCode('require(`left${"-"}pad`)');
|
const ast = astFromCode('require(`left${"-"}pad`)');
|
||||||
|
const {dependencies, dependencyMapName} = collectDependencies(ast);
|
||||||
|
expect(dependencies).toEqual([{name: 'left-pad', isAsync: false}]);
|
||||||
|
expect(codeFromAst(ast)).toEqual(
|
||||||
|
comparableCode(`require(${dependencyMapName}[0], \`left\${"-"}pad\`);`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws template literals with dyncamic interpolations', () => {
|
||||||
|
const ast = astFromCode('let foo;require(`left${foo}pad`)');
|
||||||
try {
|
try {
|
||||||
collectDependencies(ast);
|
collectDependencies(ast);
|
||||||
throw new Error('should not reach');
|
throw new Error('should not reach');
|
||||||
|
@ -132,6 +142,49 @@ it('throws on tagged template literals', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('supports multiple static strings concatenated', () => {
|
||||||
|
const ast = astFromCode('require("foo_" + "bar")');
|
||||||
|
const {dependencies, dependencyMapName} = collectDependencies(ast);
|
||||||
|
expect(dependencies).toEqual([{name: 'foo_bar', isAsync: false}]);
|
||||||
|
expect(codeFromAst(ast)).toEqual(
|
||||||
|
comparableCode(`require(${dependencyMapName}[0], "foo_" + "bar");`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports concatenating strings and template literasl', () => {
|
||||||
|
const ast = astFromCode('require("foo_" + "bar" + `_baz`)');
|
||||||
|
const {dependencies, dependencyMapName} = collectDependencies(ast);
|
||||||
|
expect(dependencies).toEqual([{name: 'foo_bar_baz', isAsync: false}]);
|
||||||
|
expect(codeFromAst(ast)).toEqual(
|
||||||
|
comparableCode(
|
||||||
|
`require(${dependencyMapName}[0], "foo_" + "bar" + \`_baz\`);`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports using static variables in require statements', () => {
|
||||||
|
const ast = astFromCode('const myVar="my";require("foo_" + myVar)');
|
||||||
|
const {dependencies, dependencyMapName} = collectDependencies(ast);
|
||||||
|
expect(dependencies).toEqual([{name: 'foo_my', isAsync: false}]);
|
||||||
|
expect(codeFromAst(ast)).toEqual(
|
||||||
|
comparableCode(
|
||||||
|
`const myVar = \"my\"; require(${dependencyMapName}[0], "foo_" + myVar);`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws when requiring non-strings', () => {
|
||||||
|
const ast = astFromCode('require(1)');
|
||||||
|
try {
|
||||||
|
collectDependencies(ast);
|
||||||
|
throw new Error('should not reach');
|
||||||
|
} catch (error) {
|
||||||
|
expect(error).toBeInstanceOf(InvalidRequireCallError);
|
||||||
|
expect(error.message).toMatchSnapshot();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('exposes a string as `dependencyMapName` even without collecting dependencies', () => {
|
it('exposes a string as `dependencyMapName` even without collecting dependencies', () => {
|
||||||
const ast = astFromCode('');
|
const ast = astFromCode('');
|
||||||
expect(collectDependencies(ast).dependencyMapName).toEqual(any(String));
|
expect(collectDependencies(ast).dependencyMapName).toEqual(any(String));
|
||||||
|
|
|
@ -58,7 +58,7 @@ function collectDependencies(ast: Ast): CollectedDependencies {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isRequireCall(node.callee)) {
|
if (isRequireCall(node.callee)) {
|
||||||
const reqNode = processRequireCall(context, node, depMapIdent);
|
const reqNode = processRequireCall(context, path, node, depMapIdent);
|
||||||
visited.add(reqNode);
|
visited.add(reqNode);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -76,7 +76,7 @@ function isRequireCall(callee) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function processImportCall(context, path, node, depMapIdent) {
|
function processImportCall(context, path, node, depMapIdent) {
|
||||||
const [, name] = getModuleNameFromCallArgs('import', node);
|
const [, name] = getModuleNameFromCallArgs('import', node, path);
|
||||||
const index = assignDependencyIndex(context, name, 'import');
|
const index = assignDependencyIndex(context, name, 'import');
|
||||||
const mapLookup = createDepMapLookup(depMapIdent, index);
|
const mapLookup = createDepMapLookup(depMapIdent, index);
|
||||||
const newImport = makeAsyncRequire({
|
const newImport = makeAsyncRequire({
|
||||||
|
@ -86,11 +86,15 @@ function processImportCall(context, path, node, depMapIdent) {
|
||||||
path.replaceWith(newImport);
|
path.replaceWith(newImport);
|
||||||
}
|
}
|
||||||
|
|
||||||
function processRequireCall(context, node, depMapIdent) {
|
function processRequireCall(context, path, node, depMapIdent) {
|
||||||
const [nameLiteral, name] = getModuleNameFromCallArgs('require', node);
|
const [nameExpression, name] = getModuleNameFromCallArgs(
|
||||||
|
'require',
|
||||||
|
node,
|
||||||
|
path,
|
||||||
|
);
|
||||||
const index = assignDependencyIndex(context, name, 'require');
|
const index = assignDependencyIndex(context, name, 'require');
|
||||||
const mapLookup = createDepMapLookup(depMapIdent, index);
|
const mapLookup = createDepMapLookup(depMapIdent, index);
|
||||||
node.arguments = [mapLookup, nameLiteral];
|
node.arguments = [mapLookup, nameExpression];
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,21 +102,20 @@ function processRequireCall(context, node, depMapIdent) {
|
||||||
* Extract the module name from `require` arguments. We support template
|
* Extract the module name from `require` arguments. We support template
|
||||||
* literal, for example one could write `require(`foo`)`.
|
* literal, for example one could write `require(`foo`)`.
|
||||||
*/
|
*/
|
||||||
function getModuleNameFromCallArgs(type, node) {
|
function getModuleNameFromCallArgs(type, node, path) {
|
||||||
const args = node.arguments;
|
if (node.arguments.length !== 1) {
|
||||||
if (args.length !== 1) {
|
|
||||||
throw invalidRequireOf(type, node);
|
throw invalidRequireOf(type, node);
|
||||||
}
|
}
|
||||||
const nameLiteral = args[0];
|
|
||||||
if (nameLiteral.type === 'StringLiteral') {
|
const nameExpression = node.arguments[0];
|
||||||
return [nameLiteral, nameLiteral.value];
|
|
||||||
}
|
// Try to evaluate the first argument of the require() statement.
|
||||||
if (nameLiteral.type === 'TemplateLiteral') {
|
// If it can be statically evaluated, resolve it.
|
||||||
if (nameLiteral.quasis.length !== 1) {
|
const result = path.get('arguments.0').evaluate();
|
||||||
throw invalidRequireOf(type, node);
|
if (result.confident && typeof result.value === 'string') {
|
||||||
}
|
return [nameExpression, result.value];
|
||||||
return [nameLiteral, nameLiteral.quasis[0].value.cooked];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw invalidRequireOf(type, node);
|
throw invalidRequireOf(type, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue