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:
- Add
@babel/plugin-transform-runtime
package:
yarn add --dev @babel/plugin-transform-runtime
- 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:
Screenshot of demo running in 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!