webpack 4: migration guide for plugins/loaders
This guide targets plugin and loader authors
this.options and this.rootContext
webpack 3 already deprecated
this.options in the loader context. webpack 4 removes it now.
A lot of people are missing the
this.options.context value now. It has been added as
Guideline: Loaders should receive all options via
this.query. They should not use other ways to receive options, i. e. no property in webpack options, no environment variable.
this.hot was added to the loader context. Loaders can use it to generate HMR specific code.
It’s now also possible to pass AST directly from loader to webpack to avoid double parsing.
webpack will happily accept any AST you pass. If it doesn’t match source code, bad things will happen.
New Plugin system
webpack 4 uses a new plugin system, which is only partially compatible with the old one.
See documentation here: https://github.com/webpack/tapable
plugin method on tapable objects is theoretically backward-compatible, but it causes a deprecation warning, so plugins should consider switching to
Each function attached to a hook must now have a name. This name is used by i. e. the ProgressPlugin to display the plugin in the progress or the ProfilingPlugin to name the profiled timings for function.
There are 3 variants of
tapPromise. Choose what fits best for you. Note that some hooks only allow the sync
There are additional steps needed when defining custom hooks: You need to declare the hook now by adding a property to the
enhanced-resolve major upgrade
enhanced-resolve was also upgraded to the new plugin system. This required a few interfaces changes too. The callback function no longer carries additional properties. Instead a resolverContext object is passed as additional argument.
Resolver plugins are backward-compatible, but should be migrated to the new plugin system.
Templates render manifest
Templates can now generate multiple assets. To do so a new method
getRenderManifest returns an array of asset configurations with render functions and filename template.
This can be modified via plugin hook. Plugins can use this to generate assets from chunks.
Chunk graph and ChunkGroups
A major change happened with the chunk graph. In webpack 3 and lower chunks were connected with parent-child-relationships and contain modules. A chunk with multiple parents could only rely on the assumption that at least one parent is already loaded at runtime.
These schema is insufficient for advanced optimization, especially when aggressively splitting chunks into smaller pieces.
The new chunk graph in webpack 4 has chunk groups connected via parent-child-relationships which contain chunks which contain modules. All chunks of a chunk group are loaded in parallel and can rely on the fact that at least one parent chunk group is already loaded at runtime.
Chunk groups do not affect the output. Only chunks and modules are known at runtime. They are only used for optimization.
import()) now points to a single chunk group, instead of to a list of chunks as in webpack ≤ 3.
In addition to that change some
forEachXXX helper methods in
Chunk were removed/deprecated in favor of using
Parser renames and definitions
The Parser used to track definitions and variable renames in scope via an Array and an Object used as map. To check existence in an Array
indexOf was used. When entering a child scope these info has to be carried over but when leaving a child scope it should not leak out of the scope.
webpack 3 uses clever tricks to avoid copying the data: For Arrays length is reseted back to original value when leaving the scope. For Objects a prototype chain is used via
Migrating this to ES15 and
Set was not trivial, so a new datastructure was introduced. It uses a multiple levels for
Maps to avoid copying data while keeping fast access. It has a Map/Set-like interface.
While applying plugins
Compiler.options is no longer accessible. Plugins should not access options. They should only depend on options passed to the plugin via constructor.
Compilation.notCacheable flag was removed
EcmaScript modules dependencies
Dependencies for ESM support has changed fundamentally.
Module.buildMeta.harmony was removed.
Module.buildMeta.exportsType was added.
exportsType defines how the exports of a module are handled by an import statement (and
exportsType = undefined handles them like CommonJS. The exports are exposed via
default import. In non-strict esm mode named imports are mapped to properties on the exports at runtime. A truthy
__esModule property maps the
default import to the
default property instead (non-strict esm mode).
exportsType = "namespace" handle them like ESM. Named imports are mapped to properties of the exports object. The
default import is mapped to the
exportsType = "named": Named imports are mapped to properties of the exports object. The
default import is mapped directly to the exports object. (This is used for JSON)
usedExports is an Array, properties are mangled.
Dependency.getReference() may now return a
Dependency.weak is now used by the
Dependency base class and returned in the base impl of
Dependency.isEqualResource has been replaced with
Dependency.getResourceIdentifier for performance reasons.
Constructor arguments changed for all
Modules. Most of them use an options object now. A
type argument was added to the
webpack now supports different Resolvers per Module. This means it’s no longer possible to change the “global” resolvers. Instead hooks can be used to modify resolvers when created.
Compiler.resolvers is no longer available. Instead
Compiler.resolverFactory has hooks to hook into resolver creation.
Template class used to be a base class of all Templates. It’s now a static class with helper methods.
A new RuntimeTemplate class has been added and
requestShortener has been moved to this class.
Many methods has been updated to use the RuntimeTemplate instead. Some generated code has been move into the RuntimeTemplate. We plan to add hooks to the RuntimeTemplate to modify the generated code, but this has not happened yet (Won’t be a breaking change).
Module.meta has been replaced with
buildMeta carry all information which is required to import a module correctly.
buildInfo carries internal information which is not required for importing, but may be required for generating the module’s source. The plan is to keep less properties directly on the
Module class. Most properties has been moved into
factoryMeta, which has also been added.
NormalModules now take a
Generator instance as constructor argument. This instance handles the Code Generation of the Module. It was separate to allow customizing Code Generation for custom module types.
createGenerator hooks has been added to the
NormalModuleFactory to customize it.
Maps and Sets
Compiler.contextTimestamps are now
Maps instead of
Objects used as map.