From 77adb90543015f1754b2fd36e8812b111286f073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Sun, 15 Mar 2020 01:09:15 +0100 Subject: [PATCH] Add "allowArrayLike" support to the for-of transform --- packages/babel-helpers/src/helpers.js | 14 ++++++++++---- .../babel-plugin-transform-for-of/src/index.js | 13 ++++++++++--- .../fixtures/loose-allowArrayLike/holes/exec.js | 7 +++++++ .../loose-allowArrayLike/holes/options.json | 6 ++++++ .../loose-allowArrayLike/length-cropped/exec.js | 6 ++++++ .../length-cropped/options.json | 6 ++++++ .../fixtures/loose-allowArrayLike/simple/exec.js | 6 ++++++ .../fixtures/loose-allowArrayLike/simple/input.js | 1 + .../loose-allowArrayLike/simple/options.json | 6 ++++++ .../fixtures/loose-allowArrayLike/simple/output.js | 4 ++++ .../fixtures/spec-allowArrayLike/holes/exec.js | 7 +++++++ .../spec-allowArrayLike/holes/options.json | 6 ++++++ .../spec-allowArrayLike/length-cropped/exec.js | 6 ++++++ .../length-cropped/options.json | 6 ++++++ .../fixtures/spec-allowArrayLike/simple/exec.js | 6 ++++++ .../fixtures/spec-allowArrayLike/simple/input.js | 1 + .../spec-allowArrayLike/simple/options.json | 6 ++++++ .../fixtures/spec-allowArrayLike/simple/output.js | 13 +++++++++++++ .../fixtures/sanity/block-scoping-for-of/output.js | 2 +- 19 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/holes/exec.js create mode 100644 packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/holes/options.json create mode 100644 packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/length-cropped/exec.js create mode 100644 packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/length-cropped/options.json create mode 100644 packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/simple/exec.js create mode 100644 packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/simple/input.js create mode 100644 packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/simple/options.json create mode 100644 packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/simple/output.js create mode 100644 packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/holes/exec.js create mode 100644 packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/holes/options.json create mode 100644 packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/length-cropped/exec.js create mode 100644 packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/length-cropped/options.json create mode 100644 packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/simple/exec.js create mode 100644 packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/simple/input.js create mode 100644 packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/simple/options.json create mode 100644 packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/simple/output.js diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index 356efc1ae13d..76045b3ca416 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -1009,10 +1009,13 @@ helpers.createForOfIteratorHelper = helper("7.9.0")` // e: error (called whenever something throws) // f: finish (always called at the end) - export default function _createForOfIteratorHelper(o) { + export default function _createForOfIteratorHelper(o, allowArrayLike) { if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { // Fallback for engines without symbol support - if (Array.isArray(o)) { + if ( + Array.isArray(o) || + (allowArrayLike && o && typeof o.length === "number") + ) { var i = 0; var F = function(){}; return { @@ -1056,12 +1059,15 @@ helpers.createForOfIteratorHelper = helper("7.9.0")` `; helpers.createForOfIteratorHelperLoose = helper("7.9.0")` - export default function _createForOfIteratorHelperLoose(o) { + export default function _createForOfIteratorHelperLoose(o, allowArrayLike) { var i = 0; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { // Fallback for engines without symbol support - if (Array.isArray(o)) + if ( + Array.isArray(o) || + (allowArrayLike && o && typeof o.length === "number") + ) return function() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; diff --git a/packages/babel-plugin-transform-for-of/src/index.js b/packages/babel-plugin-transform-for-of/src/index.js index 41a2ec6b8daa..54dd310b7a09 100644 --- a/packages/babel-plugin-transform-for-of/src/index.js +++ b/packages/babel-plugin-transform-for-of/src/index.js @@ -4,7 +4,7 @@ import { template, types as t } from "@babel/core"; export default declare((api, options) => { api.assertVersion(7); - const { loose, assumeArray } = options; + const { loose, assumeArray, allowArrayLike } = options; if (loose === true && assumeArray === true) { throw new Error( @@ -12,6 +12,12 @@ export default declare((api, options) => { ); } + if (assumeArray === true && allowArrayLike === true) { + throw new Error( + `The assumeArray and allowArrayLike options cannot be used together in @babel/plugin-transform-for-of`, + ); + } + if (assumeArray) { return { name: "transform-for-of", @@ -84,12 +90,12 @@ export default declare((api, options) => { `); const buildForOfLoose = template.statements(` - for (var ITERATOR_HELPER = CREATE_ITERATOR_HELPER(OBJECT), STEP_KEY; + for (var ITERATOR_HELPER = CREATE_ITERATOR_HELPER(OBJECT, ALLOW_ARRAY_LIKE), STEP_KEY; !(STEP_KEY = ITERATOR_HELPER()).done;) BODY; `); const buildForOf = template.statements(` - var ITERATOR_HELPER = CREATE_ITERATOR_HELPER(OBJECT), STEP_KEY; + var ITERATOR_HELPER = CREATE_ITERATOR_HELPER(OBJECT, ALLOW_ARRAY_LIKE), STEP_KEY; try { for (ITERATOR_HELPER.s(); !(STEP_KEY = ITERATOR_HELPER.n()).done;) BODY; } catch (err) { @@ -202,6 +208,7 @@ export default declare((api, options) => { const nodes = builder.build({ CREATE_ITERATOR_HELPER: state.addHelper(builder.helper), ITERATOR_HELPER: scope.generateUidIdentifier("iteratorHelper"), + ALLOW_ARRAY_LIKE: allowArrayLike ? t.booleanLiteral(true) : null, STEP_KEY: t.identifier(stepKey), OBJECT: node.right, BODY: node.body, diff --git a/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/holes/exec.js b/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/holes/exec.js new file mode 100644 index 000000000000..4bd0a5df0097 --- /dev/null +++ b/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/holes/exec.js @@ -0,0 +1,7 @@ +var p2 = { 0: "a", 2: "c", length: 3 }; + +var arr = []; +for (var x of p2) arr.push(x); + +expect(arr).toEqual(["a", undefined, "c"]); +expect(1 in arr).toBe(true); // Not holey diff --git a/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/holes/options.json b/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/holes/options.json new file mode 100644 index 000000000000..92f525cc511d --- /dev/null +++ b/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/holes/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + ["external-helpers", { "helperVersion": "7.100.0" }], + ["transform-for-of", { "loose": true, "allowArrayLike": true }] + ] +} diff --git a/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/length-cropped/exec.js b/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/length-cropped/exec.js new file mode 100644 index 000000000000..16d81476a95e --- /dev/null +++ b/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/length-cropped/exec.js @@ -0,0 +1,6 @@ +var p2 = { 0: "b", 1: "c", 2: "d", length: 2 }; + +var arr = []; +for (var x of p2) arr.push(x); + +expect(arr).toEqual(["b", "c"]); diff --git a/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/length-cropped/options.json b/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/length-cropped/options.json new file mode 100644 index 000000000000..92f525cc511d --- /dev/null +++ b/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/length-cropped/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + ["external-helpers", { "helperVersion": "7.100.0" }], + ["transform-for-of", { "loose": true, "allowArrayLike": true }] + ] +} diff --git a/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/simple/exec.js b/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/simple/exec.js new file mode 100644 index 000000000000..d016662a6048 --- /dev/null +++ b/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/simple/exec.js @@ -0,0 +1,6 @@ +var p2 = { 0: "b", 1: "c", 2: "d", length: 3 }; + +var arr = []; +for (var x of p2) arr.push(x); + +expect(arr).toEqual(["b", "c", "d"]); diff --git a/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/simple/input.js b/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/simple/input.js new file mode 100644 index 000000000000..9bcb06d816be --- /dev/null +++ b/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/simple/input.js @@ -0,0 +1 @@ +for (var x of p2) arr.push(x); diff --git a/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/simple/options.json b/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/simple/options.json new file mode 100644 index 000000000000..92f525cc511d --- /dev/null +++ b/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/simple/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + ["external-helpers", { "helperVersion": "7.100.0" }], + ["transform-for-of", { "loose": true, "allowArrayLike": true }] + ] +} diff --git a/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/simple/output.js b/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/simple/output.js new file mode 100644 index 000000000000..8b0155e9acff --- /dev/null +++ b/packages/babel-plugin-transform-for-of/test/fixtures/loose-allowArrayLike/simple/output.js @@ -0,0 +1,4 @@ +for (var _iteratorHelper = babelHelpers.createForOfIteratorHelperLoose(p2, true), _step; !(_step = _iteratorHelper()).done;) { + var x = _step.value; + arr.push(x); +} diff --git a/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/holes/exec.js b/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/holes/exec.js new file mode 100644 index 000000000000..4bd0a5df0097 --- /dev/null +++ b/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/holes/exec.js @@ -0,0 +1,7 @@ +var p2 = { 0: "a", 2: "c", length: 3 }; + +var arr = []; +for (var x of p2) arr.push(x); + +expect(arr).toEqual(["a", undefined, "c"]); +expect(1 in arr).toBe(true); // Not holey diff --git a/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/holes/options.json b/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/holes/options.json new file mode 100644 index 000000000000..d708a284b707 --- /dev/null +++ b/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/holes/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + ["external-helpers", { "helperVersion": "7.100.0" }], + ["transform-for-of", { "allowArrayLike": true }] + ] +} diff --git a/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/length-cropped/exec.js b/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/length-cropped/exec.js new file mode 100644 index 000000000000..16d81476a95e --- /dev/null +++ b/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/length-cropped/exec.js @@ -0,0 +1,6 @@ +var p2 = { 0: "b", 1: "c", 2: "d", length: 2 }; + +var arr = []; +for (var x of p2) arr.push(x); + +expect(arr).toEqual(["b", "c"]); diff --git a/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/length-cropped/options.json b/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/length-cropped/options.json new file mode 100644 index 000000000000..d708a284b707 --- /dev/null +++ b/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/length-cropped/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + ["external-helpers", { "helperVersion": "7.100.0" }], + ["transform-for-of", { "allowArrayLike": true }] + ] +} diff --git a/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/simple/exec.js b/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/simple/exec.js new file mode 100644 index 000000000000..d016662a6048 --- /dev/null +++ b/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/simple/exec.js @@ -0,0 +1,6 @@ +var p2 = { 0: "b", 1: "c", 2: "d", length: 3 }; + +var arr = []; +for (var x of p2) arr.push(x); + +expect(arr).toEqual(["b", "c", "d"]); diff --git a/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/simple/input.js b/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/simple/input.js new file mode 100644 index 000000000000..9bcb06d816be --- /dev/null +++ b/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/simple/input.js @@ -0,0 +1 @@ +for (var x of p2) arr.push(x); diff --git a/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/simple/options.json b/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/simple/options.json new file mode 100644 index 000000000000..d708a284b707 --- /dev/null +++ b/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/simple/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + ["external-helpers", { "helperVersion": "7.100.0" }], + ["transform-for-of", { "allowArrayLike": true }] + ] +} diff --git a/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/simple/output.js b/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/simple/output.js new file mode 100644 index 000000000000..2be8a7727f8f --- /dev/null +++ b/packages/babel-plugin-transform-for-of/test/fixtures/spec-allowArrayLike/simple/output.js @@ -0,0 +1,13 @@ +var _iteratorHelper = babelHelpers.createForOfIteratorHelper(p2, true), + _step; + +try { + for (_iteratorHelper.s(); !(_step = _iteratorHelper.n()).done;) { + var x = _step.value; + arr.push(x); + } +} catch (err) { + _iteratorHelper.e(err); +} finally { + _iteratorHelper.f(); +} diff --git a/packages/babel-preset-env/test/fixtures/sanity/block-scoping-for-of/output.js b/packages/babel-preset-env/test/fixtures/sanity/block-scoping-for-of/output.js index 41207d311b26..2fe27545b69e 100644 --- a/packages/babel-preset-env/test/fixtures/sanity/block-scoping-for-of/output.js +++ b/packages/babel-preset-env/test/fixtures/sanity/block-scoping-for-of/output.js @@ -6,7 +6,7 @@ function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } -function _createForOfIteratorHelper(o) { if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); var it, normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e) { didErr = true; err = _e; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } +function _createForOfIteratorHelper(o, allowArrayLike) { if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || allowArrayLike && o && typeof o.length === "number") { var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: F, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var it, normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e) { didErr = true; err = _e; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } // https://github.com/babel/babel/issues/7557 var _iteratorHelper = _createForOfIteratorHelper(c),