Just the mere mention of Webpack starts to build those feelings of semi-dread but also a lot of curiosity. What is Webpack really? Should I bother? Is it just a fad?

There’s a lot of opinion just around those questions, so instead of me just standing on a soapbox, it might be a lot more useful for me to present Webpack and THEN maybe a bit of soapboxing, I mean c’mon it IS the Internet!

To help illustrate where we might take advantage of Webpack, we’re going to create a very small one page type of project. Think of something just smaller than the standard ToDo application.

the Doctor on Time

[…] is more like a big ball of wibbly-wobbly, timey-wimey … stuff.

  • the TimeLord either talking about Time… or Webpack

first things first - initialize the project

You can grab the source code to this guide HERE.

Let’s get a new project going to set everything up. Enter your favorite project home and create a new directory for playing around with Webpack.

  • mkdir hello-webpack
  • cd hello-webpack
  • npm init
  • npm install webpack webpack-dev-server html-webpack-plugin --save-dev

After those packages finish installing themselves, we can setup the project structure we’re going to reference:

project tree structure

setup and test webpack configuration

All those nasty and mean webpack jokes by internet people aside, one of the first things I tackle in a project is to solidify the “build” process tooling. While it can sometimes feel like a task that should come just a few steps later into a project, my intention is to setup a development environment where I can feel productive once I start writing actual code.

During the initial excitement of a project kickoff, my brain is jumping from potential challenges and things to tackle within the code.

However I’ve learned this is when my productivity level isn’t very optimal.

Instead of staring at a blank JavaScript file right away, I first setup the build tooling to calm my brain down, and try to “burn off” those early jitters by focused work on build tools.

touch webpack.config.js

If you’re slightly unfamiliar with Webpack (not a problem), it is a popular front-end build and pipeline tool. If you’ve used task based tools such as Grunt or Gulp, then someone new to Webpack it is roughly similar. Grunt has its Gruntfile.js, while Gulp has its gulpfile.js. So to is the relationship between webpack.config.js and Webpack.

webpack.config.js

 1 'use strict';
 2 
 3 const path = require('path');
 4 const webpack = require('webpack');
 5 const HtmlWebpackPlugin = require('html-webpack-plugin');
 6 
 7 module.exports = {
 8 
 9     entry: path.join(__dirname, './src/index.js'),
10 
11     output: {
12         path: path.join(__dirname, 'public'),
13         filename: '[name].bundle.js'
14     },
15 
16     plugins: [
17         new HtmlWebpackPlugin({
18           template: './src/index.tmpl',
19           filename: 'index.html',
20           inject: 'body'
21         })
22     ],
23 
24     devServer: {
25         contentBase: path.join(__dirname, 'public'),
26         port: 3000,
27         open: true
28     }
29 
30 };

The Webpack documentation has been improving at a fantastic pace, so if my explanations don’t make sense, then it shouldn’t be too much of a time sink hunting down where your issue is.

Here’s a section of the documentation on Webpack configuration

entry - this is the path to your main / root JavaScript module. Webpack tries to create your application bundle from spidering out via your entry module. So if all of your modules aren’t included somewhere within that spidering effort, it won’t be included. This setting is to define where your initial JavaScript starting point is.

output.path - Webpack has a concept of plugins within its pipeline which are used to greatly enhance and apply transformations to your application code. We won’t go too deep into that just yet. Just remember that the output.path should JUST contain the folder ROOT path of where you want the finished bundle. All of Webpack’s transformation plugins will reference the same output.path, so don’t think of it as being solely focused on just your JavaScript bundle.

output.filename - This is the specific filename of our final application bundle. If you want it within a sub-folder from output.path, then you should be doing that here in the output.filename. aka output.filename: 'app.bundle.js' or output.filename: 'scripts/app.bundle.js.

plugins - this is an Array of Webpack plugins that you can just drop right into your pipepline. We are most interested right now with HtmlWebpackPlugin which will take a file as the template input, then automatically inject the necessary <script> markup along with your output.path + output.filename. If that explanation doesn’t click, then just inspect the resulting index.html when the build completes. It will make sense.

devServer - This object is just some settings for applying to webpack-dev-server. While running, the dev-server keeps your entire application in memory, so while it’s using your Webpack configuration settings, nothing is being written to disk. contentBase defines the folder you want web accessible. port just gives you the freedom to define your own port. open will open your favorite web browser for you to the given contentBase:port.

updating the start script in package.json

Just for your own convenience during local testing, let’s stuff all of our webpack-dev-server stuff into a much shorter npm script.

