Home > Software design >  How to organize Webpack 5 files for different modes with same plugins
How to organize Webpack 5 files for different modes with same plugins

Time:12-25

Some context:

I have been told by coworkers that the new version of Webpack handles the configuration in different files: webpack.development.config.js, webpack.production.config.js... (and you can even create custom modes). But they didn't know how the explain the whole thing. Only that now everything is done with multiple config files.

From the official docs, I see that the different modes have a comment on the top stating different file names:

Development

// webpack.development.config.js
module.exports = {
  mode: 'development',
};

Production

// webpack.production.config.js
module.exports = {
  mode: 'production',
};

So there must be some truth in this and I want to follow best practices. It seems logic that keeping config for different modes separate is a good thing to do.

But I don't know how I should correctly load plugins now.

I used to have this at the top of my files:

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const WatchTimePlugin = require('webpack-watch-time-plugin');
const cssnano = require('cssnano');
const autoprefixer = require('autoprefixer');
const webpack = require('webpack');

So, this ends up leaving me with the following questions.

Questions

Do I still need a global config file for Webpack? If there isn't any global config file, how can I have common tasks that I want to run both for production and development?

Does webpack load all files called webpack.xxxxxx.config.js? Can I just create whatever config files I like with that name and it will take them all into consideration?

How can I achieve common config for dev and prod, and then special extended config for dev and prod?

I find that the documentation isn't really clear about all this. So any help will be appreciated.

CodePudding user response:

It really depends on the plugin, for example, HtmlWebpackPlugin injects scripts and styles into html templates, thats something you need in both development and production builds.
MinicssExtractPlugin on the other hand extracts CSS into seperate files, something you may not need in development.
I suggest that you get a pen and paper, visit each plugin's website and decide if the plugin:

  • Is (or can be) used both in development and production
  • Is used only in development (react-refresh-webpack-plugin for example)
  • Is used only in production (I put MiniCssExtractPlugin here)

Then in the config:

// webpack.shared.config.js
module.exports = {
    plugins: [
        // Shared plugins
    ]
};

// webpack.development.config.js
module.exports = {
    plugins: [
        // Dev only plugins
    ]
};

// webpack.production.config.js
module.exports = {
    plugins: [
        // Prod only plugins
    ]
};

A good way of organizing plugins is:

  1. Does it improve the user experience (loading time, usage)?
  2. Is it necessary for the app to work?
  3. Does it help in development

If your answers are:

  • yes, no, no: production plugin
  • no, yes, no: production plugin
  • no, no, yes: development plugin
  • yes, no, yes: shared plugin
  • yes, yes, no: production plugin
  • yes, yes, yes: shared plugin

Does webpack load all files called webpack.xxxxxx.config.js? Can I just create whatever config files I like with that name and it will take them all into consideration?

Webpack automatically (if the config is not specified in the command line) loads only one file named webpack.config.js. If it doesn't exist then it uses a default config. You should always specify the config file used:

npx webpack --config config/webpack.config.js

I suggest you do the following to separate development and production configuration

npm install webpack-merge --save-dev
# or if you're using yarn
yarn add webpack-merge --dev

Directory structure:

src
|_ Some files
|_ Other files
package.json
config
|_webpack.shared-config.js
|_webpack.dev-config.js
|_webpack.prod-config.js
// config/webpack.shared.config.js
module.exports = {
    // Configuration to be used in both modes, `entry` goes here
};

// config/webpack.dev-config.js
let {merge} = require("webpack-merge");

module.exports = merge(require("./webpack.shared-config.js"), {
    mode: "development",
    // Other dev only config
});

// config/webpack.prod-config.js
let {merge} = require("webpack-merge");

module.exports = merge(require("./webpack.shared-config.js"), {
    mode: "production",
    // Other production only config
});

Then you can specify the config file in the webpack cli:

npx webpack --config config/webpack.dev-config.js
# or, in production
npx webpack --config config/webpack.prod-config.js

Additionally, you can add the build (and serve) scripts to package.json:

{
    // ...
    "scripts": {
        "build": "webpack --config config/webpack.prod-config.js",
        "build-dev": "webpack --config config/webpack.dev-config.js",
        "serve": "webpack serve --hmr --config config/webpack.dev-config.js",
        "serve-prod": "webpack serve --config config/webpack.prod-config.js",
    }
    // ...
}

Notes:

  1. webpack serve requires webpack-dev-server to be installed:
npm install webpack-dev-server --save-dev
# or
yarn add webpack-dev-server --dev
  1. webpack-merge does a deep merge:
let a = {
    b: {
        c: [1]
    }
};

console.log(
    merge(a, {
        b: {
            c: [2]
        }
    })
);
// Outputs:
// {
//   b: {
//     c: [1, 2]
//   }
// }
  1. Merged object doesn't necessarily need to be a webpack config, it can be anything.

Also see:

  • Related