Using webpack 2 and NPM to bundle various resources of a web application part 5: adding babel.js for ES5 transformation

In the previous post we first briefly discussed the output that webpack generates in the bundle.js file. Simply put, webpack extracts the dependency graph from the various JS input files and organises the elements into functions that in turn are placed into an array. The functions in the array are invoked in a sequence that reflects the dependency graph. Then we also built a minimal HTML page to execute the bundle in various browsers. Our ES6 code was successfully executed in all of them except for IE11 which is obviously too old to correctly interpret the new ES6 features. We would see the same in older versions of Chrome and FF as well.

In this post we’ll explore another great feature of webpack, namely the ease at which we can add plug-ins to the bundling process. We’ll specifically add babel.js to transform ES6 code to ES5. The point here is that JS developers want to be able to use the latest and greatest of ES6 and other future versions of JavaScript without having to worry about JS support across various browsers.

Adding more ES6 features

Let’s add some more ES6 language features to our index.js and see how well the most current web browsers, mentioned in the previous post, have implemented them:

If you don’t know these features then consult this page where you can find links to examples and explanations.

Then open a command prompt, navigate to the project root and run the following command to generate the bundle.js file:

npm run build

…and then open index.html in a web browser with Development Tools open (F12). If you have the latest of Chrome, FF and Edge then you’ll see that all of them managed to run our code. Here’s the console output in Chrome 60:

That’s very positive but we cannot assume that all clients have updated their browsers. Also, later on when ES7, ES8 etc. features come along then browser manufacturers will always require some time to implement them in their product. Also, JS developers want to be able to use new features of the language without worrying much about browser support. This is where babel.js enters the scene.

Configuring Babel

Babel.js is a JS compiler that basically transforms “new” JS into “old” JS. Babel has a number of NPM packages to make it work with webpack. We’ll need to install 3 of them to be exact. Run the following NPM command in the command prompt:

npm install –save-dev babel-loader babel-core babel-preset-env

The loader package ensures that Babel can work with webpack. It is a so-called module loader in webpack. A module loader is a pre-processor that is applied on all or some subset of the source files in the webpack project. Pre-processors are executed before webpack creates the bundle. Make sure you understand the distinction between these pre-processing libraries and webpack itself. We can instruct webpack to execute a pre-processor but it’s not webpack’s responsibility to make them work properly. It is up to the pre-processor to execute its work. When it’s done then webpack takes over and finishes its own main task. Babel-core is a JS parser and the preset package contains the rules how to transform ES6 specific features to ES5 equivalent code.

The terminology is a bit confusing because a module loader is called a rule in webpack 2 and rules make part of the webpack module system. We can also tell webpack which files to apply the rule on. This is called a test. E.g. we want the Babel rule to be applied on JS files only and not e.g. CSS files. That wouldn’t make any sense.

Rules – or module loaders – make webpack even more exciting than what it is. They show the pluggable nature of webpack where we can add any number of modules to perform some action on the source files. So by default webpack is “only” responsible for creating the JS bundle but it can be extended with rules.

Here’s the extended webpack.config.js with the new section called “module”:

“module” stands for the module system of webpack mentioned above. Then comes the array of rules with a single entry with 3 properties. We declare the loader to be the Babel loader. We want to apply it to all files ending with .js which is declared through a regular expression of the test property. Finally we want the files in the node_modules folder to be excluded.

We’re not done yet. We have to tell the Babel loader to apply the babel-preset-env transpiler on each input file. We don’t do that in the webpack config file though. Recall that it’s not webpack that performs the JS code transformation but Babel. As soon as webpack calls upon the Babel rule we’re on Babel’s turf, not webpack’s. At this moment the Babel loader will need to know what we want it to do and we can achieve it through Babel’s own configuration file called .babelrc :

The Babel loader will be looking for this file. Add the following JSON object to .babelrc:

{
"presets": ["env"]
}

We tell Babel to use the preset called “env” which stands for the babel-preset-env processor.

All right, let’s see if this thing works. Run the usual npm command again…

npm run build

Run index.html in your web browser and… I don’t know about you but I got an exception:

…where “regeneratorRuntime” is not defined. This is a case of where webpack and the various loaders are sort of out of sync and we need to find a solution. This thread on StackOverflow gives the necessary hints. First we need to install the babel-polyfill processor as well:

npm install –save-dev babel-polyfill

…and add it to the first position of the entry point array in webpack.config.js:

Build the project with npm run build again, test it in Chrome and it should work again.

The code also works in IE11:

Babel stages

Another important component related to Babel is called a stage. This website comes with a good overview of Babel including stages:

“JavaScript has some proposals for new features that are not yet finalized. They are separated into 5 stages (0 to 4). As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4. There is no babel-preset-stage-4 as it’s simply babel-preset-es2015.”

These stages imply that if a new JS feature comes along that has not yet been fully accepted developers can still use it and have Babel transpile it into ES5. The requirement is to install an extra package that corresponds to the stage where the feature is located. Here’s how stage 2 would be installed:

npm install –save-dev babel-preset-stage-2

…and then we can tell Babel to apply the stage 2 level code additions as follows in .babelrc: