Webpack Academy #6: Split your config for dev & prod mode
Tags: webpack, webdev, javascript, programming
Published: 2021-09-09 09:34:41

So from now we manage a lot of things, but we need to divide our config into two parts:

- Dev part (for developer)

- Prod part (when we deploy our application on the web)

Until now we just focus on the **Prod part**!

So in this article, we will split our config into 3 files!

- Common config webpack

- Prod config

- Dev config

### Common config webpack

Some properties are common to dev & prod config files, so we can create a file that will contain these fields to avoid repeating code!

Before starting we need to install a package named `webpack-merge`, which will merge two configs files into one! So we can merge-common file with another file !

After this, we create a common config file 👇

```js
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
const HtmlWebpackPlugin = require('html-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const cdn = require("./cdn")

const config = {
    // Webpack start from this entry point
    entry: {
        myApp: [
            "./src/style.css",
            "./src/main.js",
        ],
    },
    // External lib that will not be put in bundle but use from CDN
    externals: {
        lodash: '_',
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            title: 'Webpack academy title',
            template: './src/index.html',
            inject: 'body',
            cdn,
            minify: {
                removeComments: true,
                collapseWhitespace: false
            }
        }),
        new BundleAnalyzerPlugin({
            openAnalyzer: true,
            analyzerMode: 'server',
        })
    ],
}

module.exports = config
```

In **common config** we need to use *bundle analyser for prod & dev mode*, we need also *HTML plugin* & *clean webpack* !

We also need to use *cdn* ! 

And we create a prod config file !

### Prod config file

```js
const commonConfig = require("./webpack.config.common")
const merge = require('webpack-merge')
const MiniCssExtractPlugin = require("mini-css-extract-plugin")

const config = {
    mode: "production",
    module: {
        rules: [
            {
                // Match file extension
                test: /\.css$/,
                // Order of loader from bottom to up
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader'
                ],
            }
        ]
    },
    // This is the output of Webpack
    output: {
        // From current folder + dist folder that will contains all bundle
        path: path.resolve(__dirname, "dist/"),
        filename: "[contenthash:8].js"
    },
    optimization: {
        minimize: true,
    },
    plugins: [
        new MiniCssExtractPlugin({
            // Name output by extract
            filename: "style.css",
        }),
    ],
}

// Merge commonConfig with prod config, priority to prod config
module.exports = merge(commonConfig, {
    ...config,
})
```

In **prod mode** we need to use *hash*, *extract css* & *minimize bundle code* !

It's almost finished for the **prod part**, we need to rename `cdn` file to `cdn.prod.js` since we will use two separate files for cdn in prod & dev mode !

And we need to edit `script file` 👇

```
"build": "cross-env --env.NODE_ENV=prod webpack --config webpack.config.prod.js"
```

And it's finished from now for the *prod part !*

All things are working since before ! It's a good start to continue and go on the *prod part!* 

### Dev config file

Let's start by creating `cdn.dev` !

Unlike `prod cdn`, we will **NOT** use the `minify` version of library ! We will just use the *classic* !

```js
module.exports = {
    js: [
        "https://unpkg.com/lodash@4.17.15/lodash.js",
    ],
}
```

After this we need to install `webpack-dev-server`, which serves to handle dev service like a dev server for developer !

And create the dev config 👇

```js
const path = require("path")
const commonConfig = require("./webpack.config.common")
const merge = require('webpack-merge')

const config = {
    mode: "development",
    devServer: {
        // Show info about dev server
        noInfo: false,
        // Port of dev server
        port: 8080,
        // Asking the server to fallback to index.html in the event that a requested resource cannot be found, need to vue router
        historyApiFallback: true,
        // Allows https in dev server
        // Use this https://stackoverflow.com/questions/35531347/localhost-blocked-on-chrome-with-privacy-error for allow https in localhost directly on chrome
        https: true,
    },
    // This is the output of Webpack
    output: {
        // From current folder + dist folder that will contains all bundle
        path: path.resolve(__dirname, "dist/"),
        filename: "bundle.dev.js"
    },
    module: {
        rules: [
            {
                // Match file extension
                test: /\.css$/,
                // Use multiple loader
                // Order => from bottom to top
                use: [
                    'style-loader',
                    'css-loader'
                ],
            }
        ]
    }
}


// Merge commonConfig with prod config, priority to prod config
module.exports = merge(commonConfig, {
    ...config,
})
```

