diff --git a/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-iife/exec.js b/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-iife/exec.js new file mode 100644 index 000000000000..455e0b795f0c --- /dev/null +++ b/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-iife/exec.js @@ -0,0 +1,10 @@ +expect( + do { + var bar = "foo"; + if (!bar) throw new Error( + "unreachable" + ) + bar; + } +).toBe("foo"); +expect(bar).toBe("foo"); diff --git a/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-iife/input.js b/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-iife/input.js new file mode 100644 index 000000000000..3c6736678600 --- /dev/null +++ b/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-iife/input.js @@ -0,0 +1,7 @@ +var x = do { + var bar = "foo"; + if (!bar) throw new Error( + "unreachable" + ) + bar; +}; diff --git a/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-iife/output.js b/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-iife/output.js new file mode 100644 index 000000000000..0444f3903f31 --- /dev/null +++ b/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-iife/output.js @@ -0,0 +1,7 @@ +var bar; + +var x = function () { + bar = "foo"; + if (!bar) throw new Error("unreachable"); + return bar; +}(); diff --git a/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-sequence/exec.js b/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-sequence/exec.js new file mode 100644 index 000000000000..6f5c411593db --- /dev/null +++ b/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-sequence/exec.js @@ -0,0 +1,7 @@ +expect( + do { + var bar = "foo"; + bar; + } +).toBe("foo"); +expect(bar).toBe("foo"); diff --git a/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-sequence/input.js b/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-sequence/input.js new file mode 100644 index 000000000000..46a1c3d0ae74 --- /dev/null +++ b/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-sequence/input.js @@ -0,0 +1,4 @@ +var x = do { + var bar = "foo"; + bar; +}; diff --git a/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-sequence/output.js b/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-sequence/output.js new file mode 100644 index 000000000000..746aa12d0267 --- /dev/null +++ b/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start-to-sequence/output.js @@ -0,0 +1,2 @@ +var bar; +var x = (bar = "foo", bar); diff --git a/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start.js b/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start.js deleted file mode 100644 index 5e5027c56c21..000000000000 --- a/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-start.js +++ /dev/null @@ -1,4 +0,0 @@ -expect(do { - var bar = "foo"; - bar; -}).toBe("foo"); diff --git a/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-within-function.js b/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-within-function.js new file mode 100644 index 000000000000..a58b208ec7c6 --- /dev/null +++ b/packages/babel-plugin-proposal-do-expressions/test/fixtures/do-expressions/variable-declaration-within-function.js @@ -0,0 +1,8 @@ +expect( + () => do { + () => { + var bar = "foo"; + }; + bar; + } +).toThrow(ReferenceError); diff --git a/packages/babel-traverse/package.json b/packages/babel-traverse/package.json index 9d1be0b61066..93e5ebe0d7cf 100644 --- a/packages/babel-traverse/package.json +++ b/packages/babel-traverse/package.json @@ -19,6 +19,7 @@ "@babel/code-frame": "workspace:^7.12.13", "@babel/generator": "workspace:^7.14.2", "@babel/helper-function-name": "workspace:^7.14.2", + "@babel/helper-hoist-variables": "workspace:^7.13.0", "@babel/helper-split-export-declaration": "workspace:^7.12.13", "@babel/parser": "workspace:^7.14.2", "@babel/types": "workspace:^7.14.2", diff --git a/packages/babel-traverse/src/path/replacement.ts b/packages/babel-traverse/src/path/replacement.ts index abb5d0551481..3c3939ed7c11 100644 --- a/packages/babel-traverse/src/path/replacement.ts +++ b/packages/babel-traverse/src/path/replacement.ts @@ -6,35 +6,7 @@ import NodePath from "./index"; import { path as pathCache } from "../cache"; import { parse } from "@babel/parser"; import * as t from "@babel/types"; - -const hoistVariablesVisitor = { - Function(path) { - path.skip(); - }, - - VariableDeclaration(path) { - if (path.node.kind !== "var") return; - - const bindings = path.getBindingIdentifiers(); - for (const key of Object.keys(bindings)) { - path.scope.push({ id: bindings[key] }); - } - - const exprs = []; - - for (const declar of path.node.declarations as Array) { - if (declar.init) { - exprs.push( - t.expressionStatement( - t.assignmentExpression("=", declar.id, declar.init), - ), - ); - } - } - - path.replaceWithMultiple(exprs); - }, -}; +import hoistVariables from "@babel/helper-hoist-variables"; /** * Replace a node with an array of multiple. This method performs the following steps: @@ -238,7 +210,16 @@ export function replaceExpressionWithStatements( t.CallExpression & { callee: t.ArrowFunctionExpression } >; - this.traverse(hoistVariablesVisitor); + // hoist variable declaration in do block + // `(do { var x = 1; x;})` -> `var x; (() => { x = 1; return x; })()` + const callee = (this as ThisType).get("callee"); + hoistVariables( + callee.get("body"), + (id: t.Identifier) => { + this.scope.push({ id }); + }, + "var", + ); // add implicit returns to all ending expression statements const completionRecords: Array = (this as ThisType) @@ -252,7 +233,6 @@ export function replaceExpressionWithStatements( let uid = loop.getData("expressionReplacementReturnUid"); if (!uid) { - const callee = (this as ThisType).get("callee"); uid = callee.scope.generateDeclaredUidIdentifier("ret"); callee .get("body") @@ -272,10 +252,11 @@ export function replaceExpressionWithStatements( } } - const callee = this.get("callee") as NodePath; - // This is an IIFE, so we don't need to worry about the noNewArrows assumption callee.arrowFunctionToExpression(); + // Fixme: we can not `assert this is NodePath` in `arrowFunctionToExpression` + // because it is not a class method known at compile time. + const newCallee = callee as unknown as NodePath; // (() => await xxx)() -> await (async () => await xxx)(); const needToAwaitFunction = @@ -293,18 +274,18 @@ export function replaceExpressionWithStatements( t.FUNCTION_TYPES, ); if (needToAwaitFunction) { - callee.set("async", true); + newCallee.set("async", true); // yield* will await the generator return result if (!needToYieldFunction) { this.replaceWith(t.awaitExpression((this as ThisType).node)); } } if (needToYieldFunction) { - callee.set("generator", true); + newCallee.set("generator", true); this.replaceWith(t.yieldExpression((this as ThisType).node, true)); } - return callee.get("body.body"); + return newCallee.get("body.body"); } export function replaceInline(this: NodePath, nodes: t.Node | Array) { diff --git a/yarn.lock b/yarn.lock index ae968b5a2e37..37a8246fe749 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3565,6 +3565,7 @@ __metadata: "@babel/code-frame": "workspace:^7.12.13" "@babel/generator": "workspace:^7.14.2" "@babel/helper-function-name": "workspace:^7.14.2" + "@babel/helper-hoist-variables": "workspace:^7.13.0" "@babel/helper-plugin-test-runner": "workspace:*" "@babel/helper-split-export-declaration": "workspace:^7.12.13" "@babel/parser": "workspace:^7.14.2"