Webpack Made Simple: Building ES6 & LESS with autorefresh

Update on 2015/11/08: Now works with Babel 6.0

Just want a starter kit? Clone webpack-black-triangle for something minimal, or the Unicorn Standard Starter Kit for the works.

There’s been a huge amount of press since ES6 was finalised as ES2015 – but for all the awesome features it adds, it won’t help you much if you can’t actually use it. The problem is that while ES6 is the future, the current crop of browsers are stuck in the past. Want proof? Give Chrome an arrow function, and it’ll give you this:

Chrome being stuck in the present

Of course, there is more to the story than this. You can use ES6 in the vast majority of modern browsers, but with a catch: you’ll need a build step. And while this has been a deal-breaker for many in the past, we’ve entered an age where it doesn’t have have to be.

Build steps no longer mean 300 line configuration files

There’s been a lot of progress in the world of JavaScript build tools over the past few years. In particular, webpack burst onto the scene to the delight of all who’ve had to deal with long, complicated Gulpfiles and Gruntfiles.

Webpack lets us do a lot with very little effort – for example, the configuration file in the github project for this post tells webpack how to transpile ES6 with Babel, compile LESS, and reload the page when files are changed – all in 26 lines.

Reddit’s /u/nickguletskii200 put it like this:

Webpack is the least retarded piece of software I’ve used that’s written in JS.

With all this in mind, I recently chose webpack for the build system of memamug.com, a small open-source app I’m writing with a React-based frontend. And given the biggest complaint with webpack is it’s lack of documentation, I thought I’d walk you through the process while it’s still fresh in my mind.

The code we’ll use to test our build process

But before we can start, we need some ES6 code to run our build process on. And as luck might have it, I’ve made an over-engineered black triangle, just for you. It will even start spinning once you’ve got things working.

Let’s give the code a directory and get it displaying in your browser before we worry about the build process. To start, either copy across the files to a new directory, or clone the gist with git:

$ git clone https://gist.github.com/9b7db05268e747b4aa4d.git black-triangle

Assuming you’ve now got a black-triangle directory, let’s change into it and improve the project’s feng-shui by moving the source files to their own directory:

$ cd black-triangle
$ mkdir src
$ mv index.html main.js BlackTriangle.js src

Then all that’s left is to start her up – by opening index.html in a web browser. And assuming you’re not living in the future, you should see an incredibly boring (and immobile) black triangle, like this:

title

Minimum Viable Webpack

Of course this isn’t a blog about black triangles – it’s a blog about webpack & Babel – so let’s install them. Assuming you have node.js set up, this is as easy as:

$ npm install webpack-dev-server -g
$ npm install webpack-dev-server webpack babel-core babel-loader babel-preset-es2015 babel-polyfill

All that’s left is to start the server! We do so with the webpack-dev-server command, which takes the path to our HTML content (src in our case), and the JavaScript entry point (src/main.js). Give it a shot from your black-triangle directory:

$ webpack-dev-server --content-base src src/main.js

Doh. It looks like webpack is still choking on ES6. And, if we view our app at localhost:8080, we still see an immobile black triangle. What gives?

Configuring Webpack

While we’ve installed everything we need to get webpack to understand ES6, we still need to tell it how to do so. We do this with webpack’s configuration file, webpack.config.js. This file lives in the root directory of your project, and is just plain-ol’ JavaScript.

To spin your triangle up, add a webpack.config.js file in your black-triangle directory with the following content, then restart webpack-dev-server and reload your page. Don’t worry about how it works just yet – I’ll walk you through it in a moment.

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

module.exports = {
  entry: [
    'babel-polyfill',
    './src/main'
  ],
  output: {
      publicPath: '/',
      filename: 'main.js'
  },
  devtool: 'source-map',
  module: {
    loaders: [
      {
        test: /\.js$/,
        include: path.join(__dirname, 'src'),
        loader: 'babel-loader',
        query: {
          presets: ["es2015"],  
        }
      }
    ]
  },
  debug: true
};
To stop webpack-dev-server, press ctrl-c (yes, even on a mac).
There is no need to restart the server after changing files other than webpack.config.js – just refresh the page.

Have you got your triangle spinning? If not – leave a comment and I’ll try and help you out! Otherwise, let’s take a quick tour of the config file.

webpack.config.js in two minutes

First, about the file itself:

  1. webpack.config.js is a normal JavaScript file, evaluated with node.js.
  2. You can do normal node.js things like require modules and check environment variables.
  3. Webpack’s configuration is placed in the object which the file exports, i.e. whatever you assign to module.exports.

