Befor generating
This commit is contained in:
1 parent
3383f4bf4a
commit
1aa1b5f625
6756 files changed
+649946
-1
No files matched your search
+22
@@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014-present Sebastian McKenzie and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
# @babel/plugin-transform-block-scoping
|
||||
|
||||
> Compile ES2015 block scoping (const and let) to ES5
|
||||
|
||||
See our website [@babel/plugin-transform-block-scoping](https://babeljs.io/docs/babel-plugin-transform-block-scoping) for more information.
|
||||
|
||||
## Install
|
||||
|
||||
Using npm:
|
||||
|
||||
```sh
|
||||
npm install --save-dev @babel/plugin-transform-block-scoping
|
||||
```
|
||||
|
||||
or using yarn:
|
||||
|
||||
```sh
|
||||
yarn add @babel/plugin-transform-block-scoping --dev
|
||||
```
|
||||
Generated
Vendored
+101
@@ -0,0 +1,101 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.annexB33FunctionsVisitor = void 0;
|
||||
exports.isVarScope = isVarScope;
|
||||
var _core = require("@babel/core");
|
||||
const annexB33FunctionsVisitor = exports.annexB33FunctionsVisitor = Object.assign({
|
||||
VariableDeclaration(path) {
|
||||
if (isStrict(path)) return;
|
||||
if (path.node.kind !== "var") return;
|
||||
const varScope = path.scope.getFunctionParent() || path.scope.getProgramParent();
|
||||
varScope.path.traverse(functionsToVarVisitor, {
|
||||
names: Object.keys(path.getBindingIdentifiers())
|
||||
});
|
||||
}
|
||||
}, {
|
||||
BlockStatement(path) {
|
||||
if (isStrict(path)) return;
|
||||
if (_core.types.isFunction(path.parent, {
|
||||
body: path.node
|
||||
})) return;
|
||||
transformStatementList(path.get("body"));
|
||||
},
|
||||
SwitchCase(path) {
|
||||
if (isStrict(path)) return;
|
||||
transformStatementList(path.get("consequent"));
|
||||
}
|
||||
});
|
||||
function transformStatementList(paths) {
|
||||
outer: for (const path of paths) {
|
||||
if (!path.isFunctionDeclaration()) continue;
|
||||
if (path.node.async || path.node.generator) return;
|
||||
const {
|
||||
scope
|
||||
} = path.parentPath;
|
||||
if (isVarScope(scope)) return;
|
||||
const {
|
||||
name
|
||||
} = path.node.id;
|
||||
let currScope = scope;
|
||||
do {
|
||||
if (currScope.parent.hasOwnBinding(name)) continue outer;
|
||||
currScope = currScope.parent;
|
||||
} while (!isVarScope(currScope));
|
||||
maybeTransformBlockScopedFunction(path);
|
||||
}
|
||||
}
|
||||
function maybeTransformBlockScopedFunction(path) {
|
||||
const {
|
||||
node,
|
||||
parentPath: {
|
||||
scope
|
||||
}
|
||||
} = path;
|
||||
const {
|
||||
id
|
||||
} = node;
|
||||
scope.removeOwnBinding(id.name);
|
||||
node.id = null;
|
||||
const varNode = _core.types.variableDeclaration("var", [_core.types.variableDeclarator(id, _core.types.toExpression(node))]);
|
||||
varNode._blockHoist = 2;
|
||||
const [varPath] = path.replaceWith(varNode);
|
||||
scope.registerDeclaration(varPath);
|
||||
}
|
||||
const functionsToVarVisitor = {
|
||||
Scope(path, {
|
||||
names
|
||||
}) {
|
||||
for (const name of names) {
|
||||
const binding = path.scope.getOwnBinding(name);
|
||||
if ((binding == null ? void 0 : binding.kind) === "hoisted") {
|
||||
maybeTransformBlockScopedFunction(binding.path);
|
||||
}
|
||||
}
|
||||
},
|
||||
"Expression|Declaration"(path) {
|
||||
path.skip();
|
||||
}
|
||||
};
|
||||
function isVarScope(scope) {
|
||||
return scope.path.isFunctionParent() || scope.path.isProgram();
|
||||
}
|
||||
function isStrict(path) {
|
||||
return !!path.find(({
|
||||
node
|
||||
}) => {
|
||||
var _node$directives;
|
||||
if (_core.types.isProgram(node)) {
|
||||
if (node.sourceType === "module") return true;
|
||||
} else if (_core.types.isClass(node)) {
|
||||
return true;
|
||||
} else if (!_core.types.isBlockStatement(node)) {
|
||||
return false;
|
||||
}
|
||||
return (_node$directives = node.directives) == null ? void 0 : _node$directives.some(directive => directive.value.value === "use strict");
|
||||
});
|
||||
}
|
||||
|
||||
//# sourceMappingURL=annex-B_3_3.js.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"names":["_core","require","annexB33FunctionsVisitor","exports","Object","assign","VariableDeclaration","path","isStrict","node","kind","varScope","scope","getFunctionParent","getProgramParent","traverse","functionsToVarVisitor","names","keys","getBindingIdentifiers","BlockStatement","t","isFunction","parent","body","transformStatementList","get","SwitchCase","paths","outer","isFunctionDeclaration","async","generator","parentPath","isVarScope","name","id","currScope","hasOwnBinding","maybeTransformBlockScopedFunction","removeOwnBinding","varNode","variableDeclaration","variableDeclarator","toExpression","_blockHoist","varPath","replaceWith","registerDeclaration","Scope","binding","getOwnBinding","Expression|Declaration","skip","isFunctionParent","isProgram","find","_node$directives","sourceType","isClass","isBlockStatement","directives","some","directive","value"],"sources":["../src/annex-B_3_3.ts"],"sourcesContent":["import { types as t } from \"@babel/core\";\nimport type { NodePath, Visitor, Scope } from \"@babel/core\";\n\n// Whenever a function declaration in a nested block scope\n// doesn't conflict with a block-scoped binding from an outer\n// scope, we transform it to a variable declaration.\n//\n// This implements the Annex B.3.3 behavior.\n//\n// TODO(Babel 8): Figure out how this should interact with\n// the transform-block-scoped functions plugin (it feels\n// wrong to handle this transform here), and what we want\n// to do with Annex B behavior in general.\n\n// To avoid confusing block-scoped variables transformed to\n// var with original vars, this transformation happens in two\n// different places:\n// 1. For functions that \"conflict\" with var-variables, in\n// the VariableDeclaration visitor.\n// 2. For functions that don't conflict with any variable,\n// in the FunctionDeclaration visitor.\n\nexport const annexB33FunctionsVisitor: Visitor = {\n VariableDeclaration(path) {\n if (isStrict(path)) return;\n if (path.node.kind !== \"var\") return;\n\n const varScope =\n path.scope.getFunctionParent() || path.scope.getProgramParent();\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n varScope.path.traverse(functionsToVarVisitor, {\n names: Object.keys(path.getBindingIdentifiers()),\n });\n },\n ...(process.env.BABEL_8_BREAKING\n ? {}\n : {\n // NOTE: These two visitors target the same nodes as the\n // block-scoped-functions plugin\n BlockStatement(path) {\n if (isStrict(path)) return;\n if (t.isFunction(path.parent, { body: path.node })) return;\n transformStatementList(path.get(\"body\"));\n },\n SwitchCase(path) {\n if (isStrict(path)) return;\n transformStatementList(path.get(\"consequent\"));\n },\n }),\n};\n\nfunction transformStatementList(paths: NodePath<t.Statement>[]) {\n outer: for (const path of paths) {\n if (!path.isFunctionDeclaration()) continue;\n // Annex B.3.3 only applies to plain functions.\n if (path.node.async || path.node.generator) return;\n\n const { scope } = path.parentPath;\n if (isVarScope(scope)) return;\n\n const { name } = path.node.id;\n let currScope = scope;\n do {\n if (currScope.parent.hasOwnBinding(name)) continue outer;\n currScope = currScope.parent;\n } while (!isVarScope(currScope));\n\n maybeTransformBlockScopedFunction(path);\n }\n}\n\nfunction maybeTransformBlockScopedFunction(\n path: NodePath<t.FunctionDeclaration>,\n) {\n const {\n node,\n parentPath: { scope },\n } = path;\n\n const { id } = node;\n scope.removeOwnBinding(id.name);\n node.id = null;\n\n const varNode = t.variableDeclaration(\"var\", [\n t.variableDeclarator(id, t.toExpression(node)),\n ]);\n // @ts-expect-error undocumented property\n varNode._blockHoist = 2;\n\n const [varPath] = path.replaceWith(varNode);\n scope.registerDeclaration(varPath);\n}\n\nconst functionsToVarVisitor: Visitor<{ names: string[] }> = {\n Scope(path, { names }) {\n for (const name of names) {\n const binding = path.scope.getOwnBinding(name);\n if (binding?.kind === \"hoisted\") {\n maybeTransformBlockScopedFunction(\n binding.path as NodePath<t.FunctionDeclaration>,\n );\n }\n }\n },\n \"Expression|Declaration\"(path) {\n path.skip();\n },\n};\n\nexport function isVarScope(scope: Scope) {\n return scope.path.isFunctionParent() || scope.path.isProgram();\n}\n\nfunction isStrict(path: NodePath) {\n return !!path.find(({ node }) => {\n if (t.isProgram(node)) {\n if (node.sourceType === \"module\") return true;\n } else if (t.isClass(node)) {\n return true;\n } else if (!t.isBlockStatement(node)) {\n return false;\n }\n\n return node.directives?.some(\n directive => directive.value.value === \"use strict\",\n );\n });\n}\n"],"mappings":";;;;;;;AAAA,IAAAA,KAAA,GAAAC,OAAA;AAsBO,MAALine truncated
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _helperPluginUtils = require("@babel/helper-plugin-utils");
|
||||
var _core = require("@babel/core");
|
||||
var _loop = require("./loop.js");
|
||||
var _validation = require("./validation.js");
|
||||
var _annexB_3_ = require("./annex-B_3_3.js");
|
||||
var _default = exports.default = (0, _helperPluginUtils.declare)((api, opts) => {
|
||||
api.assertVersion(7);
|
||||
const {
|
||||
throwIfClosureRequired = false,
|
||||
tdz: tdzEnabled = false
|
||||
} = opts;
|
||||
if (typeof throwIfClosureRequired !== "boolean") {
|
||||
throw new Error(`.throwIfClosureRequired must be a boolean, or undefined`);
|
||||
}
|
||||
if (typeof tdzEnabled !== "boolean") {
|
||||
throw new Error(`.tdz must be a boolean, or undefined`);
|
||||
}
|
||||
return {
|
||||
name: "transform-block-scoping",
|
||||
visitor: _core.traverse.visitors.merge([_annexB_3_.annexB33FunctionsVisitor, {
|
||||
Loop(path, state) {
|
||||
const isForStatement = path.isForStatement();
|
||||
const headPath = isForStatement ? path.get("init") : path.isForXStatement() ? path.get("left") : null;
|
||||
let needsBodyWrap = false;
|
||||
const markNeedsBodyWrap = () => {
|
||||
if (throwIfClosureRequired) {
|
||||
throw path.buildCodeFrameError("Compiling let/const in this block would add a closure " + "(throwIfClosureRequired).");
|
||||
}
|
||||
needsBodyWrap = true;
|
||||
};
|
||||
const body = path.get("body");
|
||||
let bodyScope;
|
||||
if (body.isBlockStatement()) {
|
||||
bodyScope = body.scope;
|
||||
}
|
||||
const bindings = (0, _loop.getLoopBodyBindings)(path);
|
||||
for (const binding of bindings) {
|
||||
const {
|
||||
capturedInClosure
|
||||
} = (0, _loop.getUsageInBody)(binding, path);
|
||||
if (capturedInClosure) markNeedsBodyWrap();
|
||||
}
|
||||
const captured = [];
|
||||
const updatedBindingsUsages = new Map();
|
||||
if (headPath && isBlockScoped(headPath)) {
|
||||
const names = Object.keys(headPath.getBindingIdentifiers());
|
||||
const headScope = headPath.scope;
|
||||
for (let name of names) {
|
||||
var _bodyScope;
|
||||
if ((_bodyScope = bodyScope) != null && _bodyScope.hasOwnBinding(name)) continue;
|
||||
let binding = headScope.getOwnBinding(name);
|
||||
if (!binding) {
|
||||
headScope.crawl();
|
||||
binding = headScope.getOwnBinding(name);
|
||||
}
|
||||
const {
|
||||
usages,
|
||||
capturedInClosure,
|
||||
hasConstantViolations
|
||||
} = (0, _loop.getUsageInBody)(binding, path);
|
||||
if (headScope.parent.hasBinding(name) || headScope.parent.hasGlobal(name)) {
|
||||
const newName = headScope.generateUid(name);
|
||||
headScope.rename(name, newName);
|
||||
name = newName;
|
||||
}
|
||||
if (capturedInClosure) {
|
||||
markNeedsBodyWrap();
|
||||
captured.push(name);
|
||||
}
|
||||
if (isForStatement && hasConstantViolations) {
|
||||
updatedBindingsUsages.set(name, usages);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (needsBodyWrap) {
|
||||
const varPath = (0, _loop.wrapLoopBody)(path, captured, updatedBindingsUsages);
|
||||
if (headPath != null && headPath.isVariableDeclaration()) {
|
||||
transformBlockScopedVariable(headPath, state, tdzEnabled);
|
||||
}
|
||||
varPath.get("declarations.0.init").unwrapFunctionEnvironment();
|
||||
}
|
||||
},
|
||||
VariableDeclaration(path, state) {
|
||||
transformBlockScopedVariable(path, state, tdzEnabled);
|
||||
},
|
||||
ClassDeclaration(path) {
|
||||
const {
|
||||
id
|
||||
} = path.node;
|
||||
if (!id) return;
|
||||
const {
|
||||
scope
|
||||
} = path.parentPath;
|
||||
if (!(0, _annexB_3_.isVarScope)(scope) && scope.parent.hasBinding(id.name, {
|
||||
noUids: true
|
||||
})) {
|
||||
path.scope.rename(id.name);
|
||||
}
|
||||
}
|
||||
}])
|
||||
};
|
||||
});
|
||||
const conflictingFunctionsVisitor = {
|
||||
Scope(path, {
|
||||
names
|
||||
}) {
|
||||
for (const name of names) {
|
||||
const binding = path.scope.getOwnBinding(name);
|
||||
if ((binding == null ? void 0 : binding.kind) === "hoisted") {
|
||||
path.scope.rename(name);
|
||||
}
|
||||
}
|
||||
},
|
||||
"Expression|Declaration"(path) {
|
||||
path.skip();
|
||||
}
|
||||
};
|
||||
function transformBlockScopedVariable(path, state, tdzEnabled) {
|
||||
if (!isBlockScoped(path)) return;
|
||||
const dynamicTDZNames = (0, _validation.validateUsage)(path, state, tdzEnabled);
|
||||
path.node.kind = "var";
|
||||
const bindingNames = Object.keys(path.getBindingIdentifiers());
|
||||
for (const name of bindingNames) {
|
||||
const binding = path.scope.getOwnBinding(name);
|
||||
if (!binding) continue;
|
||||
binding.kind = "var";
|
||||
}
|
||||
if (isInLoop(path) && !(0, _loop.isVarInLoopHead)(path) || dynamicTDZNames.length > 0) {
|
||||
for (const decl of path.node.declarations) {
|
||||
var _decl$init;
|
||||
(_decl$init = decl.init) != null ? _decl$init : decl.init = path.scope.buildUndefinedNode();
|
||||
}
|
||||
}
|
||||
const blockScope = path.scope;
|
||||
const varScope = blockScope.getFunctionParent() || blockScope.getProgramParent();
|
||||
if (varScope !== blockScope) {
|
||||
for (const name of bindingNames) {
|
||||
let newName = name;
|
||||
if (blockScope.parent.hasBinding(name, {
|
||||
noUids: true
|
||||
}) || blockScope.parent.hasGlobal(name)) {
|
||||
newName = blockScope.generateUid(name);
|
||||
blockScope.rename(name, newName);
|
||||
}
|
||||
blockScope.moveBindingTo(newName, varScope);
|
||||
}
|
||||
}
|
||||
blockScope.path.traverse(conflictingFunctionsVisitor, {
|
||||
names: bindingNames
|
||||
});
|
||||
for (const name of dynamicTDZNames) {
|
||||
path.scope.push({
|
||||
id: _core.types.identifier(name),
|
||||
init: state.addHelper("temporalUndefined")
|
||||
});
|
||||
}
|
||||
}
|
||||
function isLetOrConst(kind) {
|
||||
return kind === "let" || kind === "const";
|
||||
}
|
||||
function isInLoop(path) {
|
||||
if (!path.parentPath) return false;
|
||||
if (path.parentPath.isLoop()) return true;
|
||||
if (path.parentPath.isFunctionParent()) return false;
|
||||
return isInLoop(path.parentPath);
|
||||
}
|
||||
function isBlockScoped(path) {
|
||||
const {
|
||||
node
|
||||
} = path;
|
||||
if (!_core.types.isVariableDeclaration(node)) return false;
|
||||
const {
|
||||
kind
|
||||
} = node;
|
||||
if (kind === "using" || kind === "await using") {
|
||||
throw path.buildCodeFrameError(`The ${kind} declaration should be first transformed by \`@babel/plugin-transform-explicit-resource-management\`.`);
|
||||
} else if (!isLetOrConst(kind)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//# sourceMappingURL=index.js.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"names":["_helperPluginUtils","require","_core","_loop","_validation","_annexB_3_","_default","exports","default","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","names","Object","keys","getBindingIdentifiers","headScope","_bodyScope","hasOwnBinding","getOwnBinding","crawl","usages","hasConstantViolations","parent","hasBinding","hasGlobal","newName","generateUid","rename","push","set","varPath","wrapLoopBody","isVariableDeclaration","transformBlockScopedVariable","unwrapFunctionEnvironment","VariableDeclaration","ClassDeclaration","id","node","parentPath","isVarScope","noUids","conflictingFunctionsVisitor","Scope","kind","Expression|Declaration","skip","dynamicTDZNames","validateUsage","bindingNames","isInLoop","isVarInLoopHead","length","decl","declarations","_decl$init","init","buildUndefinedNode","blockScope","varScope","getFunctionParent","getProgramParent","moveBindingTo","t","identifier","addHelper","isLetOrConst","isLoop","isFunctionParent"],"sources":["../src/index.ts"],"sourcesContent":["import { declare } from \"@babel/helper-plugin-utils\";\nimport type { NodePath, Scope, Visitor, PluginPass } from \"@babel/core\";\nimport { types as t, traverse } from \"@babel/core\";\n\nimport {\n getLoopBodyBindings,\n getUsageInBody,\n isVarInLoopHead,\n wrapLoopBody,\n} from \"./loop.ts\";\nimport { validateUsage } from \"./validation.ts\";\nimport { annexB33FunctionsVisitor, isVarScope } from \"./annex-B_3_3.ts\";\n\nexport interface Options {\n tdz?: boolean;\n throwIfClosureRequired?: boolean;\n}\n\nexport default declare((api, opts: Options) => {\n api.assertVersion(REQUIRED_VERSION(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 const captured: string[] = [];\n const updatedBindingsUsages = new Map<\n string,\n NodePath<t.Identifier>[]\n >();\n\n if (headPath && isBlockScoped(headPath)) {\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 (\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.rLine truncated
|
||||
+291
@@ -0,0 +1,291 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.getLoopBodyBindings = getLoopBodyBindings;
|
||||
exports.getUsageInBody = getUsageInBody;
|
||||
exports.isVarInLoopHead = isVarInLoopHead;
|
||||
exports.wrapLoopBody = wrapLoopBody;
|
||||
var _core = require("@babel/core");
|
||||
const collectLoopBodyBindingsVisitor = {
|
||||
"Expression|Declaration|Loop"(path) {
|
||||
path.skip();
|
||||
},
|
||||
Scope(path, state) {
|
||||
if (path.isFunctionParent()) path.skip();
|
||||
const {
|
||||
bindings
|
||||
} = path.scope;
|
||||
for (const name of Object.keys(bindings)) {
|
||||
const binding = bindings[name];
|
||||
if (binding.kind === "let" || binding.kind === "const" || binding.kind === "hoisted") {
|
||||
state.blockScoped.push(binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
function getLoopBodyBindings(loopPath) {
|
||||
const state = {
|
||||
blockScoped: []
|
||||
};
|
||||
loopPath.traverse(collectLoopBodyBindingsVisitor, state);
|
||||
return state.blockScoped;
|
||||
}
|
||||
function getUsageInBody(binding, loopPath) {
|
||||
const seen = new WeakSet();
|
||||
let capturedInClosure = false;
|
||||
const constantViolations = filterMap(binding.constantViolations, path => {
|
||||
const {
|
||||
inBody,
|
||||
inClosure
|
||||
} = relativeLoopLocation(path, loopPath);
|
||||
if (!inBody) return null;
|
||||
capturedInClosure || (capturedInClosure = inClosure);
|
||||
const id = path.isUpdateExpression() ? path.get("argument") : path.isAssignmentExpression() ? path.get("left") : null;
|
||||
if (id) seen.add(id.node);
|
||||
return id;
|
||||
});
|
||||
const references = filterMap(binding.referencePaths, path => {
|
||||
if (seen.has(path.node)) return null;
|
||||
const {
|
||||
inBody,
|
||||
inClosure
|
||||
} = relativeLoopLocation(path, loopPath);
|
||||
if (!inBody) return null;
|
||||
capturedInClosure || (capturedInClosure = inClosure);
|
||||
return path;
|
||||
});
|
||||
return {
|
||||
capturedInClosure,
|
||||
hasConstantViolations: constantViolations.length > 0,
|
||||
usages: references.concat(constantViolations)
|
||||
};
|
||||
}
|
||||
function relativeLoopLocation(path, loopPath) {
|
||||
const bodyPath = loopPath.get("body");
|
||||
let inClosure = false;
|
||||
for (let currPath = path; currPath; currPath = currPath.parentPath) {
|
||||
if (currPath.isFunction() || currPath.isClass() || currPath.isMethod()) {
|
||||
inClosure = true;
|
||||
}
|
||||
if (currPath === bodyPath) {
|
||||
return {
|
||||
inBody: true,
|
||||
inClosure
|
||||
};
|
||||
} else if (currPath === loopPath) {
|
||||
return {
|
||||
inBody: false,
|
||||
inClosure
|
||||
};
|
||||
}
|
||||
}
|
||||
throw new Error("Internal Babel error: path is not in loop. Please report this as a bug.");
|
||||
}
|
||||
const collectCompletionsAndVarsVisitor = {
|
||||
Function(path) {
|
||||
path.skip();
|
||||
},
|
||||
LabeledStatement: {
|
||||
enter({
|
||||
node
|
||||
}, state) {
|
||||
state.labelsStack.push(node.label.name);
|
||||
},
|
||||
exit({
|
||||
node
|
||||
}, state) {
|
||||
const popped = state.labelsStack.pop();
|
||||
if (popped !== node.label.name) {
|
||||
throw new Error("Assertion failure. Please report this bug to Babel.");
|
||||
}
|
||||
}
|
||||
},
|
||||
Loop: {
|
||||
enter(_, state) {
|
||||
state.labellessContinueTargets++;
|
||||
state.labellessBreakTargets++;
|
||||
},
|
||||
exit(_, state) {
|
||||
state.labellessContinueTargets--;
|
||||
state.labellessBreakTargets--;
|
||||
}
|
||||
},
|
||||
SwitchStatement: {
|
||||
enter(_, state) {
|
||||
state.labellessBreakTargets++;
|
||||
},
|
||||
exit(_, state) {
|
||||
state.labellessBreakTargets--;
|
||||
}
|
||||
},
|
||||
"BreakStatement|ContinueStatement"(path, state) {
|
||||
const {
|
||||
label
|
||||
} = path.node;
|
||||
if (label) {
|
||||
if (state.labelsStack.includes(label.name)) return;
|
||||
} else if (path.isBreakStatement() ? state.labellessBreakTargets > 0 : state.labellessContinueTargets > 0) {
|
||||
return;
|
||||
}
|
||||
state.breaksContinues.push(path);
|
||||
},
|
||||
ReturnStatement(path, state) {
|
||||
state.returns.push(path);
|
||||
},
|
||||
VariableDeclaration(path, state) {
|
||||
if (path.parent === state.loopNode && isVarInLoopHead(path)) return;
|
||||
if (path.node.kind === "var") state.vars.push(path);
|
||||
}
|
||||
};
|
||||
function wrapLoopBody(loopPath, captured, updatedBindingsUsages) {
|
||||
const loopNode = loopPath.node;
|
||||
const state = {
|
||||
breaksContinues: [],
|
||||
returns: [],
|
||||
labelsStack: [],
|
||||
labellessBreakTargets: 0,
|
||||
labellessContinueTargets: 0,
|
||||
vars: [],
|
||||
loopNode
|
||||
};
|
||||
loopPath.traverse(collectCompletionsAndVarsVisitor, state);
|
||||
const callArgs = [];
|
||||
const closureParams = [];
|
||||
const updater = [];
|
||||
for (const [name, updatedUsage] of updatedBindingsUsages) {
|
||||
callArgs.push(_core.types.identifier(name));
|
||||
const innerName = loopPath.scope.generateUid(name);
|
||||
closureParams.push(_core.types.identifier(innerName));
|
||||
updater.push(_core.types.assignmentExpression("=", _core.types.identifier(name), _core.types.identifier(innerName)));
|
||||
for (const path of updatedUsage) path.replaceWith(_core.types.identifier(innerName));
|
||||
}
|
||||
for (const name of captured) {
|
||||
if (updatedBindingsUsages.has(name)) continue;
|
||||
callArgs.push(_core.types.identifier(name));
|
||||
closureParams.push(_core.types.identifier(name));
|
||||
}
|
||||
const id = loopPath.scope.generateUid("loop");
|
||||
const fn = _core.types.functionExpression(null, closureParams, _core.types.toBlock(loopNode.body));
|
||||
let call = _core.types.callExpression(_core.types.identifier(id), callArgs);
|
||||
const fnParent = loopPath.findParent(p => p.isFunction());
|
||||
if (fnParent) {
|
||||
const {
|
||||
async,
|
||||
generator
|
||||
} = fnParent.node;
|
||||
fn.async = async;
|
||||
fn.generator = generator;
|
||||
if (generator) call = _core.types.yieldExpression(call, true);else if (async) call = _core.types.awaitExpression(call);
|
||||
}
|
||||
const updaterNode = updater.length > 0 ? _core.types.expressionStatement(_core.types.sequenceExpression(updater)) : null;
|
||||
if (updaterNode) fn.body.body.push(updaterNode);
|
||||
const [varPath] = loopPath.insertBefore(_core.types.variableDeclaration("var", [_core.types.variableDeclarator(_core.types.identifier(id), fn)]));
|
||||
const bodyStmts = [];
|
||||
const varNames = [];
|
||||
for (const varPath of state.vars) {
|
||||
const assign = [];
|
||||
for (const decl of varPath.node.declarations) {
|
||||
varNames.push(...Object.keys(_core.types.getBindingIdentifiers(decl.id)));
|
||||
if (decl.init) {
|
||||
assign.push(_core.types.assignmentExpression("=", decl.id, decl.init));
|
||||
} else if (_core.types.isForXStatement(varPath.parent, {
|
||||
left: varPath.node
|
||||
})) {
|
||||
assign.push(decl.id);
|
||||
}
|
||||
}
|
||||
if (assign.length > 0) {
|
||||
const replacement = assign.length === 1 ? assign[0] : _core.types.sequenceExpression(assign);
|
||||
varPath.replaceWith(replacement);
|
||||
} else {
|
||||
varPath.remove();
|
||||
}
|
||||
}
|
||||
if (varNames.length) {
|
||||
varPath.pushContainer("declarations", varNames.map(name => _core.types.variableDeclarator(_core.types.identifier(name))));
|
||||
}
|
||||
const labelNum = state.breaksContinues.length;
|
||||
const returnNum = state.returns.length;
|
||||
if (labelNum + returnNum === 0) {
|
||||
bodyStmts.push(_core.types.expressionStatement(call));
|
||||
} else if (labelNum === 1 && returnNum === 0) {
|
||||
for (const path of state.breaksContinues) {
|
||||
const {
|
||||
node
|
||||
} = path;
|
||||
const {
|
||||
type,
|
||||
label
|
||||
} = node;
|
||||
let name = type === "BreakStatement" ? "break" : "continue";
|
||||
if (label) name += " " + label.name;
|
||||
path.replaceWith(_core.types.addComment(_core.types.returnStatement(_core.types.numericLiteral(1)), "trailing", " " + name, true));
|
||||
if (updaterNode) path.insertBefore(_core.types.cloneNode(updaterNode));
|
||||
bodyStmts.push(_core.template.statement.ast`
|
||||
if (${call}) ${node}
|
||||
`);
|
||||
}
|
||||
} else {
|
||||
const completionId = loopPath.scope.generateUid("ret");
|
||||
if (varPath.isVariableDeclaration()) {
|
||||
varPath.pushContainer("declarations", [_core.types.variableDeclarator(_core.types.identifier(completionId))]);
|
||||
bodyStmts.push(_core.types.expressionStatement(_core.types.assignmentExpression("=", _core.types.identifier(completionId), call)));
|
||||
} else {
|
||||
bodyStmts.push(_core.types.variableDeclaration("var", [_core.types.variableDeclarator(_core.types.identifier(completionId), call)]));
|
||||
}
|
||||
const injected = [];
|
||||
for (const path of state.breaksContinues) {
|
||||
const {
|
||||
node
|
||||
} = path;
|
||||
const {
|
||||
type,
|
||||
label
|
||||
} = node;
|
||||
let name = type === "BreakStatement" ? "break" : "continue";
|
||||
if (label) name += " " + label.name;
|
||||
let i = injected.indexOf(name);
|
||||
const hasInjected = i !== -1;
|
||||
if (!hasInjected) {
|
||||
injected.push(name);
|
||||
i = injected.length - 1;
|
||||
}
|
||||
path.replaceWith(_core.types.addComment(_core.types.returnStatement(_core.types.numericLiteral(i)), "trailing", " " + name, true));
|
||||
if (updaterNode) path.insertBefore(_core.types.cloneNode(updaterNode));
|
||||
if (hasInjected) continue;
|
||||
bodyStmts.push(_core.template.statement.ast`
|
||||
if (${_core.types.identifier(completionId)} === ${_core.types.numericLiteral(i)}) ${node}
|
||||
`);
|
||||
}
|
||||
if (returnNum) {
|
||||
for (const path of state.returns) {
|
||||
const arg = path.node.argument || path.scope.buildUndefinedNode();
|
||||
path.replaceWith(_core.template.statement.ast`
|
||||
return { v: ${arg} };
|
||||
`);
|
||||
}
|
||||
bodyStmts.push(_core.template.statement.ast`
|
||||
if (${_core.types.identifier(completionId)}) return ${_core.types.identifier(completionId)}.v;
|
||||
`);
|
||||
}
|
||||
}
|
||||
loopNode.body = _core.types.blockStatement(bodyStmts);
|
||||
return varPath;
|
||||
}
|
||||
function isVarInLoopHead(path) {
|
||||
if (_core.types.isForStatement(path.parent)) return path.key === "init";
|
||||
if (_core.types.isForXStatement(path.parent)) return path.key === "left";
|
||||
return false;
|
||||
}
|
||||
function filterMap(list, fn) {
|
||||
const result = [];
|
||||
for (const item of list) {
|
||||
const mapped = fn(item);
|
||||
if (mapped) result.push(mapped);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//# sourceMappingURL=loop.js.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"names":["_core","require","collectLoopBodyBindingsVisitor","Expression|Declaration|Loop","path","skip","Scope","state","isFunctionParent","bindings","scope","name","Object","keys","binding","kind","blockScoped","push","getLoopBodyBindings","loopPath","traverse","getUsageInBody","seen","WeakSet","capturedInClosure","constantViolations","filterMap","inBody","inClosure","relativeLoopLocation","id","isUpdateExpression","get","isAssignmentExpression","add","node","references","referencePaths","has","hasConstantViolations","length","usages","concat","bodyPath","currPath","parentPath","isFunction","isClass","isMethod","Error","collectCompletionsAndVarsVisitor","Function","LabeledStatement","enter","labelsStack","label","exit","popped","pop","Loop","_","labellessContinueTargets","labellessBreakTargets","SwitchStatement","BreakStatement|ContinueStatement","includes","isBreakStatement","breaksContinues","ReturnStatement","returns","VariableDeclaration","parent","loopNode","isVarInLoopHead","vars","wrapLoopBody","captured","updatedBindingsUsages","callArgs","closureParams","updater","updatedUsage","t","identifier","innerName","generateUid","assignmentExpression","replaceWith","fn","functionExpression","toBlock","body","call","callExpression","fnParent","findParent","p","async","generator","yieldExpression","awaitExpression","updaterNode","expressionStatement","sequenceExpression","varPath","insertBefore","variableDeclaration","variableDeclarator","bodyStmts","varNames","assign","decl","declarations","getBindingIdentifiers","init","isForXStatement","left","replacement","remove","pushContainer","map","labelNum","returnNum","type","addComment","returnStatement","numericLiteral","cloneNode","template","statement","ast","completionId","isVariableDeclaration","injected","i","indexOf","hasInjected","arg","argument","buildUndefinedNode","blockStatement","isForStatement","key","list","result","item","mapped"],"sources":["../src/loop.ts"],"sourcesContent":["import { template, types as t } from \"@babel/core\";\nimport type { NodePath, Visitor, Scope } from \"@babel/core\";\n\ninterface LoopBodyBindingsState {\n blockScoped: Scope.Binding[];\n}\n\nconst collectLoopBodyBindingsVisitor: Visitor<LoopBodyBindingsState> = {\n \"Expression|Declaration|Loop\"(path) {\n path.skip();\n },\n Scope(path, state) {\n if (path.isFunctionParent()) path.skip();\n\n const { bindings } = path.scope;\n for (const name of Object.keys(bindings)) {\n const binding = bindings[name];\n if (\n binding.kind === \"let\" ||\n binding.kind === \"const\" ||\n binding.kind === \"hoisted\"\n ) {\n state.blockScoped.push(binding);\n }\n }\n },\n};\n\nexport function getLoopBodyBindings(loopPath: NodePath<t.Loop>) {\n const state: LoopBodyBindingsState = { blockScoped: [] };\n loopPath.traverse(collectLoopBodyBindingsVisitor, state);\n return state.blockScoped;\n}\n\nexport function getUsageInBody(\n binding: Scope.Binding,\n loopPath: NodePath<t.Loop>,\n) {\n // UpdateExpressions are counted both as a reference and a mutation,\n // so we need to de-duplicate them.\n const seen = new WeakSet<t.Node>();\n\n let capturedInClosure = false;\n\n const constantViolations = filterMap(binding.constantViolations, path => {\n const { inBody, inClosure } = relativeLoopLocation(path, loopPath);\n if (!inBody) return null;\n capturedInClosure ||= inClosure;\n\n const id = path.isUpdateExpression()\n ? path.get(\"argument\")\n : path.isAssignmentExpression()\n ? path.get(\"left\")\n : null;\n if (id) seen.add(id.node);\n return id as NodePath<t.Identifier> | null;\n });\n\n const references = filterMap(binding.referencePaths, path => {\n if (seen.has(path.node)) return null;\n\n const { inBody, inClosure } = relativeLoopLocation(path, loopPath);\n if (!inBody) return null;\n capturedInClosure ||= inClosure;\n\n return path as NodePath<t.Identifier>;\n });\n\n return {\n capturedInClosure,\n hasConstantViolations: constantViolations.length > 0,\n usages: references.concat(constantViolations),\n };\n}\n\nfunction relativeLoopLocation(path: NodePath, loopPath: NodePath<t.Loop>) {\n const bodyPath = loopPath.get(\"body\");\n let inClosure = false;\n\n for (let currPath = path; currPath; currPath = currPath.parentPath) {\n if (currPath.isFunction() || currPath.isClass() || currPath.isMethod()) {\n inClosure = true;\n }\n if (currPath === bodyPath) {\n return { inBody: true, inClosure };\n } else if (currPath === loopPath) {\n return { inBody: false, inClosure };\n }\n }\n\n throw new Error(\n \"Internal Babel error: path is not in loop. Please report this as a bug.\",\n );\n}\n\ninterface CompletionsAndVarsState {\n breaksContinues: NodePath<t.BreakStatement | t.ContinueStatement>[];\n returns: NodePath<t.ReturnStatement>[];\n labelsStack: string[];\n labellessContinueTargets: Line truncated
|
||||
Generated
Vendored
+133
@@ -0,0 +1,133 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.validateUsage = validateUsage;
|
||||
var _core = require("@babel/core");
|
||||
function validateUsage(path, state, tdzEnabled) {
|
||||
const dynamicTDZNames = [];
|
||||
for (const name of Object.keys(path.getBindingIdentifiers())) {
|
||||
const binding = path.scope.getBinding(name);
|
||||
if (!binding) continue;
|
||||
if (tdzEnabled) {
|
||||
if (injectTDZChecks(binding, state)) dynamicTDZNames.push(name);
|
||||
}
|
||||
if (path.node.kind === "const") {
|
||||
disallowConstantViolations(name, binding, state);
|
||||
}
|
||||
}
|
||||
return dynamicTDZNames;
|
||||
}
|
||||
function disallowConstantViolations(name, binding, state) {
|
||||
for (const violation of binding.constantViolations) {
|
||||
const readOnlyError = state.addHelper("readOnlyError");
|
||||
const throwNode = _core.types.callExpression(readOnlyError, [_core.types.stringLiteral(name)]);
|
||||
if (violation.isAssignmentExpression()) {
|
||||
const {
|
||||
operator,
|
||||
left,
|
||||
right
|
||||
} = violation.node;
|
||||
if (operator === "=") {
|
||||
const exprs = [right];
|
||||
exprs.push(throwNode);
|
||||
violation.replaceWith(_core.types.sequenceExpression(exprs));
|
||||
} else if (["&&=", "||=", "??="].includes(operator)) {
|
||||
violation.replaceWith(_core.types.logicalExpression(operator.slice(0, -1), left, _core.types.sequenceExpression([right, throwNode])));
|
||||
} else {
|
||||
violation.replaceWith(_core.types.sequenceExpression([_core.types.binaryExpression(operator.slice(0, -1), left, right), throwNode]));
|
||||
}
|
||||
} else if (violation.isUpdateExpression()) {
|
||||
violation.replaceWith(_core.types.sequenceExpression([_core.types.unaryExpression("+", violation.get("argument").node), throwNode]));
|
||||
} else if (violation.isForXStatement()) {
|
||||
violation.ensureBlock();
|
||||
violation.get("left").replaceWith(_core.types.variableDeclaration("var", [_core.types.variableDeclarator(violation.scope.generateUidIdentifier(name))]));
|
||||
violation.node.body.body.unshift(_core.types.expressionStatement(throwNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
function getTDZStatus(refPath, bindingPath) {
|
||||
const executionStatus = bindingPath._guessExecutionStatusRelativeTo(refPath);
|
||||
if (executionStatus === "before") {
|
||||
return "outside";
|
||||
} else if (executionStatus === "after") {
|
||||
return "inside";
|
||||
} else {
|
||||
return "maybe";
|
||||
}
|
||||
}
|
||||
const skipTDZChecks = new WeakSet();
|
||||
function buildTDZAssert(status, node, state) {
|
||||
if (status === "maybe") {
|
||||
const clone = _core.types.cloneNode(node);
|
||||
skipTDZChecks.add(clone);
|
||||
return _core.types.callExpression(state.addHelper("temporalRef"), [clone, _core.types.stringLiteral(node.name)]);
|
||||
} else {
|
||||
return _core.types.callExpression(state.addHelper("tdz"), [_core.types.stringLiteral(node.name)]);
|
||||
}
|
||||
}
|
||||
function getTDZReplacement(path, state, id = path.node) {
|
||||
var _path$scope$getBindin;
|
||||
if (skipTDZChecks.has(id)) return;
|
||||
skipTDZChecks.add(id);
|
||||
const bindingPath = (_path$scope$getBindin = path.scope.getBinding(id.name)) == null ? void 0 : _path$scope$getBindin.path;
|
||||
if (!bindingPath || bindingPath.isFunctionDeclaration()) return;
|
||||
const status = getTDZStatus(path, bindingPath);
|
||||
if (status === "outside") return;
|
||||
if (status === "maybe") {
|
||||
bindingPath.parent._tdzThis = true;
|
||||
}
|
||||
return {
|
||||
status,
|
||||
node: buildTDZAssert(status, id, state)
|
||||
};
|
||||
}
|
||||
function injectTDZChecks(binding, state) {
|
||||
const allUsages = new Set(binding.referencePaths);
|
||||
binding.constantViolations.forEach(allUsages.add, allUsages);
|
||||
let dynamicTdz = false;
|
||||
for (const path of binding.constantViolations) {
|
||||
const {
|
||||
node
|
||||
} = path;
|
||||
if (skipTDZChecks.has(node)) continue;
|
||||
skipTDZChecks.add(node);
|
||||
if (path.isUpdateExpression()) {
|
||||
const arg = path.get("argument");
|
||||
const replacement = getTDZReplacement(path, state, arg.node);
|
||||
if (!replacement) continue;
|
||||
if (replacement.status === "maybe") {
|
||||
dynamicTdz = true;
|
||||
path.insertBefore(replacement.node);
|
||||
} else {
|
||||
path.replaceWith(replacement.node);
|
||||
}
|
||||
} else if (path.isAssignmentExpression()) {
|
||||
const nodes = [];
|
||||
const ids = path.getBindingIdentifiers();
|
||||
for (const name of Object.keys(ids)) {
|
||||
const replacement = getTDZReplacement(path, state, ids[name]);
|
||||
if (replacement) {
|
||||
nodes.push(_core.types.expressionStatement(replacement.node));
|
||||
if (replacement.status === "inside") break;
|
||||
if (replacement.status === "maybe") dynamicTdz = true;
|
||||
}
|
||||
}
|
||||
if (nodes.length > 0) path.insertBefore(nodes);
|
||||
}
|
||||
}
|
||||
for (const path of binding.referencePaths) {
|
||||
if (path.parentPath.isUpdateExpression()) continue;
|
||||
if (path.parentPath.isForXStatement({
|
||||
left: path.node
|
||||
})) continue;
|
||||
const replacement = getTDZReplacement(path, state);
|
||||
if (!replacement) continue;
|
||||
if (replacement.status === "maybe") dynamicTdz = true;
|
||||
path.replaceWith(replacement.node);
|
||||
}
|
||||
return dynamicTdz;
|
||||
}
|
||||
|
||||
//# sourceMappingURL=validation.js.map
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"names":["_core","require","validateUsage","path","state","tdzEnabled","dynamicTDZNames","name","Object","keys","getBindingIdentifiers","binding","scope","getBinding","injectTDZChecks","push","node","kind","disallowConstantViolations","violation","constantViolations","readOnlyError","addHelper","throwNode","t","callExpression","stringLiteral","isAssignmentExpression","operator","left","right","exprs","replaceWith","sequenceExpression","includes","logicalExpression","slice","binaryExpression","isUpdateExpression","unaryExpression","get","isForXStatement","ensureBlock","variableDeclaration","variableDeclarator","generateUidIdentifier","body","unshift","expressionStatement","getTDZStatus","refPath","bindingPath","executionStatus","_guessExecutionStatusRelativeTo","skipTDZChecks","WeakSet","buildTDZAssert","status","clone","cloneNode","add","getTDZReplacement","id","_path$scope$getBindin","has","isFunctionDeclaration","parent","_tdzThis","allUsages","Set","referencePaths","forEach","dynamicTdz","arg","replacement","insertBefore","nodes","ids","length","parentPath"],"sources":["../src/validation.ts"],"sourcesContent":["import { types as t } from \"@babel/core\";\nimport type { Scope, NodePath, PluginPass } from \"@babel/core\";\n\nexport function validateUsage(\n path: NodePath<t.VariableDeclaration>,\n state: PluginPass,\n tdzEnabled: boolean,\n) {\n const dynamicTDZNames = [];\n\n for (const name of Object.keys(path.getBindingIdentifiers())) {\n const binding = path.scope.getBinding(name);\n // binding may be null. ref: https://github.com/babel/babel/issues/15300\n if (!binding) continue;\n if (tdzEnabled) {\n if (injectTDZChecks(binding, state)) dynamicTDZNames.push(name);\n }\n if (path.node.kind === \"const\") {\n disallowConstantViolations(name, binding, state);\n }\n }\n\n return dynamicTDZNames;\n}\n\nfunction disallowConstantViolations(\n name: string,\n binding: Scope.Binding,\n state: PluginPass,\n) {\n for (const violation of binding.constantViolations) {\n const readOnlyError = state.addHelper(\"readOnlyError\");\n const throwNode = t.callExpression(readOnlyError, [t.stringLiteral(name)]);\n\n if (violation.isAssignmentExpression()) {\n const { operator, left, right } = violation.node;\n if (operator === \"=\") {\n const exprs = [right];\n exprs.push(throwNode);\n violation.replaceWith(t.sequenceExpression(exprs));\n } else if ([\"&&=\", \"||=\", \"??=\"].includes(operator)) {\n violation.replaceWith(\n t.logicalExpression(\n // @ts-expect-error todo: give a better type to operator\n operator.slice(0, -1),\n left,\n t.sequenceExpression([right, throwNode]),\n ),\n );\n } else {\n violation.replaceWith(\n t.sequenceExpression([\n t.binaryExpression(\n // @ts-expect-error todo: give a better type to operator\n operator.slice(0, -1),\n left,\n right,\n ),\n throwNode,\n ]),\n );\n }\n } else if (violation.isUpdateExpression()) {\n violation.replaceWith(\n t.sequenceExpression([\n t.unaryExpression(\"+\", violation.get(\"argument\").node),\n throwNode,\n ]),\n );\n } else if (violation.isForXStatement()) {\n violation.ensureBlock();\n violation\n .get(\"left\")\n .replaceWith(\n t.variableDeclaration(\"var\", [\n t.variableDeclarator(violation.scope.generateUidIdentifier(name)),\n ]),\n );\n (violation.node.body as t.BlockStatement).body.unshift(\n t.expressionStatement(throwNode),\n );\n }\n }\n}\n\nfunction getTDZStatus(refPath: NodePath, bindingPath: NodePath) {\n const executionStatus = bindingPath._guessExecutionStatusRelativeTo(refPath);\n\n if (executionStatus === \"before\") {\n return \"outside\";\n } else if (executionStatus === \"after\") {\n return \"inside\";\n } else {\n return \"maybe\";\n }\n}\n\nconst skipTDZChecks = new WeakSet();\n\nfunction buildTDZAssert(\n status: \"maybe\" | \"inside\",\n node: t.Identifier | t.JSXIdentifier,\n state: PluginPass,\n) {\n if (status === \"maybe\") {\n const clone = t.cloneNode(node);\n skipTDZChecks.add(clone);\n return t.callExpression(state.addHelper(\"temporalRef\"), [\n // @ts-expect-error Fixme: we may need to handle JSXIdentifier\n clone,\n t.stringLiteral(node.name),\n ]);\n } else {\n return t.callExpression(state.addHelper(\"tdz\"), [\n t.stringLiteral(node.name),\n ]);\n }\n}\n\ntype TDZReplacement = { status: \"maybe\" | \"inside\"; node: t.Expression };\nfunction getTDZReplacement(\n path: NodePath<t.Identifier | t.JSXIdentifier>,\n state: PluginPass,\n): TDZReplacement | undefined;\nfunction getTDZReplacement(\n path: NodePath,\n state: PluginPass,\n Line truncated
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "@babel/plugin-transform-block-scoping",
|
||||
"version": "7.29.7",
|
||||
"description": "Compile ES2015 block scoping (const and let) to ES5",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/babel/babel.git",
|
||||
"directory": "packages/babel-plugin-transform-block-scoping"
|
||||
},
|
||||
"homepage": "https://babel.dev/docs/en/next/babel-plugin-transform-block-scoping",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.29.7"
|
||||
},
|
||||
"keywords": [
|
||||
"babel-plugin"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.29.7",
|
||||
"@babel/helper-plugin-test-runner": "^7.29.7",
|
||||
"@babel/traverse": "^7.29.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"author": "The Babel Team (https://babel.dev/team)",
|
||||
"type": "commonjs"
|
||||
}
|
||||
Reference in new issue
Block a user