Upgrade to Webpack v5

Edit on GitHub

Upgrading Webpack from version 4.* to version 5.*

This document provides instructions for upgrading Webpack to version 5 in your Spryker project.

Overview

The Webpack v5 was released two years ago. To unblock future upgrades of other dependencies, we need to migrate as well.

Estimated migration time: 1h

1) Update dependencies

  1. In package.json, update the following dependencies to the new version:
{
    "clean-webpack-plugin": "~4.0.0",
    "compression-webpack-plugin": "~10.0.0",
    "copy-webpack-plugin": "~11.0.0",
    "mini-css-extract-plugin": "~2.7.2",
    "optimize-css-assets-webpack-plugin": "~6.0.1",
    "postcss": "~8.4.20",
    "terser-webpack-plugin": "~5.3.6",
    "webpack": "~5.74.0",
    "webpack-merge": "~5.8.0"
}
  • If Merchant Portal is used:

    {
        "@types/webpack": "~5.28.0"
    }
    
  1. Update and install package dependencies:
rm -rf node_modules
npm install
Verification

Ensure that the package-lock.json file and the node_modules folder have been updated.

2) Update configuration files

To update configuration files, make the following changes:

  1. Adjust frontend/configs/development-watch.js:
const merge = require('webpack-merge');
// must be
const { merge } = require('webpack-merge');
  1. In frontend/configs/development.js, do the following:

    • Rename the jsonpFunction property to the chunkLoadingGlobal of the output object.
    • Rename the vendors property to the defaultVendors of the cacheGroups object.
  2. In frontend/configs/production.js, make the following changes:

const merge = require('webpack-merge');
// must be
const { mergeWithCustomize, customizeObject } = require('webpack-merge');
const mergeWithStrategy = merge.smartStrategy({
    plugins: 'prepend',
});
// must be
const mergeWithStrategy = mergeWithCustomize({
    customizeObject: customizeObject({
        plugins: 'prepend',
    })
});
plugins: [
    new CompressionPlugin({
        filename: '[path].gz[query]',
    }),

    new BrotliPlugin({
        asset: '[path].br[query]',
        test: /\.js$|\.css$|\.svg$|\.html$/,
        threshold: 10240,
        minRatio: 0.8
    })
]
// must be
plugins: [
    new CompressionPlugin({
        filename: '[path][base].br[query]',
        algorithm: 'brotliCompress',
        test: /\.(js|css|html|svg)$/,
        threshold: 10240,
        minRatio: 0.8,
    }),
]

In the new TerserPlugin({ ... }) minimizer plugin, remove the cache property.

  1. In frontend/libs/assets-configurator.js, make the following changes:
const CleanWebpackPlugin = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
// must be
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const getCopyConfig = (appSettings) =>
    Object.values(appSettings.paths.assets).reduce((copyConfig, assetsPath) => {
        if (fs.existsSync(assetsPath)) {
            copyConfig.push({
                from: assetsPath,
                to: '.',
                ignore: ['*.gitkeep'],
            });
        }

        return copyConfig;
    }, []);
// must be
const getCopyConfig = (appSettings) =>
    Object.values(appSettings.paths.assets).reduce((copyConfig, assetsPath) => {
        if (fs.existsSync(assetsPath)) {
            copyConfig.push({
                from: assetsPath,
                to: '.',
                context: appSettings.context,
                globOptions: {
                    dot: true,
                    ignore: ['**/.gitkeep'],
                },
                noErrorOnMissing: true,
            });
        }

        return copyConfig;
    }, []);
const getCopyStaticConfig = (appSettings) => {
    const staticAssetsPath = appSettings.paths.assets.staticAssets;

    if (fs.existsSync(staticAssetsPath)) {
        return [
            {
                from: staticAssetsPath,
                to: appSettings.paths.publicStatic,
            },
        ];
    }

    return [];
};
// must be
const getCopyStaticConfig = (appSettings) => {
    const staticAssetsPath = appSettings.paths.assets.staticAssets;

    if (fs.existsSync(staticAssetsPath)) {
        return [
            {
                from: staticAssetsPath,
                to: appSettings.paths.publicStatic,
                context: appSettings.context,
            },
        ];
    }

    return [];
};
const getAssetsConfig = (appSettings) => [
    new CleanWebpackPlugin(
        [
            appSettings.paths.public,
            appSettings.paths.publicStatic,
        ],
        {
            root: appSettings.context,
            verbose: true,
            beforeEmit: true,
        }
    ),

    new CopyWebpackPlugin(getCopyConfig(appSettings), {
        context: appSettings.context,
    }),

    new CopyWebpackPlugin(getCopyStaticConfig(appSettings), {
        context: appSettings.context,
    }),
];
// must be
const getAssetsConfig = (appSettings) => [
    new CleanWebpackPlugin({
        cleanOnceBeforeBuildPatterns: [
            appSettings.paths.public,
            appSettings.paths.publicStatic,
        ],
        verbose: true,
    }),

    new CopyPlugin({
        patterns: getCopyConfig(appSettings),
    }),

    new CopyPlugin({
        patterns: getCopyStaticConfig(appSettings),
    }),
];

3) Build assets

Check correct builds for all layers Yves, Zed, and MP (if Merchant Portal is used), including watch and production modes:

  1. Yves:
npm run yves
npm run yves:production
npm run yves:watch
  1. Zed:
npm run zed
npm run zed:production
npm run zed:watch
  1. MP (If Merchant Portal is used):
npm run mp:build
npm run mp:build:production
npm run mp:build:watch
Note

For more information, see the official Webpack Migration guide.