如何编写wenpack插件
源码太多,边听歌曲边看吧!
理解插件的书写格式:
// 简单插件
class MyPlugin {
constructor (options) {
console.log(options)
}
apply (compiler) {
compiler.hooks.done.tap('MyPlugin', () => {
console.log('构建结束')
})
}
}
// 复杂插件
class AssetPlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
compiler.hooks.compilation.tap('AssetPlugin', (compilation) => {
compilation.hooks.chunkAsset.tap('AssetPlugin', (chunk, filename) => {
console.log(chunk.name || chunk.id, filename);
});
});
}
}
compiler 对象代表了完整的 webpack 环境配置,这个对象在启动 webpack 时被一次性建立。compilation 对象代表了一次资源版本构建,每次 watch 会重新生成。
compiler.hooks.done.tap
这个怎么来的,我们怎么知道有哪些钩子?
class Compiler {
/**
* @param {string} context the compilation path
*/
constructor(context) {
this.hooks = Object.freeze({
/** @type {SyncHook<[]>} */
initialize: new SyncHook([]),
/** @type {SyncBailHook<[Compilation], boolean>} */
shouldEmit: new SyncBailHook(["compilation"]),
/** @type {AsyncSeriesHook<[Stats]>} */
done: new AsyncSeriesHook(["stats"]),
/** @type {SyncHook<[Stats]>} */
afterDone: new SyncHook(["stats"]),
/** @type {AsyncSeriesHook<[]>} */
additionalPass: new AsyncSeriesHook([]),
/** @type {AsyncSeriesHook<[Compiler]>} */
beforeRun: new AsyncSeriesHook(["compiler"]),
/** @type {AsyncSeriesHook<[Compiler]>} */
run: new AsyncSeriesHook(["compiler"]),
/** @type {AsyncSeriesHook<[Compilation]>} */
emit: new AsyncSeriesHook(["compilation"]),
/** @type {AsyncSeriesHook<[string, AssetEmittedInfo]>} */
assetEmitted: new AsyncSeriesHook(["file", "info"]),
/** @type {AsyncSeriesHook<[Compilation]>} */
afterEmit: new AsyncSeriesHook(["compilation"]),
/** @type {SyncHook<[Compilation, CompilationParams]>} */
thisCompilation: new SyncHook(["compilation", "params"]),
/** @type {SyncHook<[Compilation, CompilationParams]>} */
compilation: new SyncHook(["compilation", "params"]),
/** @type {SyncHook<[NormalModuleFactory]>} */
normalModuleFactory: new SyncHook(["normalModuleFactory"]),
/** @type {SyncHook<[ContextModuleFactory]>} */
contextModuleFactory: new SyncHook(["contextModuleFactory"]),
/** @type {AsyncSeriesHook<[CompilationParams]>} */
beforeCompile: new AsyncSeriesHook(["params"]),
/** @type {SyncHook<[CompilationParams]>} */
compile: new SyncHook(["params"]),
/** @type {AsyncParallelHook<[Compilation]>} */
make: new AsyncParallelHook(["compilation"]),
/** @type {AsyncParallelHook<[Compilation]>} */
finishMake: new AsyncSeriesHook(["compilation"]),
/** @type {AsyncSeriesHook<[Compilation]>} */
afterCompile: new AsyncSeriesHook(["compilation"]),
/** @type {AsyncSeriesHook<[Compiler]>} */
watchRun: new AsyncSeriesHook(["compiler"]),
/** @type {SyncHook<[Error]>} */
failed: new SyncHook(["error"]),
/** @type {SyncHook<[string | null, number]>} */
invalid: new SyncHook(["filename", "changeTime"]),
/** @type {SyncHook<[]>} */
watchClose: new SyncHook([]),
/** @type {AsyncSeriesHook<[]>} */
shutdown: new AsyncSeriesHook([]),
/** @type {SyncBailHook<[string, string, any[]], true>} */
infrastructureLog: new SyncBailHook(["origin", "type", "args"]),
// TODO the following hooks are weirdly located here
// TODO move them for webpack 5
/** @type {SyncHook<[]>} */
environment: new SyncHook([]),
/** @type {SyncHook<[]>} */
afterEnvironment: new SyncHook([]),
/** @type {SyncHook<[Compiler]>} */
afterPlugins: new SyncHook(["compiler"]),
/** @type {SyncHook<[Compiler]>} */
afterResolvers: new SyncHook(["compiler"]),
/** @type {SyncBailHook<[string, Entry], boolean>} */
entryOption: new SyncBailHook(["context", "entry"])
});
打开源码看到如此多的钩子,我们看到的 SyncHook 和 SyncBailHook 等等这样的 hook 又来自哪里,这个是 tapable 库里面的。
每当 compiler 开始一次新的构建,创建一个新的 compilation 实例,会触发一次钩子事件,所以必须在 compiler.hooks.compilation.tap
里面拿到 compilation。
class Compilation {
createCompilation() {
this._cleanupLastCompilation();
return (this._lastCompilation = new Compilation(this));
}
/**
* @param {CompilationParams} params the compilation parameters
* @returns {Compilation} the created compilation
*/
newCompilation(params) {
const compilation = this.createCompilation();
compilation.name = this.name;
compilation.records = this.records;
this.hooks.thisCompilation.call(compilation, params);
this.hooks.compilation.call(compilation, params);
return compilation;
}
那么 compilation 里面有哪些钩子呢?
class Compilation {
this.hooks = Object.freeze({
/** @type {SyncHook<[Module]>} */
buildModule: new SyncHook(["module"]),
/** @type {SyncHook<[Module]>} */
rebuildModule: new SyncHook(["module"]),
/** @type {SyncHook<[Module, WebpackError]>} */
failedModule: new SyncHook(["module", "error"]),
/** @type {SyncHook<[Module]>} */
succeedModule: new SyncHook(["module"]),
/** @type {SyncHook<[Module]>} */
stillValidModule: new SyncHook(["module"]),
/** @type {SyncHook<[Dependency, EntryOptions]>} */
addEntry: new SyncHook(["entry", "options"]),
/** @type {SyncHook<[Dependency, EntryOptions, Error]>} */
failedEntry: new SyncHook(["entry", "options", "error"]),
/** @type {SyncHook<[Dependency, EntryOptions, Module]>} */
succeedEntry: new SyncHook(["entry", "options", "module"]),
/** @type {SyncWaterfallHook<[(string[] | ReferencedExport)[], Dependency, RuntimeSpec]>} */
dependencyReferencedExports: new SyncWaterfallHook([
"referencedExports",
"dependency",
"runtime"
]),
/** @type {SyncHook<[ExecuteModuleArgument, ExecuteModuleContext]>} */
executeModule: new SyncHook(["options", "context"]),
/** @type {AsyncParallelHook<[ExecuteModuleArgument, ExecuteModuleContext]>} */
prepareModuleExecution: new AsyncParallelHook(["options", "context"]),
/** @type {AsyncSeriesHook<[Iterable
]>} */ finishModules: new AsyncSeriesHook(["modules"]),
/** @type {AsyncSeriesHook<[Module]>} */
finishRebuildingModule: new AsyncSeriesHook(["module"]),
/** @type {SyncHook<[]>} */
unseal: new SyncHook([]),
/** @type {SyncHook<[]>} */
seal: new SyncHook([]),
/** @type {SyncHook<[]>} */
beforeChunks: new SyncHook([]),
/** @type {SyncHook<[Iterable
]>} */ afterChunks: new SyncHook(["chunks"]),
/** @type {SyncBailHook<[Iterable
]>} */ optimizeDependencies: new SyncBailHook(["modules"]),
/** @type {SyncHook<[Iterable
]>} */ afterOptimizeDependencies: new SyncHook(["modules"]),
/** @type {SyncHook<[]>} */
optimize: new SyncHook([]),
/** @type {SyncBailHook<[Iterable
]>} */ optimizeModules: new SyncBailHook(["modules"]),
/** @type {SyncHook<[Iterable
]>} */ afterOptimizeModules: new SyncHook(["modules"]),
/** @type {SyncBailHook<[Iterable
, ChunkGroup[]]>} */ optimizeChunks: new SyncBailHook(["chunks", "chunkGroups"]),
/** @type {SyncHook<[Iterable
, ChunkGroup[]]>} */ afterOptimizeChunks: new SyncHook(["chunks", "chunkGroups"]),
/** @type {AsyncSeriesHook<[Iterable
, Iterable ]>} */ optimizeTree: new AsyncSeriesHook(["chunks", "modules"]),
/** @type {SyncHook<[Iterable
, Iterable ]>} */ afterOptimizeTree: new SyncHook(["chunks", "modules"]),
/** @type {AsyncSeriesBailHook<[Iterable
, Iterable ]>} */ optimizeChunkModules: new AsyncSeriesBailHook(["chunks", "modules"]),
/** @type {SyncHook<[Iterable
, Iterable ]>} */ afterOptimizeChunkModules: new SyncHook(["chunks", "modules"]),
/** @type {SyncBailHook<[], boolean>} */
shouldRecord: new SyncBailHook([]),
/** @type {SyncHook<[Chunk, Set
, RuntimeRequirementsContext]>} */ additionalChunkRuntimeRequirements: new SyncHook([
"chunk",
"runtimeRequirements",
"context"
]),
/** @type {HookMap
, RuntimeRequirementsContext]>>} */ runtimeRequirementInChunk: new HookMap(
() => new SyncBailHook(["chunk", "runtimeRequirements", "context"])
),
/** @type {SyncHook<[Module, Set
, RuntimeRequirementsContext]>} */ additionalModuleRuntimeRequirements: new SyncHook([
"module",
"runtimeRequirements",
"context"
]),
/** @type {HookMap
, RuntimeRequirementsContext]>>} */ runtimeRequirementInModule: new HookMap(
() => new SyncBailHook(["module", "runtimeRequirements", "context"])
),
/** @type {SyncHook<[Chunk, Set
, RuntimeRequirementsContext]>} */ additionalTreeRuntimeRequirements: new SyncHook([
"chunk",
"runtimeRequirements",
"context"
]),
/** @type {HookMap
, RuntimeRequirementsContext]>>} */ runtimeRequirementInTree: new HookMap(
() => new SyncBailHook(["chunk", "runtimeRequirements", "context"])
),
/** @type {SyncHook<[RuntimeModule, Chunk]>} */
runtimeModule: new SyncHook(["module", "chunk"]),
/** @type {SyncHook<[Iterable
, any]>} */ reviveModules: new SyncHook(["modules", "records"]),
/** @type {SyncHook<[Iterable
]>} */ beforeModuleIds: new SyncHook(["modules"]),
/** @type {SyncHook<[Iterable
]>} */ moduleIds: new SyncHook(["modules"]),
/** @type {SyncHook<[Iterable
]>} */ optimizeModuleIds: new SyncHook(["modules"]),
/** @type {SyncHook<[Iterable
]>} */ afterOptimizeModuleIds: new SyncHook(["modules"]),
/** @type {SyncHook<[Iterable
, any]>} */ reviveChunks: new SyncHook(["chunks", "records"]),
/** @type {SyncHook<[Iterable
]>} */ beforeChunkIds: new SyncHook(["chunks"]),
/** @type {SyncHook<[Iterable
]>} */ chunkIds: new SyncHook(["chunks"]),
/** @type {SyncHook<[Iterable
]>} */ optimizeChunkIds: new SyncHook(["chunks"]),
/** @type {SyncHook<[Iterable
]>} */ afterOptimizeChunkIds: new SyncHook(["chunks"]),
/** @type {SyncHook<[Iterable
, any]>} */ recordModules: new SyncHook(["modules", "records"]),
/** @type {SyncHook<[Iterable
, any]>} */ recordChunks: new SyncHook(["chunks", "records"]),
/** @type {SyncHook<[Iterable
]>} */ optimizeCodeGeneration: new SyncHook(["modules"]),
/** @type {SyncHook<[]>} */
beforeModuleHash: new SyncHook([]),
/** @type {SyncHook<[]>} */
afterModuleHash: new SyncHook([]),
/** @type {SyncHook<[]>} */
beforeCodeGeneration: new SyncHook([]),
/** @type {SyncHook<[]>} */
afterCodeGeneration: new SyncHook([]),
/** @type {SyncHook<[]>} */
beforeRuntimeRequirements: new SyncHook([]),
/** @type {SyncHook<[]>} */
afterRuntimeRequirements: new SyncHook([]),
/** @type {SyncHook<[]>} */
beforeHash: new SyncHook([]),
/** @type {SyncHook<[Chunk]>} */
contentHash: new SyncHook(["chunk"]),
/** @type {SyncHook<[]>} */
afterHash: new SyncHook([]),
/** @type {SyncHook<[any]>} */
recordHash: new SyncHook(["records"]),
/** @type {SyncHook<[Compilation, any]>} */
record: new SyncHook(["compilation", "records"]),
/** @type {SyncHook<[]>} */
beforeModuleAssets: new SyncHook([]),
/** @type {SyncBailHook<[], boolean>} */
shouldGenerateChunkAssets: new SyncBailHook([]),
/** @type {SyncHook<[]>} */
beforeChunkAssets: new SyncHook([]),
// TODO webpack 6 remove
/** @deprecated */
additionalChunkAssets: createProcessAssetsHook(
"additionalChunkAssets",
Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
() => [this.chunks],
"DEP_WEBPACK_COMPILATION_ADDITIONAL_CHUNK_ASSETS"
),
// TODO webpack 6 deprecate
/** @deprecated */
additionalAssets: createProcessAssetsHook(
"additionalAssets",
Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
() => []
),
// TODO webpack 6 remove
/** @deprecated */
optimizeChunkAssets: createProcessAssetsHook(
"optimizeChunkAssets",
Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE,
() => [this.chunks],
"DEP_WEBPACK_COMPILATION_OPTIMIZE_CHUNK_ASSETS"
),
// TODO webpack 6 remove
/** @deprecated */
afterOptimizeChunkAssets: createProcessAssetsHook(
"afterOptimizeChunkAssets",
Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE + 1,
() => [this.chunks],
"DEP_WEBPACK_COMPILATION_AFTER_OPTIMIZE_CHUNK_ASSETS"
),
// TODO webpack 6 deprecate
/** @deprecated */
optimizeAssets: processAssetsHook,
// TODO webpack 6 deprecate
/** @deprecated */
afterOptimizeAssets: afterProcessAssetsHook,
processAssets: processAssetsHook,
afterProcessAssets: afterProcessAssetsHook,
/** @type {AsyncSeriesHook<[CompilationAssets]>} */
processAdditionalAssets: new AsyncSeriesHook(["assets"]),
/** @type {SyncBailHook<[], boolean>} */
needAdditionalSeal: new SyncBailHook([]),
/** @type {AsyncSeriesHook<[]>} */
afterSeal: new AsyncSeriesHook([]),
/** @type {SyncWaterfallHook<[RenderManifestEntry[], RenderManifestOptions]>} */
renderManifest: new SyncWaterfallHook(["result", "options"]),
/** @type {SyncHook<[Hash]>} */
fullHash: new SyncHook(["hash"]),
/** @type {SyncHook<[Chunk, Hash, ChunkHashContext]>} */
chunkHash: new SyncHook(["chunk", "chunkHash", "ChunkHashContext"]),
/** @type {SyncHook<[Module, string]>} */
moduleAsset: new SyncHook(["module", "filename"]),
/** @type {SyncHook<[Chunk, string]>} */
chunkAsset: new SyncHook(["chunk", "filename"]),
/** @type {SyncWaterfallHook<[string, object, AssetInfo]>} */
assetPath: new SyncWaterfallHook(["path", "options", "assetInfo"]),
/** @type {SyncBailHook<[], boolean>} */
needAdditionalPass: new SyncBailHook([]),
/** @type {SyncHook<[Compiler, string, number]>} */
childCompiler: new SyncHook([
"childCompiler",
"compilerName",
"compilerIndex"
]),
/** @type {SyncBailHook<[string, LogEntry], true>} */
log: new SyncBailHook(["origin", "logEntry"]),
/** @type {SyncWaterfallHook<[WebpackError[]]>} */
processWarnings: new SyncWaterfallHook(["warnings"]),
/** @type {SyncWaterfallHook<[WebpackError[]]>} */
processErrors: new SyncWaterfallHook(["errors"]),
/** @type {HookMap
, CreateStatsOptionsContext]>>} */ statsPreset: new HookMap(() => new SyncHook(["options", "context"])),
/** @type {SyncHook<[Partial
, CreateStatsOptionsContext]>} */ statsNormalize: new SyncHook(["options", "context"]),
/** @type {SyncHook<[StatsFactory, NormalizedStatsOptions]>} */
statsFactory: new SyncHook(["statsFactory", "options"]),
/** @type {SyncHook<[StatsPrinter, NormalizedStatsOptions]>} */
statsPrinter: new SyncHook(["statsPrinter", "options"]),
get normalModuleLoader() {
return getNormalModuleLoader();
}
});
评论