//

BABEL & WEBPACK

Babel has been one of the big newcomers of JavaScript community in 2015. It allows us to use features from the future of JavaScript. It will transform your futuristic code into a format, browsers understand. You can even use it to develop your own language features. Babel’s built-in JSX support will come in handy here.

Babel provides support for certain experimental features from ES7 beyond standard ES6. Some of these might make it to the core language while some might be dropped altogether.

The language proposals

The core language proposals for JavaScript have been categorized within stages:

  • Stage 0 - Strawman
  • Stage 1 - Proposal
  • Stage 2 - Draft
  • Stage 3 - Candidate
  • Stage 4 - Finished

You have to be very careful with stage 0 features - if a feature changes/removed your code will end up broken. But it's alright for smaller experimental projects.

Stage 2 - Draft

Features starting from stage 2 have been enabled by default

Configuring babel-loader

The easiest way of using Babel with Webpack is via babel-loader. It takes our ES6 module definition based code and turn it into ES5 bundles.

Installing babel-loader

As usual with any node package, use NPM to install babel-loader, and babel-core, which contains core logic of Babel.

npm install babel-loader babel-core --save-dev

Now we need to add a loader declaration for babel-loader to the loaders section of the config. It matches both .js and .jsx using regular expression /\.jsx?$/.

Configuring Webpack

Let's restrict the loader to operate only within ./app directory by adding include rule. This way, it won't traverse through directories like node_modules. Another way of doing this is by adding an exclude rule against node_modules.

var webpack = require('webpack');
var path = require('path');

const config = {
  context: __dirname + '/src',
  entry: './app.js',

  resolve: {
    extensions: ['', '.js', '.jsx']
  },

  output: {
    path: __dirname + 'build',
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      {
        test: /\.css$/,
        loaders: ['style', 'css'],
        include: path.join(__dirname, 'src'),
      },
      {
        test: /\.jsx?$/,
        loaders: ['babel?cacheDirectory'],
        include: path.join(__dirname, 'src'),
      }
    ]
  }
};

module.exports = config;

Line 8 - Add resolve.extensions. '' is needed to allow imports without an extension. Note the .'s before extensions, the matching will fail without those.

Line 24 - This accepts jsx and js thanks to Regular Expressions.

Line 25 - Enable caching for improved performance during development. It uses default OS directory by default. If you need something more custom, pass a path to it. i.e., babel?cacheDirectory=<path>.

resolve.extensions

As resolve.extensions gets evaluated from left to right, we can use it to control which code gets loaded for given configuration. For instance, you could have .web.js to define web specific parts and then have something like '', '.web.js', '.js', '.jsx'. If a web version of the file is found, Webpack would use that instead of the default.

Setting up .babelrc

We can pass Babel settings through Webpack, for instance babel?presets[]=react,presets[]=es2015, but doing so it will only work for Wepback.

For Babel settings to be accessed by anything, not just Webpack, we are going to create .babelrc.

Plugins

Babel 6 relies on plugins. There are two types of them: syntax and transform.

  • Syntax - Allows Babel to parse additional syntax
  • Transform - Applies transformation

This helps code that is using future syntax can get transformed back to JavaScript older environments can understand.

Presets

To make it easier to consume plugins, Babel supports the concept of presets.

Each preset comes with a set of plugins so, you don't have to wire them up separately. In this case, we'll be relying on ES2015 and JSX presets:

npm i babel-preset-es2015 babel-preset-react --save-dev

Now let's set up a .babelrc file.

{
  "presets": [
    "es2015",
    "react"
  ]
}

Babel provides stage-specific presets. This documents your project well and keeps it maintainable.

resolve.extensions

If you don't like to maintain a .babelrc file, another alternative is to write the configuration below babel field at package.json. Babel will pick it up from there.

Alternative Loader Declarations

Webpack's loader declaration allows passing parameters to a loader through a query string.

{
  test: /\.jsx?$/,
  loader: 'babel',
  query: {
    cacheDirectory: true,
    presets: ['react', 'es2015']
  },
  include: path.join(__dirname, 'src')
}

Note the loader and query fields. It can be alternatively declared as:

loader: 'babel?cacheDirectory,pesets[]=react,presets[]=es2015'

This is a bit hard to read, so the former is better for readability.

It's a good idea to keep in mind that Webpack loaders are always evaluated from right to left and from bottom to top (separate definitions). The following two declarations are equivalent based on this rule:

{
    test: /\.css$/,
    loaders: ['style', 'css'],
},

And

{
    test: /\.css$/,
    loaders: ['style'],
},
{
    test: /\.css$/,
    loaders: ['css'],
},

Conclusion

This tutorial will provide you with a good understanding of how Webpack and Babel play together, when it comes to building an application from the ground up and you will find it will be great for React apps.