"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.simplifyScope = exports.addAllReferences = exports.addReference = exports.addVariable = exports.replaceScope = exports.removeScope = exports.removeReference = exports.removeIdentifierReference = exports.getAllReferences = exports.removeIdentifierVariable = exports.getProgramScope = exports.getScopeFromNode = exports.removeAllScopeAndVariableAndReference = void 0; const traverse_1 = require("../traverse"); const utils_1 = require("../utils"); /** Remove all scope, variable, and reference */ function removeAllScopeAndVariableAndReference(target, info) { const targetScopes = new Set(); (0, traverse_1.traverseNodes)(target, { visitorKeys: info.visitorKeys, enterNode(node) { const scope = info.scopeManager.acquire(node); if (scope) { targetScopes.add(scope); return; } if (node.type === "Identifier") { let scope = getScopeFromNode(info.scopeManager, node); while (scope && scope.block.type !== "Program" && target.range[0] <= scope.block.range[0] && scope.block.range[1] <= target.range[1]) { scope = scope.upper; } if (targetScopes.has(scope)) { return; } removeIdentifierVariable(node, scope); removeIdentifierReference(node, scope); } }, leaveNode() { // noop }, }); for (const scope of targetScopes) { removeScope(info.scopeManager, scope); } } exports.removeAllScopeAndVariableAndReference = removeAllScopeAndVariableAndReference; /** * Gets the scope for the current node */ function getScopeFromNode(scopeManager, currentNode) { let node = currentNode; for (; node; node = node.parent || null) { const scope = scopeManager.acquire(node, false); if (scope) { if (scope.type === "function-expression-name") { return scope.childScopes[0]; } if (scope.type === "global" && node.type === "Program" && node.sourceType === "module") { return scope.childScopes.find((s) => s.type === "module") || scope; } return scope; } } const global = scopeManager.globalScope; return global; } exports.getScopeFromNode = getScopeFromNode; /** * Gets the scope for the Program node */ function getProgramScope(scopeManager) { const globalScope = scopeManager.globalScope; return (globalScope.childScopes.find((s) => s.type === "module") || globalScope); } exports.getProgramScope = getProgramScope; /* eslint-disable complexity -- ignore X( */ /** Remove variable */ function removeIdentifierVariable( /* eslint-enable complexity -- ignore X( */ node, scope) { if (node.type === "ObjectPattern") { for (const prop of node.properties) { if (prop.type === "Property") { removeIdentifierVariable(prop.value, scope); } else if (prop.type === "RestElement") { removeIdentifierVariable(prop, scope); } } return; } if (node.type === "ArrayPattern") { for (const element of node.elements) { if (!element) continue; removeIdentifierVariable(element, scope); } return; } if (node.type === "AssignmentPattern") { removeIdentifierVariable(node.left, scope); return; } if (node.type === "RestElement") { removeIdentifierVariable(node.argument, scope); return; } if (node.type === "MemberExpression") { return; } if (node.type !== "Identifier") { return; } for (let varIndex = 0; varIndex < scope.variables.length; varIndex++) { const variable = scope.variables[varIndex]; const defIndex = variable.defs.findIndex((def) => def.name === node); if (defIndex < 0) { continue; } variable.defs.splice(defIndex, 1); if (variable.defs.length === 0) { // Remove variable referencesToThrough(variable.references, scope); variable.references.forEach((r) => { if (r.init) r.init = false; r.resolved = null; }); scope.variables.splice(varIndex, 1); const name = node.name; if (variable === scope.set.get(name)) { scope.set.delete(name); } } else { const idIndex = variable.identifiers.indexOf(node); if (idIndex >= 0) { variable.identifiers.splice(idIndex, 1); } } return; } } exports.removeIdentifierVariable = removeIdentifierVariable; /** Get all references */ function* getAllReferences(node, scope) { if (node.type === "ObjectPattern") { for (const prop of node.properties) { if (prop.type === "Property") { yield* getAllReferences(prop.value, scope); } else if (prop.type === "RestElement") { yield* getAllReferences(prop, scope); } } return; } if (node.type === "ArrayPattern") { for (const element of node.elements) { if (!element) continue; yield* getAllReferences(element, scope); } return; } if (node.type === "AssignmentPattern") { yield* getAllReferences(node.left, scope); return; } if (node.type === "RestElement") { yield* getAllReferences(node.argument, scope); return; } if (node.type === "MemberExpression") { return; } if (node.type !== "Identifier") { return; } const ref = scope.references.find((ref) => ref.identifier === node); if (ref) yield ref; } exports.getAllReferences = getAllReferences; /** Remove reference */ function removeIdentifierReference(node, scope) { const reference = scope.references.find((ref) => ref.identifier === node); if (reference) { removeReference(reference, scope); return true; } const location = node.range[0]; const pendingScopes = []; for (const childScope of scope.childScopes) { const range = childScope.block.range; if (range[0] <= location && location < range[1]) { if (removeIdentifierReference(node, childScope)) { return true; } } else { pendingScopes.push(childScope); } } for (const childScope of pendingScopes) { if (removeIdentifierReference(node, childScope)) { return true; } } return false; } exports.removeIdentifierReference = removeIdentifierReference; /** Remove reference */ function removeReference(reference, baseScope) { if (reference.resolved) { if (reference.resolved.defs.some((d) => d.name === reference.identifier)) { // remove var const varIndex = baseScope.variables.indexOf(reference.resolved); if (varIndex >= 0) { baseScope.variables.splice(varIndex, 1); } const name = reference.identifier.name; if (reference.resolved === baseScope.set.get(name)) { baseScope.set.delete(name); } } else { const refIndex = reference.resolved.references.indexOf(reference); if (refIndex >= 0) { reference.resolved.references.splice(refIndex, 1); } } } let scope = baseScope; while (scope) { const refIndex = scope.references.indexOf(reference); if (refIndex >= 0) { scope.references.splice(refIndex, 1); } const throughIndex = scope.through.indexOf(reference); if (throughIndex >= 0) { scope.through.splice(throughIndex, 1); } scope = scope.upper; } } exports.removeReference = removeReference; /** Move reference to through */ function referencesToThrough(references, baseScope) { let scope = baseScope; while (scope) { addAllReferences(scope.through, references); scope = scope.upper; } } /** Remove scope */ function removeScope(scopeManager, scope) { while (scope.childScopes[0]) { removeScope(scopeManager, scope.childScopes[0]); } while (scope.references[0]) { removeReference(scope.references[0], scope); } const upper = scope.upper; if (upper) { const index = upper.childScopes.indexOf(scope); if (index >= 0) { upper.childScopes.splice(index, 1); } } const index = scopeManager.scopes.indexOf(scope); if (index >= 0) { scopeManager.scopes.splice(index, 1); } } exports.removeScope = removeScope; /** Replace scope */ function replaceScope(scopeManager, scope, newChildScopes = []) { // remove scope from scopeManager scopeManager.scopes = scopeManager.scopes.filter((s) => s !== scope); const upper = scope.upper; if (upper) { // remove scope from upper and marge childScopes upper.childScopes.splice(upper.childScopes.indexOf(scope), 1, ...newChildScopes); for (const child of newChildScopes) { child.upper = upper; replaceVariableScope(child, scope); } } /** Replace variableScope */ function replaceVariableScope(child, replaceTarget) { if (child.variableScope === replaceTarget) { child.variableScope = child.upper.variableScope; for (const c of child.childScopes) { replaceVariableScope(c, replaceTarget); } } } } exports.replaceScope = replaceScope; /** * Add variable to array */ function addVariable(list, variable) { (0, utils_1.addElementToSortedArray)(list, variable, (a, b) => { const idA = getFirstId(a); const idB = getFirstId(b); return idA.range[0] - idB.range[0]; }); /** Get first id from give variable */ function getFirstId(v) { var _a, _b; return v.identifiers[0] || ((_a = v.defs[0]) === null || _a === void 0 ? void 0 : _a.name) || ((_b = v.references[0]) === null || _b === void 0 ? void 0 : _b.identifier); } } exports.addVariable = addVariable; /** * Add reference to array */ function addReference(list, reference) { (0, utils_1.addElementToSortedArray)(list, reference, (a, b) => a.identifier.range[0] - b.identifier.range[0]); } exports.addReference = addReference; /** * Add all references to array */ function addAllReferences(list, elements) { (0, utils_1.addElementsToSortedArray)(list, elements, (a, b) => a.identifier.range[0] - b.identifier.range[0]); } exports.addAllReferences = addAllReferences; /** * Simplify scope data. * @deprecated For Debug */ function simplifyScope(scope) { return { type: scope.type, childScopes: scope.childScopes.map(simplifyScope), block: { type: scope.block.type, loc: JSON.stringify(scope.block.loc), }, variables: scope.type === "global" ? null : simplifyVariables(scope.variables), references: scope.references.map(simplifyReference), through: scope.through.map(simplifyReference), get original() { return scope; }, }; } exports.simplifyScope = simplifyScope; /** * Simplify variables data. * @deprecated For Debug */ function simplifyVariables(variables) { return Object.fromEntries(variables.map((v) => { var _a; return [ v.name, { loc: JSON.stringify((_a = v.defs[0]) === null || _a === void 0 ? void 0 : _a.node.loc), }, ]; })); } /** * Simplify reference data. * @deprecated For Debug */ function simplifyReference(reference) { return { name: reference.identifier.name, loc: JSON.stringify(reference.identifier.loc), }; }