Skip to content

Instantly share code, notes, and snippets.

@mikahimself
Last active April 9, 2021 07:46
Show Gist options
  • Save mikahimself/53596b820ceca3c0756b8baa5f3bab90 to your computer and use it in GitHub Desktop.
Save mikahimself/53596b820ceca3c0756b8baa5f3bab90 to your computer and use it in GitHub Desktop.
Example of creating a composed webpack configuration using Webpack 5

Composing Webpack configurations

Handling multiple Webpack configurations for development, production, and maybe for reviews can be a chore, especially if the configuration files are made up of mostly shared content, and changes in one place have to be copied into the other configuration files.

One way to make life simpler is to compose the configuration file to be used from a base configuration file and a specialized configuration file, and then merge them together using webpack-merge.

The idea behind this is explored in much more detail in Sean Larkin's Webpack 4 Fundamentals course at FrontendMasters.com.

Base configuration file

To start off, you'll need a simple base configuration:

const HtmlWebpackPlugin = require("html-webpack-plugin");
const { merge } = require("webpack-merge");
const getConfig = (env) => require(`./build-utils/webpack.${env}.js`)(env);

module.exports = (env) => {
    return merge(
    {
        mode: env.mode,
        module:{
            rules: [
                {
                    test: /\.jsx?$/,
                    exclude: /node_modules/,
                    use: "babel-loader"
            
                }
            ]
        },
        plugins: [
            new HtmlWebpackPlugin({
                template: "src/index.html"
            })
        ]
    }, getConfig(env.mode))
}

There are a few key things to notice here. First, we define a getConfig() function that we use to require the correct, specialized configuration file.

const getConfig = (env) => require(`./build-utils/webpack.${env}.js`)(env);

Second, we are exporting a function from module.exports instead of an object. This allows us to take in an argument through package.json and use it to set the mode option, and to pass it as an argument to getConfig(). The getConfig() function uses the argument to pick the correct, specialized configuration file that will be merged with the base configuration:

module.exports = (env) => {
    return merge(
    {
        mode: env.mode,
        // ...
        // ...
    }, getConfig(env.mode))
}

Specialized configuration files

Next, we can create simple, specialized configuration files for development and production.

Development configuration

The development configuration here is simple. All it does is define that we first use css-loader to pick up and resolve css files that are imported - at least in most of my use cases - using the @import statement. css-loader then passes these files to style-loader that injects them into the DOM. Note that filename must follow the pattern defined earlier in getConfig().

// webpack.development.js

module.exports = (env) => ({
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            }
        ]
    }
})

Production configuration

The production configuration is also fairly simple. For production, we are cleaning the output folder before each build, and we use a filename that contains a has for the bundled javascript file. The css will be handled differently as well. Instead of injecting the css into the DOM, we use the mini-css-extract-plugin to extract the css file and saving it into the output folder.

// webpack.production.js

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = (env) => ({
    output: {
        clean: true,
        filename: "[name]_[contenthash].js"
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, "css-loader"]
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            chunkFilename: "[name].css"
        }),
    ]
})

Adding scripts in package.json

Last, we need to add scripts to package.json that pass the variable(s) we need into our webpack configurations. Here is a simple example that uses webpack-dev-server for the development case and runs a production build:

"scripts": {
    "dev": "webpack serve --env mode=development",
    "prod": "webpack --env mode=production",
},
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment