Top 5 Changes in webpack v5

A new version of your favorite bundler? It’s not every day (or year) that happens, but the world was lucky enough in October of 2020 to get a new major version of webpack. It was more than 2 years in the making, so in this article, I want to go over some of the major changes and how to upgrade from webpack v4.

If you want to watch a video about this new release, check it out here: 

Welcome to v5

Webpack v5 brings a ton of new features, changes, and deprecations. The release article was more than 10,000 words, and I wouldn’t expect you to read the whole thing unless maybe you are doing some deep things with webpack (like if you are a plugin or loader author). However, reading some of the top-level major changes in the article is a good idea, even for regular users. This article is going to cover these major changes.

It has been over two years between the release of webpack v4 (released in February 2018) and webpack v5 (released October 2020). That means two years of Webpack adding new features, making changes, and putting warnings behind soon-to-be deprecated parts of Webpack. So when webpack v5 was launched, especially since webpack is a very active open source project, there was a ton of stuff included in the release. 

Here Today, Gone in v5  

Deprecations and removals are an expected change in any major version release of software. With webpack v5, anything that was deprecated or warned that it was being deprecated in v4 has actually been removed. This means that if you had been receiving deprecation warnings, but ignored them or never saw them, those things that were deprecated in v4 won’t be present in v5, and your build will break. For most simple use cases, this matters more to any loaders or plugins you are using. Upgrading any of those dependencies will be necessary when upgrading to webpack v5.

node --trace-deprecation node_modules/webpack/bin/webpack.js

There is a trace-deprecation flag that you can include when running a webpack build, which will give you the information on what’s being deprecated, and in which plugin or loader the deprecation is taking place. If you’re trying to upgrade and migrate to v5, you can use the trace-deprecation flag to find out what’s actually deprecated. Then you can note the plugins or loaders that are the culprits and upgrade those before migrating.

Webpack Core Expands

A lot of the functionality of some popular plugins and loaders has been rolled into webpack Core with the release of webpack v5. This also happened with webpack v4 when they moved many optimization plugins to Core. Over the two years that v5 was being developed, there were several plugins that became popular in the community. At a certain point, it made more sense to roll them into the Core framework than require users to continue to install them separately. By converting plugins to native functionality, there’s also improved performance that can be gained.

Here are some examples of plugins that have been rolled into webpack Core with v5:

NamedModulesPlugin

NamedChunksPlugin

HashedModulesPlugin

Now, these plugins are simply configuration properties that you’ll configure in your webpack.config.js. Here’s the mapping from plugin to configuration property:

NamedModulesPlugin => optimization.moduleIds: 'named'

NamedChunksPlugin => optimization.chunkIds: 'named'

HashedModulesPlugin => optimization.moduleIds: 'deterministic'

Where Did My Polyfills Go?

This is a big one. With webpack v5, Node.js polyfills are no longer included. If you’re only marginally familiar with polyfills, you’ve probably heard of them in reference to front-end development. Polyfills were popular years ago, mostly used to patch in modern JavaScript features that weren’t present in older versions of Internet Explorer.

The webpack polyfills that have been removed in v5 are polyfills for the Node.js common modules and Node.js core library that doesn’t exist in the browser. Webpack is primarily used to bundle code for web browsers, but because many npm modules were originally written with Node.js in mind, the Node.js polyfills in webpack added the ability to run them in the browser. 

At this point, the JavaScript community is creating most modules with dual-use in mind, so webpack v5 removed the polyfills to reduce the codebase and make your bundled code smaller. 

In addition to the removal of the Node.js polyfills, the process object and the Buffer object have also been removed in webpack v5.

Configuration File Confetti

There’s a saying that goes something like “Death by a thousand webpack configuration file changes”. Isn’t that it? Hmm… maybe not. 

In webpack v5, the webpack.config.js file has over 100 new properties or changes to properties. This is the file you create and maintain, and then maybe change once every couple of years whenever you’re upgrading your version of webpack. In webpack v5, there are a ton of changes to properties that I’ve never seen or used in the configuration file. Seriously, over 100?

