Support transforming params of arrow functions in class fields (#13941)
This commit is contained in:
parent
a6a526968d
commit
43f989941b
@ -23,6 +23,10 @@ export default declare((api, options) => {
|
|||||||
) {
|
) {
|
||||||
// default/rest visitors require access to `arguments`, so it cannot be an arrow
|
// default/rest visitors require access to `arguments`, so it cannot be an arrow
|
||||||
path.arrowFunctionToExpression({ noNewArrows });
|
path.arrowFunctionToExpression({ noNewArrows });
|
||||||
|
|
||||||
|
// In some cases arrowFunctionToExpression replaces the function with a wrapper.
|
||||||
|
// Return early; the wrapped function will be visited later in the AST traversal.
|
||||||
|
if (!path.isFunctionExpression()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const convertedRest = convertFunctionRest(path);
|
const convertedRest = convertFunctionRest(path);
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
let f = (x = 0) => x + 1;
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
var _this = this;
|
||||||
|
|
||||||
|
let f = function f() {
|
||||||
|
let x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
||||||
|
babelHelpers.newArrowCheck(this, _this);
|
||||||
|
return x + 1;
|
||||||
|
}.bind(this);
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["transform-parameters"],
|
||||||
|
"assumptions": {
|
||||||
|
"noNewArrows": false
|
||||||
|
}
|
||||||
|
}
|
||||||
8
packages/babel-plugin-transform-parameters/test/fixtures/regression/13939-complex/input.js
vendored
Normal file
8
packages/babel-plugin-transform-parameters/test/fixtures/regression/13939-complex/input.js
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
class A extends B {
|
||||||
|
handle = ((x = 0) => {
|
||||||
|
console.log(x, this, new.target, super.y);
|
||||||
|
})(() => {
|
||||||
|
let y = 0;
|
||||||
|
return (x = y) => x + this;
|
||||||
|
})((x = 1) => {})(this);
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"transform-parameters"
|
||||||
|
]
|
||||||
|
}
|
||||||
22
packages/babel-plugin-transform-parameters/test/fixtures/regression/13939-complex/output.js
vendored
Normal file
22
packages/babel-plugin-transform-parameters/test/fixtures/regression/13939-complex/output.js
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
class A extends B {
|
||||||
|
handle = (() => {
|
||||||
|
var _newtarget = new.target,
|
||||||
|
_superprop_getY = () => super.y,
|
||||||
|
_this = this;
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
let x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
||||||
|
console.log(x, _this, _newtarget, _superprop_getY());
|
||||||
|
};
|
||||||
|
})()(() => {
|
||||||
|
var _this2 = this;
|
||||||
|
|
||||||
|
let y = 0;
|
||||||
|
return function () {
|
||||||
|
let x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : y;
|
||||||
|
return x + _this2;
|
||||||
|
};
|
||||||
|
})((() => function () {
|
||||||
|
let x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
|
||||||
|
})())(this);
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
class A extends B {
|
||||||
|
#handle = ((x = 0) => {
|
||||||
|
console.log(x, this, new.target, super.y);
|
||||||
|
})(() => {
|
||||||
|
let y = 0;
|
||||||
|
return (x = y) => x + this;
|
||||||
|
})((x = 1) => {})(this);
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"transform-parameters"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
class A extends B {
|
||||||
|
#handle = (() => {
|
||||||
|
var _newtarget = new.target,
|
||||||
|
_superprop_getY = () => super.y,
|
||||||
|
_this = this;
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
let x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
||||||
|
console.log(x, _this, _newtarget, _superprop_getY());
|
||||||
|
};
|
||||||
|
})()(() => {
|
||||||
|
var _this2 = this;
|
||||||
|
|
||||||
|
let y = 0;
|
||||||
|
return function () {
|
||||||
|
let x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : y;
|
||||||
|
return x + _this2;
|
||||||
|
};
|
||||||
|
})((() => function () {
|
||||||
|
let x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
|
||||||
|
})())(this);
|
||||||
|
}
|
||||||
5
packages/babel-plugin-transform-parameters/test/fixtures/regression/13939-private/input.js
vendored
Normal file
5
packages/babel-plugin-transform-parameters/test/fixtures/regression/13939-private/input.js
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
class A {
|
||||||
|
#handle = (x = 0) => {
|
||||||
|
console.log(x);
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"transform-parameters"
|
||||||
|
]
|
||||||
|
}
|
||||||
6
packages/babel-plugin-transform-parameters/test/fixtures/regression/13939-private/output.js
vendored
Normal file
6
packages/babel-plugin-transform-parameters/test/fixtures/regression/13939-private/output.js
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
class A {
|
||||||
|
#handle = (() => function () {
|
||||||
|
let x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
||||||
|
console.log(x);
|
||||||
|
})();
|
||||||
|
}
|
||||||
5
packages/babel-plugin-transform-parameters/test/fixtures/regression/13939/input.js
vendored
Normal file
5
packages/babel-plugin-transform-parameters/test/fixtures/regression/13939/input.js
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
class A {
|
||||||
|
handle = (x = 0) => {
|
||||||
|
console.log(x);
|
||||||
|
};
|
||||||
|
}
|
||||||
5
packages/babel-plugin-transform-parameters/test/fixtures/regression/13939/options.json
vendored
Normal file
5
packages/babel-plugin-transform-parameters/test/fixtures/regression/13939/options.json
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"transform-parameters"
|
||||||
|
]
|
||||||
|
}
|
||||||
6
packages/babel-plugin-transform-parameters/test/fixtures/regression/13939/output.js
vendored
Normal file
6
packages/babel-plugin-transform-parameters/test/fixtures/regression/13939/output.js
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
class A {
|
||||||
|
handle = (() => function () {
|
||||||
|
let x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
||||||
|
console.log(x);
|
||||||
|
})();
|
||||||
|
}
|
||||||
@ -22,6 +22,7 @@ import {
|
|||||||
stringLiteral,
|
stringLiteral,
|
||||||
super as _super,
|
super as _super,
|
||||||
thisExpression,
|
thisExpression,
|
||||||
|
toExpression,
|
||||||
unaryExpression,
|
unaryExpression,
|
||||||
} from "@babel/types";
|
} from "@babel/types";
|
||||||
import type * as t from "@babel/types";
|
import type * as t from "@babel/types";
|
||||||
@ -146,27 +147,26 @@ export function arrowFunctionToExpression(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const thisBinding = hoistFunctionEnvironment(
|
const { thisBinding, fnPath: fn } = hoistFunctionEnvironment(
|
||||||
this,
|
this,
|
||||||
noNewArrows,
|
noNewArrows,
|
||||||
allowInsertArrow,
|
allowInsertArrow,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.ensureBlock();
|
fn.ensureBlock();
|
||||||
// @ts-expect-error todo(flow->ts): avoid mutating nodes
|
fn.node.type = "FunctionExpression";
|
||||||
this.node.type = "FunctionExpression";
|
|
||||||
if (!noNewArrows) {
|
if (!noNewArrows) {
|
||||||
const checkBinding = thisBinding
|
const checkBinding = thisBinding
|
||||||
? null
|
? null
|
||||||
: this.parentPath.scope.generateUidIdentifier("arrowCheckId");
|
: fn.scope.generateUidIdentifier("arrowCheckId");
|
||||||
if (checkBinding) {
|
if (checkBinding) {
|
||||||
this.parentPath.scope.push({
|
fn.parentPath.scope.push({
|
||||||
id: checkBinding,
|
id: checkBinding,
|
||||||
init: objectExpression([]),
|
init: objectExpression([]),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.get("body").unshiftContainer(
|
fn.get("body").unshiftContainer(
|
||||||
"body",
|
"body",
|
||||||
expressionStatement(
|
expressionStatement(
|
||||||
callExpression(this.hub.addHelper("newArrowCheck"), [
|
callExpression(this.hub.addHelper("newArrowCheck"), [
|
||||||
@ -178,10 +178,10 @@ export function arrowFunctionToExpression(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.replaceWith(
|
fn.replaceWith(
|
||||||
callExpression(
|
callExpression(
|
||||||
memberExpression(
|
memberExpression(
|
||||||
nameFunction(this, true) || this.node,
|
nameFunction(this, true) || fn.node,
|
||||||
identifier("bind"),
|
identifier("bind"),
|
||||||
),
|
),
|
||||||
[checkBinding ? identifier(checkBinding.name) : thisExpression()],
|
[checkBinding ? identifier(checkBinding.name) : thisExpression()],
|
||||||
@ -193,26 +193,53 @@ export function arrowFunctionToExpression(
|
|||||||
/**
|
/**
|
||||||
* Given a function, traverse its contents, and if there are references to "this", "arguments", "super",
|
* Given a function, traverse its contents, and if there are references to "this", "arguments", "super",
|
||||||
* or "new.target", ensure that these references reference the parent environment around this function.
|
* or "new.target", ensure that these references reference the parent environment around this function.
|
||||||
|
*
|
||||||
|
* @returns `thisBinding`: the name of the injected reference to `this`; for example "_this"
|
||||||
|
* @returns `fnPath`: the new path to the function node. This is different from the fnPath
|
||||||
|
* parameter when the function node is wrapped in another node.
|
||||||
*/
|
*/
|
||||||
function hoistFunctionEnvironment(
|
function hoistFunctionEnvironment(
|
||||||
fnPath,
|
fnPath: NodePath<t.Function>,
|
||||||
// TODO(Babel 8): Consider defaulting to `false` for spec compliancy
|
// TODO(Babel 8): Consider defaulting to `false` for spec compliancy
|
||||||
noNewArrows = true,
|
noNewArrows = true,
|
||||||
allowInsertArrow = true,
|
allowInsertArrow = true,
|
||||||
) {
|
): { thisBinding: string; fnPath: NodePath<t.Function> } {
|
||||||
const thisEnvFn = fnPath.findParent(p => {
|
let arrowParent;
|
||||||
|
let thisEnvFn = fnPath.findParent(p => {
|
||||||
|
if (p.isArrowFunctionExpression()) {
|
||||||
|
arrowParent ??= p;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
(p.isFunction() && !p.isArrowFunctionExpression()) ||
|
p.isFunction() ||
|
||||||
p.isProgram() ||
|
p.isProgram() ||
|
||||||
p.isClassProperty({ static: false })
|
p.isClassProperty({ static: false }) ||
|
||||||
|
p.isClassPrivateProperty({ static: false })
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
const inConstructor = thisEnvFn?.node.kind === "constructor";
|
const inConstructor = thisEnvFn.isClassMethod({ kind: "constructor" });
|
||||||
|
|
||||||
if (thisEnvFn.isClassProperty()) {
|
if (thisEnvFn.isClassProperty() || thisEnvFn.isClassPrivateProperty()) {
|
||||||
throw fnPath.buildCodeFrameError(
|
if (arrowParent) {
|
||||||
"Unable to transform arrow inside class property",
|
thisEnvFn = arrowParent;
|
||||||
);
|
} else if (allowInsertArrow) {
|
||||||
|
// It's safe to wrap this function in another and not hoist to the
|
||||||
|
// top level because the 'this' binding is constant in class
|
||||||
|
// properties (since 'super()' has already been called), so we don't
|
||||||
|
// need to capture/reassign it at the top level.
|
||||||
|
fnPath.replaceWith(
|
||||||
|
callExpression(
|
||||||
|
arrowFunctionExpression([], toExpression(fnPath.node)),
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
thisEnvFn = fnPath.get("callee");
|
||||||
|
fnPath = thisEnvFn.get("body");
|
||||||
|
} else {
|
||||||
|
throw fnPath.buildCodeFrameError(
|
||||||
|
"Unable to transform arrow inside class property",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { thisPaths, argumentsPaths, newTargetPaths, superProps, superCalls } =
|
const { thisPaths, argumentsPaths, newTargetPaths, superProps, superCalls } =
|
||||||
@ -365,7 +392,7 @@ function hoistFunctionEnvironment(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return thisBinding;
|
return { thisBinding, fnPath };
|
||||||
}
|
}
|
||||||
|
|
||||||
function standardizeSuperProperty(superProp) {
|
function standardizeSuperProperty(superProp) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user