diff --git a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-unpack-optimisation/output.js b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-unpack-optimisation/output.js index 7743f54e7c6a..a4f4e8cc6a9d 100644 --- a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-unpack-optimisation/output.js +++ b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-unpack-optimisation/output.js @@ -21,7 +21,7 @@ var _ref4 = [a[1], a[0]]; a[0] = _ref4[0]; a[1] = _ref4[1]; -var _ref5 = babelHelpers.toConsumableArray(foo).concat([bar]), +var _ref5 = Array.prototype.concat(babelHelpers.toConsumableArray(foo), [bar]), a = _ref5[0], b = _ref5[1]; diff --git a/packages/babel-plugin-transform-spread/src/index.js b/packages/babel-plugin-transform-spread/src/index.js index 17428ca362bc..4edcc7fc71e3 100644 --- a/packages/babel-plugin-transform-spread/src/index.js +++ b/packages/babel-plugin-transform-spread/src/index.js @@ -57,19 +57,44 @@ export default declare((api, options) => { if (!hasSpread(elements)) return; const nodes = build(elements, scope); - const first = nodes.shift(); + const first = nodes[0]; - if (nodes.length === 0 && first !== elements[0].argument) { + // If there is only one element in the ArrayExpression and + // the element was transformed (Array.prototype.slice.call or toConsumableArray) + // we know that the transformed code already takes care of cloning the array. + // So we can simply return that element. + if (nodes.length === 1 && first !== elements[0].argument) { path.replaceWith(first); return; } - path.replaceWith( - t.callExpression( - t.memberExpression(first, t.identifier("concat")), - nodes, - ), - ); + // If the first element is a ArrayExpression we can directly call + // concat on it. + // [..].concat(..) + // If not then we have to use Array.prototype.concat() and not identifier.concat + // because identifier could be extended/modified (e.g. Immutable) and we do not know exactly + // what concat would produce. + if (t.isArrayExpression(first)) { + path.replaceWith( + t.callExpression( + t.memberExpression(nodes.shift(), t.identifier("concat")), + nodes, + ), + ); + } else { + path.replaceWith( + t.callExpression( + t.memberExpression( + t.memberExpression( + t.identifier("Array"), + t.identifier("prototype"), + ), + t.identifier("concat"), + ), + nodes, + ), + ); + } }, CallExpression(path) { diff --git a/packages/babel-plugin-transform-spread/test/fixtures/regression/issue-8907/exec.js b/packages/babel-plugin-transform-spread/test/fixtures/regression/issue-8907/exec.js new file mode 100644 index 000000000000..0755ca2f5ca6 --- /dev/null +++ b/packages/babel-plugin-transform-spread/test/fixtures/regression/issue-8907/exec.js @@ -0,0 +1,9 @@ +const arr = []; + +arr.concat = () => { + throw new Error('Should not be called'); +}; + +expect(() => { + const x = [...arr]; +}).not.toThrow(); diff --git a/packages/babel-plugin-transform-spread/test/fixtures/regression/issue-8907/input.js b/packages/babel-plugin-transform-spread/test/fixtures/regression/issue-8907/input.js new file mode 100644 index 000000000000..b95bf724af4f --- /dev/null +++ b/packages/babel-plugin-transform-spread/test/fixtures/regression/issue-8907/input.js @@ -0,0 +1,7 @@ +const arr = []; + +arr.concat = () => { + throw new Error('Should not be called'); +}; + +const x = [...arr]; diff --git a/packages/babel-plugin-transform-spread/test/fixtures/regression/issue-8907/output.js b/packages/babel-plugin-transform-spread/test/fixtures/regression/issue-8907/output.js new file mode 100644 index 000000000000..ba38e7b97fc8 --- /dev/null +++ b/packages/babel-plugin-transform-spread/test/fixtures/regression/issue-8907/output.js @@ -0,0 +1,7 @@ +const arr = []; + +arr.concat = () => { + throw new Error('Should not be called'); +}; + +const x = Array.prototype.concat(arr); diff --git a/packages/babel-plugin-transform-spread/test/fixtures/spread/array-literal-first/output.js b/packages/babel-plugin-transform-spread/test/fixtures/spread/array-literal-first/output.js index fbe8da8f82ea..5283dc3b9bd4 100644 --- a/packages/babel-plugin-transform-spread/test/fixtures/spread/array-literal-first/output.js +++ b/packages/babel-plugin-transform-spread/test/fixtures/spread/array-literal-first/output.js @@ -1 +1 @@ -var lyrics = babelHelpers.toConsumableArray(parts).concat(["head", "and", "toes"]); +var lyrics = Array.prototype.concat(babelHelpers.toConsumableArray(parts), ["head", "and", "toes"]); diff --git a/packages/babel-plugin-transform-spread/test/fixtures/spread/known-rest/output.js b/packages/babel-plugin-transform-spread/test/fixtures/spread/known-rest/output.js index 64c67e4ca8d7..b888110d55b4 100644 --- a/packages/babel-plugin-transform-spread/test/fixtures/spread/known-rest/output.js +++ b/packages/babel-plugin-transform-spread/test/fixtures/spread/known-rest/output.js @@ -3,5 +3,5 @@ function foo() { bar[_key] = arguments[_key]; } - return bar.concat(); + return Array.prototype.concat(bar); }