Webpack config for better monitoring

RelativeCI does not require a specific webpack configuration, but some options can make it easier to identify changes between builds. Some of the following webpack configuration options are also recommended for long-term caching.

Use a common pattern for filename content hashes

Adding the content hash suffix to file names helps browsers cache the assets and only request the asset from the server when the content changes. This method improves the experience for returning users who will only need to download the changed assets.

When comparing asset names between different builds, bundle-stats extracts the content hash and compares the assets by source file name. The most common supported patterns are:

file-HASH.js
file.HASH.js

To configure asset file names, you can set the following options for the production mode config:

// webpack.config.js
module.exports = {
  output: {
    // Configure entry file names
    filename: '[name].[contenthash].js',    // Configure chunk file names
    chunkFilename: '[name].[chunkhash].js',    // Configure asset file names
    assetModuleFilename: '[path][name].[contenthash][ext][query]'  },
  plugins: [
    // CSS file name pattern when using mini-css-extract-plugin
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css'    })
  ]
}
assets by path *.js 10.5 KiB
  asset vendors.f1eef34c98603711f45e.js 8.71 KiB [emitted] [immutable] [minimized] (name: vendors) (id hint: vendors)
  asset main.7c920d907e4c753d0935.js 1.79 KiB [emitted] [immutable] [minimized] (name: main)
asset images/logo.31b9235056a3dc35c4c5.png 6.4 KiB [emitted] [immutable] [from: images/logo.png] (auxiliary name: main)
asset index.html 364 bytes [emitted]
asset main.b231cc1c6e07b9d4ff06.css 122 bytes [emitted] [immutable] (name: main)
// webpack.config.js
module.exports = {
  output: {
    // Configure entry file names
    filename: '[name].[contenthash].js',    // Configure chunk file names
    chunkFilename: '[name].[chunkhash].js'  },
  module: {
    rules: [
      // file name pattern when using file-loader
      {
        test: /\.(png|jpe?g|gif)$/i,
        loader: 'file-loader',
        options: {
          name: '[path][name].[contenthash].[ext]'        }
      }
    ]
  },
  plugins: [
    // CSS file name pattern when using mini-css-extract-plugin
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css'    })
  ]
}
                                           Asset       Size  Chunks                         Chunk Names
images/logo.31b9235056a3dc35c4c5d9806ab46e4f.png    6.4 KiB          [emitted] [immutable]
                                      index.html  336 bytes          [emitted]
                   main.63b96e941d400fc1b806.css  122 bytes       0  [emitted] [immutable]  main
                    main.d7df904f48c9631839e8.js   1.75 KiB       0  [emitted] [immutable]  main
                 vendors.0e025fde233b62c30823.js   8.72 KiB       1  [emitted] [immutable]  vendors

You can adjust the hash length globally using output.hashDigestLength option (default 20). bundle-stats hash replacement supports a minimum of 5 characters.

Name asynchronous chunks

Asynchronous chunks defer code loading/execution, decrease the initial JS/CSS size and improve the loading performance. By default, webpack uses the chunk ID for the chunk file name. When the chunk ID changes, the chunk name will be different, forcing the users to re-download the asset even if there are no code changes.

To output predictable asset names for chunks, use webpack annotation for async chunks:

const HomePageComponent = React.lazy(
  () => import(/* webpackChunkName: 'home' */'./pages/home'));
const DetailsPage = Loadable({
  loader: () => import(/* webpackChunkName: 'details' */'./pages/details'),  loading: Loading
});

Use deterministic chunkIds (webpack 4)

By default, webpack 4 generates chunk IDs based on their order of appearance('natural' algorithm). When the order changes or when a chunk is added/removed, the chunk ids map may change across related chunks. The change is causing unnecessary cache invalidation and does not allow RelativeCI to compare the chunk modules between different builds.

To generate deterministic chunk ids between different builds, you can use optimization.chunkIds option:

// webpack.config.js
{
  optimization: {
    chunkIds: 'name'
  }
}

Webpack 5 uses default option chunkIds: 'deterministic' Read more