Don't extract rest elements from nested expressions (#7364)
* Don't extract rest elements from nested expressions * Node 4
This commit is contained in:
parent
4d17a96d50
commit
3d49766f6b
@ -10,15 +10,30 @@ export default function(api, opts) {
|
|||||||
|
|
||||||
function hasRestElement(path) {
|
function hasRestElement(path) {
|
||||||
let foundRestElement = false;
|
let foundRestElement = false;
|
||||||
path.traverse({
|
visitRestElements(path, () => {
|
||||||
RestElement() {
|
foundRestElement = true;
|
||||||
foundRestElement = true;
|
path.stop();
|
||||||
path.stop();
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
return foundRestElement;
|
return foundRestElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function visitRestElements(path, visitor) {
|
||||||
|
path.traverse({
|
||||||
|
Expression(path) {
|
||||||
|
const parentType = path.parent.type;
|
||||||
|
if (
|
||||||
|
(parentType == "AssignmentPattern" && path.key === "right") ||
|
||||||
|
(parentType == "ObjectProperty" &&
|
||||||
|
path.parent.computed &&
|
||||||
|
path.key === "key")
|
||||||
|
) {
|
||||||
|
path.skip();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RestElement: visitor,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function hasSpread(node) {
|
function hasSpread(node) {
|
||||||
for (const prop of node.properties) {
|
for (const prop of node.properties) {
|
||||||
if (t.isSpreadElement(prop)) {
|
if (t.isSpreadElement(prop)) {
|
||||||
@ -147,114 +162,102 @@ export default function(api, opts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let insertionPath = path;
|
let insertionPath = path;
|
||||||
|
const originalPath = path;
|
||||||
|
|
||||||
path.get("id").traverse(
|
visitRestElements(path.get("id"), path => {
|
||||||
{
|
if (!path.parentPath.isObjectPattern()) {
|
||||||
// If there's a default-value AssignmentPattern within the ObjectPattern,
|
// Return early if the parent is not an ObjectPattern, but
|
||||||
// we should not traverse into it, lest we end up in another function body.
|
// (for example) an ArrayPattern or Function, because that
|
||||||
// (The parent traversal will handle it.)
|
// means this RestElement is an not an object property.
|
||||||
AssignmentPattern(path) {
|
return;
|
||||||
path.skip();
|
}
|
||||||
},
|
|
||||||
RestElement(path) {
|
|
||||||
if (!path.parentPath.isObjectPattern()) {
|
|
||||||
// Return early if the parent is not an ObjectPattern, but
|
|
||||||
// (for example) an ArrayPattern or Function, because that
|
|
||||||
// means this RestElement is an not an object property.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
// skip single-property case, e.g.
|
// skip single-property case, e.g.
|
||||||
// const { ...x } = foo();
|
// const { ...x } = foo();
|
||||||
// since the RHS will not be duplicated
|
// since the RHS will not be duplicated
|
||||||
this.originalPath.node.id.properties.length > 1 &&
|
originalPath.node.id.properties.length > 1 &&
|
||||||
!t.isIdentifier(this.originalPath.node.init)
|
!t.isIdentifier(originalPath.node.init)
|
||||||
) {
|
) {
|
||||||
// const { a, ...b } = foo();
|
// const { a, ...b } = foo();
|
||||||
// to avoid calling foo() twice, as a first step convert it to:
|
// to avoid calling foo() twice, as a first step convert it to:
|
||||||
// const _foo = foo(),
|
// const _foo = foo(),
|
||||||
// { a, ...b } = _foo;
|
// { a, ...b } = _foo;
|
||||||
const initRef = path.scope.generateUidIdentifierBasedOnNode(
|
const initRef = path.scope.generateUidIdentifierBasedOnNode(
|
||||||
this.originalPath.node.init,
|
originalPath.node.init,
|
||||||
"ref",
|
"ref",
|
||||||
);
|
);
|
||||||
// insert _foo = foo()
|
// insert _foo = foo()
|
||||||
this.originalPath.insertBefore(
|
originalPath.insertBefore(
|
||||||
t.variableDeclarator(initRef, this.originalPath.node.init),
|
t.variableDeclarator(initRef, originalPath.node.init),
|
||||||
);
|
);
|
||||||
// replace foo() with _foo
|
// replace foo() with _foo
|
||||||
this.originalPath.replaceWith(
|
originalPath.replaceWith(
|
||||||
t.variableDeclarator(
|
t.variableDeclarator(originalPath.node.id, t.cloneNode(initRef)),
|
||||||
this.originalPath.node.id,
|
);
|
||||||
t.cloneNode(initRef),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ref = this.originalPath.node.init;
|
let ref = originalPath.node.init;
|
||||||
const refPropertyPath = [];
|
const refPropertyPath = [];
|
||||||
let kind;
|
let kind;
|
||||||
|
|
||||||
path.findParent(path => {
|
path.findParent(path => {
|
||||||
if (path.isObjectProperty()) {
|
if (path.isObjectProperty()) {
|
||||||
refPropertyPath.unshift(path.node.key.name);
|
refPropertyPath.unshift(path.node.key.name);
|
||||||
} else if (path.isVariableDeclarator()) {
|
} else if (path.isVariableDeclarator()) {
|
||||||
kind = path.parentPath.node.kind;
|
kind = path.parentPath.node.kind;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (refPropertyPath.length) {
|
if (refPropertyPath.length) {
|
||||||
refPropertyPath.forEach(prop => {
|
refPropertyPath.forEach(prop => {
|
||||||
ref = t.memberExpression(ref, t.identifier(prop));
|
ref = t.memberExpression(ref, t.identifier(prop));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const objectPatternPath = path.findParent(path =>
|
const objectPatternPath = path.findParent(path =>
|
||||||
path.isObjectPattern(),
|
path.isObjectPattern(),
|
||||||
);
|
);
|
||||||
const [
|
const [
|
||||||
impureComputedPropertyDeclarators,
|
impureComputedPropertyDeclarators,
|
||||||
argument,
|
argument,
|
||||||
callExpression,
|
callExpression,
|
||||||
] = createObjectSpread(objectPatternPath, file, ref);
|
] = createObjectSpread(objectPatternPath, file, ref);
|
||||||
|
|
||||||
t.assertIdentifier(argument);
|
t.assertIdentifier(argument);
|
||||||
|
|
||||||
insertionPath.insertBefore(impureComputedPropertyDeclarators);
|
insertionPath.insertBefore(impureComputedPropertyDeclarators);
|
||||||
|
|
||||||
insertionPath.insertAfter(
|
insertionPath.insertAfter(
|
||||||
t.variableDeclarator(argument, callExpression),
|
t.variableDeclarator(argument, callExpression),
|
||||||
);
|
);
|
||||||
|
|
||||||
insertionPath = insertionPath.getSibling(insertionPath.key + 1);
|
insertionPath = insertionPath.getSibling(insertionPath.key + 1);
|
||||||
|
|
||||||
path.scope.registerBinding(kind, insertionPath);
|
path.scope.registerBinding(kind, insertionPath);
|
||||||
|
|
||||||
if (objectPatternPath.node.properties.length === 0) {
|
if (objectPatternPath.node.properties.length === 0) {
|
||||||
objectPatternPath
|
objectPatternPath
|
||||||
.findParent(
|
.findParent(
|
||||||
path =>
|
path => path.isObjectProperty() || path.isVariableDeclarator(),
|
||||||
path.isObjectProperty() || path.isVariableDeclarator(),
|
)
|
||||||
)
|
.remove();
|
||||||
.remove();
|
}
|
||||||
}
|
});
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
originalPath: path,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
// taken from transform-destructuring/src/index.js#visitor
|
// taken from transform-destructuring/src/index.js#visitor
|
||||||
// export var { a, ...b } = c;
|
// export var { a, ...b } = c;
|
||||||
ExportNamedDeclaration(path) {
|
ExportNamedDeclaration(path) {
|
||||||
const declaration = path.get("declaration");
|
const declaration = path.get("declaration");
|
||||||
if (!declaration.isVariableDeclaration()) return;
|
if (!declaration.isVariableDeclaration()) return;
|
||||||
if (!hasRestElement(declaration)) return;
|
|
||||||
|
const hasRest = declaration
|
||||||
|
.get("declarations")
|
||||||
|
.some(path => hasRestElement(path.get("id")));
|
||||||
|
if (!hasRest) return;
|
||||||
|
|
||||||
const specifiers = [];
|
const specifiers = [];
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
const {
|
||||||
|
[({ ...rest }) => {
|
||||||
|
let { ...b } = {};
|
||||||
|
}]: a,
|
||||||
|
[({ ...d } = {})]: c,
|
||||||
|
} = {};
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
var _ref2;
|
||||||
|
|
||||||
|
const {
|
||||||
|
[(_ref) => {
|
||||||
|
let rest = babelHelpers.objectWithoutProperties(_ref, []);
|
||||||
|
let b = babelHelpers.objectWithoutProperties({}, []);
|
||||||
|
}]: a,
|
||||||
|
[(_ref2 = {}, ({} = _ref2), d = babelHelpers.objectWithoutProperties(_ref2, []), _ref2)]: c
|
||||||
|
} = {};
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
const {
|
||||||
|
a = ({ ...rest }) => {
|
||||||
|
let { ...b } = {};
|
||||||
|
},
|
||||||
|
c = ({ ...d } = {}),
|
||||||
|
} = {};
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
var _ref2;
|
||||||
|
|
||||||
|
const {
|
||||||
|
a = (_ref) => {
|
||||||
|
let rest = babelHelpers.objectWithoutProperties(_ref, []);
|
||||||
|
let b = babelHelpers.objectWithoutProperties({}, []);
|
||||||
|
},
|
||||||
|
c = (_ref2 = {}, ({} = _ref2), d = babelHelpers.objectWithoutProperties(_ref2, []), _ref2)
|
||||||
|
} = {};
|
||||||
21
packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-rest/nested-order/exec.js
vendored
Normal file
21
packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-rest/nested-order/exec.js
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
var result = "";
|
||||||
|
|
||||||
|
var obj = {
|
||||||
|
get foo() {
|
||||||
|
result += "foo"
|
||||||
|
},
|
||||||
|
a: {
|
||||||
|
get bar() {
|
||||||
|
result += "bar";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
b: {
|
||||||
|
get baz() {
|
||||||
|
result += "baz";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var { a: { ...bar }, b: { ...baz }, ...foo } = obj;
|
||||||
|
|
||||||
|
assert.strictEqual(result, "barbazfoo");
|
||||||
@ -0,0 +1 @@
|
|||||||
|
const { a: { ...bar }, b: { ...baz }, ...foo } = obj;
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
const bar = babelHelpers.objectWithoutProperties(obj.a, []),
|
||||||
|
baz = babelHelpers.objectWithoutProperties(obj.b, []),
|
||||||
|
foo = babelHelpers.objectWithoutProperties(obj, []);
|
||||||
Loading…
x
Reference in New Issue
Block a user