Introduction
Migrating from Webpack 3 to Webpack 4 is a very cumbersome process. I went through many articles and blogs to find a nice, easy and quick way of doing it. However, I could not find one single article that explains everything and lists out the pain points of migration. So, I decided to write something to help other fellow developers who are considering the upgrade in the near future.
Background
The first question is why do we need to upgrade to webpack 4. I am sure there are tons of articles over the internet to explain the benefits. So, I am skipping this part.
I'll explain upgrade of react/angular/other js bundles, less/Scss bundles. I am sure that once you get a handle on these assets, fonts/images are going to be a cakewalk.
Using the Code
Few things to consider before starting the migration are listed below:
- Update the
npm
version. It should be greater than 8.9.0
. Run command npm -v
to check the current version and to upgrade on Windows machine - just download node msi from here. For mac/linux, visit nodejs site and follow upgrade steps from there. - Start with a new webpack.config.js file. It's always good to start from scratch and gradually do the migration with one asset type at a time. Rename the existing webpack.config.js file to webpack_old.config.js.
- Always try to use
webpack-bundle-analyzer
during migration to visualize which bundle file contains what packages. It helps in deciding which bundle to group in what chunk, therefore helps in optimising the chunks. - Webpack 4 does not use
extract-text-css-plugin
, common-chunks-plugin
, uglify-js-plugin
(this works with earlier versions of webpack 4, not with the latest), so just get rid of these plugins. The alternate to these are mini-css-extract-plugin, splitChunksplugin
(inbuilt in webpack 4) and terser-webpack-plugin
consecutively. - For the
sass/scss
transformation, you will need to add a postcss.config.js file inside your root folder. I'll attach this file for reference:
module.exports = {
plugins: [
require('autoprefixer')
]
}
- Try to start with modifying the package.json file first, then update all the required migration packages.
- I got some error related to
cosmiconfig
. So, I had to include this package inside package.json file.
The package json should have minimum of these packages to run the webpack 4 for js/jsx/css/less/scss files.
Package.json
"autoprefixer": "^9.4.7",
"babel-core": "^6.26.3",
"babel-loader": "7.1.2",
"babel-polyfill": "6.26.0",
"babel-preset-env": "1.6.1",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "6.24.1",
"babel-preset-stage-3": "6.24.1",
"clean-webpack-plugin": "^0.1.16",
"cosmiconfig": "4.0.0",
"less": "^3.9.0",
"less-loader": "4.1.0",
"mini-css-extract-plugin": "0.5.0",
"node-sass": "4.11.0",
"npm": "^5.8.0",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"postcss-loader": "3.0.0",
"sass-loader": "7.1.0",
"style-loader": "0.23.1",
"suppress-chunks-webpack-plugin": "0.0.4",
"terser-webpack-plugin": "1.1.0",
"toastr": "^2.1.4",
"webpack": "4.29.0",
"webpack-bundle-analyzer": "^3.0.4",
"webpack-cli": "3.2.3",
Webpack.config.js
Define the const
at the top in webpack.config.js file:
'use strict';
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const SuppressChunksPlugin = require('suppress-chunks-webpack-plugin').default;
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssPlugin = new MiniCssExtractPlugin({
filename: 'assets/css/[name].css'
});
const CleanPLugin = new CleanWebpackPlugin(
['./dist/js', './dist/css'], {
verbose: false,
dry: false
});
Start with webpack module export and then define the config
entries inside as follows:
module.exports = (env) => {
const isDev = env === 'development';
const jsIdentifier = './assets/js/[name].js';
const plugins = isDev ? [CleanPLugin, MiniCssPlugin,
new BundleAnalyzerPlugin()] : [CleanPLugin, MiniCssPlugin];
const config = {};
config.mode = env;
config.watch = isDev;
config.resolve = {
extensions: ['.js', '.jsx']
};
config.devtool = 'source-map';
config.entry =
{
'angularapp.min': './angularapp/app.js',
sassStyle: './assets/scss/main.scss',
lessStyle: './assets/less/main.less',
'reactapp.min': './reactapp/index.js'
};
config.output = {
path: __dirname,
filename: jsIdentifier,
chunkFilename: jsIdentifier
};
config.optimization = {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor.min',
chunks: 'initial'
}
}
},
minimizer: [
new TerserPlugin({
sourceMap: isDev,
cache: true,
parallel: true,
terserOptions: {
mangle: false,
keep_classnames: true,
keep_fnames: true,
output: {
comments: false
}
}
}),
new OptimizeCSSAssetsPlugin({})
]
};
config.plugins = plugins;config.module = {
rules: [
{
test: /\.(js|jsx)$/, exclude: /node_modules/,
use: {
loader: 'babel-loader',
query: {
presets: ['es2015', 'react', 'stage-3']
}
}
},
{
test: /\.scss$/,
use: [MiniCssExtractPlugin.loader,
{
loader: "css-loader", options: {
sourceMap: true
}
},
'postcss-loader',
{
loader: "sass-loader", options: {
sourceMap: true
}
}]
},
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader,
'css-loader',
'less-loader']
}
]
};
return config;
};
There are two ways in which you can define the production/development config
s in webpack:
- One way is using the
--mode production
(in package json command) - The other, that I have used
--env production
flag (refer to the attached package.json file build
scripts command)
There is no right or wrong approach. It totally depends upon how you want to define the configuration inside webpack.config.js file.
And that is all you have to change to migrate to webpack 4. I have attached the package.json and webpack.config.js file for reference.
Points of Interest
You can learn more about Webpack 4 configuration here.
History
- 25th February, 2019: Initial version