Babel Polyfills, Transforms & Presets

Babel is really an amazing accomplishment by an open source community and an example of another kind of declarative, plugin-driven piece of software, like Webpack that pushes Javascript forward.

You may start from where we left off or clone the git repo and checkout this branch like so.

git clone git@github.com:lawwantsin/webpack-course.git
cd webpack-course
git checkout babel2
npm install

Let’s get a bit more into how Babel works, by adding polyfills to our codebase to extend the browser’s current capabilities and run features that still being debated in the standards committee. Really cool way to evolve a language.

Now let’s add something that’s definitely going to be in the language. In our main.js.

var a = async () => {
  await console.log("Hello from the future!")
}

And in .babelrc we add the plugin.

{
  "plugins": ["transform-es2015-arrow-functions", "async-to-promises"]
}

This should add code to build a promise in the ES5 syntax. When we run npm install babel-plugin-async-to-promises in our terminal we see.

Babel Output

And we see, if we run this, the promise code is added and called.

babel src/main.js

Async/Await functions are working their way into many evergreen browsers, but at this point, it's still a part of the ES2017 spec. Either way, in IE 11, we'll need to include a polyfill to run even the transpiled code.

So in the terminal, let's install a new package:

npm install babel-polyfill

In webpack.dev.js add babel-polyfill to the front of main.js.

main: ["babel-polyfill", "./src/main.js"]

Run npm start. We can see main-bundle.js is quite a bit bigger now. Adding babel-polyfill to that array tacked the whole module to the front of our main.js code.

To reduce this and any polyfill, it's really best to use the exact fill you'll need. Change the main entry again and rerun the server.

main: ["core-js/fn/promise", "./src/main.js"]

Polyfill Comparison

The difference is clear. While polyfills will only be run if they're not already implemented natively in the user's browser, they'll still be shipped in the bundle.

Let's restore main to it's former self.

main: "./src/main.js"

Babel Presets

We can add features one at a time with plugins, or we can use presets to include all the features of a particular year or a particular way of working. There’s a preset for react development which compiles JSX for you. Presets make setup easier. And env is the current preset that works best in most situations.

So let’s wrap up our overview of Babel and setup a full ES2017 feature stack, so we can finally start coding our project the modern way. In your terminal:

npm install babel-preset-env

In .babelrc, just this:

{
  "presets": [
    [
      "env",
      {
        "debug": true
      }
    ]
  ]
}

When we rerun the devServer, we see debugging information

Babel Output

If we change the target, we'll see a different output of polyfills and transforms based on what that browser needs. Babel uses The Compat-table to determine the current state of browser features. So if we add a target to our .babelrc:

{
  "presets": [
    [
      "env",
      {
        "targets": {
          "browsers": ["last 2 versions"]
        },
        "debug": true
      }
    ]
  ],
  "plugins": ["transform-runtime"]
}

Your main.js should look like this:

require("babel-runtime/regenerator")
require("./main.css")
require("./images/link.jpg")
require("./index.html")

var a = async args => {
  const { a, b } = args
  await console.log("Hello from the future!", a, b)
}

a({ a: 1, b: 2 })

Finally add the package and rerun:

npm install babel-plugin-transform-runtime
npm run start

We see a list of the supported browsers and the plugins needed to target them. No surprise that IE 10 and Android 4.4.3 are causing the extra transforms and the total bundle size has increased. Interestingly, if you change the target to the lastest 1 version, it's IE 11 and Android 56 and the bundle size is the same.

Babel Output

Challenge

Feel free to add a mystery Javascript feature yourself or specific target browser as outlined here and practice looking at the output with babel src/main.js, see if it’s what you’d expect.

In Sum

Transpilers a fascinating part of the javascript evolution and allows for lots of experimentation with the look and feel of a new syntax, before the makers of the engines commit to implementation.

This is the final code:

git checkout babel2-final

Next Up

In the next article we’ll ditch the webpack dev server to serve our files from a server we’ll build using express and some clever middleware. We’ll customize our output and get setup for a real full stack node app.