Now, about the keys in the configuration object we just set up:

  1. entry is an array of files which will be run at startup. We run Babel’s polyfill first, before running our main JavaScript file.
  2. output tells webpack-dev-server where to serve compiled files from. We tell it to build a single file labeled main.js, directly on the server’s root – hiding the original main.js from your browser.
  3. devtool tells webpack to automatically serve us source maps as well, so browsers can display the original source in their development console (as opposed to the compiled source).
  4. module.loaders is a list of loaders – npm packages which allow webpack to transform our source. We tell webpack to run .js files from the src directory through babel-loader, using the es2015 plugin to transform ES2015 (i.e. ES6) to ES5.
  5. debug let’s our loaders know that they don’t need to make release-ready code

If you ever need to know more about webpack’s config object, there is documentation available on it’s website. It’s not for the faint of heart, though, so don’t read it until you have to.

To summarise, you’ll generally find that all you need to know is:

  • entry points are where self-contained scripts go
  • module.loaders is where you put things which transform your code

There is, as always, an exception: the plugins configuration options is super-useful – but generally only once you want to deploy. I’ll explain how to use plugins for things like minification in a future guide, and the only way to ensure you don’t miss out is to join my Newsletter. And in return for your e-mail, you’ll immediately receive a free print-optimised ES6 cheatsheet! But for today, let’s finish by adding a few nifty tweaks to your webpack configuration.

Get the ES6 Cheatsheet

Tweaks to make your life easier

1. Automatically reload page on save

One of the biggest wins from using webpack-dev-server is that it notifies your browser when files change. Of course, nothing will happen unless you listen to these notifications, but luckily, webpack-dev-server includes a handy self-contained script which both listens to the notifications, and reloads the page for you when it receives them.

The script in question is 'webpack-dev-server/client?http://localhost:8080'. Why not have a shot at adding it yourself based on what you learned above. Once you’re done, touch or hover over the blank box below for an answer.

  entry: [
    'babel-polyfill',
    './src/main',
    'webpack-dev-server/client?http://localhost:8080'
  ],

2. Default options for webpack-dev-server

Typing out this monstrosity every time you want to start the server can get a little tiring:

$ webpack-dev-server --content-base src src/main.js

Luckily, you can specify default options for webpack-dev-server by adding a devServer object in webpack.config.js. The keys in this object are just the camelized versions of those you’d pass to the command line version.

If that’s a bit of a mouthful, don’t worry – all you need to know is you should add this to your webpack.config.js:

  devServer: {
    contentBase: "./src"
  }

And then you can omit the --content-base src from the above command.

3. Record your dependencies and entry point with package.json

If you already know how package.json works, you can skip this section.

My final tip is to set up a package.json. There are so many good reasons to use it that I can’t list all of them, but here are two relevant ones:

  • You can record your dependencies (e.g. webpack & babel), then install them again using npm install.
  • You can define a start script, so you don’t even need to remember webpack-dev-server – just npm start.

Setting up package.json is so incredibly simple that you really have no reason not to do it, so let’s do it now. Ready? From your black-triangle directory, type this:

npm init

Now follow the prompts, remembering to enter src/main.js as your entry point.

And now you’ve got your package.json. How easy was that! Let’s finish it off by telling npm about what to do when we type npm start: do so by adding a new key to the generated package.json file’s scripts object, as so:

"start": "node_modules/webpack-dev-server/bin/webpack-dev-server.js"

Finally, test it out by shutting off your existing server (if it is running), typing in npm start, and visiting localhost:8080.

And there you have it – an ES6-compiling, automatic reloading dev server, which can be started as easily as typing npm start. Neat!

But don’t close the tab just yet. To really make it worth your while having read this article, you need to remember the important parts – and what better way to do so than with a useful exercise.

Exercise: Compiling and watching your LESS/SCSS files

This exercise is small in scope but big in result. It involves adding only two lines to your webpack.config.js, and in return you’ll get near-instant updates to your browser’s stylesheet as soon as you save the changes to your .less or .scss files.

To get started, you’ll need to install a few loader packages:

npm install style-loader autoprefixer-loader css-loader less-loader --save-dev

Substitute less-loader for scss-loader if that’s how you roll.

Once the packages are installed, you’ll need to make two changes to webpack.config.js:

  1. Add your new loaders to a .less or .css line in the module.loaders section, using the ! character to feed loaders into each other (see the documentation for more details)
  2. Add your new stylesheet to the entry section (yep, you can include more than just JavaScript with webpack)
Don’t forget to move the styles out of index.html while testing!

Make sure you don’t get bogged down in this exercise – feel free to quit after 10 minutes or so. The important thing is that you give it a shot. Once you have given it a shot, you can check out my solution, and how I’ve wired this whole project together, at github.com/jamesknelson/webpack-black-triangle.

