In plugins prior to Webpack 5, we often used Compiler.links.emit to do some processing on resources (such as deleting comments, compressing files, etc.). Using this hook in Webpack 5 will result in the following warning:
(node:3881) [DEP_WEBPACK_COMPILATION_ASSETS] DeprecationWarning: Compilation.assets will be frozen in future, all modifications are deprecated. BREAKING CHANGE: No more changes should happen to Compilation.assets after sealing the Compilation. Do changes to assets earlier, e. g. in Compilation.hooks.processAssets. Make sure to select an appropriate stage from Compilation.PROCESS_ASSETS_STAGE_*. (Use `node --trace-deprecation ... ` to show where the warning was created)Copy the code
We need to use the Compilation processAssets hook to reprocess the resources. This hook has the following parameters:
name
: Name of the plug-in.stage
:hook
Order of execution.additionalAssets
: Callback when adding resources.
There have been doubts about the use of stage and additionalAssets parameters (the official explanation is also ambiguous). Today, I took time to study them and recorded relevant conclusions here for your reference.
stage
Webpack provides 15 stages such as PROCESS_ASSETS_STAGE_ADDITIONAL, PROCESS_ASSETS_STAGE_PRE_PROCESS, This parameter is used to control the order in which processAssets Hooks are executed, as in the following example:
//plugins.js
const webpack = require('webpack');
class OnePlugin {
apply(compiler) {
compiler.hooks.compilation.tap('OnePlugin'.(compilation) = > {
compilation.hooks.processAssets.tapAsync({
name: 'OnePlugin'.stage: webpack.Compilation.PROCESS_ASSETS_STAGE_DERIVED,
}, (_, callback) = > {
console.log('OnePlugin PROCESS_ASSETS_STAGE_DERIVED');
callback();
});
compilation.hooks.processAssets.tapAsync({
name: 'OnePlugin'.stage: webpack.Compilation.PROCESS_ASSETS_STAGE_PRE_PROCESS,
}, (_, callback) = > {
console.log('OnePlugin PROCESS_ASSETS_STAGE_PRE_PROCESS'); callback(); }); }); }}class TwoPlugin {
apply(compiler) {
compiler.hooks.compilation.tap('TwoPlugin'.(compilation) = > {
compilation.hooks.processAssets.tapAsync({
name: 'TwoPlugin'.stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
}, (_, callback) = > {
console.log('TwoPlugin PROCESS_ASSETS_STAGE_ADDITIONAL'); callback(); }); }); }}module.exports = {
OnePlugin,
TwoPlugin,
};
//webpack.config.js
const path = require('path');
const { OnePlugin, TwoPlugin } = require('./plugins');
module.exports = {
entry: './src/index.js'.mode: process.env.NODE_ENV ?? 'development'.output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
plugins: [
new OnePlugin(),
new TwoPlugin(),
]
};
Copy the code
Execute NPX webpack command:
TwoPlugin PROCESS_ASSETS_STAGE_ADDITIONAL
OnePlugin PROCESS_ASSETS_STAGE_PRE_PROCESS
OnePlugin PROCESS_ASSETS_STAGE_DERIVED
Copy the code
From the above output, we can see that when Webpack triggers processAssets hook, it will execute the corresponding callback according to the execution order priority of the specified stages, ignoring the registration order. If the stages specified by two callbacks are the same, the one registered first will execute first, as in the following example:
//plugins.js
const webpack = require('webpack');
class OnePlugin {
apply(compiler) {
compiler.hooks.compilation.tap('OnePlugin'.(compilation) = > {
compilation.hooks.processAssets.tapAsync({
name: 'OnePlugin'.stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
}, (_, callback) = > {
console.log('OnePlugin PROCESS_ASSETS_STAGE_ADDITIONAL'); callback(); }); }); }}class TwoPlugin {
apply(compiler) {
compiler.hooks.compilation.tap('TwoPlugin'.(compilation) = > {
compilation.hooks.processAssets.tapAsync({
name: 'TwoPlugin'.stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
}, (_, callback) = > {
console.log('TwoPlugin PROCESS_ASSETS_STAGE_ADDITIONAL'); callback(); }); }); }}module.exports = {
OnePlugin,
TwoPlugin,
};
//webpack.config.js
const path = require('path');
const { OnePlugin, TwoPlugin } = require('./plugins');
module.exports = {
entry: './src/index.js'.mode: process.env.NODE_ENV ?? 'development'.output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
plugins: [
new OnePlugin(),
new TwoPlugin(),
]
};
Copy the code
Execute NPX webpack command:
OnePlugin PROCESS_ASSETS_STAGE_ADDITIONAL
TwoPlugin PROCESS_ASSETS_STAGE_ADDITIONAL
Copy the code
According to the output, when the same stage is specified, the first one registered is executed first, and the last one registered is executed later.
additionalAssets
Take a look at the following example:
//plugins.js
const webpack = require('webpack');
class OnePlugin {
apply(compiler) {
compiler.hooks.compilation.tap('OnePlugin'.(compilation) = > {
compilation.hooks.processAssets.tapAsync({
name: 'OnePlugin'.stage: webpack.Compilation.PROCESS_ASSETS_STAGE_PRE_PROCESS,
additionalAssets: true,},(_, callback) = > {
console.log('OnePlugin PROCESS_ASSETS_STAGE_PRE_PROCESS');
callback();
});
compilation.hooks.processAssets.tapAsync({
name: 'OnePlugin'.stage: webpack.Compilation.PROCESS_ASSETS_STAGE_DERIVED,
additionalAssets: true,},(_, callback) = > {
console.log('OnePlugin PROCESS_ASSETS_STAGE_DERIVED');
callback();
});
compilation.hooks.processAssets.tapAsync({
name: 'OnePlugin'.stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
additionalAssets: (_, callback) = > {
console.log('OnePlugin PROCESS_ASSETS_STAGE_ADDITIONAL additionalAssets callback') callback(); }},(_, callback) = > {
console.log('OnePlugin PROCESS_ASSETS_STAGE_ADDITIONAL');
callback();
});
compilation.hooks.processAssets.tapAsync({
name: 'OnePlugin'.stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS ,
additionalAssets: true,},(_, callback) = > {
console.log('OnePlugin PROCESS_ASSETS_STAGE_ADDITIONS'); callback(); }); }); }}class TwoPlugin {
apply(compiler) {
compiler.hooks.compilation.tap('TwoPlugin'.(compilation) = > {
compilation.hooks.processAssets.tapAsync({
name: 'TwoPlugin'.stage: webpack.Compilation.PROCESS_ASSETS_STAGE_DERIVED,
}, (_, callback) = > {
console.log('TwoPlugin PROCESS_ASSETS_STAGE_DERIVED');
compilation.emitAsset('hello.txt'.new webpack.sources.RawSource('Hello World')); callback(); }); }); }}module.exports = {
OnePlugin,
TwoPlugin,
};
//webpack.config.js
const path = require('path');
const { OnePlugin, TwoPlugin } = require('./plugins');
module.exports = {
entry: './src/index.js'.mode: process.env.NODE_ENV ?? 'development'.output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
plugins: [
new OnePlugin(),
new TwoPlugin(),
]
};
Copy the code
Execute NPX webpack command:
OnePlugin PROCESS_ASSETS_STAGE_ADDITIONAL OnePlugin PROCESS_ASSETS_STAGE_PRE_PROCESS OnePlugin PROCESS_ASSETS_STAGE_DERIVED TwoPlugin PROCESS_ASSETS_STAGE_DERIVED OnePlugin PROCESS_ASSETS_STAGE_PRE_PROCESS OnePlugin PROCESS_ASSETS_STAGE_DERIVED OnePlugin PROCESS_ASSETS_STAGE_ADDITIONAL additionalAssets callback OnePlugin PROCESS_ASSETS_STAGE_ADDITIONSCopy the code
According to the previous section on stage, the output of this command should be:
OnePlugin PROCESS_ASSETS_STAGE_ADDITIONAL
OnePlugin PROCESS_ASSETS_STAGE_PRE_PROCESS
OnePlugin PROCESS_ASSETS_STAGE_DERIVED
TwoPlugin PROCESS_ASSETS_STAGE_DERIVED
OnePlugin PROCESS_ASSETS_STAGE_ADDITIONS
Copy the code
But according to the output, the following output is added between TwoPlugin PROCESS_ASSETS_STAGE_DERIVED and OnePlugin PROCESS_ASSETS_STAGE_ADDITIONS:
OnePlugin PROCESS_ASSETS_STAGE_PRE_PROCESS
OnePlugin PROCESS_ASSETS_STAGE_DERIVED
OnePlugin PROCESS_ASSETS_STAGE_ADDITIONAL additionalAssets callback
Copy the code
This is because in the TwoPlugin’s processAssets hook we’ve added a hello.txt resource through compilation. EmitAsset, And several processAssets hooks executed in OnePlugin set additionalAssets, so at this point Webpack goes back and triggers the relevant callback. It then proceeds to execute the other processAssets hooks according to the stage rules.
AdditionalAssets according to the above analysis, the main function of additionalAssets is to listen to the resources added by the processAssets hook to be executed later, so that it can be processed.