diff --git a/packages/babel-helper-wrap-function/src/index.ts b/packages/babel-helper-wrap-function/src/index.ts index d9f84c2fc6d1..744e238457bd 100644 --- a/packages/babel-helper-wrap-function/src/index.ts +++ b/packages/babel-helper-wrap-function/src/index.ts @@ -6,11 +6,29 @@ import { callExpression, functionExpression, isAssignmentPattern, + isFunctionDeclaration, isRestElement, returnStatement, } from "@babel/types"; import type * as t from "@babel/types"; +type ExpressionWrapperBuilder = ( + replacements?: Parameters>[0], +) => t.CallExpression & { + callee: t.FunctionExpression & { + body: { + body: [ + t.VariableDeclaration & { + declarations: [ + { init: t.FunctionExpression | t.ArrowFunctionExpression }, + ]; + }, + ...ExtraBody, + ]; + }; + }; +}; + const buildAnonymousExpressionWrapper = template.expression(` (function () { var REF = FUNCTION; @@ -18,7 +36,9 @@ const buildAnonymousExpressionWrapper = template.expression(` return REF.apply(this, arguments); }; })() -`); +`) as ExpressionWrapperBuilder< + [t.ReturnStatement & { argument: t.FunctionExpression }] +>; const buildNamedExpressionWrapper = template.expression(` (function () { @@ -28,7 +48,9 @@ const buildNamedExpressionWrapper = template.expression(` } return NAME; })() -`); +`) as ExpressionWrapperBuilder< + [t.FunctionDeclaration, t.ReturnStatement & { argument: t.Identifier }] +>; const buildDeclarationWrapper = template.statements(` function NAME(PARAMS) { return REF.apply(this, arguments); } @@ -72,28 +94,23 @@ function plainFunction( noNewArrows: boolean, ignoreFunctionLength: boolean, ) { - const node = path.node; - const isDeclaration = path.isFunctionDeclaration(); - // @ts-expect-error id is not in ArrowFunctionExpression - const functionId = node.id; - const wrapper = isDeclaration - ? buildDeclarationWrapper - : functionId - ? buildNamedExpressionWrapper - : buildAnonymousExpressionWrapper; - + let functionId = null; + let node; if (path.isArrowFunctionExpression()) { - path.arrowFunctionToExpression({ noNewArrows }); + path = path.arrowFunctionToExpression({ noNewArrows }); + node = path.node as t.FunctionDeclaration | t.FunctionExpression; + } else { + node = path.node as t.FunctionDeclaration | t.FunctionExpression; } - // @ts-expect-error node is FunctionDeclaration|FunctionExpression - node.id = null; - if (isDeclaration) { - node.type = "FunctionExpression"; - } + const isDeclaration = isFunctionDeclaration(node); + + functionId = node.id; + node.id = null; + node.type = "FunctionExpression"; const built = callExpression(callId, [ - node as Exclude, + node as Exclude, ]); const params: t.Identifier[] = []; @@ -104,34 +121,35 @@ function plainFunction( params.push(path.scope.generateUidIdentifier("x")); } - const container = wrapper({ + const wrapperArgs = { NAME: functionId || null, REF: path.scope.generateUidIdentifier(functionId ? functionId.name : "ref"), FUNCTION: built, PARAMS: params, - }); + }; if (isDeclaration) { - path.replaceWith((container as t.Statement[])[0]); - path.insertAfter((container as t.Statement[])[1]); + const container = buildDeclarationWrapper(wrapperArgs); + path.replaceWith(container[0]); + path.insertAfter(container[1]); } else { - // @ts-expect-error todo(flow->ts) separate `wrapper` for `isDeclaration` and `else` branches - const retFunction = container.callee.body.body[1].argument; - if (!functionId) { + let container; + + if (functionId) { + container = buildNamedExpressionWrapper(wrapperArgs); + } else { + container = buildAnonymousExpressionWrapper(wrapperArgs); + + const returnFn = container.callee.body.body[1].argument; nameFunction({ - node: retFunction, + node: returnFn, parent: path.parent, scope: path.scope, }); + functionId = returnFn.id; } - if ( - !retFunction || - retFunction.id || - (!ignoreFunctionLength && params.length) - ) { - // we have an inferred function id or params so we need this wrapper - // @ts-expect-error todo(flow->ts) separate `wrapper` for `isDeclaration` and `else` branches + if (functionId || (!ignoreFunctionLength && params.length)) { path.replaceWith(container); } else { // we can omit this wrapper as the conditions it protects for do not apply diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-newableArrowFunctions-false/basic/exec.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-noNewArrows-false/basic/exec.js similarity index 100% rename from packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-newableArrowFunctions-false/basic/exec.js rename to packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-noNewArrows-false/basic/exec.js diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-newableArrowFunctions-false/basic/input.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-noNewArrows-false/basic/input.js similarity index 100% rename from packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-newableArrowFunctions-false/basic/input.js rename to packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-noNewArrows-false/basic/input.js diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-newableArrowFunctions-false/basic/options.json b/packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-noNewArrows-false/basic/options.json similarity index 100% rename from packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-newableArrowFunctions-false/basic/options.json rename to packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-noNewArrows-false/basic/options.json diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-newableArrowFunctions-false/basic/output.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-noNewArrows-false/basic/output.js similarity index 89% rename from packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-newableArrowFunctions-false/basic/output.js rename to packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-noNewArrows-false/basic/output.js index c0d2e6069da5..6c6182babb06 100644 --- a/packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-newableArrowFunctions-false/basic/output.js +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-noNewArrows-false/basic/output.js @@ -4,4 +4,4 @@ var _this = this; babelHelpers.asyncToGenerator(function* () { babelHelpers.newArrowCheck(this, _this); return 2; -}); +}).bind(this); diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-newableArrowFunctions-false/bluebird/input.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-noNewArrows-false/bluebird/input.js similarity index 100% rename from packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-newableArrowFunctions-false/bluebird/input.js rename to packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-noNewArrows-false/bluebird/input.js diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-newableArrowFunctions-false/bluebird/options.json b/packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-noNewArrows-false/bluebird/options.json similarity index 100% rename from packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-newableArrowFunctions-false/bluebird/options.json rename to packages/babel-plugin-transform-async-to-generator/test/fixtures/assumption-noNewArrows-false/bluebird/options.json diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/in-uncompiled-class-fields/input.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/in-uncompiled-class-fields/input.js new file mode 100644 index 000000000000..e62d139684bb --- /dev/null +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/in-uncompiled-class-fields/input.js @@ -0,0 +1,8 @@ +// Angular needs to alwys compile async functions, even if +// the targets support class fields. +// https://github.com/babel/babel/issues/14749 + +class A { + a = async () => this; + b = async (x, y, z) => this; +} diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/in-uncompiled-class-fields/output.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/in-uncompiled-class-fields/output.js new file mode 100644 index 000000000000..64bff6ab2dc4 --- /dev/null +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/in-uncompiled-class-fields/output.js @@ -0,0 +1,25 @@ +// Angular needs to alwys compile async functions, even if +// the targets support class fields. +// https://github.com/babel/babel/issues/14749 +class A { + a = (() => { + var _this = this; + + return babelHelpers.asyncToGenerator(function* () { + return _this; + }); + })(); + b = (() => { + var _this2 = this; + + return function () { + var _ref2 = babelHelpers.asyncToGenerator(function* (x, y, z) { + return _this2; + }); + + return function (_x, _x2, _x3) { + return _ref2.apply(this, arguments); + }; + }(); + })(); +} diff --git a/packages/babel-traverse/src/path/conversion.ts b/packages/babel-traverse/src/path/conversion.ts index d5156950d232..1352f6a04c73 100644 --- a/packages/babel-traverse/src/path/conversion.ts +++ b/packages/babel-traverse/src/path/conversion.ts @@ -135,6 +135,13 @@ export function unwrapFunctionEnvironment(this: NodePath) { hoistFunctionEnvironment(this); } +function setType( + path: NodePath, + type: T, +): asserts path is NodePath> { + path.node.type = type; +} + /** * Convert a given arrow function into a normal ES5 function expression. */ @@ -151,7 +158,7 @@ export function arrowFunctionToExpression( specCompliant?: boolean | void; noNewArrows?: boolean; } = {}, -) { +): NodePath> { if (!this.isArrowFunctionExpression()) { throw (this as NodePath).buildCodeFrameError( "Cannot convert non-arrow function to a function expression.", @@ -166,7 +173,8 @@ export function arrowFunctionToExpression( // @ts-expect-error TS requires explicit fn type annotation fn.ensureBlock(); - fn.node.type = "FunctionExpression"; + setType(fn, "FunctionExpression"); + if (!noNewArrows) { const checkBinding = thisBinding ? null @@ -178,7 +186,7 @@ export function arrowFunctionToExpression( }); } - (fn.get("body") as NodePath).unshiftContainer( + fn.get("body").unshiftContainer( "body", expressionStatement( callExpression(this.hub.addHelper("newArrowCheck"), [ @@ -200,7 +208,11 @@ export function arrowFunctionToExpression( [checkBinding ? identifier(checkBinding.name) : thisExpression()], ), ); + + return fn.get("callee.object"); } + + return fn; } const getSuperCallsVisitor = mergeVisitors<{