Skip to content

Commit

Permalink
Add "allowArrayLike" support to the for-of transform
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed Mar 15, 2020
1 parent d5b9ea3 commit 77adb90
Show file tree
Hide file tree
Showing 19 changed files with 114 additions and 8 deletions.
14 changes: 10 additions & 4 deletions packages/babel-helpers/src/helpers.js
Expand Up @@ -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 {
Expand Down Expand Up @@ -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++] };
Expand Down
13 changes: 10 additions & 3 deletions packages/babel-plugin-transform-for-of/src/index.js
Expand Up @@ -4,14 +4,20 @@ 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(
`The loose and assumeArray options cannot be used together in @babel/plugin-transform-for-of`,
);
}

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",
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand Down
@@ -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
@@ -0,0 +1,6 @@
{
"plugins": [
["external-helpers", { "helperVersion": "7.100.0" }],
["transform-for-of", { "loose": true, "allowArrayLike": true }]
]
}
@@ -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"]);
@@ -0,0 +1,6 @@
{
"plugins": [
["external-helpers", { "helperVersion": "7.100.0" }],
["transform-for-of", { "loose": true, "allowArrayLike": true }]
]
}
@@ -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"]);
@@ -0,0 +1 @@
for (var x of p2) arr.push(x);
@@ -0,0 +1,6 @@
{
"plugins": [
["external-helpers", { "helperVersion": "7.100.0" }],
["transform-for-of", { "loose": true, "allowArrayLike": true }]
]
}
@@ -0,0 +1,4 @@
for (var _iteratorHelper = babelHelpers.createForOfIteratorHelperLoose(p2, true), _step; !(_step = _iteratorHelper()).done;) {
var x = _step.value;
arr.push(x);
}
@@ -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
@@ -0,0 +1,6 @@
{
"plugins": [
["external-helpers", { "helperVersion": "7.100.0" }],
["transform-for-of", { "allowArrayLike": true }]
]
}
@@ -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"]);
@@ -0,0 +1,6 @@
{
"plugins": [
["external-helpers", { "helperVersion": "7.100.0" }],
["transform-for-of", { "allowArrayLike": true }]
]
}
@@ -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"]);
@@ -0,0 +1 @@
for (var x of p2) arr.push(x);
@@ -0,0 +1,6 @@
{
"plugins": [
["external-helpers", { "helperVersion": "7.100.0" }],
["transform-for-of", { "allowArrayLike": true }]
]
}
@@ -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();
}
Expand Up @@ -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),
Expand Down

0 comments on commit 77adb90

Please sign in to comment.