UNPKG

15.9 kBSource Map (JSON)View Raw
1{"version":3,"names":["declare","api","opts","assertVersion","throwIfClosureRequired","tdz","tdzEnabled","Error","name","visitor","traverse","visitors","merge","annexB33FunctionsVisitor","Loop","path","state","isForStatement","headPath","get","isForXStatement","needsBodyWrap","markNeedsBodyWrap","buildCodeFrameError","body","bodyScope","isBlockStatement","scope","bindings","getLoopBodyBindings","binding","capturedInClosure","getUsageInBody","captured","updatedBindingsUsages","Map","isBlockScoped","node","names","Object","keys","getBindingIdentifiers","headScope","hasOwnBinding","getOwnBinding","crawl","usages","hasConstantViolations","push","parent","hasBinding","hasGlobal","newName","generateUid","rename","set","varPath","wrapLoopBody","isVariableDeclaration","transformBlockScopedVariable","unwrapFunctionEnvironment","VariableDeclaration","ClassDeclaration","id","parentPath","isVarScope","noUids","conflictingFunctionsVisitor","Scope","kind","skip","dynamicTDZNames","validateUsage","bindingNames","isInLoop","isVarInLoopHead","length","decl","declarations","init","buildUndefinedNode","blockScope","varScope","getFunctionParent","getProgramParent","moveBindingTo","t","identifier","addHelper","isLetOrConst","isLoop","isFunctionParent","BLOCK_SCOPED_SYMBOL"],"sources":["../src/index.ts"],"sourcesContent":["import { declare } from \"@babel/helper-plugin-utils\";\nimport type { NodePath, Scope, Visitor } from \"@babel/traverse\";\nimport { type PluginPass, types as t, traverse } from \"@babel/core\";\n\nimport {\n getLoopBodyBindings,\n getUsageInBody,\n isVarInLoopHead,\n wrapLoopBody,\n} from \"./loop\";\nimport { validateUsage } from \"./validation\";\nimport { annexB33FunctionsVisitor, isVarScope } from \"./annex-B_3_3\";\n\nexport interface Options {\n tdz?: boolean;\n throwIfClosureRequired?: boolean;\n}\n\nexport default declare((api, opts: Options) => {\n api.assertVersion(7);\n\n const { throwIfClosureRequired = false, tdz: tdzEnabled = false } = opts;\n if (typeof throwIfClosureRequired !== \"boolean\") {\n throw new Error(`.throwIfClosureRequired must be a boolean, or undefined`);\n }\n if (typeof tdzEnabled !== \"boolean\") {\n throw new Error(`.tdz must be a boolean, or undefined`);\n }\n\n return {\n name: \"transform-block-scoping\",\n\n visitor: traverse.visitors.merge<PluginPass>([\n // TODO: Consider adding an option to control Annex B behavior.\n annexB33FunctionsVisitor,\n {\n Loop(path: NodePath<t.Loop>, state) {\n const isForStatement = path.isForStatement();\n const headPath = isForStatement\n ? path.get(\"init\")\n : path.isForXStatement()\n ? path.get(\"left\")\n : null;\n\n let needsBodyWrap = false;\n const markNeedsBodyWrap = () => {\n if (throwIfClosureRequired) {\n throw path.buildCodeFrameError(\n \"Compiling let/const in this block would add a closure \" +\n \"(throwIfClosureRequired).\",\n );\n }\n needsBodyWrap = true;\n };\n\n const body = path.get(\"body\");\n let bodyScope: Scope | null;\n if (body.isBlockStatement()) {\n bodyScope = body.scope;\n\n const bindings = getLoopBodyBindings(path);\n for (const binding of bindings) {\n const { capturedInClosure } = getUsageInBody(binding, path);\n if (capturedInClosure) markNeedsBodyWrap();\n }\n }\n\n const captured: string[] = [];\n const updatedBindingsUsages: Map<string, NodePath<t.Identifier>[]> =\n new Map();\n\n if (headPath && isBlockScoped(headPath.node)) {\n const names = Object.keys(headPath.getBindingIdentifiers());\n const headScope = headPath.scope;\n\n for (let name of names) {\n if (bodyScope?.hasOwnBinding(name)) continue; // shadowed\n\n let binding = headScope.getOwnBinding(name);\n if (!binding) {\n headScope.crawl();\n binding = headScope.getOwnBinding(name);\n }\n const { usages, capturedInClosure, hasConstantViolations } =\n getUsageInBody(binding, path);\n\n if (capturedInClosure) {\n markNeedsBodyWrap();\n captured.push(name);\n } else if (\n headScope.parent.hasBinding(name) ||\n headScope.parent.hasGlobal(name)\n ) {\n // If the binding is not captured, there is no need\n // of adding it to the closure param. However, rename\n // it if it shadows an outer binding, because the\n // closure will be moved to an outer level.\n const newName = headScope.generateUid(name);\n headScope.rename(name, newName);\n name = newName;\n }\n\n if (isForStatement && hasConstantViolations) {\n updatedBindingsUsages.set(name, usages);\n }\n }\n }\n\n if (needsBodyWrap) {\n const varPath = wrapLoopBody(path, captured, updatedBindingsUsages);\n\n if (headPath?.isVariableDeclaration<t.Node>()) {\n // If we wrap the loop body, we transform the var\n // declaration in the loop head now, to avoid\n // invalid references that break other plugins:\n //\n // for (let head of x) {\n // let i = head;\n // setTimeout(() => i);\n // }\n //\n // would become\n //\n // function _loop() {\n // let i = head;\n // setTimeout(() => i);\n // }\n // for (let head of x) _loop();\n //\n // which references `head` in a scope where it's not visible.\n transformBlockScopedVariable(headPath, state, tdzEnabled);\n }\n\n varPath.get(\"declarations.0.init\").unwrapFunctionEnvironment();\n }\n },\n\n VariableDeclaration(path, state) {\n transformBlockScopedVariable(path, state, tdzEnabled);\n },\n\n // Class declarations are block-scoped: if there is\n // a class declaration in a nested block that conflicts\n // with an outer block-scoped binding, rename it.\n // TODO: Should this be moved to the classes plugin?\n ClassDeclaration(path) {\n const { id } = path.node;\n if (!id) return;\n\n const { scope } = path.parentPath;\n if (\n !isVarScope(scope) &&\n scope.parent.hasBinding(id.name, { noUids: true })\n ) {\n path.scope.rename(id.name);\n }\n },\n },\n ]),\n };\n});\n\nconst conflictingFunctionsVisitor: Visitor<{ names: string[] }> = {\n Scope(path, { names }) {\n for (const name of names) {\n const binding = path.scope.getOwnBinding(name);\n if (binding && binding.kind === \"hoisted\") {\n path.scope.rename(name);\n }\n }\n },\n \"Expression|Declaration\"(path) {\n path.skip();\n },\n};\n\nfunction transformBlockScopedVariable(\n path: NodePath<t.VariableDeclaration>,\n state: PluginPass,\n tdzEnabled: boolean,\n) {\n if (!isBlockScoped(path.node)) return;\n\n const dynamicTDZNames = validateUsage(path, state, tdzEnabled);\n\n path.node.kind = \"var\";\n\n const bindingNames = Object.keys(path.getBindingIdentifiers());\n for (const name of bindingNames) {\n const binding = path.scope.getOwnBinding(name);\n if (!binding) continue;\n binding.kind = \"var\";\n }\n\n if (\n (isInLoop(path) && !isVarInLoopHead(path)) ||\n dynamicTDZNames.length > 0\n ) {\n for (const decl of path.node.declarations) {\n // We explicitly add `void 0` to cases like\n // for (;;) { let a; }\n // to make sure that `a` doesn't keep the value from\n // the previous iteration.\n decl.init ??= path.scope.buildUndefinedNode();\n }\n }\n\n const blockScope = path.scope;\n const varScope =\n blockScope.getFunctionParent() || blockScope.getProgramParent();\n\n if (varScope !== blockScope) {\n for (const name of bindingNames) {\n let newName = name;\n if (\n // We pass `noUids` true because, if `name` was a generated\n // UID, it has been used to declare the current variable in\n // a nested scope and thus we don't need to assume that it\n // may be declared (but not registered yet) in an upper one.\n blockScope.parent.hasBinding(name, { noUids: true }) ||\n blockScope.parent.hasGlobal(name)\n ) {\n newName = blockScope.generateUid(name);\n blockScope.rename(name, newName);\n }\n\n blockScope.moveBindingTo(newName, varScope);\n }\n }\n\n blockScope.path.traverse(conflictingFunctionsVisitor, {\n names: bindingNames,\n });\n\n for (const name of dynamicTDZNames) {\n path.scope.push({\n id: t.identifier(name),\n init: state.addHelper(\"temporalUndefined\"),\n });\n }\n}\n\nfunction isLetOrConst(kind: string): kind is \"let\" | \"const\" {\n return kind === \"let\" || kind === \"const\";\n}\n\nfunction isInLoop(path: NodePath<t.Node>): boolean {\n if (!path.parentPath) return false;\n if (path.parentPath.isLoop()) return true;\n if (path.parentPath.isFunctionParent()) return false;\n return isInLoop(path.parentPath);\n}\n\nfunction isBlockScoped(node: t.Node): node is t.VariableDeclaration {\n if (!t.isVariableDeclaration(node)) return false;\n if (\n // @ts-expect-error Fixme: document symbol properties\n node[t.BLOCK_SCOPED_SYMBOL]\n ) {\n return true;\n }\n\n if (!isLetOrConst(node.kind) && node.kind !== \"using\") {\n return false;\n }\n\n return true;\n}\n"],"mappings":"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"}
\No newline at end of file