@babel/traverse
Version:
The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes
1,726 lines (1,712 loc) • 152 kB
JavaScript
import { createDebug } from 'obug';
import { codeFrameColumns } from '@babel/code-frame';
import * as _t from '@babel/types';
import { parse } from '@babel/parser';
import globalsBuiltinLower from '@babel/helper-globals/data/builtin-lower.json' with { type: 'json' };
import globalsBuiltinUpper from '@babel/helper-globals/data/builtin-upper.json' with { type: 'json' };
import generator from '@babel/generator';
import template from '@babel/template';
const ReferencedIdentifier = ["Identifier", "JSXIdentifier"];
const ReferencedMemberExpression = ["MemberExpression"];
const BindingIdentifier = ["Identifier"];
const Statement = ["Statement"];
const Expression = ["Expression"];
const Scope$1 = ["Scopable", "Pattern"];
const Referenced = null;
const BlockScoped = ["FunctionDeclaration", "ClassDeclaration", "VariableDeclaration"];
const Var = ["VariableDeclaration"];
const User = null;
const Generated = null;
const Pure = null;
const Flow = ["Flow", "ImportDeclaration", "ExportDeclaration", "ImportSpecifier"];
const RestProperty = ["RestElement"];
const SpreadProperty = ["RestElement"];
const ExistentialTypeParam = ["ExistsTypeAnnotation"];
const NumericLiteralTypeAnnotation = ["NumberLiteralTypeAnnotation"];
const ForAwaitStatement = ["ForOfStatement"];
const virtualTypes = /*#__PURE__*/Object.defineProperty({
__proto__: null,
BindingIdentifier,
BlockScoped,
ExistentialTypeParam,
Expression,
Flow,
ForAwaitStatement,
Generated,
NumericLiteralTypeAnnotation,
Pure,
Referenced,
ReferencedIdentifier,
ReferencedMemberExpression,
RestProperty,
Scope: Scope$1,
SpreadProperty,
Statement,
User,
Var
}, Symbol.toStringTag, { value: 'Module' });
class TraversalContext {
constructor(opts, state) {
this.state = state;
this.opts = opts;
}
queue = null;
priorityQueue = null;
maybeQueue(path, notPriority) {
if (this.queue) {
if (notPriority) {
this.queue.push(path);
} else {
this.priorityQueue.push(path);
}
}
}
}
const {
VISITOR_KEYS: VISITOR_KEYS$4
} = _t;
function _visitPaths(ctx, paths) {
ctx.queue = paths;
ctx.priorityQueue = [];
const visited = new Set();
let stop = false;
let visitIndex = 0;
for (; visitIndex < paths.length;) {
const path = paths[visitIndex];
visitIndex++;
resync.call(path);
if (path.contexts.length === 0 || path.contexts[path.contexts.length - 1] !== ctx) {
pushContext.call(path, ctx);
}
if (path.key === null) continue;
const {
node
} = path;
if (visited.has(node)) continue;
if (node) visited.add(node);
if (_visit(ctx, path)) {
stop = true;
break;
}
if (ctx.priorityQueue.length) {
stop = _visitPaths(ctx, ctx.priorityQueue);
ctx.priorityQueue = [];
ctx.queue = paths;
if (stop) break;
}
}
for (let i = 0; i < visitIndex; i++) {
popContext.call(paths[i]);
}
ctx.queue = null;
return stop;
}
function _visit(ctx, path) {
const node = path.node;
if (!node) {
return false;
}
const opts = ctx.opts;
const denylist = opts.denylist;
if (denylist?.includes(node.type)) {
return false;
}
if (opts.shouldSkip?.(path)) {
return false;
}
if (path.shouldSkip) return path.shouldStop;
if (_call.call(path, opts.enter)) return path.shouldStop;
if (path.node) {
if (_call.call(path, opts[node.type]?.enter)) return path.shouldStop;
}
path.shouldStop = traverseNode(path.node, opts, path.scope, ctx.state, path, path.skipKeys);
if (path.node) {
if (_call.call(path, opts.exit)) return true;
}
if (path.node) {
_call.call(path, opts[node.type]?.exit);
}
return path.shouldStop;
}
function traverseNode(node, opts, scope, state, path, skipKeys, visitSelf) {
const keys = VISITOR_KEYS$4[node.type];
if (!keys?.length) return false;
const ctx = new TraversalContext(opts, state);
if (visitSelf) {
if (skipKeys?.[path.parentKey]) return false;
return _visitPaths(ctx, [path]);
}
const hub = path == null ? node.type === "Program" || node.type === "File" ? new Hub() : undefined : path.hub;
for (const key of keys) {
if (skipKeys?.[key]) continue;
const prop = node[key];
if (!prop) continue;
if (Array.isArray(prop)) {
if (!prop.length) continue;
const paths = [];
for (let i = 0; i < prop.length; i++) {
const childPath = NodePath_Final.get({
parentPath: path,
parent: node,
container: prop,
key: i,
listKey: key,
hub
});
paths.push(childPath);
}
if (_visitPaths(ctx, paths)) return true;
} else {
if (_visitPaths(ctx, [NodePath_Final.get({
parentPath: path,
parent: node,
container: node,
key,
listKey: null,
hub
})])) {
return true;
}
}
}
return false;
}
const {
isBinding,
isBlockScoped: nodeIsBlockScoped,
isExportDeclaration: isExportDeclaration$1,
isExpression: nodeIsExpression,
isFlow: nodeIsFlow,
isForStatement,
isForXStatement,
isIdentifier: isIdentifier$5,
isImportDeclaration: isImportDeclaration$1,
isImportSpecifier,
isJSXIdentifier,
isJSXMemberExpression,
isMemberExpression: isMemberExpression$1,
isRestElement: nodeIsRestElement,
isReferenced: nodeIsReferenced,
isScope: nodeIsScope,
isStatement: nodeIsStatement,
isVar: nodeIsVar,
isVariableDeclaration: isVariableDeclaration$2,
react,
isForOfStatement
} = _t;
const {
isCompatTag
} = react;
function isReferencedIdentifier(opts) {
const {
node,
parent
} = this;
if (isIdentifier$5(node, opts)) {
return nodeIsReferenced(node, parent, this.parentPath.parent);
} else if (isJSXIdentifier(node, opts)) {
if (!isJSXMemberExpression(parent) && isCompatTag(node.name)) return false;
return nodeIsReferenced(node, parent, this.parentPath.parent);
} else {
return false;
}
}
function isReferencedMemberExpression() {
const {
node,
parent
} = this;
return isMemberExpression$1(node) && nodeIsReferenced(node, parent);
}
function isBindingIdentifier() {
const {
node,
parent
} = this;
const grandparent = this.parentPath.parent;
return isIdentifier$5(node) && isBinding(node, parent, grandparent);
}
function isStatement$1() {
const {
node,
parent
} = this;
if (nodeIsStatement(node)) {
if (isVariableDeclaration$2(node)) {
if (isForXStatement(parent, {
left: node
})) return false;
if (isForStatement(parent, {
init: node
})) return false;
}
return true;
} else {
return false;
}
}
function isExpression$3() {
if (this.isIdentifier()) {
return this.isReferencedIdentifier();
} else {
return nodeIsExpression(this.node);
}
}
function isScope() {
return nodeIsScope(this.node, this.parent);
}
function isReferenced() {
return nodeIsReferenced(this.node, this.parent);
}
function isBlockScoped() {
return nodeIsBlockScoped(this.node);
}
function isVar() {
return nodeIsVar(this.node);
}
function isUser() {
return !!this.node?.loc;
}
function isGenerated() {
return !this.isUser();
}
function isPure(constantsOnly) {
return this.scope.isPure(this.node, constantsOnly);
}
function isFlow() {
const {
node
} = this;
if (nodeIsFlow(node)) {
return true;
} else if (isImportDeclaration$1(node)) {
return node.importKind === "type" || node.importKind === "typeof";
} else if (isExportDeclaration$1(node)) {
return node.exportKind === "type";
} else if (isImportSpecifier(node)) {
return node.importKind === "type" || node.importKind === "typeof";
} else {
return false;
}
}
function isRestProperty() {
return nodeIsRestElement(this.node) && this.parentPath?.isObjectPattern();
}
function isSpreadProperty() {
return nodeIsRestElement(this.node) && this.parentPath?.isObjectExpression();
}
function isForAwaitStatement() {
return isForOfStatement(this.node, {
await: true
});
}
const NodePath_virtual_types_validator = /*#__PURE__*/Object.defineProperty({
__proto__: null,
isBindingIdentifier,
isBlockScoped,
isExpression: isExpression$3,
isFlow,
isForAwaitStatement,
isGenerated,
isPure,
isReferenced,
isReferencedIdentifier,
isReferencedMemberExpression,
isRestProperty,
isScope,
isSpreadProperty,
isStatement: isStatement$1,
isUser,
isVar
}, Symbol.toStringTag, { value: 'Module' });
const {
DEPRECATED_KEYS,
DEPRECATED_ALIASES,
FLIPPED_ALIAS_KEYS,
TYPES,
__internal__deprecationWarning: deprecationWarning
} = _t;
function isVirtualType(type) {
return type in virtualTypes;
}
function isExplodedVisitor(visitor) {
return visitor?._exploded;
}
function explode$1(visitor) {
if (isExplodedVisitor(visitor)) return visitor;
visitor._exploded = true;
if (Object.hasOwn(visitor, "blacklist")) {
if (!Object.hasOwn(visitor, "denylist")) {
throw new Error("The 'blacklist' visitor option has been renamed to 'denylist'. " + "Please update your configuration.");
}
delete visitor.blacklist;
}
for (const nodeType of Object.keys(visitor)) {
if (shouldIgnoreKey(nodeType)) continue;
const parts = nodeType.split("|");
if (parts.length === 1) continue;
const fns = visitor[nodeType];
delete visitor[nodeType];
for (const part of parts) {
visitor[part] = fns;
}
}
verify$1(visitor);
delete visitor.__esModule;
ensureEntranceObjects(visitor);
ensureCallbackArrays(visitor);
const visitorBase = visitor;
for (const nodeType of Object.keys(visitor)) {
if (shouldIgnoreKey(nodeType)) continue;
if (!isVirtualType(nodeType)) continue;
const fns = visitorBase[nodeType];
for (const type of Object.keys(fns)) {
fns[type] = wrapCheck(nodeType, fns[type]);
}
delete visitorBase[nodeType];
const types = virtualTypes[nodeType];
if (types !== null) {
for (const type of types) {
visitorBase[type] ??= {};
mergePair(visitorBase[type], fns);
}
} else {
mergePair(visitor, fns);
}
}
for (const nodeType of Object.keys(visitor)) {
if (shouldIgnoreKey(nodeType)) continue;
let aliases = FLIPPED_ALIAS_KEYS[nodeType];
if (nodeType in DEPRECATED_KEYS) {
const deprecatedKey = DEPRECATED_KEYS[nodeType];
deprecationWarning(nodeType, deprecatedKey, "Visitor ");
aliases = [deprecatedKey];
} else if (nodeType in DEPRECATED_ALIASES) {
const deprecatedAlias = DEPRECATED_ALIASES[nodeType];
deprecationWarning(nodeType, deprecatedAlias, "Visitor ");
aliases = FLIPPED_ALIAS_KEYS[deprecatedAlias];
}
if (!aliases) continue;
const fns = visitor[nodeType];
delete visitor[nodeType];
for (const alias of aliases) {
const existing = visitorBase[alias];
if (existing) {
mergePair(existing, fns);
} else {
visitorBase[alias] = {
...fns
};
}
}
}
for (const nodeType of Object.keys(visitor)) {
if (shouldIgnoreKey(nodeType)) continue;
ensureCallbackArrays(visitor[nodeType]);
}
return visitor;
}
function verify$1(visitor) {
if (visitor._verified) return;
if (typeof visitor === "function") {
throw new Error("You passed `traverse()` a function when it expected a visitor object, " + "are you sure you didn't mean `{ enter: Function }`?");
}
for (const nodeType of Object.keys(visitor)) {
if (nodeType === "enter" || nodeType === "exit") {
validateVisitorMethods(nodeType, visitor[nodeType]);
}
if (shouldIgnoreKey(nodeType)) continue;
if (!TYPES.includes(nodeType)) {
throw new Error(`You gave us a visitor for the node type ${nodeType} but it's not a valid type in @babel/traverse ${"8.0.0"}`);
}
const visitors = visitor[nodeType];
if (typeof visitors === "object") {
for (const visitorKey of Object.keys(visitors)) {
if (visitorKey === "enter" || visitorKey === "exit") {
validateVisitorMethods(`${nodeType}.${visitorKey}`, visitors[visitorKey]);
} else {
throw new Error("You passed `traverse()` a visitor object with the property " + `${nodeType} that has the invalid property ${visitorKey}`);
}
}
}
}
visitor._verified = true;
}
function validateVisitorMethods(path, val) {
const fns = [].concat(val);
for (const fn of fns) {
if (typeof fn !== "function") {
throw new TypeError(`Non-function found defined in ${path} with type ${typeof fn}`);
}
}
}
function merge(visitors, states = [], wrapper) {
const mergedVisitor = {
_verified: true,
_exploded: true
};
for (let i = 0; i < visitors.length; i++) {
const visitor = explode$1(visitors[i]);
const state = states[i];
let topVisitor = visitor;
if (state || wrapper) {
topVisitor = wrapWithStateOrWrapper(topVisitor, state, wrapper);
}
mergePair(mergedVisitor, topVisitor);
for (const key of Object.keys(visitor)) {
if (shouldIgnoreKey(key)) continue;
let typeVisitor = visitor[key];
if (state || wrapper) {
typeVisitor = wrapWithStateOrWrapper(typeVisitor, state, wrapper);
}
const nodeVisitor = mergedVisitor[key] ||= {};
mergePair(nodeVisitor, typeVisitor);
}
}
return mergedVisitor;
}
function wrapWithStateOrWrapper(oldVisitor, state, wrapper) {
const newVisitor = {};
for (const phase of ["enter", "exit"]) {
let fns = oldVisitor[phase];
if (!Array.isArray(fns)) continue;
fns = fns.map(function (fn) {
let newFn = fn;
if (state) {
newFn = function (path) {
fn.call(state, path, state);
};
}
if (wrapper) {
newFn = wrapper(state?.key, phase, newFn);
}
if (newFn !== fn) {
newFn.toString = () => fn.toString();
}
return newFn;
});
newVisitor[phase] = fns;
}
return newVisitor;
}
function ensureEntranceObjects(obj) {
for (const key of Object.keys(obj)) {
if (shouldIgnoreKey(key)) continue;
const fns = obj[key];
if (typeof fns === "function") {
obj[key] = {
enter: fns
};
}
}
}
function ensureCallbackArrays(obj) {
if (obj.enter && !Array.isArray(obj.enter)) obj.enter = [obj.enter];
if (obj.exit && !Array.isArray(obj.exit)) obj.exit = [obj.exit];
}
function wrapCheck(nodeType, fn) {
const fnKey = `is${nodeType}`;
const validator = NodePath_virtual_types_validator[fnKey];
const newFn = function (path) {
if (validator.call(path)) {
return fn.apply(this, arguments);
}
};
newFn.toString = () => fn.toString();
return newFn;
}
function shouldIgnoreKey(key) {
if (key.startsWith("_")) return true;
if (key === "enter" || key === "exit" || key === "shouldSkip") return true;
if (key === "denylist" || key === "noScope" || key === "skipKeys") {
return true;
}
if (key === "blacklist") {
throw new Error("The 'blacklist' visitor option has been renamed to 'denylist'. " + "Please update your configuration.");
}
return false;
}
function mergePair(dest, src) {
for (const phase of ["enter", "exit"]) {
if (!src[phase]) continue;
dest[phase] = [].concat(dest[phase] || [], src[phase]);
}
}
const _environmentVisitor = {
FunctionParent(path) {
if (path.isArrowFunctionExpression()) return;
path.skip();
if (path.isMethod()) {
path.requeueComputedKeyAndDecorators();
}
},
Property(path) {
if (path.isObjectProperty()) return;
path.skip();
path.requeueComputedKeyAndDecorators();
}
};
function environmentVisitor(visitor) {
return merge([_environmentVisitor, visitor]);
}
const visitors = /*#__PURE__*/Object.defineProperty({
__proto__: null,
environmentVisitor,
explode: explode$1,
isExplodedVisitor,
merge,
verify: verify$1
}, Symbol.toStringTag, { value: 'Module' });
const {
getAssignmentIdentifiers: getAssignmentIdentifiers$1
} = _t;
let renameVisitor;
const getRenameVisitor = () => renameVisitor ??= explode$1({
ReferencedIdentifier({
node
}, state) {
if (node.name === state.oldName) {
node.name = state.newName;
}
},
Scope(path, state) {
if (!path.scope.bindingIdentifierEquals(state.oldName, state.binding.identifier)) {
path.skip();
if (path.isMethod()) {
path.requeueComputedKeyAndDecorators();
}
if (path.isSwitchStatement()) {
path.context.maybeQueue(path.get("discriminant"));
}
}
},
ObjectProperty({
node,
scope
}, state) {
const {
name
} = node.key;
if (node.shorthand && (name === state.oldName || name === state.newName) && scope.getBindingIdentifier(name) === state.binding.identifier) {
node.shorthand = false;
}
},
"AssignmentExpression|Declaration|VariableDeclarator"(path, state) {
if (path.isVariableDeclaration()) return;
const ids = path.isAssignmentExpression() ? getAssignmentIdentifiers$1(path.node) : path.getOuterBindingIdentifiers();
for (const name in ids) {
if (name === state.oldName) ids[name].name = state.newName;
}
}
});
class Renamer {
constructor(binding, oldName, newName) {
this.newName = newName;
this.oldName = oldName;
this.binding = binding;
}
maybeConvertFromExportDeclaration(parentDeclar) {
const maybeExportDeclar = parentDeclar.parentPath;
if (!maybeExportDeclar.isExportDeclaration()) {
return;
}
if (maybeExportDeclar.isExportDefaultDeclaration()) {
const {
declaration
} = maybeExportDeclar.node;
if (_t.isDeclaration(declaration) && !declaration.id) {
return;
}
}
if (maybeExportDeclar.isExportAllDeclaration()) {
return;
}
maybeExportDeclar.splitExportDeclaration();
}
maybeConvertFromClassFunctionDeclaration(path) {
return path;
}
maybeConvertFromClassFunctionExpression(path) {
return path;
}
rename() {
const {
binding,
oldName,
newName
} = this;
const {
scope,
path
} = binding;
const parentDeclar = path.find(path => path.isDeclaration() || path.isFunctionExpression() || path.isClassExpression());
if (parentDeclar) {
const bindingIds = parentDeclar.getOuterBindingIdentifiers();
if (bindingIds[oldName] === binding.identifier) {
this.maybeConvertFromExportDeclaration(parentDeclar);
}
}
const blockToTraverse = scope.block;
const skipKeys = {
discriminant: true
};
if (_t.isMethod(blockToTraverse)) {
if (blockToTraverse.computed) {
skipKeys.key = true;
}
if (!_t.isObjectMethod(blockToTraverse)) {
skipKeys.decorators = true;
}
}
traverseNode(blockToTraverse, getRenameVisitor(), scope, this, scope.path, skipKeys);
scope.removeOwnBinding(oldName);
scope.bindings[newName] = binding;
this.binding.identifier.name = newName;
if (parentDeclar) {
this.maybeConvertFromClassFunctionDeclaration(path);
this.maybeConvertFromClassFunctionExpression(path);
}
}
}
const {
VISITOR_KEYS: VISITOR_KEYS$3
} = _t;
function traverseForScope(path, visitors, state) {
const exploded = explode$1(visitors);
if (exploded.enter || exploded.exit) {
throw new Error("Should not be used with enter/exit visitors.");
}
_traverse(path.parentPath, path.parent, path.node, path.container, path.key, path.listKey, path.hub, path);
function _traverse(parentPath, parent, node, container, key, listKey, hub, inPath) {
if (!node) {
return;
}
const path = inPath || NodePath_Final.get({
hub,
parentPath,
parent,
container,
listKey,
key
});
_forceSetScope.call(path);
const visitor = exploded[node.type];
if (visitor?.enter) {
for (const visit of visitor.enter) {
visit.call(state, path, state);
}
}
if (path.shouldSkip) {
return;
}
const keys = VISITOR_KEYS$3[node.type];
if (!keys?.length) {
return;
}
for (const key of keys) {
const prop = node[key];
if (!prop) continue;
if (Array.isArray(prop)) {
for (let i = 0; i < prop.length; i++) {
const value = prop[i];
_traverse(path, node, value, prop, i, key);
}
} else {
_traverse(path, node, prop, node, key, null);
}
}
if (visitor?.exit) {
for (const visit of visitor.exit) {
visit.call(state, path, state);
}
}
}
}
class Binding {
identifier;
scope;
path;
kind;
constructor({
identifier,
scope,
path,
kind
}) {
this.identifier = identifier;
this.scope = scope;
this.path = path;
this.kind = kind;
if ((kind === "var" || kind === "hoisted") && isInitInLoop(path)) {
this.reassign(path);
}
this.clearValue();
}
constantViolations = [];
constant = true;
referencePaths = [];
referenced = false;
references = 0;
deoptValue() {
this.clearValue();
this.hasDeoptedValue = true;
}
setValue(value) {
if (this.hasDeoptedValue) return;
this.hasValue = true;
this.value = value;
}
clearValue() {
this.hasDeoptedValue = false;
this.hasValue = false;
this.value = null;
}
reassign(path) {
this.constant = false;
if (this.constantViolations.includes(path)) {
return;
}
this.constantViolations.push(path);
}
reference(path) {
if (this.referencePaths.includes(path)) {
return;
}
this.referenced = true;
this.references++;
this.referencePaths.push(path);
}
dereference() {
this.references--;
this.referenced = !!this.references;
}
}
function isInitInLoop(path) {
const isFunctionDeclarationOrHasInit = !path.isVariableDeclarator() || path.node.init;
for (let {
parentPath,
key
} = path; parentPath; {
parentPath,
key
} = parentPath) {
if (parentPath.isFunctionParent()) return false;
if (key === "left" && parentPath.isForXStatement() || isFunctionDeclarationOrHasInit && key === "body" && parentPath.isLoop()) {
return true;
}
}
return false;
}
let pathsCache = new WeakMap();
let scope = new WeakMap();
function clear() {
clearPath();
clearScope();
}
function clearPath() {
pathsCache = new WeakMap();
}
function clearScope() {
scope = new WeakMap();
}
function getCachedPaths(path) {
const {
parent,
parentPath
} = path;
return parentPath ? parentPath._store : pathsCache.get(parent);
}
function getOrCreateCachedPaths(node, parentPath) {
if (parentPath) {
return parentPath._store ||= new Map();
}
let paths = pathsCache.get(node);
if (!paths) pathsCache.set(node, paths = new Map());
return paths;
}
const cache = /*#__PURE__*/Object.defineProperty({
__proto__: null,
clear,
clearPath,
clearScope,
getCachedPaths,
getOrCreateCachedPaths,
get path () { return pathsCache; },
get scope () { return scope; }
}, Symbol.toStringTag, { value: 'Module' });
const {
assignmentExpression: assignmentExpression$3,
cloneNode: cloneNode$3,
getBindingIdentifiers: getBindingIdentifiers$2,
identifier: identifier$3,
isArrayExpression,
isBinary,
isCallExpression: isCallExpression$1,
isClass,
isClassBody,
isClassDeclaration,
isExportAllDeclaration,
isExportDefaultDeclaration,
isExportNamedDeclaration: isExportNamedDeclaration$1,
isFunctionDeclaration,
isIdentifier: isIdentifier$4,
isImportDeclaration,
isLiteral: isLiteral$1,
isMemberExpression,
isMethod,
isModuleSpecifier,
isNullLiteral,
isObjectExpression,
isProperty,
isPureish,
isRegExpLiteral,
isSuper: isSuper$1,
isTaggedTemplateExpression,
isTemplateLiteral,
isThisExpression,
isUnaryExpression,
isVariableDeclaration: isVariableDeclaration$1,
expressionStatement: expressionStatement$3,
matchesPattern: matchesPattern$1,
toIdentifier,
variableDeclaration: variableDeclaration$1,
variableDeclarator: variableDeclarator$1,
isObjectProperty,
isTopicReference,
isMetaProperty,
isPrivateName,
isExportDeclaration,
sequenceExpression: sequenceExpression$2
} = _t;
function gatherNodeParts(node, parts) {
switch (node?.type) {
default:
if (isImportDeclaration(node) || isExportDeclaration(node)) {
if ((isExportAllDeclaration(node) || isExportNamedDeclaration$1(node) || isImportDeclaration(node)) && node.source) {
gatherNodeParts(node.source, parts);
} else if ((isExportNamedDeclaration$1(node) || isImportDeclaration(node)) && node.specifiers?.length) {
for (const e of node.specifiers) gatherNodeParts(e, parts);
} else if ((isExportDefaultDeclaration(node) || isExportNamedDeclaration$1(node)) && node.declaration) {
gatherNodeParts(node.declaration, parts);
}
} else if (isModuleSpecifier(node)) {
gatherNodeParts(node.local, parts);
} else if (isLiteral$1(node) && !isNullLiteral(node) && !isRegExpLiteral(node) && !isTemplateLiteral(node)) {
parts.push(node.value);
}
break;
case "MemberExpression":
case "OptionalMemberExpression":
case "JSXMemberExpression":
gatherNodeParts(node.object, parts);
gatherNodeParts(node.property, parts);
break;
case "Identifier":
case "JSXIdentifier":
parts.push(node.name);
break;
case "CallExpression":
case "OptionalCallExpression":
case "NewExpression":
gatherNodeParts(node.callee, parts);
break;
case "ObjectExpression":
case "ObjectPattern":
for (const e of node.properties) {
gatherNodeParts(e, parts);
}
break;
case "SpreadElement":
case "RestElement":
gatherNodeParts(node.argument, parts);
break;
case "ObjectProperty":
case "ObjectMethod":
case "ClassProperty":
case "ClassMethod":
case "ClassPrivateProperty":
case "ClassPrivateMethod":
gatherNodeParts(node.key, parts);
break;
case "ThisExpression":
parts.push("this");
break;
case "Super":
parts.push("super");
break;
case "Import":
case "ImportExpression":
parts.push("import");
break;
case "DoExpression":
parts.push("do");
break;
case "YieldExpression":
parts.push("yield");
gatherNodeParts(node.argument, parts);
break;
case "AwaitExpression":
parts.push("await");
gatherNodeParts(node.argument, parts);
break;
case "AssignmentExpression":
gatherNodeParts(node.left, parts);
break;
case "VariableDeclarator":
gatherNodeParts(node.id, parts);
break;
case "FunctionExpression":
case "FunctionDeclaration":
case "ClassExpression":
case "ClassDeclaration":
gatherNodeParts(node.id, parts);
break;
case "PrivateName":
gatherNodeParts(node.id, parts);
break;
case "ParenthesizedExpression":
gatherNodeParts(node.expression, parts);
break;
case "UnaryExpression":
case "UpdateExpression":
gatherNodeParts(node.argument, parts);
break;
case "MetaProperty":
gatherNodeParts(node.meta, parts);
gatherNodeParts(node.property, parts);
break;
case "JSXElement":
gatherNodeParts(node.openingElement, parts);
break;
case "JSXOpeningElement":
gatherNodeParts(node.name, parts);
break;
case "JSXFragment":
gatherNodeParts(node.openingFragment, parts);
break;
case "JSXOpeningFragment":
parts.push("Fragment");
break;
case "JSXNamespacedName":
gatherNodeParts(node.namespace, parts);
gatherNodeParts(node.name, parts);
break;
}
}
function resetScope(scope) {
if (scope.path.type === "Program") {
scope.referencesSet = new Set();
scope.uidsSet = new Set();
}
scope.bindings = Object.create(null);
scope.globals = Object.create(null);
}
function isAnonymousFunctionExpression(path) {
return path.isFunctionExpression() && !path.node.id || path.isArrowFunctionExpression();
}
const collectorVisitor = {
ForStatement(path) {
const declar = path.get("init");
if (declar.isVar()) {
const {
scope
} = path;
const parentScope = scope.getFunctionParent() || scope.getProgramParent();
parentScope.registerBinding("var", declar);
}
},
Declaration(path) {
if (path.isBlockScoped()) return;
if (path.isImportDeclaration()) return;
if (path.isExportDeclaration()) return;
const parent = path.scope.getFunctionParent() || path.scope.getProgramParent();
parent.registerDeclaration(path);
},
ImportDeclaration(path) {
const parent = path.scope.getBlockParent();
parent.registerDeclaration(path);
},
TSImportEqualsDeclaration(path) {
const parent = path.scope.getBlockParent();
parent.registerDeclaration(path);
},
ReferencedIdentifier(path, state) {
if (_t.isTSQualifiedName(path.parent) && path.parent.right === path.node) {
return;
}
if (path.parentPath.isTSImportEqualsDeclaration()) return;
state.references.push(path);
},
ForXStatement(path, state) {
const left = path.get("left");
if (left.isPattern() || left.isIdentifier()) {
state.constantViolations.push(path);
} else if (left.isVar()) {
const {
scope
} = path;
const parentScope = scope.getFunctionParent() || scope.getProgramParent();
parentScope.registerBinding("var", left);
}
},
ExportDeclaration: {
exit(path) {
const {
node,
scope
} = path;
if (isExportAllDeclaration(node)) return;
const declar = node.declaration;
if (isClassDeclaration(declar) || isFunctionDeclaration(declar)) {
const id = declar.id;
if (!id) return;
const binding = scope.getBinding(id.name);
binding?.reference(path);
} else if (isVariableDeclaration$1(declar)) {
for (const decl of declar.declarations) {
for (const name of Object.keys(getBindingIdentifiers$2(decl))) {
const binding = scope.getBinding(name);
binding?.reference(path);
}
}
}
}
},
LabeledStatement(path) {
path.scope.getBlockParent().registerDeclaration(path);
},
AssignmentExpression(path, state) {
state.assignments.push(path);
},
UpdateExpression(path, state) {
state.constantViolations.push(path);
},
UnaryExpression(path, state) {
if (path.node.operator === "delete") {
state.constantViolations.push(path);
}
},
BlockScoped(path) {
let scope = path.scope;
if (scope.path === path) scope = scope.parent;
const parent = scope.getBlockParent();
parent.registerDeclaration(path);
if (path.isClassDeclaration() && path.node.id) {
const id = path.node.id;
const name = id.name;
path.scope.bindings[name] = path.scope.parent.getBinding(name);
}
},
CatchClause(path) {
path.scope.registerBinding("let", path);
},
Function(path) {
const params = path.get("params");
for (const param of params) {
path.scope.registerBinding("param", param);
}
if (path.isFunctionExpression() && path.node.id) {
path.scope.registerBinding("local", path.get("id"), path);
}
},
ClassExpression(path) {
if (path.node.id) {
path.scope.registerBinding("local", path.get("id"), path);
}
},
TSTypeAnnotation(path) {
path.skip();
}
};
let scopeVisitor;
let uid = 0;
class Scope {
uid;
path;
block;
inited;
labels;
bindings;
referencesSet;
globals;
uidsSet;
data;
crawling;
constructor(path) {
const {
node
} = path;
const cached = scope.get(node);
if (cached?.path === path) {
return cached;
}
scope.set(node, this);
this.uid = uid++;
this.block = node;
this.path = path;
this.labels = new Map();
this.inited = false;
}
static globals = [...globalsBuiltinLower, ...globalsBuiltinUpper];
static contextVariables = ["arguments", "undefined", "Infinity", "NaN"];
get parent() {
let parent,
path = this.path;
do {
const shouldSkip = path.key === "key" || path.listKey === "decorators";
path = path.parentPath;
if (shouldSkip && path.isMethod()) path = path.parentPath;
if (path?.isScope()) parent = path;
} while (path && !parent);
return parent?.scope;
}
get references() {
throw new Error("Scope#references is not available in Babel 8. Use Scope#referencesSet instead.");
}
get uids() {
throw new Error("Scope#uids is not available in Babel 8. Use Scope#uidsSet instead.");
}
generateDeclaredUidIdentifier(name) {
const id = this.generateUidIdentifier(name);
this.push({
id
});
return cloneNode$3(id);
}
generateUidIdentifier(name) {
return identifier$3(this.generateUid(name));
}
generateUid(name = "temp") {
name = toIdentifier(name).replace(/^_+/, "").replace(/\d+$/g, "");
let uid;
let i = 0;
do {
uid = `_${name}`;
if (i >= 11) uid += i - 1;else if (i >= 9) uid += i - 9;else if (i >= 1) uid += i + 1;
i++;
} while (this.hasLabel(uid) || this.hasBinding(uid) || this.hasGlobal(uid) || this.hasReference(uid));
const program = this.getProgramParent();
program.referencesSet.add(uid);
program.uidsSet.add(uid);
return uid;
}
generateUidBasedOnNode(node, defaultName) {
const parts = [];
gatherNodeParts(node, parts);
let id = parts.join("$");
id = id.replace(/^_/, "") || defaultName || "ref";
return this.generateUid(id.slice(0, 20));
}
generateUidIdentifierBasedOnNode(node, defaultName) {
return identifier$3(this.generateUidBasedOnNode(node, defaultName));
}
isStatic(node) {
if (isThisExpression(node) || isSuper$1(node) || isTopicReference(node)) {
return true;
}
if (isIdentifier$4(node)) {
const binding = this.getBinding(node.name);
if (binding) {
return binding.constant;
} else {
return this.hasBinding(node.name);
}
}
return false;
}
maybeGenerateMemoised(node, dontPush) {
if (this.isStatic(node)) {
return null;
} else {
const id = this.generateUidIdentifierBasedOnNode(node);
if (!dontPush) {
this.push({
id
});
return cloneNode$3(id);
}
return id;
}
}
checkBlockScopedCollisions(local, kind, name, id) {
if (kind === "param") return;
if (local.kind === "local") return;
const duplicate = kind === "let" || local.kind === "let" || local.kind === "const" || local.kind === "module" || local.kind === "param" && kind === "const";
if (duplicate) {
throw this.path.hub.buildError(id, `Duplicate declaration "${name}"`, TypeError);
}
}
rename(oldName, newName) {
const binding = this.getBinding(oldName);
if (binding) {
newName ||= this.generateUidIdentifier(oldName).name;
const renamer = new Renamer(binding, oldName, newName);
renamer.rename();
}
}
dump() {
const sep = "-".repeat(60);
console.log(sep);
let scope = this;
do {
console.log("#", scope.block.type);
for (const name of Object.keys(scope.bindings)) {
const binding = scope.bindings[name];
console.log(" -", name, {
constant: binding.constant,
references: binding.references,
violations: binding.constantViolations.length,
kind: binding.kind
});
}
} while (scope = scope.parent);
console.log(sep);
}
hasLabel(name) {
return !!this.getLabel(name);
}
getLabel(name) {
return this.labels.get(name);
}
registerLabel(path) {
this.labels.set(path.node.label.name, path);
}
registerDeclaration(path) {
if (path.isLabeledStatement()) {
this.registerLabel(path);
} else if (path.isFunctionDeclaration()) {
this.registerBinding("hoisted", path.get("id"), path);
} else if (path.isVariableDeclaration()) {
const declarations = path.get("declarations");
const {
kind
} = path.node;
for (const declar of declarations) {
this.registerBinding(kind === "using" || kind === "await using" ? "const" : kind, declar);
}
} else if (path.isClassDeclaration()) {
if (path.node.declare) return;
this.registerBinding("let", path);
} else if (path.isImportDeclaration()) {
const isTypeDeclaration = path.node.importKind === "type" || path.node.importKind === "typeof";
const specifiers = path.get("specifiers");
for (const specifier of specifiers) {
const isTypeSpecifier = isTypeDeclaration || specifier.isImportSpecifier() && (specifier.node.importKind === "type" || specifier.node.importKind === "typeof");
this.registerBinding(isTypeSpecifier ? "unknown" : "module", specifier);
}
} else if (path.isExportDeclaration()) {
const declar = path.get("declaration");
if (declar.isClassDeclaration() || declar.isFunctionDeclaration() || declar.isVariableDeclaration()) {
this.registerDeclaration(declar);
}
} else {
this.registerBinding("unknown", path);
}
}
registerConstantViolation(path) {
const ids = path.getAssignmentIdentifiers();
for (const name of Object.keys(ids)) {
this.getBinding(name)?.reassign(path);
}
}
registerBinding(kind, path, bindingPath = path) {
if (!kind) throw new ReferenceError("no `kind`");
if (path.isVariableDeclaration()) {
const declarators = path.get("declarations");
for (const declar of declarators) {
this.registerBinding(kind, declar);
}
return;
}
const parent = this.getProgramParent();
const ids = path.getOuterBindingIdentifiers(true);
for (const name of Object.keys(ids)) {
parent.referencesSet.add(name);
for (const id of ids[name]) {
const local = this.getOwnBinding(name);
if (local) {
if (local.identifier === id) continue;
this.checkBlockScopedCollisions(local, kind, name, id);
}
if (local) {
local.reassign(bindingPath);
} else {
this.bindings[name] = new Binding({
identifier: id,
scope: this,
path: bindingPath,
kind: kind
});
}
}
}
}
addGlobal(node) {
this.globals[node.name] = node;
}
hasUid(name) {
return this.getProgramParent().uidsSet.has(name);
}
hasGlobal(name) {
let scope = this;
do {
if (scope.globals[name]) return true;
} while (scope = scope.parent);
return false;
}
hasReference(name) {
return this.getProgramParent().referencesSet.has(name);
}
isPure(node, constantsOnly) {
if (isIdentifier$4(node)) {
const binding = this.getBinding(node.name);
if (!binding) return false;
if (constantsOnly) return binding.constant;
return true;
} else if (isThisExpression(node) || isMetaProperty(node) || isTopicReference(node) || isPrivateName(node)) {
return true;
} else if (isClass(node)) {
if (node.superClass && !this.isPure(node.superClass, constantsOnly)) {
return false;
}
if (node.decorators?.length > 0) {
return false;
}
return this.isPure(node.body, constantsOnly);
} else if (isClassBody(node)) {
for (const method of node.body) {
if (!this.isPure(method, constantsOnly)) return false;
}
return true;
} else if (isBinary(node)) {
return this.isPure(node.left, constantsOnly) && this.isPure(node.right, constantsOnly);
} else if (isArrayExpression(node)) {
for (const elem of node.elements) {
if (elem !== null && !this.isPure(elem, constantsOnly)) return false;
}
return true;
} else if (isObjectExpression(node)) {
for (const prop of node.properties) {
if (!this.isPure(prop, constantsOnly)) return false;
}
return true;
} else if (isMethod(node)) {
if (node.computed && !this.isPure(node.key, constantsOnly)) return false;
if (node.decorators?.length > 0) {
return false;
}
return true;
} else if (isProperty(node)) {
if (node.computed && !this.isPure(node.key, constantsOnly)) return false;
if (node.decorators?.length > 0) {
return false;
}
if (isObjectProperty(node) || node.static) {
if (node.value !== null && !this.isPure(node.value, constantsOnly)) {
return false;
}
}
return true;
} else if (isUnaryExpression(node)) {
return this.isPure(node.argument, constantsOnly);
} else if (isTemplateLiteral(node)) {
for (const expression of node.expressions) {
if (!this.isPure(expression, constantsOnly)) return false;
}
return true;
} else if (isTaggedTemplateExpression(node)) {
return matchesPattern$1(node.tag, "String.raw") && !this.hasBinding("String", {
noGlobals: true
}) && this.isPure(node.quasi, constantsOnly);
} else if (isMemberExpression(node)) {
return !node.computed && isIdentifier$4(node.object) && node.object.name === "Symbol" && isIdentifier$4(node.property) && node.property.name !== "for" && !this.hasBinding("Symbol", {
noGlobals: true
});
} else if (isCallExpression$1(node)) {
return matchesPattern$1(node.callee, "Symbol.for") && !this.hasBinding("Symbol", {
noGlobals: true
}) && node.arguments.length === 1 && _t.isStringLiteral(node.arguments[0]);
} else {
return isPureish(node);
}
}
setData(key, val) {
return this.data[key] = val;
}
getData(key) {
let scope = this;
do {
const data = scope.data[key];
if (data != null) return data;
} while (scope = scope.parent);
}
removeData(key) {
let scope = this;
do {
const data = scope.data[key];
if (data != null) scope.data[key] = null;
} while (scope = scope.parent);
}
init() {
if (!this.inited) {
this.inited = true;
this.crawl();
}
}
crawl() {
const path = this.path;
resetScope(this);
this.data = Object.create(null);
let scope = this;
do {
if (scope.crawling) return;
if (scope.path.isProgram()) {
break;
}
} while (scope = scope.parent);
const programParent = scope;
const state = {
references: [],
constantViolations: [],
assignments: []
};
this.crawling = true;
scopeVisitor ||= traverse.visitors.merge([{
Scope(path) {
resetScope(path.scope);
}
}, collectorVisitor]);
if (path.type !== "Program") {
const typeVisitors = scopeVisitor[path.type];
if (typeVisitors) {
for (const visit of typeVisitors.enter) {
visit.call(state, path, state);
}
}
}
traverseForScope(path, scopeVisitor, state);
this.crawling = false;
for (const path of state.assignments) {
const ids = path.getAssignmentIdentifiers();
for (const name of Object.keys(ids)) {
if (path.scope.getBinding(name)) continue;
programParent.addGlobal(ids[name]);
}
path.scope.registerConstantViolation(path);
}
for (const ref of state.references) {
const binding = ref.scope.getBinding(ref.node.name);
if (binding) {
binding.reference(ref);
} else {
programParent.addGlobal(ref.node);
}
}
for (const path of state.constantViolations) {
path.scope.registerConstantViolation(path);
}
}
push(opts) {
let path = this.path;
if (path.isPattern()) {
path = this.getPatternParent().path;
} else if (!path.isBlockStatement() && !path.isProgram()) {
path = this.getBlockParent().path;
}
if (path.isSwitchStatement()) {
path = (this.getFunctionParent() || this.getProgramParent()).path;
}
const {
init,
unique,
kind = "var",
id
} = opts;
if (!init && !unique && (kind === "var" || kind === "let") && isAnonymousFunctionExpression(path) && isCallExpression$1(path.parent, {
callee: path.node
}) && path.parent.arguments.length <= path.node.params.length && isIdentifier$4(id)) {
path.pushContainer("params", id);
path.scope.registerBinding("param", path.get("params")[path.node.params.length - 1]);
return;
}
if (path.isLoop() || path.isCatchClause() || path.isFunction()) {
path.ensureBlock();
path = path.get("body");
}
const blockHoist = opts._blockHoist == null ? 2 : opts._blockHoist;
const dataKey = `declaration:${kind}:${blockHoist}`;
let declarPath = !unique && path.getData(dataKey);
if (!declarPath) {
const declar = variableDeclaration$1(kind, []);
declar._blockHoist = blockHoist;
[declarPath] = path.unshiftContainer("body", [declar]);
if (!unique) path.setData(dataKey, declarPath);
}
const declarator = variableDeclarator$1(id, init);
const len = declarPath.node.declarations.push(declarator);
path.scope.registerBinding(kind, declarPath.get("declarations")[len - 1]);
}
getProgramParent() {
let scope = this;
do {
if (scope.path.isProgram()) {
return scope;
}
} while (scope = scope.parent);
throw new Error("Couldn't find a Program");
}
getFunctionParent() {
let scope = this;
do {
if (scope.path.isFunctionParent()) {
return scope;
}
} while (scope = scope.parent);
return null;
}
getBlockParent() {
let scope = this;
do {
if (scope.path.isBlockParent()) {
return scope;
}
} while (scope = scope.parent);
throw new Error("We couldn't find a BlockStatement, For, Switch, Function, Loop or Program...");
}
getPatternParent() {
let scope = this;
do {
if (!scope.path.isPattern()) {
return scope.getBlockParent();
}
} while (scope = scope.parent.parent);
throw new Error("We couldn't find a BlockStatement, For, Switch, Function, Loop or Program...");
}
getAllBindings() {
const ids = Object.create(null);
let scope = this;
do {
for (const key of Object.keys(scope.bindings)) {
if (key in ids === false) {
ids[key] = scope.bindings[key];
}
}
scope = scope.parent;
} while (scope);
return ids;
}
bindingIdentifierEquals(name, node) {
return this.getBindingIdentifier(name) === node;
}
getBinding(name) {
let scope = this;
let previousPath;
do {
const binding = scope.getOwnBinding(name);
if (binding) {
if (previousPath?.isPattern() && binding.kind !== "param" && binding.kind !== "local") ; else {
return binding;
}
} else if (!binding && name === "arguments" && scope.path.isFunction() && !scope.path.isArrowFunctionExpression()) {
break;
}
previousPath = scope.path;
} while (scope = scope.parent);
}
getOwnBinding(name) {
return this.bindings[name];
}
getBindingIdentifier(name) {
return this.getBinding(name)?.identifier;
}
getOwnBindingIdentifier(name) {
const binding = this.bindings[name];
return binding?.identifier;
}
hasOwnBinding(name) {
return !!this.getOwnBinding(name);
}
hasBinding(name, opts) {
if (!name) return false;
let noGlobals;
let noUids;
let upToScope;
if (typeof opts === "object") {
noGlobals = opts.noGlobals;
noUids = opts.noUids;
upToScope = opts.upToScope;
} else if (typeof opts === "boolean") {
noGlobals = opts;
}
let scope = this;
do {
if (upToScope === scope) {
break;
}
if (scope.hasOwnBinding(name)) {
return true;
}
} while (scope = scope.parent);
if (!noUids && this.hasUid(name)) return true;
if (!noGlobals && Scope.globals.includes(name)) return true;
if (!noGlobals && Scope.contextVariables.includes(name)) return true;
return false;
}
parentHasBinding(name, opts) {
return this.parent?.hasBinding(name, opts);
}
moveBindingTo(name, scope) {
const info = this.getBinding(name);
if (info) {
info.scope.removeOwnBinding(name);
info.scope = scope;
scope.bindings[name] = info;
}
}
removeOwnBinding(name) {
delete this.bindings[name];
}
removeBinding(name) {
this.getBinding(name)?.scope.removeOwnBinding(name);
this.getProgramParent().uidsSet.delete(name);
}
hoistVariables(emit = id => this.push({
id
})) {
this.crawl();
const seen = new Set();
for (const name of Object.keys(this.bindings)) {
const binding = this.bindings[name];
if (!binding) continue;
const {
path
} = binding;
if (!path.isVariableDeclarator()) continue;
const {
parent,
parentPath
} = path;
if (parent.kind !== "var" || seen.has(parent)) continue;
seen.add(path.parent);
let firstId;
const init = [];
for (const decl of parent.declarations) {
firstId ??= decl.id;
if (decl.init) {
init.push(assignmentExpression$3("=", decl.id, decl.init));
}
const ids = Object.keys(getBindingIdentifiers$2(decl, false, true, true));
for (const name of ids) {
emit(identifier$3(name), decl.init != null);
}
}
if (parentPath.parentPath.isForXStatement({
left: parent
})) {
parentPath.replaceWith(firstId);
} else if (init.length === 0) {
parentPath.remove();
} else {
const expr = init.length === 1 ? init[0] : sequenceExpression$2(init);
if (parentPath.parentPath.isForStatement({
init: pa