You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

373 lines
12 KiB
JavaScript

"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),
};
}