☯ Intuitive dev/prod webpack configuration
Tags: webpack, configuration, productivity, beginners
Published: 2020-08-03 15:54:08

## 🤔 Use Case
You want to provide webpack with a different configuration based on the environment variable.
By directly adding the instruction pairs in the configuration file with a monolithic model, you risk a headhache 😵. There must be some way to make it simpler.

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

### 📦 Packages
* [webpack](https://webpack.js.org)

---

## 💡 Solution
This is not 🚀-science - it is about the *order* and the *benefits* deriving from it. I unpack the main configuration file by placing the instruction sets into appropriate files. Simply inform webpack of the *environment variable* (**env**) and it will know how to derive the appropriate orders.
> Even from the developer’s point of view it becomes easier to follow the logical thread of the program, maybe even after a long time.

## 🤓 Implementing the Solution

Initial State:
* package.json *(`npm init -y`)*
* src/index.js *(content not relevant)*
* config

---

* [1] Add packages
* [2] Add npm scripts to `package.json`
* [3] Building components
 * usePlugins
 * useDevServer ([optional](https://github.com/didof/intuitive-webpack-config))
 * useFilename ([optional](https://github.com/didof/intuitive-webpack-config))
* [4] Assembling components

---

### - 1 - Add packages
In the terminal invoke: `npm i -D webpack webpack-cli webpack-dev-server`

### - 2 - Add npm scripts to `package.json`
Update `package.json` adding the start and the build scripts. In the first case we use the development server specifying that *env* is development.
The alternative is the use of the bundler in production mode.

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

### - 3 - Building components | 🦿 **usePlugins**
To pull up a wall first you have to make the bricks. This will be able to return a different array based on **env** as well as allowing easy and quick integration of plugins addition.

Create `usePlugins.js` in  `config`. This file is structured like so:
* plugins imports
* exported function
 * configuration panel 🎚
 * business logic ⚙

```js
// plugins imports
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

// exported function
exports.usePlugins = (e) => {

	// configuration panel
	const pluginsPanel = {
		development: 'html',
		production: 'html clean',
		values: {
			html: new HtmlWebpackPlugin({
				title: 'Webpack Loading CSS',
			}),
			clean: new CleanWebpackPlugin(),
		},
	};

	// business logic
	let message = '[plugins]: ';
	let error = '';

	const plugins = pluginsPanel[e.toString()];
	const arr = plugins.split(' ');

	const outcome = arr
		.map((plugin) => {
			const p = pluginsPanel.values[plugin];
			if (!p) {
				error += plugin + ' ';
				return;
			}
			message += plugin + ' '
			return p;
		})
		.filter((plugin) => typeof plugin !== 'undefined');

	console.info(message);
	console.warn('[plugins|error]: ' + (error !== '' ? error : 'none'));
	return outcome;
};

```
As you will appreciate in the end all the cumbersome import of plugins are in this secondary file, while `webpack.config.js` will report only the essentials.
In the *configuration panel* are specified two different sets containing labels - all real plugin builders are crammed into a container (.values).
The *exported function* receives the **env** and according to it selects the right set.
It’s up to the *business logic* to read the label, to understand if the relative plugin is present into the said container and if so place it in the final product.
> Optionally it is best to sanitize the final result by filtering away the plugins not found and showing an error message

### - 3 - Building components | ⛓ **index**
If you plan to expand this technique on later occasions it becomes useful to have to opportunity to get all these *components* with a single import.
In `config` folder add an `index.js` file.

```js
const { usePlugins } = require('./usePlugins');
// const { anotherPlugin } = require('./another');

module.exports = {
   usePlugins,
   // anotherPlugin
};
```

> The utility of this practice is more visible as the number of plugins grows.

---

### - 4 - Assembling components 🔧
It’s time to connect the wires in `webpack.config.js`.

```js
const path = require('path');
const helpers = require('./config'); //[1]

module.exports = (env) => ({ //[2]
	entry: {
		main: './src/index.js',
	},
	output: {
		path: path.join(__dirname, 'dist'),
		filename: '[name].js',
	},
	plugins: helpers.usePlugins(env),
});
```

1. When you require the contents of a folder rather than a specific file contained in it, is selected by default the file named *index*.
2. Rather than returning a configuration object directly, a function is exported - it can accept the **env** variable and use it to generate the appropriate outcome.

---

## 😎 Checking the Outcome
Try to assign different plugin combinations in the control panel and compare expectations with the message received at the console. Then try to make up some funny plugin label and add it - the server/build process will not crack and in the console is reported the culprit.

> Check the [example code](https://github.com/didof/intuitive-webpack-config) for other use cases.