estree-toolkit
Version:
Traverser, scope tracker, and more tools for working with ESTree AST
193 lines (192 loc) • 7.37 kB
JavaScript
export const hasBinding = (() => {
const findInPattern = (node, bindingName) => {
switch (node.type) {
case 'Identifier':
return node.name === bindingName;
case 'ObjectPattern': {
const { properties } = node;
for (let i = 0; i < properties.length; i++) {
const property = properties[i];
if (property.type === 'RestElement') {
if (findInPattern(property.argument, bindingName))
return true;
}
else {
/* istanbul ignore else */
if (property.value != null) {
if (findInPattern(property.value, bindingName))
return true;
}
else {
if (!property.computed &&
property.key.type === 'Identifier' &&
property.key.name === bindingName) {
return true;
}
}
}
}
break;
}
case 'ArrayPattern': {
const { elements } = node;
for (let i = 0; i < elements.length; i++) {
const element = elements[i];
if (element == null)
continue;
if (findInPattern(element, bindingName))
return true;
}
break;
}
case 'RestElement':
return findInPattern(node.argument, bindingName);
case 'AssignmentPattern':
return findInPattern(node.left, bindingName);
}
return false;
};
const findInVariableDeclaration = (node, bindingName) => {
const { declarations } = node;
for (let i = 0; i < declarations.length; i++) {
if (findInPattern(declarations[i].id, bindingName))
return true;
}
return false;
};
const findInImportDeclaration = (node, bindingName) => {
for (let i = 0; i < node.specifiers.length; i++) {
const specifier = node.specifiers[i];
switch (specifier.type) {
case 'ImportDefaultSpecifier':
case 'ImportNamespaceSpecifier':
if (specifier.local.name === bindingName)
return true;
break;
case 'ImportSpecifier':
/* istanbul ignore else */
if (specifier.local != null) {
if (specifier.local.name === bindingName)
return true;
}
else {
if (specifier.imported.type === 'Identifier' && specifier.imported.name === bindingName)
return true;
}
break;
}
}
return false;
};
const findInStatements = (statements, bindingName) => {
var _a;
for (let i = 0; i < statements.length; i++) {
const statement = statements[i];
switch (statement.type) {
case 'ImportDeclaration': {
if (findInImportDeclaration(statement, bindingName)) {
return true;
}
break;
}
case 'VariableDeclaration': {
if (findInVariableDeclaration(statement, bindingName)) {
return true;
}
break;
}
case 'FunctionDeclaration':
case 'ClassDeclaration': {
if (((_a = statement.id) === null || _a === void 0 ? void 0 : _a.name) === bindingName)
return true;
break;
}
}
}
return false;
};
const parentTypes = [
'BlockStatement',
'CatchClause',
'ForStatement',
'ForInStatement',
'ForOfStatement',
'FunctionDeclaration',
'FunctionExpression',
'ArrowFunctionExpression',
'ClassDeclaration',
'ClassExpression',
'Program'
];
const findInParent = (path, bindingName) => {
var _a, _b;
const parent = path.findParent((p) => parentTypes.includes(p.type));
if (parent != null && parent.node != null) {
const { node } = parent;
switch (node.type) {
case 'BlockStatement': {
if (findInStatements(node.body, bindingName)) {
return true;
}
break;
}
case 'CatchClause': {
if (node.param != null && findInPattern(node.param, bindingName)) {
return true;
}
break;
}
case 'ForStatement': {
/* istanbul ignore else */
if (node.init != null && node.init.type === 'VariableDeclaration') {
if (findInVariableDeclaration(node.init, bindingName)) {
return true;
}
}
break;
}
case 'ForInStatement':
case 'ForOfStatement': {
/* istanbul ignore else */
if (node.left.type === 'VariableDeclaration') {
if (findInVariableDeclaration(node.left, bindingName)) {
return true;
}
}
break;
}
case 'FunctionDeclaration':
case 'FunctionExpression':
case 'ArrowFunctionExpression': {
if (node.type !== 'ArrowFunctionExpression' &&
((_a = node.id) === null || _a === void 0 ? void 0 : _a.name) === bindingName)
return true;
for (let i = 0; i < node.params.length; i++) {
if (findInPattern(node.params[i], bindingName)) {
return true;
}
}
break;
}
case 'ClassExpression':
case 'ClassDeclaration': {
/* istanbul ignore else */
if (((_b = node.id) === null || _b === void 0 ? void 0 : _b.name) === bindingName)
return true;
break;
}
case 'Program': {
if (findInStatements(node.body, bindingName)) {
return true;
}
break;
}
}
return findInParent(parent, bindingName);
}
return false;
};
return (path, bindingName) => {
return findInParent(path, bindingName);
};
})();