Using eslint with Webpack

This morning I realized my eslint configuration was not running. I had set up the wrong command in the pretest script of my package.json and as a result none of the files were being run through eslint – it was just silently failing. My old gulp configuration had a gulp.src() definition that used globbing to find the files, so eslint was always fed a list of files rather than having to do a recursive search. I discovered this quite by accident. I had added a stage-1 proposal for static class properties to my BabelJS configuration and not updated by eslint configuration to compensate for this. I expected an error – I didn’t get one. What doesn’t happen is relevant too.

Wouldn’t it be nice if Webpack could do some pre-tests on my files prior to loading them?

Well, it turns out that this is possible. Like many things in Webpack, it turned out to be really easy. First of all, I needed a new loader for eslint:

npm install --save-dev eslint-loader

This should be fairly common now. There is an extensive list of pluggable loaders available for Webpack, just like there are plugins for grunt, gulp and browserify. It seems every single tool must be extensible these days – something I really like about the ecosystem. Now, on to the configuration within webpack.config.js:

var ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
    entry: {
        grumpywizards: './client/src/app.jsx'
    devtool: 'source-map',
    module: {
        preLoaders: [
            // Javascript
            { test: /\.jsx?$/, loader: 'eslint', exclude: /node_modules/ }
        loaders: [
            // Javascript
            { test: /\.jsx?$/, loader: 'babel', exclude: /node_modules/ },
            // Stylesheets
            { test: /\.css$/, loader: ExtractTextPlugin.extract( 'style', 'css?sourceMap') },
            { test: /\.scss$/, loader: ExtractTextPlugin.extract( 'style', 'css?sourceMap!sass?sourceMap') },
            // Font Definitions
            { test: /\.svg$/, loader: 'url?limit=65000&mimetype=image/svg+xml&name=public/fonts/[name].[ext]' },
            { test: /\.woff$/, loader: 'url?limit=65000&mimetype=application/font-woff&name=public/fonts/[name].[ext]' },
            { test: /\.woff2$/, loader: 'url?limit=65000&mimetype=application/font-woff2&name=public/fonts/[name].[ext]' },
            { test: /\.[ot]tf$/, loader: 'url?limit=65000&mimetype=application/octet-stream&name=public/fonts/[name].[ext]' },
            { test: /\.eot$/, loader: 'url?limit=65000&mimetype=application/[name].[ext]' }
    externals: {
        'react': 'React',
        'react-dom': 'ReactDOM'
    output: {
        filename: 'public/[name].js'
    eslint: {
        failOnWarning: false,
        failOnError: true
    sassLoader: {
        includePaths: [ 'client/style' ]
    plugins: [
        new ExtractTextPlugin('public/grumpywizards.css')

Lines 9-12 pull in a new section called preloaders. These are still a list of loaders but they are run before the loaders section. There is another section called postLoaders – you can guess what that does. I also needed to configure what happens when eslint fails. By default, things just carry on. I want to ensure that the build carries on if there is a warning (the warnings will still be printed), but an error stops the build. Unfortunately, loaders operate on a per-file basis, so this is really “stop on the first error”.

There was one other change to support Webpack I needed to make – to my .eslintrc.js. I use require() to bring in the stylesheets within my JSX files. I could switch them all over to use import (the ES6 method) but I like the require method as it puts a visual distinction for me within my code. I needed to add the commonjs environment to the .eslintrc.js file in client/src:

var OFF = 0, WARN = 1, ERROR = 2;

module.exports = exports = {
    env: {
        'es6': true,        // We are writing ES6 code
        'browser': true,    // for the browser
        'commonjs': true    // and use require() for stylesheets
    ecmaFeatures: {
        'jsx': true,
        'modules': true
    plugins: [

Don’t forget to also set up your eslint settings for stage-1 proposals. You can get the code so far from my GitHub Repository.