Along with new property additions, property structure changes, and property name changes, there are also new defaults for those properties. For example, the properties in your configuration file may have had one default behavior in v4, but in v5, they have a completely different default behavior. For instance, a property could have had a default value of true before, but now it’s false. I did notice that most of these default changes aren’t on the main path of webpack. They’re mostly to nested or niche properties. It’s probably a good idea to take a look at the changes to defaults, in case they affect any properties you’re using. You can see the changes here.

Node.js v10 or GTFO

This last big change will probably be felt the hardest on your build servers. Webpack v5 requires a minimum of Node.js v10 to run. Node.js v10 just left LTS support in June 2021, so you should really already be running a more recent version than 10. However, I know how it goes with most teams, especially when CI/CD pipelines are set and production servers are running. Those can often be the last to be upgraded.

I prefer to have the Node.js version on my computer to match whatever the active LTS version is because I love to check out the new features. But if the active LTS version is too far from the version running on the production server that my application is on, I’ll try to match my Node.js version to that Node.js version during development. I like to use nvm so I can switch Node.js versions easily. If webpack is also running through a CI/CD pipeline in CircleCI or AWS CodeBuild, then the Node.js version there should match the version in the production servers. This is where the requirement for a Node.js v10 minimum may hit a snag and make it difficult to upgrade to webpack v5.

Let’s say you haven’t had the time or have been intimidated to upgrade the Node.js version for your application. You may find something like Node.js v6 or v8 running on your CI/CD pipeline. Webpack v5 requires Node.js v10, which is a couple of years old now, as Node.js v16 is actually the active LTS version. I wouldn’t be surprised if people are going to find roadblocks upgrading to webpack v5 with this Node.js version requirement. It’s still a good change, and one that I support, however, I'm a little surprised they didn’t go past v10. Just be aware that it’s a big change that makes it potentially more difficult to use webpack in this case.

The Road to v5

With some major changes like the ones above, it can be helpful to have a clear guide for the migration process between v4 and v5. Hopefully, you’re already on Version 4, and not moving from v3 to v5. Version 4 had a lot more breaking changes in the configuration file structure. Version 5 doesn’t have nearly as many, though there are a couple of interesting things. Here are some of the things you can use to make your migration process easier. 

Things to consider

 You may just not want to migrate yet, as what you’d be getting from v5 might not be worth it yet. Most of the ecosystem of plugins and loaders has likely upgraded to v5 by now, and, if you’re like me, you probably use many different plugins and loaders in your webpack configuration file. If this is you, you’ll have to go through and upgrade all plugins and loaders, which is a lot of work. That extra time upgrading your build might not give you a good return, since the optimizations might not improve your build time or package size. 

 Make sure you’re aware of the time it’ll take to upgrade and that it will be worth it to your team.

Plugins and Loaders

 If you’re going to migrate, the first thing you would need to check is all of those plugins and loaders that your project uses, making sure that they actually support webpack v5. Some of the third-party plugins might not support the changes in webpack v5 yet. A lot of the changes to the v5 internals are deep, so loaders and plugins are going to have the most changes if you’re an end-user of Webpack. 

 After going through the major changes above, you may have realized you’re already on Node.js v10, or that you don’t use any of the plugins that have changed. You may be totally fine with all these changes, but all of your loaders and plugins may not. So you will need to go through and check every loader and plugin, either upgrading if it has a newer version, or finding a beta of that plugin. If you do find any dependencies that you’re using that don’t support v5, you’ll want to look for an alternative to it before proceeding.

 An advantage of doing this check is that you may find plugins that have been deprecated or are no longer supported. When going through this process myself, I found that a plugin I was using called optimize-css-assets-webpack-plugin was deprecated. I hadn’t upgraded the build for this project in over two years, so I wasn’t surprised something in there was deprecated. This was something valuable I learned during the plugin/loader check process.

 

