GT2/GT2-Android/node_modules/babel-preset-fbjs/plugins/inline-requires.js

159 lines
4.9 KiB
JavaScript

/**
* Copyright (c) 2013-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.
*/
'use strict';
/**
* This transform inlines top-level require(...) aliases with to enable lazy
* loading of dependencies.
*
* Continuing with the example above, this replaces all references to `Foo` in
* the module to `require('ModuleFoo')`.
*/
module.exports = function fbjsInlineRequiresTransform(babel) {
var t = babel.types;
function buildRequireCall(name, inlineRequiredDependencyMap) {
var call = t.callExpression(
t.identifier('require'),
[t.stringLiteral(inlineRequiredDependencyMap.get(name))]
);
call.new = true;
return call;
}
function inlineRequire(path, inlineRequiredDependencyMap, identifierToPathsMap) {
var node = path.node;
if (path.isReferenced()) {
path.replaceWith(buildRequireCall(node.name, inlineRequiredDependencyMap));
var paths = identifierToPathsMap.get(node.name);
if (paths) {
paths.delete(path);
}
}
}
return {
visitor: {
Program(_, state) {
/**
* Map of require(...) aliases to module names.
*
* `Foo` is an alias for `require('ModuleFoo')` in the following example:
* var Foo = require('ModuleFoo');
*/
state.inlineRequiredDependencyMap = new Map();
/**
* Map of variable names that have not yet been inlined.
* We track them in case we later remove their require()s,
* In which case we have to come back and update them.
*/
state.identifierToPathsMap = new Map();
},
/**
* Collect top-level require(...) aliases.
*/
CallExpression: function(path, state) {
var node = path.node;
if (isTopLevelRequireAlias(path)) {
var varName = path.parent.id.name;
var moduleName = node.arguments[0].value;
var inlineRequiredDependencyMap = state.inlineRequiredDependencyMap;
var identifierToPathsMap = state.identifierToPathsMap;
inlineRequiredDependencyMap.set(varName, moduleName);
// If we removed require() statements for variables we've already seen,
// We need to do a second pass on this program to replace them with require().
var maybePaths = identifierToPathsMap.get(varName);
if (maybePaths) {
maybePaths.forEach(path =>
inlineRequire(path, inlineRequiredDependencyMap, identifierToPathsMap));
maybePaths.delete(varName);
}
// Remove the declaration.
path.parentPath.parentPath.remove();
// And the associated binding in the scope.
path.scope.removeBinding(varName);
}
},
/**
* Inline require(...) aliases.
*/
Identifier: function(path, state) {
var node = path.node;
var parent = path.parent;
var scope = path.scope;
var identifierToPathsMap = state.identifierToPathsMap;
if (!shouldInlineRequire(node, scope, state.inlineRequiredDependencyMap)) {
// Monitor this name in case we later remove its require().
// This won't happen often but if it does we need to come back and update here.
var paths = identifierToPathsMap.get(node.name);
if (paths) {
paths.add(path);
} else {
identifierToPathsMap.set(node.name, new Set([path]));
}
return;
}
if (
parent.type === 'AssignmentExpression' &&
path.isBindingIdentifier() &&
!scope.bindingIdentifierEquals(node.name, node)
) {
throw new Error(
'Cannot assign to a require(...) alias, ' + node.name +
'. Line: ' + node.loc.start.line + '.'
);
}
inlineRequire(path, state.inlineRequiredDependencyMap, identifierToPathsMap);
},
},
};
};
function isTopLevelRequireAlias(path) {
return (
isRequireCall(path.node) &&
path.parent.type === 'VariableDeclarator' &&
path.parent.id.type === 'Identifier' &&
path.parentPath.parent.type === 'VariableDeclaration' &&
path.parentPath.parent.declarations.length === 1 &&
path.parentPath.parentPath.parent.type === 'Program'
);
}
function shouldInlineRequire(node, scope, inlineRequiredDependencyMap) {
return (
inlineRequiredDependencyMap.has(node.name) &&
!scope.hasBinding(node.name, true /* noGlobals */)
);
}
function isRequireCall(node) {
return (
!node.new &&
node.type === 'CallExpression' &&
node.callee.type === 'Identifier' &&
node.callee.name === 'require' &&
node['arguments'].length === 1 &&
node['arguments'][0].type === 'StringLiteral'
);
}