🪓 How to split dev/prod webpack configuration
Tags: webpack, dry, configuration
Published: 2020-08-02 17:28:15

## 🤔 Use Case
As the your project grows also the size of configuration increases. If to this is added the need for a different webpack behavior based on the environment it becomes clear that a monolithic configuration is no longer optimal.

### 📜 Example Code
> [webpack-configuration-split](https://github.com/didof/webpack-configuration-split)


### 📦 Packages
* [webpack](https://webpack.js.org/)
* [webpack-merge](https://github.com/survivejs/webpack-merge)

---

## 💡 Solution
Levaraging the fact that webpack uses a *JavaScript* file as a configuration, we can break it down into three blocks that we can merge according to the environment.
1. `webpack.common.js` - fixed chunk, whose instructions are necessary both in development and production phase
2. `webpack.development.js` - merged to the previous in response to `npm start`
3. `webpack.production.js` - merged to common chunk in response to `npm run build`

## 🤓 Implementing the Solution

Initial State:
* src/index.js
* webpack.config.js
* package.json

### package.json
Before writing the configuration, I set the npm scripts specifying the different environment variables.

```json
{
...
"scripts": {
    "start": "webpack-dev-server --env development",
    "build": "webpack --env production"
  },
...
}
```

---

Now I am going to create the three partial configuration and group them in a special folder named `config`.

### webpack.common.js
The following instructions are common in both cases:
1. location of the entries point.
2. where to create the outcome of the process - but not the naming of chunks since it depends on the environment.
3. generate the *HTML* index file.

```js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
	entry: { //[1]
		main: './src/index.js'
	},
	output: { //[2]
		path: path.join(__dirname, '..', 'dist')
	},
	plugins: [ //[3]
		new HtmlWebpackPlugin({
			title: 'Webpack Configuration Split',
		}),
	],
};
```

---


### webpack.development.js
In this block we are going to provide a set of instructions needed in developing.
1. For the sake of simplicity I chose only one plugin, [system-bell-webpack-plugin](https://www.npmjs.com/package/system-bell-webpack-plugin).
2. Since webpack-dev-server allocates the outcome in memory, I do not need any hash to filenames.
3. It makes sense to provide instructions for the devolpment server only when it is actually used.

```js
const SystemBellPlugin = require('system-bell-webpack-plugin');

module.exports = {
	output: {
		filename: '[name].js', //[2]
	},
	devServer: { //[3]
		compress: false,
		open: 'chrome',
		stats: 'errors-only',
		overlay: true,
	},
	plugins: [new SystemBellPlugin()], //[1]
};
```

---


### webpack.production.js
Same concept as above - here we got the instructions needed for obtain a production-ready dist.
1. To prevent the caching of the bundles (or any other file) by the browser, a simple solution is to associate a hash within the filenames.
2. Once again for simplicity, I show only one basic plugin [2]

```js
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
   output: {
      filename: '[name].[hash].js' //[1]
   },
	plugins: [new CleanWebpackPlugin()], //[2]
};
```

---

All that's left is to enable `webpack.config.js` to merge what we just created.
### webpack.config.js

1. Import the package [webpack-merge](https://github.com/survivejs/webpack-merge)
 * if needed, download it via `npm i -D webpack-merge`
2. Since it's always used, import the common chunk.
3. Differently, import only the required chunk based on the environment.
 * the dynamism of this operation can be obtained in several ways. The simplest implementation is the use of an `if(env == 'development')`. The example I reported, although more streamlined, requires that the variable **env** (which we will declare shortly) must be 'development' or 'production'.
4. Finally, use merge on the fixed configuration and the dynamically imported one.

```js
const { merge } = require('webpack-merge'); //[1]

const commonConfig = require('./config/webpack.common'); //[2]

module.exports = (env) => {
	const config = require('./config/webpack.' + env); //[3]
	return merge(commonConfig, config); //[4]
};
```

## 😎 Checking the Outcome
`npm start` - Google Chrome open by itself, open up devTools and reach Network section. Reload the page and check the chunk name.
`npm run build` - peek in the oven-fresh dist folder, see the hashed name? Try to modify index.js, run the script again to visualize that in fact you are using the plugin only offered by the production configuration file.

## ❣ More Details [surviveJS](https://survivejs.com/webpack/developing/composing-configuration/)