Write plug and play configs instead of writing everything in one file.
If you’re someone who is using webpack in your project then the next few minutes will make you go back and revisit the way your configuration has been written.
Like how we all love modular coding, there is a way to write the configuration modularly. Instead of clubbing plugins, loaders, and other configurations in the famous “webpack.config.babel.js” file, we can divide them into pieces and add them only when it is required.
There could be hundreds of reasons why we need to do it, but the reason why I implemented it in our organization is simple.
We have multiple commands to build Handlebars, js files, and CSS files so that when we have changes in one file the compiler will not have to build all the files again.
Writing different configuration files for the different commands will only increase the maintainability of the project instead of solving the problem.
I am no inventor of the concept, I did learn this from Frontend masters and decided to write this post to help those who won’t have access to such content.
The concept is very simple, We divide the configurations into need basis and will merge it (using Webpack-merge package) based on the presets passed in the command.
Let me give a simple example by adding three files here.
webpack.config.babel.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | import path from 'path'; import webpack from 'webpack'; import webpackMerge from 'webpack-merge'; import applyPresets from './presetLoader'; module.exports = ({ mode='production', presets='' }) => webpackMerge({ mode : mode, bail: true, resolve: { extensions: [".js", ".jsx"], modules: [ path.resolve(__dirname), "stylesheets" ] }, plugins : [ new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/) ], module: { rules: [{ test: /\.js$/, exclude: /node_modules/, use: { loader: "babel-loader", options: { babelrc: false, cacheDirectory: false, plugins: ["transform-object-assign"], presets: [['env', { modules: false }], 'stage-3'] } } }] } }, applyPresets({ mode , presets, environment }) ); |
presetLoader.js
1 2 3 4 5 6 7 8 9 10 11 12 13 | import webpackMerge from 'webpack-merge'; const applyPresets = props => { let { presets } = props; presets = presets.split(',') const mergePresets = [].concat(...[presets]); const mergedConfigs = mergePresets.map( presentName => require(`./presets/webpack.${presentName}`)(props) ); return webpackMerge({},...mergedConfigs); } export default applyPresets; |
Handlebar config
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | import path from 'path'; import webpack from 'webpack'; module.exports = () => { return { module: { rules: [ { test: /\.hbs$/, loader: "handlebars-loader", options: { helperDirs: path.resolve(__dirname, '../../../templates/helpers/'), precompileOptions: { knownHelpersOnly: false, }, } } ] }, plugins: [ new webpack.LoaderOptionsPlugin({ options: { handlebarsLoader: { rootRelative : '../../templates/' } } }) ], resolve: { extensions: [".hbs"] } } } |
Finally, If you’re wondering how we pass these preset names in command.
1 | webpack --env.mode=development --env.presets handlebar,anotherconfig1,anotherconfig2 |
The presetLoaders.js is the file that does the core part of the concept here, As you can see it is reading the files and merging them into one configuration using WebackMerge.
This concept is not only used in dividing the existing configuration, But It also helps when you want to try out a new configuration or adding support for different types of files in your project. You can simply write a separate config file and plug it into the command without affecting the existing functionality.
Happy coding…….!