Extra, extra! webpack 5 is coming…
Tobias Koppers mentioned in his talk at JSNation Amsterdam this past week that webpack developers tend to dislike breaking changes.
So, why now?
webpack 4 shipped a whopping 33 minor and 43 patch releases.
Technical debt
With so many ‘hacks’ and ‘todos’ mounting in the codebase whilst trying to avoid a major release for as long as possible to keep the community happy, technical debt can start to get out of hand and is bad for the project in the long run.
User demand
Right now, on the webpack voting page the two most requested webpack features are as follows:
Persistent Cache
What’s the issue?
Incremental builds in watch mode are fast, but restarting webpack can be super slow.
Persistent cache promises to:
- improve build start up performance.
- cache on disk (or other persistent mediums)
Consistent Hashing
What’s the issue?
- People have difficulties using hashing for long term caching.
- webpack changes file content more often than expected
Consistent hashing promises to:
- Make hashing easier and more reliable.
Both of these features have been added to webpack v5!
npm install —save-dev webpack@next
What’s going to break?
Say goodbye to deprecated features from v4
All features that were deprecated in v4 of webpack will be removed from v5. So when you start migrating to v5 make sure that you don’t have any deprecation warnings.
The CommonsChunkPlugin was removed.
module.loaders were deprecated since webpack 2 and were removed in favour of module.rules.
The IgnorePlugin and BannerPlugin have also been removed and must be passed as an options object.
Automatic Node.js polyfills are history
Using polyfills makes it easy to use modules written for Node.js, however this also increases the bundle size as core module polyfills are often very large.
Developers are often unaware of the fact that they are using node core module polyfills as it is too easy to accidentally include them.
Polyfills are often outdated and the native web platform doesn’t support them. Polyfills differ from node core modules due to these browser limitations.
To prevent this, webpack 5 removes this automatic polyfilling and focuses on frontend focused modules.
Users will now have better control of the versions of polyfills used and the install size of webpack will decrease.
Migration:
Use frontend compatible modules whenever possible.
Users can still opt-in to use polyfills by installing them manually and setting an alias in their config files.
Modernization
webpack 5 sees a bump in the minimum node.js version from version 6 to version 8.
You should update your node.js version to the latest version.
In addition to this, we will also avail of new data structures like Sets for Array like objects and Maps for objects.
Compilation.modules
and Compilation.chunks
are now Sets
and as such, you should use Set methods instead of Array methods.
Deterministic by default
New algorithms were added for long term caching. These are enabled by default in production mode.
When migrating from webpack 4 to webpack 5, you should run tests on your bundle sizes and calculate the difference to see if long term caching is relevant for you. It most likely is.
However, if your bundle size is of a higher priority to you than long term caching you might want to opt in for size optimization.
It’s worth noting that this is only a breaking change in production environments/build configurations.
New algorithms were also added to mangle export names. It is enabled by default and will mangle export names in a deterministic way when possible.
Migration:
It is recommended to use the default values for chunkIds and moduleIds.
chunkIds: “deterministic”,
moduleIds: “deterministic”
You can also opt-in to the old defaults:
chunkIds: “size”,
moduleIds: “size”
The old defaults will generate smaller bundles, but it will invalidate them more often for caching.
There are no steps necessary for migrating to more deterministic mangle export names.
Async caching
The caching interface is synchronous and very limited because it was created for a single use case: in-memory caching.
In webpack 5, the interface has changed to be async and extensive via plugins.
Changes to the way the caching interface will affect plugins authors and therefore some of the plugins you use may break as a result of the changes to the interface.
Cost of caching
Persistent caching is more expensive due to serialisation and disk allocation. Persistent caching should be run when the compiler is idle or closing.
The issue: the public compiler API has no notation about closing or disposing of the persistent cache.
The solution: a close method has been added that you should call.
New filenames by default
A new named chunk ID algorithm enabled by default in development mode gives chunks and filenames human-readable names.
— A module ID is determined by its path, relative to its context.
— A chunk ID is determined by the chunk’s content.
For example, instead of getting useless bundle names like:
1.bundle.js, 2.bundle.js
The new algorithm will generate more useful names like:
import(‘./lazy-loaded’) would generate a bundle with a name like:
src_lazy-loaded_js.bundle.js
A split chunk for react-dom would generate a bundle with a name like:
react_react-dom.bundle.js
In most cases, there is nothing to do. If you wish, you can omit:
webpackChunkName: “name”
It’s worth noting that this is only a breaking change in development environments/build configurations.
It’s possible to use chunkIds: "named"
in production. However, be cautious and make sure you do not accidentically expose sensitive information about module names.
Migration:
You no longer need to use import(/* webpackChunkName: "name" */ "module")
for debugging. But it would still make sense, if you want to control the filenames for production environments.
If you don’t want your filenames to be changed whilst in development, you can set the following value, in order to use the old numberic mode:
chunkIds: “natural”
Serialization
What’s the issue?
In JavaScript it is easy to handle serialization of simple objects. Module objects have not been serializable in webpack due subclasses, non primitive nested objects like Dependencies, Sources and Source Maps.
Now what?
A serialization mechanism has been added to allow for serialization of complex objects. Classes that should be serialized need to be explicitly flagged.
Migration:
When using custom modules or dependencies, it is recommended to make them serializable to benefit from persistent caching.
Changes to SplitChunks and Module Sizes values
Modules now express size differently. Rather than displaying a single number, there are different measures of size.
The SplitChunksPlugin now knows how to handle these different values and uses them for minSize and maxSize.
By default, only JavaScript is handled. But you can now pass mutiple values to manage size limits:
minSize: {
javascript: 30000,
style: 50000,
}
Migration:
Check which types of sizes are used in your builds and configure the values in the splitChunks.minSize
and optionally in splitChunks.maxSize
Hot Module Replacement
Hot module replacement will now be extensible via plugins in webpack v5.
Previously, hot module replacement had only been available for JavaScript modules but it will now support style modules for CSS.
Tobias has said that in a future minor release, they could possibly support WASM and html hot module replacement too.
Changes to default values
optimization.moduleIds
Defaults to deterministic
in production mode, instead of size
optimization.moduleIds
Defaults to deterministic
in production mode, instead of size
optimization.chunkIds
Defaults to deterministic
in production mode, instead of total-size
optimization.nodeEnv
Defaults to false
in none
mode
optimization.splitChunks
minRemainingSize
Defaults to minSize
(since alpha.13). This will lead to less split chunks created in cases where the remaining part would be too small
resolve(Loader).cache
Defaults to true
when cache
is used
resolve(Loader).cacheWithContext
Defaults to false
node.global
Defaults to false
(since alpha.4 removed)
resolveLoader.extensions
remove .json
(since alpha.8)
node.global
node.__filename
and node.__dirname
Defaults to false
in node-target
s (since alpha.14)
NOTE
I put this article together by assembling all of the valuable information found in the resources mentioned below.
I was inspired to do so after having seen Tobias’ talk at Amsterdam JSNation last week. My hope is to help inspire other developers to be excited and prepared for the new changes and features coming to webpack v5.
If you use webpack, please consider making a donation here: https://opencollective.com/webpack