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) {
|
||||
let foundRestElement = false;
|
||||
path.traverse({
|
||||
RestElement() {
|
||||
foundRestElement = true;
|
||||
path.stop();
|
||||
},
|
||||
visitRestElements(path, () => {
|
||||
foundRestElement = true;
|
||||
path.stop();
|
||||
});
|
||||
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) {
|
||||
for (const prop of node.properties) {
|
||||
if (t.isSpreadElement(prop)) {
|
||||
@ -147,114 +162,102 @@ export default function(api, opts) {
|
||||
}
|
||||
|
||||
let insertionPath = path;
|
||||
const originalPath = path;
|
||||
|
||||
path.get("id").traverse(
|
||||
{
|
||||
// If there's a default-value AssignmentPattern within the ObjectPattern,
|
||||
// we should not traverse into it, lest we end up in another function body.
|
||||
// (The parent traversal will handle it.)
|
||||
AssignmentPattern(path) {
|
||||
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;
|
||||
}
|
||||
visitRestElements(path.get("id"), 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 (
|
||||
// skip single-property case, e.g.
|
||||
// const { ...x } = foo();
|
||||
// since the RHS will not be duplicated
|
||||
this.originalPath.node.id.properties.length > 1 &&
|
||||
!t.isIdentifier(this.originalPath.node.init)
|
||||
) {
|
||||
// const { a, ...b } = foo();
|
||||
// to avoid calling foo() twice, as a first step convert it to:
|
||||
// const _foo = foo(),
|
||||
// { a, ...b } = _foo;
|
||||
const initRef = path.scope.generateUidIdentifierBasedOnNode(
|
||||
this.originalPath.node.init,
|
||||
"ref",
|
||||
);
|
||||
// insert _foo = foo()
|
||||
this.originalPath.insertBefore(
|
||||
t.variableDeclarator(initRef, this.originalPath.node.init),
|
||||
);
|
||||
// replace foo() with _foo
|
||||
this.originalPath.replaceWith(
|
||||
t.variableDeclarator(
|
||||
this.originalPath.node.id,
|
||||
t.cloneNode(initRef),
|
||||
),
|
||||
);
|
||||
if (
|
||||
// skip single-property case, e.g.
|
||||
// const { ...x } = foo();
|
||||
// since the RHS will not be duplicated
|
||||
originalPath.node.id.properties.length > 1 &&
|
||||
!t.isIdentifier(originalPath.node.init)
|
||||
) {
|
||||
// const { a, ...b } = foo();
|
||||
// to avoid calling foo() twice, as a first step convert it to:
|
||||
// const _foo = foo(),
|
||||
// { a, ...b } = _foo;
|
||||
const initRef = path.scope.generateUidIdentifierBasedOnNode(
|
||||
originalPath.node.init,
|
||||
"ref",
|
||||
);
|
||||
// insert _foo = foo()
|
||||
originalPath.insertBefore(
|
||||
t.variableDeclarator(initRef, originalPath.node.init),
|
||||
);
|
||||
// replace foo() with _foo
|
||||
originalPath.replaceWith(
|
||||
t.variableDeclarator(originalPath.node.id, t.cloneNode(initRef)),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let ref = this.originalPath.node.init;
|
||||
const refPropertyPath = [];
|
||||
let kind;
|
||||
let ref = originalPath.node.init;
|
||||
const refPropertyPath = [];
|
||||
let kind;
|
||||
|
||||
path.findParent(path => {
|
||||
if (path.isObjectProperty()) {
|
||||
refPropertyPath.unshift(path.node.key.name);
|
||||
} else if (path.isVariableDeclarator()) {
|
||||
kind = path.parentPath.node.kind;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
path.findParent(path => {
|
||||
if (path.isObjectProperty()) {
|
||||
refPropertyPath.unshift(path.node.key.name);
|
||||
} else if (path.isVariableDeclarator()) {
|
||||
kind = path.parentPath.node.kind;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (refPropertyPath.length) {
|
||||
refPropertyPath.forEach(prop => {
|
||||
ref = t.memberExpression(ref, t.identifier(prop));
|
||||
});
|
||||
}
|
||||
if (refPropertyPath.length) {
|
||||
refPropertyPath.forEach(prop => {
|
||||
ref = t.memberExpression(ref, t.identifier(prop));
|
||||
});
|
||||
}
|
||||
|
||||
const objectPatternPath = path.findParent(path =>
|
||||
path.isObjectPattern(),
|
||||
);
|
||||
const [
|
||||
impureComputedPropertyDeclarators,
|
||||
argument,
|
||||
callExpression,
|
||||
] = createObjectSpread(objectPatternPath, file, ref);
|
||||
const objectPatternPath = path.findParent(path =>
|
||||
path.isObjectPattern(),
|
||||
);
|
||||
const [
|
||||
impureComputedPropertyDeclarators,
|
||||
argument,
|
||||
callExpression,
|
||||
] = createObjectSpread(objectPatternPath, file, ref);
|
||||
|
||||
t.assertIdentifier(argument);
|
||||
t.assertIdentifier(argument);
|
||||
|
||||
insertionPath.insertBefore(impureComputedPropertyDeclarators);
|
||||
insertionPath.insertBefore(impureComputedPropertyDeclarators);
|
||||
|
||||
insertionPath.insertAfter(
|
||||
t.variableDeclarator(argument, callExpression),
|
||||
);
|
||||
insertionPath.insertAfter(
|
||||
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) {
|
||||
objectPatternPath
|
||||
.findParent(
|
||||
path =>
|
||||
path.isObjectProperty() || path.isVariableDeclarator(),
|
||||
)
|
||||
.remove();
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
originalPath: path,
|
||||
},
|
||||
);
|
||||
if (objectPatternPath.node.properties.length === 0) {
|
||||
objectPatternPath
|
||||
.findParent(
|
||||
path => path.isObjectProperty() || path.isVariableDeclarator(),
|
||||
)
|
||||
.remove();
|
||||
}
|
||||
});
|
||||
},
|
||||
// taken from transform-destructuring/src/index.js#visitor
|
||||
// export var { a, ...b } = c;
|
||||
ExportNamedDeclaration(path) {
|
||||
const declaration = path.get("declaration");
|
||||
if (!declaration.isVariableDeclaration()) return;
|
||||
if (!hasRestElement(declaration)) return;
|
||||
|
||||
const hasRest = declaration
|
||||
.get("declarations")
|
||||
.some(path => hasRestElement(path.get("id")));
|
||||
if (!hasRest) return;
|
||||
|
||||
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