Using for of loop adds ~300 bytes of polyfill code
everdimension opened this issue · comments
Hi!
I noticed in my project that if I use a for of
loop instead of a regular for
loop, microbundle adds approx. 300 bytes of polyfill code:
t=function(r,e){var t="undefined"!=typeof Symbol&&r[Symbol.iterator]||r["@@iterator"];if(t)return(t=t.call(r)).next.bind(t);if(Array.isArray(r)||(t=function(r,e){if(r){if("string"==typeof r)return n(r,e);var t=Object.prototype.toString.call(r).slice(8,-1);return"Object"===t&&r.constructor&&(t=r.constructor.name),"Map"===t||"Set"===t?Array.from(r):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?n(r,e):void 0}}(r))){t&&(r=t);var o=0;return function(){return o>=r.length?{done:!0}:{done:!1,value:r[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}(arguments);
My compilation target is esnext
(defined in tsconfig.json
)
Is there a way to tell microbundle to omit this polyfill? I thought all browsers already support it: https://kangax.github.io/compat-table/es6/
That's not for...of
being polyfilled, but your non-iterable (in the target format, anyway) being polyfilled.
For example,
input
const arr = [1, 2, 3];
for (const el of arr) {
console.log(el);
}
CJS output
var arr = [1, 2, 3];
for (var _i = 0, _arr = arr; _i < _arr.length; _i++) {
var el = _arr[_i];
console.log(el);
}
input
const arr = 'foo';
for (const el of arr) {
console.log(el);
}
CJS output
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _createForOfIteratorHelperLoose(o, allowArrayLike) {
var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
if (it) return (it = it.call(o)).next.bind(it);
if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
if (it) o = it;
var i = 0;
return function () {
if (i >= o.length) return {
done: true
};
return {
done: false,
value: o[i++]
};
};
}
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 arr = 'foo';
for (var _iterator = _createForOfIteratorHelperLoose(arr), _step; !(_step = _iterator()).done;) {
var el = _step.value;
console.log(el);
}
This is clearer if you disable compression with the --no-compress
flag, or --target node
.
My compilation target is esnext (defined in tsconfig.json)
Your tsconfig.json
only controls TS-specific compilation, and even then, it's mostly done through Microbundle's flags instead (like JSX pragma). cjs
, umd
, and esm
are ES5 targetting, while modern
is ES2017. What you set as your tsconfig.json
compilation target doesn't affect anything.
I thought all browsers already support it
Just to add the earlier examples, this isn't so much for...of
not being able to be transpiled in a byte-concious way, but you're iterating over something that needs a helper to be iterable in ES5. Options include changing data structures (if possible) or pointing users at the more modern output, and calling the verbose ES5 as simply legacy cost.
I believe using --format modern
avoids this downleveling. The esm+cjs+umd bundles are ES5.
Yes, the modern
output format is just as terse as the source.
Going to close this out as everything seems to be working as intended from microbundle's side.
Feel free to respond if you have any further issues.
Yes, this answers my question, thanks @rschristian @developit!
Small comment, the value I'm iteraring over is the arguments
object, which seemingly doesn't require conversion if you fallback to a classic for-loop
But I suppose the complier can't make that decision