Taking the Plunge

 Finally, before you go through the upgrade, use the --trace-deprecations flag when running your build while you’re still on webpack v4. This will let you know what isn’t going to work when you upgrade to v5, even after you’ve done upgrades for loaders and plugins; it lets you know what is still lingering. 

 If you want to bypass this check and just try it out, you can just create a new git branch and do the upgrade work to webpack v5. You’ll upgrade webpack, webpack-CLI, and then upgrade any remaining loaders and plugins like Babel. 

 npm install -D webpack@latest webpack-cli@latest

 Once you run your build again, you can see what has changed and if it actually finishes. It would be great if you could upgrade Webpack and have it immediately work, but I wouldn’t be surprised if that did not happen; it’s never happened for me. Expect some errors to be thrown that need to be resolved.

 This is a basic migration process you can follow which should take you from webpack v4 to webpack v5.

 

My Personal Migration Experience 

 I also wanted to share my personal experience migrating one of my projects from Webpack v4 to Webpack v5. This project is not a huge production app, but there is a little complexity in the build, so I think it’s a good example. Here’s my original, webpack v4 compatible webpack.config.js:

const webpack = require('webpack')
const path = require('path')
const UglifyJSPlugin = require('uglifyjs-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')

module.exports = {
  devtool: 'source-map',
  entry: {
    application: './app/index.jsx'
  },
  mode: 'production',
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env', '@babel/preset-react'],
          plugins: ['@babel/plugin-proposal-object-rest-spread', '@babel/plugin-proposal-class-properties']
        }
      },
      {
        test: /\.(less|css)$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader?modules',
          'less-loader'
        ]
      }
    ]
  },
 
  optimization: {
    minimize: true,
    minimizer: [new UglifyJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
  },
  output: {
    filename: '[name].min.js',
    path: path.resolve(__dirname, 'public')
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('production')
      }
    }),
    new MiniCssExtractPlugin({ filename: 'stylesheet.css' }),
    new webpack.optimize.ModuleConcatenationPlugin()
  ]
}

After upgrading to webpack v5, here is what that webpack.config.js looks like:

const webpack = require('webpack')
const path = require('path')
const TerserPlugin = require('terser-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')

module.exports = {
  devtool: 'source-map',
  entry: {
    application: './app/index.jsx'
  },
  mode: 'production',
  cache: {
    type: ‘filesystem’
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env', '@babel/preset-react'],
          plugins: ['@babel/plugin-proposal-object-rest-spread', '@babel/plugin-proposal-class-properties']
        }
      },
      {
        test: /\.(less|css)$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader?modules',
          'less-loader'
        ]
      }
    ]
  },
  optimization: {
    minimize: true,
    minimizer: [new TerserPlugin(), new CssMinimizerPlugin()],
  },
  output: {
    filename: '[name].min.js',
    path: path.resolve(__dirname, 'public')
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('production')
      }
    }),
    new MiniCssExtractPlugin({ filename: 'stylesheet.css' }),
    new webpack.optimize.ModuleConcatenationPlugin()
  ],
  stats: 'errors-only'
}


There’s not much going on; there aren't many loaders, mostly just some CSS building. There is some chunk splitting and some minimization. But beyond that, not a ton going on. 

I really only needed to change two plugins that had been deprecated, and update the other loaders and plugins to the versions that supported webpack v5. I also added a couple of options to take advantage of new features.

 The build for this project is very fast, completing in around a half-second. This is the result of a new configuration feature with webpack v5: the filesystem cache. As far as I understand, this was not available in Webpack v4, and boy, does it make a difference. 

cache: {
  type: ‘filesystem’
} 

Without the cache property in the configuration, the build takes about four-and-a-half seconds. The filesystem cache caches things in the cache folder of the webpack folder in your Node.js modules folder. This new feature should dramatically speed up the build for any size project.

There is another cool thing with the filesystem cache. If you’re using some kind of continuous integration server and already caching your node_modules folder in there (which is something I’ve always done), then that cache will also be applied to your Webpack builds if you have the filesystem cache option enabled. This will help make your builds not only faster locally, but also on your build server.

Summary

Overall, the changes in Webpack v5 are pretty awesome. If you have a simple build like mine, you could probably jump to it quickly with minimal changes. If you have a very complicated build, with custom loaders or plugins, you may want to block out a few days to work through the migration.

If a smaller file size or a faster build sounds like what your project could benefit from, I think it’s worth taking the plunge into the new version. New webpack versions only come along every few years, so you hopefully won’t need to upgrade again soon.

May 18, 2021 javascript node.js webpack