Home

Awesome

parcel-babel-runtime-example

The goal of this PoC (proof of concept) is to setup working parcel-bundler environment where missing JS functionalities are patched by babel-runtime (aka. Ponyfills) instead of polyfilling global namespace.

Why are polyfills bad?

Polyfilling means only patching functionalities that are missing, usually on older browsers, while using native implementations whenever it is available (modern browsers). The problem with polyfilling is it patches global objects, so for example if Array.from(..) method is missing on IE a polyfill will add a form method to Array.prototype object.

That means if there are two polyfilling solutions used on a single page only the first implementation will be used. This may (and actually did to me) lead to incompatible mix of polyfills.

The extra polyfills are usually introduced with 3rd party software like all kinds of analytics or ads providers.

The solution

The solution is not to polyfill at all, but instead wrap missing functionalities in functions. No global patching takes place. Thats exactly the difference between polyfills and ponyfills.

So instead:

[1, 2, 3, 4, 5].find(x => x > 3);

We want to write:

find([1, 2, 3, 4, 5], (x) => x > 3);

Where find is a function providing same behaviour as that one in modern browsers Array.find(...).

Different syntaxes

We can see in an example above that, even though the goal of having a desired functionality while not patching global namespace is achieved, the syntax is clearly different.

To use exactly same syntax when coding, we use babel plugin to do the conversion. Babel is a transpiler and that's exactly what it is designed for. The plugin we want to use is @babel/plugin-transform-runtime.

Patching library

The library we use is core-js. It provides both approaches: polyfiling (default) and ponyfilling (which they call "pure").

Bundle file size

@babel/plugin-transform-runtime transpiles and then puts import only for core-js functions that are being used, so in most of cases the bundle size will be more optimise than just importing whole babel-polyfill or core-js.

Setup

Setup is actually extremely simple. What we need to do is:

  1. Add @babel/plugin-transform-runtime package:
yarn add --dev @babel/plugin-transform-runtime
  1. Configure .babelrc:
{
  "plugins": [
    ["@babel/plugin-transform-runtime", {
      "corejs": 3,
    }]
  ]
}

Why don't use @babel/preset-env?

Alternative, theoretical solution (it doesn't work unfortunately) would be to use @babel/preset-env because it supports ponyfilling out of the box - see useBuiltIns option.

Unfortunately it doesn't work with parcel because parcel overwrites preset-env config with its own.

Demo

This demo transpiles:

const array = [1, 2, 3, 4, 5];
const number = array.find((x) => x > 3);
console.log(`find number greater than 3 from array [${array}]: ${number}`);

into:

//...

var array = [1, 2, 3, 4, 5];
var number = (0, _find.default)(array).call(array, function (x) {
  return x > 3;
});
console.log((0, _concat.default)(_context = "find number greater than 3 from array [".concat(array, "]: ")).call(_context, number));
//...

As we can see the native Array.find method is not used but _find method (coming from core-js) is used instead.

Screenshots:

ie11 Screenshot of demo running in IE11


ie11 Screenshot of demo running in Firefox

To run the demo:

yarn install
yarn run serve
# go to http://localhost:3000/, and see dev console!