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 index 5e5027c56c21..6f5c411593db 100644 --- 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 @@ -1,4 +1,7 @@ -expect(do { - var bar = "foo"; - bar; -}).toBe("foo"); +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-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 1ce31ae23f74..3d28d4f61704 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.13.9", "@babel/helper-function-name": "workspace:^7.12.13", + "@babel/helper-hoist-variables": "workspace:^7.13.0", "@babel/helper-split-export-declaration": "workspace:^7.12.13", "@babel/parser": "workspace:^7.13.13", "@babel/types": "workspace:^7.13.13", diff --git a/packages/babel-traverse/src/path/replacement.ts b/packages/babel-traverse/src/path/replacement.ts index abb5d0551481..7917d05567c4 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 605fda1024e0..2fdd7d652a53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3492,6 +3492,7 @@ __metadata: "@babel/code-frame": "workspace:^7.12.13" "@babel/generator": "workspace:^7.13.9" "@babel/helper-function-name": "workspace:^7.12.13" + "@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.13.13"