For `css part` we need to use `style-loader` since we need to inject style in the DOM !

We use some properties like `port`, `https` etc... (check comment above)

But on thing is very interesting and I should explain you 🔥

**historyApiFallback:**

When you use a framework like Vuejs or React, you will choose between two things -> Use Router in **front part or back part**.

If you use vuejs for example and you need to use **VueRouter** (handling routing of your app in the Front part).

You will need to inform the current server that hosts your app (in my case webpack server) that you handle route by the front and not by the server (back).

But why ?

For example if you use router front and you will try this URL 👇

```
https://toto.com/test
```

The server will try to access to `/test` file from the server files, and it will get nothing since it's not an existing file (because you handle routing by front). And you will get a `404` !

To avoid this **behavior** in our case, we need to tell the server -> `If you have a 404, access to the root file (main file) and not looking for /test file`

And after this you can enter any URL and your project will works !

So this options `historyApiFallback` is just an option to prevent this if you use Router in the front part, you should put `true` ! Else put `false` !


We almost finish 💪

Create the command to run our app in dev mode

```
"dev": "cross-env --env.NODE_ENV=dev webpack serve --hot --config webpack.config.dev.js",
```

`webpack serve --hot`: Command to run webpack server in hot reload mode (when a file is changed, we reload our project automatically)


### Source map

We will not get in details about this, if you want to check this URL -> *https://blog.teamtreehouse.com/introduction-source-maps*

To be short, in dev mode we will need to debug some files, if we don't use source map, we will have some files that are a little weird, for example, our `main.js` 👇

```js
lodash__WEBPACK_IMPORTED_MODULE_1___default().cloneDeep({})
console.log(_three__WEBPACK_IMPORTED_MODULE_0__.three)

document.getElementById("button").addEventListener("click", function() {
    jsonObjectImport().then(jsonObject => console.log(jsonObject.default))
})
```

Here it's not a problem, but with big files we can have some trouble to debug this !

So `source map` is our hero ! 🦸‍♂️

```js
devtool: 'eval-cheap-source-map',
```

(You have a lot of choices about source map, for me the best to use in dev is 'eval-cheap-source-map'), check at this URL to have more information: *https://webpack.js.org/configuration/devtool/*

When you want to debug the `main.js`, you will see another file like `main.js?56d7` and this is this file that we will need to debug ! Open it and 👇

```js
import { three } from './three'
import _ from 'lodash'
const jsonObjectImport = async () => import(/* webpackChunkName: "myChunkName" */ "./big-object.json")

_.cloneDeep({})
console.log(three)

document.getElementById("button").addEventListener("click", function() {
    jsonObjectImport().then(jsonObject => console.log(jsonObject.default))
})
```

It's like our original `main.js` file ! 

**To summarise source map :**

When we bundle our code with Webpack, it will **compile** our original file into another special file (can be weird), so if we want to **debug our code**, we will have this special file that is **different** from the original file code.

To fix this we can use **source map** that will keep track of our original file (each original file has a special hash). So if we need to debug code we can find the file like the **original file !**

Well done ! 💪🔥
 
We made it ! We manage to split the original config into two parts ! 

We almost finish the webpack academy! 

I hope you like this, [You can check the source code at this commit](https://github.com/Code-Oz/webpack-academy/tree/30801d65f84625857d706242bee55ad1a111b31d)

*📝 Note: We create two more commands for bundle analyzer, in prod and dev mode if we need to check bundle size in both cases*


---

I hope you like this reading!

🎁 You can get my new book `Underrated skills in javascript, make the difference` for FREE if you follow me on [Twitter](https://twitter.com/code__oz) and MP me 😁

Or get it [HERE](https://codeoz.gumroad.com/l/RXLYp)

🎁 [MY NEWSLETTER](https://www.getrevue.co/profile/code__oz)

☕️ You can [SUPPORT MY WORKS](https://www.buymeacoffee.com/CodeoZ) 🙏

🏃‍♂️ You can follow me on 👇

🕊 Twitter : [https://twitter.com/code__oz](https://twitter.com/code__oz)

👨‍💻 Github: https://github.com/Code-Oz

And you can mark 🔖 this article!