webpack-config-assign
What it is
A utility to merge separate webpack configs (e.g. webpack.config.prod.js
, webpack.config.development.js
, etc.) in a smarter way than a simple merge or assign.
What it does
It looks at each configuration key passed in (e.g. plugins
, module
, entry
, etc.) and if it can merge it intelligently, the utility attempts to do so. For instance, consider the following scenarios:
- A development config that inlines CSS into the bundle
- A production config that extracts the CSS out into a separate plugin
- Uglification plugin(s) and other production enhancements only applied in production
This plugin is able to intelligently merge each of the above, in essence, in concatenates plugins, and intelligently merges loaders when the test
matches.
How to use
The utility can be seamlessly integrated into an existing workflow, particularly one that has already been segmented out into environment configs. However, if using a monolithic webpack.config.js
with a bunch of environment checks, then some work will need to be done to separate these out into their respective environments/phases.
Consider the following "base" config (configuration that will be shared between dev, test, and/or production). I like to name it webpack.config.base.js
, but it can just as easily be inlined into webpack.config.js
:
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const pkg = require(path.resolve('./package.json'));
module.exports = {
devtool: 'source-map',
entry: {
[pkg.name]: [
'./src/index'
],
vendor: Object.keys(pkg.dependencies)
.filter((dep) => {
return ['normalize.css'].indexOf(dep) === -1;
})
},
output: {
filename: '[name].js',
path: path.join(__dirname, './dist/')
},
module: {
rules: [
{
test: /\.js(x)?$/,
include: [path.join(__dirname, 'src')],
use: [
'react-hot-loader/webpack',
'babel-loader'
]
},
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: true
}
},
'postcss-loader'
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
filename: path.join(__dirname, 'dist', 'index.html'),
inject: 'body',
template: path.join(__dirname, 'src', 'index.html')
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: Infinity
}),
new webpack.LoaderOptionsPlugin({
options: {
context: __dirname,
postcss: [
require('postcss-nested'),
require('postcss-cssnext')({
browsers: ['ie >= 9', 'last 2 versions']
})
]
}
}),
new webpack.NoErrorsPlugin(),
new webpack.NamedModulesPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
}
})
]
};
the above is a fairly typical config, and this could be dropped into webpack.config.js
quite easily. However, what if we want super speedy reload times, webpack-dev-server, and so on and so forth in development. Well, let's create a webpack.config.development.js
, like so:
const DashboardPlugin = require('webpack-dashboard/plugin');
module.exports = {
devtool: 'eval',
entry: {
[pkg.name]: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'react-hot-loader/patch',
'./src/index'
]
},
plugins: [
new DashboardPlugin()
]
};
now we're cooking. We are using the excellent webpack-dashboard
plugin, and we're also doing some nice webpack hot reloading with the webpack dev server. Now, let's get to merging those configurations
const webpackConfigAssign = require('webpack-config-assign');
const devConfig = process.env.NODE_ENV === 'development' ? require('./webpack.config.development') : {};
module.exports = webpackConfigAssign(require('./webpack.config.base'), devConfig);
and like that, we're done. Easy as pie! Let's wrap up what specifically happened:
- Our entry for development will use the webpack-dev-server
- Because the keys match, the
entry[pkg.name]
will be overwritten by the development config, rather than merged
- Because the keys match, the
- Our plugins for development are merged/concatenated with our base config
- We have super speedy hot reloads and instant feedback, and an ultra slick dashboard, with little effort and manual intervention