Breaking - not building. Or devolution

    Recently, a couple of interesting articles appeared on Habré. The first was devoted to the ES6 minification problem , the second about general useful webpack optimization tips .

    Everything would be fine, but they both bypassed the issue of splitting bundles into ES6 and ES5 for minification and other optimization purposes. And in general, while some write and write articles about it , others (almost all) ignore this technique.

    Because for a long time. Expensive. And not so very.

    But it is necessary quickly, cheaply, and dumber. Perhaps you should just reverse evolution.


    Describing an “idea” is not a good idea. It’s better to describe how it should work. How the bundle formation process should work:

    • I have a code
    • I compile it under my "development" browser
    • and it all works.

    The development browser is here - so that async / await, generator, classes, arrow functions and so on. In general, target: esmodules in the babel.
    I don’t know about you, but I like this idea. Here are just the old browsers that are still among us, this idea does not fit in that way. (and that’s why we all hiss es5 in production, seasoning with half a megabyte of polyphiles)

    And this is exactly what needs to be fixed.


    Devolution is a small cli utility that takes your bundle compiled into target: esmodules and degrades it to es5, adding all the necessary polyfiles along the way.

    In short, then:

    • all js scripts are
    • run through babel with one active plugin (fork useBuiltins: “usage”) that defines the required polyfiles. This is fast, as there are no transformations.
    • for each file, all the polyphiles it needs are collected (minus those that are already in the main bundle), merged, run through terser and added to the top of the file.
    • each file will be run through swc, a rust version of babel that de-upgrades the code to a level that IE11 understands. Works 10-60 times faster than babel. It does not support various plugins, but this is not necessary - all that is needed is __ already__ applied.
    • terser is once again superimposed on the result, but with mangle turned off (name compression), which again is fast.
    • All this is done in the workers.

    I ran the code on three projects of different difficulty levels:

    • project 1, 60 final js files (code-splitting). Build time 400s. Devolution 30s.
    • project 2, 1 final js file (30mb). Build time 120s. Devolution 10s.
    • project 3, 1 final js file (2mb). Build time 20s. Devolution 5s (at the start of the workers a lot of things are lost).

    The bonus from the ESM bundle was a bit strange:

    • one project lost 400kb babel / polyfill. Nothing “over” browser chips was used there trite, and in “esm” they do not need to be polished
    • one project lost 10% due to the much more compact code of generators, async / await and class constructors
    • One project has grown fatter, as “loose” babel transformations sometimes make code more compact. But loose mode is a little dangerous option, while the “ES6” code is “safe”.


    • we take ES6 code (more precisely esmodule, let / const will be replaced with var for speed purposes)
    • make of it ES5
    • throw on the side of polyphiles
    • scatter on daddies, add symlinks to other files
    • we change the connection of scripts to pages to a little smarter (IE11 modules / nomodules do not understand)
    • done - ESM for 85% of the custom meters, ES5 for those in the tank.

    Simply. Quickly. Just stupid. We de-upgraded the bundle. Old browsers! Au - served.

    Well, new browsers will receive a bundle with almost no polyfills, without terrible transformations of generators and async / await, with arrow functions without tambourines (and they are generally faster). In general, everyone is happy, it seems like this was originally intended.
    PS: Actually, at the moment, devolution does not use swc , since it sometimes makes the code not very working - , Babel is not so much slower - where swc was corrected in 20 seconds, babel can do it in a minute. With a “normal” build time - from 5 onwards - this is a big plus
    PS: If suddenly it became interesting why devolution - the video is here .

    Also popular now: