diff --git a/packages/babel-plugin-transform-destructuring/src/index.js b/packages/babel-plugin-transform-destructuring/src/index.js index 52a76928e19fb..e728d99851e14 100644 --- a/packages/babel-plugin-transform-destructuring/src/index.js +++ b/packages/babel-plugin-transform-destructuring/src/index.js @@ -1,15 +1,43 @@ import { declare } from "@babel/helper-plugin-utils"; import { types as t } from "@babel/core"; +import escapeRegExp from "lodash/escapeRegExp"; + +function checkNameMatchesSelectiveLoose(name, selectiveLoose) { + for (let i = 0; i < selectiveLoose.length; i++) { + if (selectiveLoose[i].test(name)) { + return true; + } + } + return false; +} export default declare((api, options) => { api.assertVersion(7); - const { loose = false, useBuiltIns = false } = options; + const { + loose = false, + useBuiltIns = false, + selectiveLoose = false, + } = options; if (typeof loose !== "boolean") { throw new Error(`.loose must be a boolean or undefined`); } + // Format selectiveLoose entries to RegExp expressions that can + // match generated babel names, i.e. `_x`, `_x2`, `_x3`, etc. + if (selectiveLoose) { + if (selectiveLoose instanceof Array) { + for (let i = 0; i < selectiveLoose.length; i++) { + selectiveLoose[i] = new RegExp( + `^_${escapeRegExp(selectiveLoose[i])}\\d*$`, + ); + } + } else { + throw new Error(`.selectiveLoose must be an array or undefined`); + } + } + const arrayOnlySpread = loose; function getExtendsHelper(file) { @@ -73,6 +101,7 @@ export default declare((api, options) => { this.kind = opts.kind; this.arrayOnlySpread = opts.arrayOnlySpread; this.addHelper = opts.addHelper; + this.selectiveLoose = opts.selectiveLoose; } buildVariableAssignment(id, init) { @@ -210,8 +239,12 @@ export default declare((api, options) => { ); } + const isLoose = + (this.selectiveLoose && + checkNameMatchesSelectiveLoose(objRef.name, this.selectiveLoose)) || + loose; value = t.callExpression( - this.addHelper(`objectWithoutProperties${loose ? "Loose" : ""}`), + this.addHelper(`objectWithoutProperties${isLoose ? "Loose" : ""}`), [t.cloneNode(objRef), keyExpression], ); } @@ -479,6 +512,7 @@ export default declare((api, options) => { scope: scope, nodes: nodes, arrayOnlySpread, + selectiveLoose, addHelper: name => this.addHelper(name), }); @@ -504,6 +538,7 @@ export default declare((api, options) => { scope: scope, nodes: nodes, arrayOnlySpread, + selectiveLoose, addHelper: name => this.addHelper(name), }); destructuring.init(pattern, ref); @@ -522,6 +557,7 @@ export default declare((api, options) => { scope: scope, nodes: nodes, arrayOnlySpread, + selectiveLoose, addHelper: name => this.addHelper(name), }); @@ -579,6 +615,7 @@ export default declare((api, options) => { scope: scope, kind: node.kind, arrayOnlySpread, + selectiveLoose, addHelper: name => this.addHelper(name), }); diff --git a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/es7-object-rest-selective-loose/exec.js b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/es7-object-rest-selective-loose/exec.js new file mode 100644 index 0000000000000..4b9c599e435eb --- /dev/null +++ b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/es7-object-rest-selective-loose/exec.js @@ -0,0 +1,6 @@ +const useState = () => (["state", "dispatch"]); + +var [state, dispatch] = useState(); + +expect(state).toBe("state"); +expect(dispatch).toBe("dispatch"); diff --git a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/es7-object-rest-selective-loose/input.js b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/es7-object-rest-selective-loose/input.js new file mode 100644 index 0000000000000..1997973d3f99d --- /dev/null +++ b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/es7-object-rest-selective-loose/input.js @@ -0,0 +1,9 @@ +var z = {}; +var { ...x } = z; +var { x, ...y } = z; +var { [x]: x, ...y } = z; +(function({ x, ...y }) { }); + +({ x, y, ...z } = o); + +var [state, dispatch] = useState(); diff --git a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/es7-object-rest-selective-loose/options.json b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/es7-object-rest-selective-loose/options.json new file mode 100644 index 0000000000000..7ac0fc857cf01 --- /dev/null +++ b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/es7-object-rest-selective-loose/options.json @@ -0,0 +1,7 @@ +{ + "plugins": [ + ["transform-destructuring", { "selectiveLoose": ["z", "useState"] }], + "proposal-object-rest-spread", + ["external-helpers", { "helperVersion": "7.1.5" }] + ] +} diff --git a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/es7-object-rest-selective-loose/output.js b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/es7-object-rest-selective-loose/output.js new file mode 100644 index 0000000000000..f4e4df89d7b90 --- /dev/null +++ b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/es7-object-rest-selective-loose/output.js @@ -0,0 +1,24 @@ +var z = {}; +var _z = z, + x = babelHelpers.extends({}, _z); +var _z2 = z, + x = _z2.x, + y = babelHelpers.objectWithoutPropertiesLoose(_z2, ["x"]); +var _z3 = z, + x = _z3[x], + y = babelHelpers.objectWithoutPropertiesLoose(_z3, [x].map(babelHelpers.toPropertyKey)); + +(function (_ref) { + let x = _ref.x, + y = babelHelpers.objectWithoutProperties(_ref, ["x"]); +}); + +var _o = o; +x = _o.x; +y = _o.y; +z = babelHelpers.objectWithoutProperties(_o, ["x", "y"]); + +var _useState = useState(), + _useState2 = babelHelpers.slicedToArray(_useState, 2), + state = _useState2[0], + dispatch = _useState2[1];