Setting Up A Test-Driven React Project From Scratch - Part 1: webpack

Last updated on
22nd
of July,
2015.

You will learn how to scaffold a React project from scratch with Karma and webpack.

I’ve tried to make it as unopinionated as possible. Hopefully, once you understand the rationale behind each of the moving parts, you can roll your own or use one of the many templates available elsewhere.

At the end of this part, you would have set up webpack with:

babel-loader

webpack-dev-server

HtmlWebpackPlugin

Source maps with the -d flag

Let’s create a simple project that allows users to create reviews of beer and populate a list. We’ll call it Beerist.

In index.js, let’s just write a simple one-liner for the purposes of testing:

console.log("index.js is loaded!");

webpack

webpack was born as a agnostic module loader. Its Motivation page introduces itself quite nicely.

Since its inception, webpack has grown to encompass some elements of build tooling as well, which greatly reduces the need for Gulp or Grunt.

We’ll install webpack1 first so that we can write our React files in ES6 (or ES2015, or ES6+, or ES7, or whatever they call it nowadays) from the get-go and have webpack run the files through Babel to transpile our ES6 down to ES5.

We’ll also install webpack’s dev server so that we can serve the files locally using a built-in Express server and sockets (for hotloading):

npm install webpack webpack-dev-server --save-dev

This creates a node_modules directory if it hasn’t previously been created, and downloads the latest version of all the modules specified from npm. --save-dev saves it as a development dependency entry within your package.json2:

The config starts off by requiring webpack and path, which is a built-in Node library that provides utility methods for manipulating file path strings. In particular, we use path.resolve together with the “global” variable __dirname to resolve relative file paths to absolute ones.

Now how do we run webpack and/or its dev server? If you’ve installed webpack and/or webpack-dev-server globally, you can simply run webpack or webpack-dev-server on the command line to fire it up. Else, you can call it from an npm script inside package.json3:

"scripts": {
"start": "webpack-dev-server",
"build": "webpack"
},

Now, we can run npm start or npm run build (check out the difference between the two for yourself). Our directory should look like this:

To see it in action in the browser though, we’ll need a HTML entry point that loads our bundle.js.

Plugins

Introducing: webpack plugins, which provide additional functionality to the webpack building and bundling process. We’ll start by installing 2 of them:

npm install babel-loader html-webpack-plugin --save-dev

html-webpack-plugin is a plugin that we’re using here to generate a HTML file that includes all our webpack bundle(s).

Loaders are a type of plugin which transforms files from one state to another. babel-loader, as mentioned earlier, transforms ES6 code into ES5 code so that current browsers can run our app.

Our webpack.config.js is changed to accommodate these new plugins.

Adding a new plugin is as simple as requiring it, and adding a new instance of it in the plugins array.

Adding a loader is only slightly more complicated - the additional thing we need to specify is test, which tells the loader which files are eligible to go through the loader for transformation. Here, we specify with a regular expression that only files ending with .js or .jsx are eligible to be transformed by babel-loader.

I’ve demarcated diffed lines with // for single line changes and //- for block changes.

Immediately, you’ll see that ES6 module imports are hoisted to the top of the file, whereas CommonJS imports are run in-place (this is a simplistic treatment of the issue, but it is generally true).

Source Maps

You’ll also notice the line number on the right says something like bundle.js(59), even for console.logs emanating from different source files. This could potentially make your debugging very difficult in the future when you have many source files and all you have is bundle.js(38593) or some such to work backwards with.

Source maps allow the browser to map bundled JS files back to their unbundled state, so that things like errors’ and console.logs’ line numbers correspond back to their sources.

The simplest way to enable source maps is by indicating the -d flag5. In the package.json:

"scripts": {
"start": "webpack-dev-server -d"
}

The -d flag is actually short for --debug --devtool source-map --output-pathinfo. Each of these options are explained in further detail here.

In the next part, we’ll look at hot reloading with hot-loader, linting with eslint and eslint-react, as well as a couple of other useful webpack plugins.

Footnotes

We’re installing it locally for now, but you can also install it globally by indicating a -g flag. This will allow you access to the webpack command on the command line. ↩

In case you were wondering, the ^ in "^0.5.2" means that the package.json will update you to the latest release within a specified major version, which is indicated the first number in the version. In this case, ^0.5.2 is valid for all versions that match 0.x.x. ↩

The reason we’re doing this is because running npm scripts specified using package.json will add node_modules/.bin to the path, thus making node_modules/.bin/webpack and node_modules/.bin/webpack-dev-server available to the script to use (open up the node_modules folder to see for yourself, its there). ↩

Regardless of ES6 module synatax or CommonJS require syntax, please put all your imports at the top of the file. ↩

One can also enable source maps for other assets using the built-in source maps plugin: