UNPKG

@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
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