1 "scripts": {
2     "start": "webpack-dev-server --d --colors --watch --hot",

Now to run everything locally, just type npm start in your terminal. You can keep this running in the background while you make source code changes, and Webpack will manage refreshing the browser when necessary.

adding babel support

Okay we’re not quite done with Webpack configuration. Rather then throw a giant blob at you, which will likely cause your eyes to just glaze over, I thought it’d be an easier digest if things were broken into chunks (kinda like Webpack itself! get it? get it?)

We want to take advantage of some of the more modern conveniences in JavaScript. Whether that’s ES6 or beyond, it’d be nice to setup a way for this to happen for us automatically using Babel.

Luckily there’s a Webpack loader for this purpose specifically, called babel-loader.

Just install it via NPM using:

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

adding a module section to webpack.config.js

Unlike plugins which has access to pretty much everything related to your final build (in memory), a loader is more of a pre-processing type of tool for Webpack. It might not be too helpful to go into too much detail around loaders, but they are normally used by Webpack during the building phase of your codebase.

You will typically provide Webpack with a RegEx to define which loader to apply to which type of file.

(Just keep reading, as you work through this, I’m hoping it’ll make sense and click).

Let’s update our webpack.config.js to accomodate Babel.

 1     module: {
 2         rules: [
 3         {
 4         test: /\.(js|jsx)$/,
 5         exclude: /(node_modules|bower_components)/,
 6         use: {
 7             loader: 'babel-loader',
 8             options: {
 9             presets: ['env']
10             }
11         }
12         }
13         ]
14     },

Other loaders will make its way into our configuration before we’re through. The module object contains a rules Array which is where we define what kind of loader to apply to which filetype.

In rules[0].test we have defined a RegEx pattern that specifies that this loader is concerned about files with an extension of either .js or .jsx. Once Webpack finds a file that satisfies this criteria, it will look in the rules[0].use Object for settings on how to handle it.

In this particular case, we’re telling Webpack to use the babel-loader when it finds files that pass the test. The options aren’t really important, just that we want to use the env preset for Babel (which is why we needed the babel-preset-env package). Other presets exist for toolkits like React or when working with es2015, but for now env is enough.

our starting index.tmpl

Before putting down some explanations on Webpack and HTML templates, let’s go ahead and look at our ./src/index.tmpl.

 1 <!DOCTYPE html>
 2 <html lang="en">
 3     <head>
 4     <meta charset="utf-8">
 5     <meta http-equiv="X-UA-Compatible" content="IE=edge">
 6     <meta name="viewport" content="width=device-width, initial-scale=1">
 7     <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
 8 
 9     </head>
10     <body>
11     <div id="root"></div>
12     </body>
13 </html>

This is quite a basic index template, but believe it or not, depending on your project – it might be all that’s required.

html-webpack-plugin

One of the more common Webpack plugins available is html-webpack-plugin. I’ve already mentioned that plugins are (for the most part) transformers. They work (in memory) with the pieces of your entire application, and help transform from one state or style into another one.

Back to this specific plugin. Our main index.html currently lives in ./src as a template, index.tmpl. Really you can name this file whatever you want, I just wanted a visual cue that it’s a template.

Notice that we don’t need to put any of our script references in there, because we’re handing over control of everything over to Webpack.

As Webpack processes your entry source code, it will then load up the html-webpack-plugin and the index.tmpl template file and combine everything together. The end result will be your ./public/index.html file along with the necessary script references to your newly generated application bundle!

bam!

Adding support for SASS (optional)

Okay we are on a Webpack roll here. Most web sites or applications rely on some kind of use of CSS. I don’t want to go through the advantages of using SASS right here, but I’m going to assume for the moment that you want it in your projects as much as I do.

npm install node-sass sass-loader css-loader style-loader --save-dev

As you can probably guess given this dependency list, we are using loaders to intake and transform our SASS to CSS.

time to update your webpack.config.js

 1 // in the modules.rules Array
 2 {
 3         test: /\.scss$/,
 4         use: [{
 5             loader: "style-loader" // creates style nodes from JS strings
 6         }, {
 7             loader: "css-loader" // translates CSS into CommonJS
 8         }, {
 9             loader: "sass-loader" // compiles Sass to CSS
10         }]
11 }

reference your SASS from your JS module

Again, you might now notice that the ./src/index.tmpl template makes no reference to our application CSS. No, I didn’t forget.

The same way that Webpack will create a bundle for your script references during a build, it can do the same thing for your SASS.

Right at the top of your entry file, just make a reference to the SASS file.

update ./src/index.js

1 require('./styles/main.scss');

That’s it we are done with the basics of Webpack

While maybe it felt like a “long” process, this is the kind of thing that you really only need to setup and configure once. Once you put in the work of setting up your own Webpack configuration, you can just about carry it from project to project.

However we’ve only just covered the bare basics of Webpack. There’s so much left to explore and enjoy. There’s topics like JSON loading, image optimization, code-splitting and so much more.

Stay productive - and like, comment and share please!