ast-utils
Version:
Utility library to manipulate ASTs
274 lines (260 loc) • 9.15 kB
JavaScript
'use strict';
function introduces(name, node) { // eslint-disable-line complexity
if (!node) {
return false;
}
switch (node.type) {
case 'Identifier':
return node.name === name;
case 'FunctionDeclaration':
return introduces(name, node.id) ||
someIntroduce(name, node.params);
case 'ArrowFunctionExpression':
return someIntroduce(name, node.params);
case 'FunctionExpression':
return someIntroduce(name, node.params);
case 'BlockStatement':
return someIntroduce(name, node.body);
case 'VariableDeclaration':
return someIntroduce(name, node.declarations);
case 'VariableDeclarator':
return introduces(name, node.id);
case 'ObjectPattern':
return someIntroduce(name, node.properties);
case 'ArrayPattern':
return someIntroduce(name, node.elements);
case 'Property':
return introduces(name, node.value);
case 'ExperimentalRestProperty':
return introduces(name, node.argument);
case 'ForStatement':
return introduces(name, node.init);
case 'ClassDeclaration':
return introduces(name, node.id);
case 'RestElement':
return introduces(name, node.argument);
case 'Program':
return someIntroduce(name, node.body);
case 'ImportDeclaration':
return someIntroduce(name, node.specifiers);
case 'ImportDefaultSpecifier':
return introduces(name, node.local);
case 'ImportSpecifier':
return introduces(name, node.local);
case 'ImportNamespaceSpecifier':
return introduces(name, node.local);
default:
return false;
}
}
function someIntroduce(name, array) {
return Array.isArray(array) && array.some(item => {
return introduces(name, item);
});
}
function containsIdentifier(name, node) { // eslint-disable-line complexity
if (!node) {
return false;
}
switch (node.type) {
// Primitives
case 'Identifier':
return node.name === name;
case 'Literal':
return false;
case 'ThisExpression':
return false;
// Objects / Arrays
case 'ArrayExpression':
return someContainIdentifier(name, node.elements);
case 'ObjectExpression':
return someContainIdentifier(name, node.properties);
case 'ExperimentalSpreadProperty':
return containsIdentifier(name, node.argument);
case 'Property':
return (node.computed && containsIdentifier(name, node.key)) ||
containsIdentifier(name, node.value);
// Expressions
case 'TemplateLiteral':
return someContainIdentifier(name, node.expressions);
case 'TaggedTemplateExpression':
return containsIdentifier(name, node.tag) || containsIdentifier(name, node.quasi);
case 'SequenceExpression':
return someContainIdentifier(name, node.expressions);
case 'CallExpression':
return containsIdentifier(name, node.callee) ||
someContainIdentifier(name, node.arguments);
case 'NewExpression':
return containsIdentifier(name, node.callee) ||
someContainIdentifier(name, node.arguments);
case 'MemberExpression':
if (node.computed === false) {
return containsIdentifier(name, node.object);
}
return containsIdentifier(name, node.property) ||
containsIdentifier(name, node.object);
case 'ConditionalExpression':
return containsIdentifier(name, node.test) ||
containsIdentifier(name, node.consequent) ||
containsIdentifier(name, node.alternate);
case 'BinaryExpression':
return containsIdentifier(name, node.left) ||
containsIdentifier(name, node.right);
case 'LogicalExpression':
return containsIdentifier(name, node.left) ||
containsIdentifier(name, node.right);
case 'AssignmentExpression':
return containsIdentifier(name, node.left) ||
containsIdentifier(name, node.right);
case 'UpdateExpression':
return containsIdentifier(name, node.argument);
case 'UnaryExpression':
return containsIdentifier(name, node.argument);
case 'YieldExpression':
return containsIdentifier(name, node.argument);
case 'AwaitExpression':
return containsIdentifier(name, node.argument);
case 'ArrowFunctionExpression':
if (node.params.some(param => param.type !== 'Identifier' && containsIdentifier(name, param))) {
return true;
}
return !introduces(name, node) && containsIdentifier(name, node.body);
case 'FunctionExpression':
if (node.params.some(param => param.type !== 'Identifier' && containsIdentifier(name, param))) {
return true;
}
return !introduces(name, node) && containsIdentifier(name, node.body);
case 'SpreadElement':
return containsIdentifier(name, node.argument);
// Statements / control flow
case 'ExpressionStatement':
return containsIdentifier(name, node.expression);
case 'ReturnStatement':
return containsIdentifier(name, node.argument);
case 'ThrowStatement':
return containsIdentifier(name, node.argument);
case 'IfStatement':
return containsIdentifier(name, node.test) ||
containsIdentifier(name, node.consequent) ||
containsIdentifier(name, node.alternate);
case 'BreakStatement':
return false;
case 'ContinueStatement':
return false;
case 'ForOfStatement':
return containsIdentifier(name, node.left) ||
containsIdentifier(name, node.right) ||
containsIdentifier(name, node.body);
case 'ForInStatement':
return containsIdentifier(name, node.left) ||
containsIdentifier(name, node.right) ||
containsIdentifier(name, node.body);
case 'ForStatement':
return !introduces(name, node) && (
containsIdentifier(name, node.init) ||
containsIdentifier(name, node.test) ||
containsIdentifier(name, node.update) ||
containsIdentifier(name, node.body)
);
case 'WhileStatement':
return containsIdentifier(name, node.test) ||
containsIdentifier(name, node.body);
case 'DoWhileStatement':
return containsIdentifier(name, node.test) ||
containsIdentifier(name, node.body);
case 'Program':
return !introduces(name, node) && someContainIdentifier(name, node.body);
case 'BlockStatement':
return !introduces(name, node) && someContainIdentifier(name, node.body);
case 'TryStatement':
return containsIdentifier(name, node.block) ||
containsIdentifier(name, node.handler);
case 'CatchClause':
return !introduces(name, node.param) && containsIdentifier(name, node.body);
case 'SwitchStatement':
return containsIdentifier(name, node.discriminant) || someContainIdentifier(name, node.cases);
case 'SwitchCase':
return containsIdentifier(name, node.test) || someContainIdentifier(name, node.consequent);
case 'LabeledStatement':
return containsIdentifier(name, node.body);
case 'DebuggerStatement':
return false;
case 'EmptyStatement':
return false;
// Assignment / Declaration
case 'AssignmentPattern':
return containsIdentifier(name, node.left) ||
containsIdentifier(name, node.right);
case 'VariableDeclarator':
if (node.id.type !== 'Identifier') {
return containsIdentifier(name, node.id) ||
containsIdentifier(name, node.init);
}
return containsIdentifier(name, node.init);
case 'ObjectPattern':
return node.properties.some(prop =>
prop.type === 'Property' && prop.value.type !== 'Identifier' && containsIdentifier(name, prop.value)
);
case 'FunctionDeclaration':
if (node.params.some(param => param.type !== 'Identifier' && containsIdentifier(name, param))) {
return true;
}
return !introduces(name, node) && containsIdentifier(name, node.body);
case 'ArrayPattern':
return node.elements.some(item => {
return item && item.type !== 'Identifier' && containsIdentifier(name, item);
});
case 'VariableDeclaration':
return someContainIdentifier(name, node.declarations);
case 'RestElement':
return false;
// Classes
case 'ClassDeclaration':
return !introduces(name, node) && (
containsIdentifier(name, node.superClass) ||
containsIdentifier(name, node.body)
);
case 'ClassExpression':
return containsIdentifier(name, node.superClass) ||
containsIdentifier(name, node.body);
case 'ClassBody':
return someContainIdentifier(name, node.body);
case 'MethodDefinition':
return containsIdentifier(name, node.value);
case 'Super':
return false;
// Import / export
case 'ImportDeclaration':
return false;
case 'ExportDefaultDeclaration':
return containsIdentifier(name, node.declaration);
case 'ExportNamedDeclaration':
return containsIdentifier(name, node.declaration);
// JSX
case 'JSXIdentifier':
return node.name === name;
case 'JSXElement':
return containsIdentifier(name, node.openingElement) ||
someContainIdentifier(name, node.children);
case 'JSXOpeningElement':
return containsIdentifier(name, node.name) ||
someContainIdentifier(name, node.attributes);
case 'JSXExpressionContainer':
return containsIdentifier(name, node.expression);
case 'JSXSpreadAttribute':
return containsIdentifier(name, node.argument);
case 'JSXAttribute':
return containsIdentifier(name, node.value);
default:
return false;
}
}
function someContainIdentifier(name, array) {
return Array.isArray(array) && array.some(item => {
return containsIdentifier(name, item);
});
}
module.exports = {
containsIdentifier,
someContainIdentifier
};