developit / microbundle

📦 Zero-configuration bundler for tiny modules.

Home Page:https://npm.im/microbundle

Repository from Github https://github.comdevelopit/microbundleRepository from Github https://github.comdevelopit/microbundle

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