December 9, 2016

Things I learned while upgrading to Webpack 2

Written by

My name is Brett. I’m contributing to a Yeoman generator called Confit. At Odecee, we get the opportunity to help our clients create new web applications from scratch. This means we set up development environments, dev tools, CI/CD (and related dev-ops activities), testing, UX — the works.

My focus has been setting up front-end development processes and tools for teams to embrace and extend as they write the actual applications. As the pace of change in front-end tooling has increased over the last 5 years, it gets difficult to stay on top of everything and produce web-based applications that are both functional and performant.

That’s where Confit (& Webpack 2) comes in…

confit

Confit logo (confit duck, recipe, build recipe…)

 
Confit is a Yeoman generator that generates web-development tools for the main development processes in web projects.

Confit generates most of development tooling and configuration we need for web apps and NodeJS libraries. For example, it:

  • Supports browser projects and NodeJS projects
  • Creates a Webpack (2) configuration that splits source code from vendor code
  • Supports unit testing via Karma + Jasmine (or Mocha for NodeJS) & system testing via Protractor
  • Adds git hooks to support semantic versioning via conventional commit messages, for both GitHub and regular git repos. Also adds hooks to which run unit tests before code is pushed (yay!)
  • Comes with sample applications for React (ES6), Angular 1.x (ES6) and Angular 2.x (TypeScript) with known working configuration (tested on Travis CI)
  • Supports plain CSS, or SASS & Stylus pre-processors.

These capabilities basically save us a stack of time when creating new applications, and will reduce the time to upgrade our tooling when the time comes to do so, because we’re starting with a tool-kit configuration that is known to work.

But I digress…

webpack2

Webpack 2 is (almost) here!

 
Webpack 2

In contributing to Confit and being a user of it, I’ve written a lot of configuration code. And having just upgraded Confit (v8.0) to use Webpack 2 (2.1.0-beta.25), I discovered a few things you may find noteworthy (as of 18 Nov 2016).

Documentation

The new documentation site is so much better than the old one! It’s still a work in progress (at the time of writing) but it has some of the critical information about the API changes I could not find elsewhere. The How-To Upgrade from 1.x is, funnily enough, very useful.

Loaders

Most of the changes are in the loader syntax.

The module.loaders array is renamed to module.rules:


// Old code:
module: {
  loaders: [...]
}
// New code
module: {
  rules: [...]
}

 
Module.rules.loaders is renamed to module.rules.use:


module: {
  rules: [
    { 
      test: /\.css$/,
      loaders: [{loader: 'css-loader}]
    }
  ]
}
// New code
module: {
  rules: [
    { 
      test: /\.css$/,
      use: [{loader: 'css-loader}]
    }
  ]
}

 
Loader.query configuration is renamed to loader.options, and the ‘-loader’ suffix is now required.


// Within module.rules.use...
{
  loader: 'css'
  query: '...'
}
// becomes:
{
  loader: 'css-loader',
  options: '...'
}

 
Pre and post loaders are no different to any other loader, but with an enforce: ‘pre|post’ property:


// Within module.rules...
module.preloaders: [...]
// becomes:
module.rules: [
  {
    test: /\.css$/,
    loader: 'css-loader',
    enforce: 'pre'
  }
]

 
LoaderOptions and config validation

There’s a new plugin called the LoaderOptionsPlugin. Why would you need that? Well, you can no longer pass any-ol’ config properties to Webpack or it will complain.

Previously, you could write this:


// Pass additional config to the htmlLoader, which will look for a config property called 'htmlLoader'
config.htmlLoader = {
  minimize: true,
  removeAttributeQuotes: false,
  caseSensitive: true
}
//

 
…but this won’t work any more!

The LoaderOptionsPlugin is the new way of passing loader options to your loaders until such time as all of the loaders use the new options property. For example:


config.plugins.push(new LoaderOptionsPlugin({
  debug: true,  // config.debug has to be passed this way now too!
  htmlLoader: {
    minimize: true,
    removeAttributeQuotes: false,
    caseSensitive: true  
  }
});

 
Exceptions

There are a few exceptional loaders that don’t (yet) follow these rules, or require tweaks with Webpack 2:

  • File-loader — needed to remove the ‘/’ from the front of the output path:
loader: 'file-loader?name=/foo/bar/.[hash:8].[ext]
// -->
loader: 'file-loader?name=foo/bar/.[hash:8].[ext]

 

  • ExtractTextPlugin and file-loader do not work with the use syntax (see issue):

module: {
  rules: [
    // Extract CSS during build
    {
      test: /\.css$/,
      use: [{
        ExtractTextPlugin.extract({    // THIS DOESN'T WORK
          fallbackLoader: 'style-loader',
          loader: 'css-loader'
        })
      }]
    }
  ]
}
// Workaround: use the original 'loader' syntax:
module: {
  rules: [
    // Extract CSS during build
    {
      test: /\.css$/,
      loader: ExtractTextPlugin.extract({
        fallbackLoader: 'style-loader',
        loader: 'css-loader'
      })
  ]
}

 
There are lots of nice features coming in Webpack 2, but the thing I like most is… it’s faster! 🏁😄

In the coming weeks, I plan to present some numbers on the differences between Confit v8.x (Webpack 2) and v6.x (Webpack 1) for the React, Angular 1.x and Angular 2.x sample applications that are part of Confit.

This post was originally published on Medium.

Tags: , , , ,

Categorised in: Digital, Technology

This post was written by Brett Uglow