Here at PerimeterX, we maintain a third party JS script as part of our Bot Defender solution.
If you’ve ever worked on any third party script, you know the complexity of maintaining a script that runs on your customer’s websites, on millions of different browsers and a wide variety of platforms. So, when I took on the task of updating our build system’s libraries and moving BabelJS from preset-2015 to preset-env, I was very cautious. I found a lot of interesting stuff under the hood of babelJS and gathered insights on how it works with preset-env.
What did the yearly presets do exactly
Each yearly preset added more and more features. Any other features had to be added as extra polyfill libraries. Babel relied on the babel-polyfill library for the preset polyfills.
It's also important to state that some features were replaced inline with alternative code, such as const and let moving to var. Others required extra code, such as promises. All supportive code was added to your bundle regardless of your actual use of the features, possibly adding dead code and raising the bundle size for no good reason.
One last thing I'll mention is that the yearly presets did not allow configuring the browsers one would want to support, so I had to adapt the polyfills accordingly.
Major changes with preset-env
The main idea behind preset-env was to stop creating yearly presets and just have one library which will update periodically. But this change, together with the changes in the babel library itself, led to the following shifts:
Moving from babel-polyfill lib to core-js
The preset-env useBuiltins flag determines if babel should always add corejs polyfills, add them once per file or per usage of a particular feature in your code. This is the recommended way: adding only what supports your code. Using the preset-env debug:true flag, I noticed babel pointed out which file of mine required each module within core-js while using useBuiltins: usage. Reading into the core-js library, I learned that its logic of identifying whether you actually use a feature in your code or not is flaky, sometimes relying on appearances of certain keywords and in a few cases adding unnecessary code.
Preset-env also introduced the targets feature, allowing us to tell Babel which browsers we intend to support. This capability is based on the browserslist package for defining our targets. Targets will determine which features we need to transpile or add supporting code for, as more code means a bigger bundle. The Browserslist spec lets us define our targets in a few ways. Here are some possible query parts:
- Set minimum supported version for browser such as Firefox > 20 or specific version such as ie 6-8
- Last X versions of each or specific browser last 2 versions
- According to the worldwide usage percentage for browsers > 5%
Because it was important to me to keep the bundle size small, I compared different percentages and learned there was a major difference in bundle size when adding support for old browsers like IE10. However, once you support old browsers with minor ES6 support, the bundle size won’t change significantly if you decide to support very old ones like IE9 or browsers with a minimum of 0.01% usage as I did. By installing the npm browserslist package you can run the browserslist cmd in the project root to get the minimal browsers support for different browser types.
Your Two options
In my eyes, there are two ways to go with your configuration:
The Safe way - set up useBuiltIns to false, disabling any added code or plugins, leaving only inline ES6 to ES5 transpiling. This way you make sure your code is transpiled to support older browsers but don’t take advantage of other browser bugfixes corejs has to offer. I recommend this config for third party script developers. Here is an example:
The supporting way - set up useBuiltIns to usage, adding plugins upon usage and including or excluding plugins according to your needs. This way you can support old browser bugs as well as transpile your code. You will also control the effect the corejs has over your bundle size. I recommend this configuration for Full stack developers working on their own web-app.