Remove old extract-dependencies logic

Reviewed By: davidaurelio

Differential Revision: D6447749

fbshipit-source-id: 39c4960da65a9d8ab1e2615d48f5c2868a17f82b
This commit is contained in:
Rafael Oleza 2017-12-04 08:13:55 -08:00 committed by Facebook Github Bot
parent 03e735d232
commit b456f7b61a
3 changed files with 13 additions and 235 deletions

View File

@ -25,18 +25,18 @@ jest
platform: () => 'test',
}));
// This doesn't have state, and it's huge (Babel) so it's much faster to
// require it only once. The variable name is prefixed with "mock" as an escape-hatch
// for babel-plugin-jest-hoist.
let mockExtractDependencies;
jest.mock('../../JSTransformer/worker/extract-dependencies', () => {
if (!mockExtractDependencies) {
mockExtractDependencies = require.requireActual(
'../../JSTransformer/worker/extract-dependencies',
);
// Super-simple mock for extracting dependencies
const extractDependencies = function(sourceCode: string) {
const regexp = /require\s*\(\s*(['"])(.*?)\1\s*\)/g;
const deps = [];
let match;
while ((match = regexp.exec(sourceCode))) {
deps.push(match[2]);
}
return mockExtractDependencies;
});
return deps;
};
jest.mock('graceful-fs', () => require('fs'));
@ -110,14 +110,9 @@ describe('traverseDependencies', function() {
transformCache: require('TransformCaching').mocked(),
transformCode: (module, sourceCode, transformOptions) => {
return new Promise(resolve => {
let deps = {dependencies: [], dependencyOffsets: []};
const deps = {dependencies: []};
if (!module.path.endsWith('.json')) {
if (!mockExtractDependencies) {
mockExtractDependencies = require.requireActual(
'../../JSTransformer/worker/extract-dependencies',
);
}
deps = mockExtractDependencies(sourceCode);
deps.dependencies = extractDependencies(sourceCode);
}
resolve({...deps, code: sourceCode});
});

View File

@ -1,137 +0,0 @@
/**
* 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.
*
* @format
* @emails oncall+javascript_foundation
*/
'use strict';
const extractDependencies = require('../extract-dependencies');
describe('Dependency extraction:', () => {
it('can extract calls to require', () => {
const code = `require('foo/bar');
var React = require("React");
var A = React.createClass({
render: function() {
return require ( "Component" );
}
});
require
('more');`;
const {dependencies, dependencyOffsets} = extractDependencies(code);
expect(dependencies).toEqual(['foo/bar', 'React', 'Component', 'more']);
expect(dependencyOffsets).toEqual([8, 46, 147, 203]);
});
it('can extract calls to require.async', () => {
const code = `foo();
require.async('bar').then(() => {});`;
const {dependencies, dependencyOffsets} = extractDependencies(code);
expect(dependencies).toEqual(['bar']);
expect(dependencyOffsets).toEqual([27]);
});
it('does not extract require method calls', () => {
const code = `
require('a');
foo.require('b');
bar.
require ( 'c').require('d');require('e')`;
const {dependencies, dependencyOffsets} = extractDependencies(code);
expect(dependencies).toEqual(['a', 'e']);
expect(dependencyOffsets).toEqual([15, 98]);
});
it('does not extract require calls from strings', () => {
const code = `require('foo');
var React = '\\'require("React")';
var a = ' // require("yadda")';
var a = ' /* require("yadda") */';
var A = React.createClass({
render: function() {
return require ( "Component" );
}
});
" \\" require('more')";`;
const {dependencies, dependencyOffsets} = extractDependencies(code);
expect(dependencies).toEqual(['foo', 'Component']);
expect(dependencyOffsets).toEqual([8, 226]);
});
it('does not extract require calls in comments', () => {
const code = `require('foo')//require("not/this")
/* A comment here with a require('call') that should not be extracted */require('bar')
// ending comment without newline require("baz")`;
const {dependencies, dependencyOffsets} = extractDependencies(code);
expect(dependencies).toEqual(['foo', 'bar']);
expect(dependencyOffsets).toEqual([8, 122]);
});
it('deduplicates dependencies', () => {
const code = `require('foo');require( "foo" );
require("foo");`;
const {dependencies, dependencyOffsets} = extractDependencies(code);
expect(dependencies).toEqual(['foo']);
expect(dependencyOffsets).toEqual([8, 24, 47]);
});
it('does not extract calls to function with names that start with "require"', () => {
const code = "arbitraryrequire('foo');";
const {dependencies, dependencyOffsets} = extractDependencies(code);
expect(dependencies).toEqual([]);
expect(dependencyOffsets).toEqual([]);
});
it('throws on calls to require with non-static arguments', () => {
const code = "require('foo/' + bar)";
expect(() => extractDependencies(code)).toThrowError(
'require() must have a single string literal argument',
);
});
it('throws on calls to require with non-static arguments, nested deeper than one level inside of a try block', () => {
const code = "try { if (1) { require('foo/' + bar) } } catch (e) { }";
expect(() => extractDependencies(code)).toThrowError(
'require() must have a single string literal argument',
);
});
it('does not throw on calls to require with non-static arguments, nested directly inside of a try block', () => {
const code = "try { require('foo/' + bar) } catch (e) { }";
const {dependencies, dependencyOffsets} = extractDependencies(code);
expect(dependencies).toEqual([]);
expect(dependencyOffsets).toEqual([]);
});
it('does not get confused by previous states', () => {
// yes, this was a bug
const code = 'require("a");/* a comment */ var a = /[a]/.test(\'a\');';
const {dependencies, dependencyOffsets} = extractDependencies(code);
expect(dependencies).toEqual(['a']);
expect(dependencyOffsets).toEqual([8]);
});
it('can handle regular expressions', () => {
const code = "require('a'); /[\"']/.test('foo'); require(\"b\");";
const {dependencies, dependencyOffsets} = extractDependencies(code);
expect(dependencies).toEqual(['a', 'b']);
expect(dependencyOffsets).toEqual([8, 42]);
});
});

View File

@ -1,80 +0,0 @@
/**
* 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.
*
* @flow
* @format
*/
'use strict';
const babel = require('babel-core');
const babylon = require('babylon');
/**
* Extracts dependencies (module IDs imported with the `require` function) from
* a string containing code. This walks the full AST for correctness (versus
* using, for example, regular expressions, that would be faster but inexact.)
*
* The result of the dependency extraction is an de-duplicated array of
* dependencies, and an array of offsets to the string literals with module IDs.
* The index points to the opening quote.
*
* Note the technique of recognizing the identifier "require" is not proper
* because it ignores that the scope may have reassigned or shadowed that value,
* but it's a tradeoff for simplicity.
*/
function extractDependencies(code: string, filename: string) {
const ast = babylon.parse(code, {sourceType: 'module'});
const dependencies = new Set();
const dependencyOffsets = [];
function pushDependency(nodeArgs, parentType) {
const arg = nodeArgs[0];
if (nodeArgs.length != 1 || arg.type !== 'StringLiteral') {
// Dynamic requires directly inside of a try statement are considered optional dependencies
if (parentType === 'TryStatement') {
return;
}
throw new Error(
`require() must have a single string literal argument: ${filename}:${arg
.loc.start.line - 1}`,
);
}
dependencyOffsets.push(arg.start);
dependencies.add(arg.value);
}
babel.traverse(ast, {
CallExpression(path) {
const node = path.node;
const callee = node.callee;
const parent = path.scope.parentBlock;
if (callee.type === 'Identifier' && callee.name === 'require') {
pushDependency(node.arguments, parent.type);
}
if (callee.type !== 'MemberExpression') {
return;
}
const obj = callee.object;
const prop = callee.property;
if (
obj.type === 'Identifier' &&
obj.name === 'require' &&
!callee.computed &&
prop.name === 'async'
) {
pushDependency(node.arguments);
}
},
});
return {dependencyOffsets, dependencies: Array.from(dependencies)};
}
module.exports = extractDependencies;