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.