Run Smarter, Not Harder

When we first started setting up webpack’s package.json file we defined a single build script called “common”.

“scripts” : {
“common” : “webpack –config webpack.common.config.js”
}

To run this build, in terminal we use the following command:

webpack run common

We want the best performance while developing our project, reducing the time needed for each build. We also want to get any useful additional debugging tools such as sourcemaps for our Sass, CSS and JS. And finally when we are ready to produce code for production use, we usually want to make everything as clean and compact as possible. So how do we get the best of both worlds without manually changing our configuration?

First, we want to identify which parts in our config file we want or need to keep for both a dev and prod build. This will become the base config file, then any differences between dev or prod will be held in separate files. All we need after that is a way to combine prod+common or dev+common config files into each build.
As they say, there’s a plugin for that.

Webpack Merge Plugin

Let’s get this new plugin ready for use.

Install as usual using npm in the terminal to add to our package.json’s “devDependencies”.

npm install webpack-merge –save-dev

Example result in package.json

“devDependencies”: {
… (other installed items),
“webpack-merge”: “^5.8.0”
}

 

Next we will separate our Dev and Prod build files. Make a two copies of our original webpack.common.config.js and name one webpack.dev.config.js and the other webpack.prod.config.js.  So now there are three total config files.

Since we have renamed and added our config files, modify package.json to the following as we won’t run common by itself anymore.

“scripts” : {
“dev” : “webpack –config webpack.dev.config.js”,
“prod” : “webpack –config webpack.prod.config.js”
}

Like before when we want to run either, we’ll use the command convention in terminal:

webpack run [prod or dev]

Modifying Dev and Prod Config files

The major difference between each config file will be the mode used. By default, certain build behaviors are invoked automatically by webpack depending on this property setting. Different versions of webpack will change these defaults as proven plugins tend to get added in later versions so you don’t have to bother with installing them. Check your webpack version’s documentation for any default mode behavior https://webpack.js.org/configuration/mode/ currently version 5.x.

Side note: mode can also be set to “none” to bypass any default optimizations and if not set at all defaults to “production.”

module.exports = {
… (other settings),
mode: ‘production’ (in webpack.prod.config.js)
mode: ‘development’ (in webpack.dev.config.js)
}

Once we have our two files and set the mode, the next thing is to decide what plugins are still necessary. For example, MiniCSSExtractPlugin may not be needed if you are not injecting <style> tags into a template html file with a separate CSS output file. In that case the require statement should be removed, the plugin creation for it can be removed, and instead of the Sass/Css loader in rules using it, it can be returned to just “style-loader.” Again, this will keep all the CSS in javascript form but still be available in Dev Tools along with its sourcemap.  I think generally whichever way you have your project set up it’s best to keep most things the same until you are very comfortable having each build create the output differently.

Using Webpack Merge With Our Two Build Scripts

If we want to separate our build scripts using a common file to start with, we don’t set the mode in webpack.common.config.js. Instead, the plugin itself will have a property we set within it for what mode to use.

In both config files we will need to require the merge component and the common config file. Place the code below at the top of each.

const {merge} = require(‘webpack-merge’);
const common = require(‘./webpack.common.config.js’)

The simplest way to use merge is to remove everything from module.exports and let all the plugins and rules, entry and output come from our common config file.  Here is an example for the dev and prod config files:

module.exports = merge(common, {
     mode: “development”,
     devtool: “inline-source-map”
});

module.exports = merge(common, {
mode: “production”
});

Note: “inline-source-map” adds the sourcemap into the bundle file instead of “sourcemap” which creates a separate sourcemap file

However, you may have certain options set in your Dev or Prod config files you want to keep and just have the common config file “fill-in” anything that’s missing, or at least make sure the calling file (dev or prod) takes precedence over anything in common.

To do this, instead of using the general “module.exports” for everything, we define a separate object for what we want to keep, and then run the the merge function in a module.exports statement like so in the Dev file:

const {merge} = require(‘webpack-merge’);
const common = require(‘./webpack.common.js’)

const devConfig = {
devtool: “sourcemap”
}

module.exports = merge(devConfig, common {
… any options for merge
});

Here, we define anything we want outside of module.exports as the variable devConfig. Then we call the merge function passing devConfig first and common second.  Reversing the order would reverse any override of values.  I put the “devtool” option outside just as an example but anything normally put in a webpack config file can be added. Vice versa, anything important for only prod config you use the same methodology.

Summary

Webpack like all technologies continues to evolve, and there are other options for packaging your code in an efficient manner, but it’s still widely used and should stick around for quite awhile.  It’s good practice when doing your own projects to include webpack and to know the basics. And when using it within a team, know how you can modify it locally to suit your needs and maximize your coding. One thing is for sure, there is always something new to learn it can do, and how you can do it!