Awesome. Now how do I configure my app for deployment?

Well, first you’ll want to minify it. Then, you may want to bake in some environment variables so you can set different API keys in development and production. You’ll probably want to extract your CSS to a separate file, and you’ll also want to cache-bust your generated assets. And that will require some way to modify the src attribute of your <script> tag to match.

Now you could just clone my Unicorn Standard Starter Kit. But if you actually want to understand what is going on under the hood, the only way to ensure you don’t miss the guide is to get on my Newsletter.

And to sweeten the deal, you’ll immediately receive three bonus print-optimised PDF cheatsheets – for React (see preview), ES6 and JavaScript promises – completely free!

I will send you useful articles, cheatsheets and code.

I won't send you useless inbox filler. No spam, ever.
Unsubscribe at any time.

One more thing – I love hearing from readers. If you have something to say, send @james_k_nelson a tweet, or send me an e-mail at james@jamesknelson.com. Thanks for reading!

Resources

Read more

13 Comments Webpack Made Simple: Building ES6 & LESS with autorefresh

  1. Jeremy

    Hey, love the post! You’re right that Webpack docs are a bit sparse, and this got me started on the right track. One note though, I had to move index.html to the black-triangle/ folder to get it to spin, since that’s the build target for webpack, but in one of the steps you say to mv index.html into src/. Thanks again! Looking forward to seeing Unicorn Standard grow.

    Reply
    1. James K Nelson

      Glad you enjoyed the post, Jeremy!

      I just tried following the instructions with a new project, and had no trouble with my index.html in the src/ directory. Not sure what you may have done differently, but as a guess, did you pass the `–content-base src` option to `webpack-dev-server`?

      Reply
  2. Maxime Warnier

    Thanks for this article. We found more and more huge and complex starterkit for webpack and people don’t understand really how it works.

    By the way, in your npm start task ( “start”: “node_modules/webpack-dev-server/bin/webpack-dev-server.js” ) you can just do :

    “start” : “webpack-dev-server”

    because npm resolve the path to the local node_modules and the webpack-dev-server bin is in node_modules/.bin

    Reply
  3. Edward Beckett

    Great shares man… succinct and to the point… JS dependency management is definitely starting to mature to the point that front-end developers are now ‘accepting’ that convention over configuration is the most effective way to produce stable builds…

    Reply
  4. Little Brainz

    In your “Tweaks” section, point 1, rather that adding ‘webpack-dev-server/client?http://localhost:8080‘ to the “entry” property, just add the “–inline” option to the webpack-dev-server command line.

    Reply
    1. Will Myers

      EDIT Re previous comment – webpack-dev-server actually does not make the output config redundant, as (for example) the filename is used in the index.html. But it is still confusing that webpack-dev-server does not create any output bundle file. The argument seems to be that you don’t need to have a bundle file in dev mode, but I would disagree with this. I’ve implemented the https://github.com/gajus/write-file-webpack-plugin which forces webpack to write a bundle file

      Reply
  5. Bharat Soni

    Hey James, thanks for the nice tutorial! I was going through Memamug’s webpack config file. I couldn’t understand much between 7th to 19th line. Could you please help me understand those lines.

    Reply
  6. Matt Conway

    Going through this today, and I had to install ‘webpack’ globally as well to get this to work.

    Reply
  7. Jeff

    I am not so convinced by your statement “long, complicated Gulpfiles and Gruntfiles.”.

    first, the complexity does not equals to line of configurations, as long as configuration is simple and easy to use, it doesn’t matter that we need a few more lines.

    second, grunt and gulp do not require you to write your javascript modules to follow any commonjs or requirejs standards. which means that you don’t need to declare “require, exports” everywhere, and you can use so many existing open source without putting them into “shim” mode.
    But by using webpack, we have to declare these “require” so that webpack can find out the dependencies. ( I had done a lot with requirejs a few years ago, and requirejs can also compile the bundle with r.js, as same as webpack)

    Back to the problems you want to resolve in this article, don’t you agree they are easy when using grunt or gulp?

    Probably, after ES6 becomes standard and everyone starts to write javascript component, and then, webpack will have some advantage there..

    Reply
  8. Ted

    “Webpack is the least retarded piece of software I’ve used that’s written in JS.”
    Webpack is the MOST retarded, stupid, and non-intuitive, piece of junk I’ve used. It bogs down creativity, speed, and workflow.

    Look around. You see the same “tutorials” written over and over and over by different people. WHY? And when you create your own workflow and try to solve it using webpack, you will eventually realize what a pain in the arse time waster nonsense this is.

    Reply

Leave a Reply

Your email address will not be published.