Define default value for vars shadowing params (#11307)
* Define default value for vars shadowing params * Remove from package.json * Only convert traverse the params if needed This prevents this plugin from running after the destructuring transform, which causes #11256 * Review * Review * Update packages/babel-plugin-transform-parameters/src/params.js [skip ci] Co-Authored-By: Brian Ng <bng412@gmail.com> Co-authored-by: Brian Ng <bng412@gmail.com>
This commit is contained in:
parent
b0315b81c7
commit
ceb54dd756
@ -9,7 +9,6 @@
|
|||||||
},
|
},
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-call-delegate": "^7.8.7",
|
|
||||||
"@babel/helper-get-function-arity": "^7.8.3",
|
"@babel/helper-get-function-arity": "^7.8.3",
|
||||||
"@babel/helper-plugin-utils": "^7.8.3"
|
"@babel/helper-plugin-utils": "^7.8.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import callDelegate from "@babel/helper-call-delegate";
|
|
||||||
import { template, types as t } from "@babel/core";
|
import { template, types as t } from "@babel/core";
|
||||||
|
|
||||||
const buildDefaultParam = template(`
|
const buildDefaultParam = template(`
|
||||||
@ -23,47 +22,40 @@ const buildSafeArgumentsAccess = template(`
|
|||||||
let $0 = arguments.length > $1 ? arguments[$1] : undefined;
|
let $0 = arguments.length > $1 ? arguments[$1] : undefined;
|
||||||
`);
|
`);
|
||||||
|
|
||||||
function isSafeBinding(scope, node) {
|
|
||||||
if (!scope.hasOwnBinding(node.name)) return true;
|
|
||||||
const { kind } = scope.getOwnBinding(node.name);
|
|
||||||
return kind === "param" || kind === "local";
|
|
||||||
}
|
|
||||||
|
|
||||||
const iifeVisitor = {
|
const iifeVisitor = {
|
||||||
ReferencedIdentifier(path, state) {
|
"ReferencedIdentifier|BindingIdentifier"(path, state) {
|
||||||
const { scope, node } = path;
|
const { scope, node } = path;
|
||||||
|
const { name } = node;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
node.name === "eval" ||
|
name === "eval" ||
|
||||||
!isSafeBinding(scope, node) ||
|
(scope.getBinding(name) === state.scope.parent.getBinding(name) &&
|
||||||
!isSafeBinding(state.scope, node)
|
state.scope.hasOwnBinding(name))
|
||||||
) {
|
) {
|
||||||
state.iife = true;
|
state.needsOuterBinding = true;
|
||||||
path.stop();
|
path.stop();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Scope(path) {
|
|
||||||
// different bindings
|
|
||||||
path.skip();
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function convertFunctionParams(path, loose) {
|
export default function convertFunctionParams(path, loose) {
|
||||||
|
const params = path.get("params");
|
||||||
|
|
||||||
|
const isSimpleParameterList = params.every(param => param.isIdentifier());
|
||||||
|
if (isSimpleParameterList) return false;
|
||||||
|
|
||||||
const { node, scope } = path;
|
const { node, scope } = path;
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
iife: false,
|
stop: false,
|
||||||
scope: scope,
|
needsOuterBinding: false,
|
||||||
|
scope,
|
||||||
};
|
};
|
||||||
|
|
||||||
const body = [];
|
const body = [];
|
||||||
const params = path.get("params");
|
const shadowedParams = new Set();
|
||||||
|
|
||||||
let firstOptionalIndex = null;
|
|
||||||
|
|
||||||
for (let i = 0; i < params.length; i++) {
|
|
||||||
const param = params[i];
|
|
||||||
|
|
||||||
|
for (const param of params) {
|
||||||
for (const name of Object.keys(param.getBindingIdentifiers())) {
|
for (const name of Object.keys(param.getBindingIdentifiers())) {
|
||||||
const constantViolations = scope.bindings[name]?.constantViolations;
|
const constantViolations = scope.bindings[name]?.constantViolations;
|
||||||
if (constantViolations) {
|
if (constantViolations) {
|
||||||
@ -74,7 +66,7 @@ export default function convertFunctionParams(path, loose) {
|
|||||||
// If so, we remove that declarator.
|
// If so, we remove that declarator.
|
||||||
// Otherwise, we have to wrap it in an IIFE.
|
// Otherwise, we have to wrap it in an IIFE.
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
case "VariableDeclarator":
|
case "VariableDeclarator": {
|
||||||
if (node.init === null) {
|
if (node.init === null) {
|
||||||
const declaration = redeclarator.parentPath;
|
const declaration = redeclarator.parentPath;
|
||||||
// The following uninitialized var declarators should not be removed
|
// The following uninitialized var declarators should not be removed
|
||||||
@ -88,14 +80,30 @@ export default function convertFunctionParams(path, loose) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// fall through
|
|
||||||
|
shadowedParams.add(name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "FunctionDeclaration":
|
case "FunctionDeclaration":
|
||||||
state.iife = true;
|
shadowedParams.add(name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shadowedParams.size === 0) {
|
||||||
|
for (const param of params) {
|
||||||
|
if (!param.isIdentifier()) param.traverse(iifeVisitor, state);
|
||||||
|
if (state.needsOuterBinding) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let firstOptionalIndex = null;
|
||||||
|
|
||||||
|
for (let i = 0; i < params.length; i++) {
|
||||||
|
const param = params[i];
|
||||||
|
|
||||||
const paramIsAssignmentPattern = param.isAssignmentPattern();
|
const paramIsAssignmentPattern = param.isAssignmentPattern();
|
||||||
if (paramIsAssignmentPattern && (loose || node.kind === "set")) {
|
if (paramIsAssignmentPattern && (loose || node.kind === "set")) {
|
||||||
@ -131,15 +139,6 @@ export default function convertFunctionParams(path, loose) {
|
|||||||
const left = param.get("left");
|
const left = param.get("left");
|
||||||
const right = param.get("right");
|
const right = param.get("right");
|
||||||
|
|
||||||
if (!state.iife) {
|
|
||||||
if (right.isIdentifier() && !isSafeBinding(scope, right.node)) {
|
|
||||||
// the right hand side references a parameter
|
|
||||||
state.iife = true;
|
|
||||||
} else {
|
|
||||||
right.traverse(iifeVisitor, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const defNode = buildDefaultParam({
|
const defNode = buildDefaultParam({
|
||||||
VARIABLE_NAME: left.node,
|
VARIABLE_NAME: left.node,
|
||||||
DEFAULT_VALUE: right.node,
|
DEFAULT_VALUE: right.node,
|
||||||
@ -162,14 +161,8 @@ export default function convertFunctionParams(path, loose) {
|
|||||||
|
|
||||||
param.replaceWith(t.cloneNode(uid));
|
param.replaceWith(t.cloneNode(uid));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!state.iife && !param.isIdentifier()) {
|
|
||||||
param.traverse(iifeVisitor, state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (body.length === 0) return false;
|
|
||||||
|
|
||||||
// we need to cut off all trailing parameters
|
// we need to cut off all trailing parameters
|
||||||
if (firstOptionalIndex !== null) {
|
if (firstOptionalIndex !== null) {
|
||||||
node.params = node.params.slice(0, firstOptionalIndex);
|
node.params = node.params.slice(0, firstOptionalIndex);
|
||||||
@ -178,13 +171,34 @@ export default function convertFunctionParams(path, loose) {
|
|||||||
// ensure it's a block, useful for arrow functions
|
// ensure it's a block, useful for arrow functions
|
||||||
path.ensureBlock();
|
path.ensureBlock();
|
||||||
|
|
||||||
if (state.iife) {
|
if (state.needsOuterBinding || shadowedParams.size > 0) {
|
||||||
// we don't want to hoist the inner declarations up
|
body.push(buildScopeIIFE(shadowedParams, path.get("body").node));
|
||||||
body.push(callDelegate(path, scope, false));
|
|
||||||
path.set("body", t.blockStatement(body));
|
path.set("body", t.blockStatement(body));
|
||||||
|
|
||||||
|
// We inject an arrow and then transform it to a normal function, to be
|
||||||
|
// sure that we correctly handle this and arguments.
|
||||||
|
const bodyPath = path.get("body.body");
|
||||||
|
const arrowPath = bodyPath[bodyPath.length - 1].get("argument.callee");
|
||||||
|
arrowPath.arrowFunctionToExpression();
|
||||||
} else {
|
} else {
|
||||||
path.get("body").unshiftContainer("body", body);
|
path.get("body").unshiftContainer("body", body);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildScopeIIFE(shadowedParams, body) {
|
||||||
|
const args = [];
|
||||||
|
const params = [];
|
||||||
|
|
||||||
|
for (const name of shadowedParams) {
|
||||||
|
// We create them twice; the other option is to use t.cloneNode
|
||||||
|
args.push(t.identifier(name));
|
||||||
|
params.push(t.identifier(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.returnStatement(
|
||||||
|
t.callExpression(t.arrowFunctionExpression(params, body), params),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
function foo() {
|
function foo() {
|
||||||
var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2;
|
var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2;
|
||||||
return function () {
|
return function (a) {
|
||||||
function a() {}
|
function a() {}
|
||||||
}();
|
}(a);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,11 +10,13 @@ var Test = /*#__PURE__*/function () {
|
|||||||
babelHelpers.createClass(Test, [{
|
babelHelpers.createClass(Test, [{
|
||||||
key: "invite",
|
key: "invite",
|
||||||
value: function invite() {
|
value: function invite() {
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
var p = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : a;
|
var p = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : a;
|
||||||
return function () {
|
return function () {
|
||||||
var a;
|
var a;
|
||||||
this;
|
_this;
|
||||||
}.apply(this);
|
}();
|
||||||
}
|
}
|
||||||
}]);
|
}]);
|
||||||
return Test;
|
return Test;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
function foo(a = 2) {
|
function foo(a = 2) {
|
||||||
for (var a, i = 0; i < 1; i++);
|
for (var a, i = 0; i < 1; i++);
|
||||||
expect(a).toBe(undefined);
|
expect(a).toBe(2);
|
||||||
}
|
}
|
||||||
foo();
|
foo();
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
function f() {
|
function f() {
|
||||||
var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2;
|
var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2;
|
||||||
var b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3;
|
var b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3;
|
||||||
return function () {
|
return function (b) {
|
||||||
var b = 4;
|
var b = 4;
|
||||||
return a + b;
|
return a + b;
|
||||||
}();
|
}(b);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
function f() {
|
function f() {
|
||||||
var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2;
|
var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2;
|
||||||
var b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3;
|
var b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3;
|
||||||
return function () {
|
return function (a) {
|
||||||
var _a = {
|
var _a = {
|
||||||
a: 4
|
a: 4
|
||||||
},
|
},
|
||||||
a = _a.a;
|
a = _a.a;
|
||||||
return a + b;
|
return a + b;
|
||||||
}();
|
}(a);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
function foo(_ref) {
|
function foo(_ref) {
|
||||||
var a = _ref.a,
|
var a = _ref.a,
|
||||||
b = _ref.b;
|
b = _ref.b;
|
||||||
return function () {
|
return function (a) {
|
||||||
var a = 3;
|
var a = 3;
|
||||||
var c = 2;
|
var c = 2;
|
||||||
var d = a + b + c;
|
var d = a + b + c;
|
||||||
}();
|
}(a);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,9 +3,9 @@ function foo(_ref) {
|
|||||||
a = _ref2[0],
|
a = _ref2[0],
|
||||||
b = _ref2[1];
|
b = _ref2[1];
|
||||||
|
|
||||||
return function () {
|
return function (a) {
|
||||||
var a = 3;
|
var a = 3;
|
||||||
var c = 2;
|
var c = 2;
|
||||||
var d = a + b + c;
|
var d = a + b + c;
|
||||||
}();
|
}(a);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
function foo() {
|
function foo() {
|
||||||
var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2;
|
var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2;
|
||||||
return function () {
|
return function (a) {
|
||||||
var a = 1;
|
var a = 1;
|
||||||
}();
|
}(a);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
function f() {
|
function f() {
|
||||||
var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2;
|
var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2;
|
||||||
var b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3;
|
var b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3;
|
||||||
return function () {
|
return function (a) {
|
||||||
var _a = {
|
var _a = {
|
||||||
a: 4
|
a: 4
|
||||||
},
|
},
|
||||||
a = _a.a;
|
a = _a.a;
|
||||||
return a + b;
|
return a + b;
|
||||||
}();
|
}(a);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
(function () {
|
(function () {
|
||||||
let i = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "__proto__";
|
let i = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "__proto__";
|
||||||
return function () {
|
return function (i) {
|
||||||
for (var i in {});
|
for (var i in {});
|
||||||
}();
|
}(i);
|
||||||
});
|
});
|
||||||
|
|||||||
7
packages/babel-plugin-transform-parameters/test/fixtures/regression/11256/input.js
vendored
Normal file
7
packages/babel-plugin-transform-parameters/test/fixtures/regression/11256/input.js
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
class A {
|
||||||
|
a = b => {
|
||||||
|
{
|
||||||
|
({ b } = {});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
8
packages/babel-plugin-transform-parameters/test/fixtures/regression/11256/options.json
vendored
Normal file
8
packages/babel-plugin-transform-parameters/test/fixtures/regression/11256/options.json
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"proposal-class-properties",
|
||||||
|
"transform-parameters",
|
||||||
|
"transform-destructuring",
|
||||||
|
["external-helpers", { "helperVersion": "7.100.0" }]
|
||||||
|
]
|
||||||
|
}
|
||||||
11
packages/babel-plugin-transform-parameters/test/fixtures/regression/11256/output.js
vendored
Normal file
11
packages/babel-plugin-transform-parameters/test/fixtures/regression/11256/output.js
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
class A {
|
||||||
|
constructor() {
|
||||||
|
babelHelpers.defineProperty(this, "a", b => {
|
||||||
|
{
|
||||||
|
var _ref = {};
|
||||||
|
b = _ref.b;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
6
packages/babel-plugin-transform-parameters/test/fixtures/regression/11303/exec.js
vendored
Normal file
6
packages/babel-plugin-transform-parameters/test/fixtures/regression/11303/exec.js
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
function test(a, b = 1) {
|
||||||
|
var a = a + b;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(test(2)).toBe(3);
|
||||||
4
packages/babel-plugin-transform-parameters/test/fixtures/regression/11303/input.js
vendored
Normal file
4
packages/babel-plugin-transform-parameters/test/fixtures/regression/11303/input.js
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
function test(a, b = 1) {
|
||||||
|
var a = a + b;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
5
packages/babel-plugin-transform-parameters/test/fixtures/regression/11303/options.json
vendored
Normal file
5
packages/babel-plugin-transform-parameters/test/fixtures/regression/11303/options.json
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"transform-parameters"
|
||||||
|
]
|
||||||
|
}
|
||||||
7
packages/babel-plugin-transform-parameters/test/fixtures/regression/11303/output.js
vendored
Normal file
7
packages/babel-plugin-transform-parameters/test/fixtures/regression/11303/output.js
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
function test(a) {
|
||||||
|
let b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
|
||||||
|
return function (a) {
|
||||||
|
var a = a + b;
|
||||||
|
return a;
|
||||||
|
}(a);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user