diff --git a/packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-rest/impure-computed-exec/exec.js b/packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-rest/impure-computed-exec/exec.js index c422b2ff8ff8..da5288308c3b 100644 --- a/packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-rest/impure-computed-exec/exec.js +++ b/packages/babel-plugin-proposal-object-rest-spread/test/fixtures/object-rest/impure-computed-exec/exec.js @@ -19,3 +19,16 @@ key = 2; expect(y).toBe("two"); expect(x).toEqual({}); expect(z).toBe("zee"); + +// rhs evaluated before lhs +var order = []; +function left() { + order.push("left"); + return 0; +} +function right() { + order.push("right"); + return {}; +} +var { [left()]: y, ...x} = right(); +expect(order).toEqual(["right", "left"]); diff --git a/packages/babel-plugin-transform-destructuring/src/index.js b/packages/babel-plugin-transform-destructuring/src/index.js index 3e8bd464298c..c58d277d9824 100644 --- a/packages/babel-plugin-transform-destructuring/src/index.js +++ b/packages/babel-plugin-transform-destructuring/src/index.js @@ -44,6 +44,19 @@ export default declare((api, options) => { return false; } + /** + * Test if an ObjectPattern's elements contain any RestElements. + */ + + function hasObjectRest(pattern) { + for (const elem of (pattern.properties: Array)) { + if (t.isRestElement(elem)) { + return true; + } + } + return false; + } + const STOP_TRAVERSAL = {}; // NOTE: This visitor is meant to be used via t.traverse @@ -259,6 +272,31 @@ export default declare((api, options) => { objRef = temp; } + // Replace impure computed key expressions if we have a rest parameter + if (hasObjectRest(pattern)) { + let copiedPattern; + for (let i = 0; i < pattern.properties.length; i++) { + const prop = pattern.properties[i]; + if (t.isRestElement(prop)) { + break; + } + const key = prop.key; + if (prop.computed && !this.scope.isPure(key)) { + const name = this.scope.generateUidIdentifierBasedOnNode(key); + this.nodes.push(this.buildVariableDeclaration(name, key)); + if (!copiedPattern) { + copiedPattern = pattern = { + ...pattern, + properties: pattern.properties.slice(), + }; + } + copiedPattern.properties[i] = { + ...copiedPattern.properties[i], + key: name, + }; + } + } + } // for (let i = 0; i < pattern.properties.length; i++) { diff --git a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/object-rest-impure-computed-keys/exec.js b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/object-rest-impure-computed-keys/exec.js new file mode 100644 index 000000000000..da5288308c3b --- /dev/null +++ b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/object-rest-impure-computed-keys/exec.js @@ -0,0 +1,34 @@ +var key, x, y, z; +// impure +key = 1; +var { [key++]: y, ...x } = { 1: 1, a: 1 }; +expect(x).toEqual({ a: 1 }); +expect(key).toBe(2); +expect(1).toBe(y); + +// takes care of the order + +key = 1; +var { [++key]: y, [++key]: z, ...rest} = {2: 2, 3: 3}; +expect(y).toBe(2); +expect(z).toBe(3); + +// pure, computed property should remain as-is +key = 2; +({ [key]: y, z, ...x } = {2: "two", z: "zee"}); +expect(y).toBe("two"); +expect(x).toEqual({}); +expect(z).toBe("zee"); + +// rhs evaluated before lhs +var order = []; +function left() { + order.push("left"); + return 0; +} +function right() { + order.push("right"); + return {}; +} +var { [left()]: y, ...x} = right(); +expect(order).toEqual(["right", "left"]); diff --git a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/object-rest-impure-computed-keys/input.js b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/object-rest-impure-computed-keys/input.js new file mode 100644 index 000000000000..af55509ef761 --- /dev/null +++ b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/object-rest-impure-computed-keys/input.js @@ -0,0 +1 @@ +var { [fn()]: x, ...y } = z; diff --git a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/object-rest-impure-computed-keys/output.js b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/object-rest-impure-computed-keys/output.js new file mode 100644 index 000000000000..94225a5e8c1d --- /dev/null +++ b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/object-rest-impure-computed-keys/output.js @@ -0,0 +1,4 @@ +var _z = z, + _fn = fn(), + x = _z[_fn], + y = babelHelpers.objectWithoutProperties(_z, [_fn].map(babelHelpers.toPropertyKey));