Plugins are now written in the following format:

// Simple plug-in
class MyPlugin {
  constructor (options) {
    console.log(options)
  }
  apply (compiler) {
    compiler.hooks.done.tap('MyPlugin'.() = > {
      console.log('Build complete')}}}Copy the code
// Complex plug-ins
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); }); }); }}Copy the code

The Compiler object represents the complete Configuration of the WebPack environment and is created once when WebPack is started. The compilation object represents a build of a resource version, and each time watch is regenerated.

Compiler.hooks. Done. Tap Where does this come from and how do we know which hooks there are?

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"])}); }}Copy the code

Open the source code to see so many hooks, we see SyncHook and SyncBailHook and so on and where do hooks come from, this is the tapable library. The documentation is linked here.

Whenever the compiler to start a new building, the creation of a new compilation instance, will trigger a hook events, so must be on the compiler.hooks.com pilation. Tap it to 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);
		returncompilation; }}Copy the code

So what are the hooks in compilation?

class Compilation {
	/**
	 * Creates an instance of Compilation.
	 * @param {Compiler} compiler the compiler which created the compilation
	 */
	constructor(compiler) {
		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<Module>]>} * /
			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<Chunk>]>} * /
			afterChunks: new SyncHook(["chunks"]),

			/ * *@type {SyncBailHook<[Iterable<Module>]>} * /
			optimizeDependencies: new SyncBailHook(["modules"]),
			/ * *@type {SyncHook<[Iterable<Module>]>} * /
			afterOptimizeDependencies: new SyncHook(["modules"]),

			/ * *@type {SyncHook<[]>} * /
			optimize: new SyncHook([]),
			/ * *@type {SyncBailHook<[Iterable<Module>]>} * /
			optimizeModules: new SyncBailHook(["modules"]),
			/ * *@type {SyncHook<[Iterable<Module>]>} * /
			afterOptimizeModules: new SyncHook(["modules"]),

			/ * *@type {SyncBailHook<[Iterable<Chunk>, ChunkGroup[]]>} * /
			optimizeChunks: new SyncBailHook(["chunks"."chunkGroups"]),
			/ * *@type {SyncHook<[Iterable<Chunk>, ChunkGroup[]]>} * /
			afterOptimizeChunks: new SyncHook(["chunks"."chunkGroups"]),

			/ * *@type {AsyncSeriesHook<[Iterable<Chunk>, Iterable<Module>]>} * /
			optimizeTree: new AsyncSeriesHook(["chunks"."modules"]),
			/ * *@type {SyncHook<[Iterable<Chunk>, Iterable<Module>]>} * /
			afterOptimizeTree: new SyncHook(["chunks"."modules"]),

			/ * *@type {AsyncSeriesBailHook<[Iterable<Chunk>, Iterable<Module>]>} * /
			optimizeChunkModules: new AsyncSeriesBailHook(["chunks"."modules"]),
			/ * *@type {SyncHook<[Iterable<Chunk>, Iterable<Module>]>} * /
			afterOptimizeChunkModules: new SyncHook(["chunks"."modules"]),
			/ * *@type {SyncBailHook<[], boolean>} * /
			shouldRecord: new SyncBailHook([]),

			/ * *@type {SyncHook<[Chunk, Set<string>, RuntimeRequirementsContext]>} * /
			additionalChunkRuntimeRequirements: new SyncHook([
				"chunk"."runtimeRequirements"."context"
			]),
			/ * *@type {HookMap<SyncBailHook<[Chunk, Set<string>, RuntimeRequirementsContext]>>} * /
			runtimeRequirementInChunk: new HookMap(
				() = > new SyncBailHook(["chunk"."runtimeRequirements"."context")),/ * *@type {SyncHook<[Module, Set<string>, RuntimeRequirementsContext]>} * /
			additionalModuleRuntimeRequirements: new SyncHook([
				"module"."runtimeRequirements"."context"
			]),
			/ * *@type {HookMap<SyncBailHook<[Module, Set<string>, RuntimeRequirementsContext]>>} * /
			runtimeRequirementInModule: new HookMap(
				() = > new SyncBailHook(["module"."runtimeRequirements"."context")),/ * *@type {SyncHook<[Chunk, Set<string>, RuntimeRequirementsContext]>} * /
			additionalTreeRuntimeRequirements: new SyncHook([
				"chunk"."runtimeRequirements"."context"
			]),
			/ * *@type {HookMap<SyncBailHook<[Chunk, Set<string>, RuntimeRequirementsContext]>>} * /
			runtimeRequirementInTree: new HookMap(
				() = > new SyncBailHook(["chunk"."runtimeRequirements"."context")),/ * *@type {SyncHook<[RuntimeModule, Chunk]>} * /
			runtimeModule: new SyncHook(["module"."chunk"]),

			/ * *@type {SyncHook<[Iterable<Module>, any]>} * /
			reviveModules: new SyncHook(["modules"."records"]),
			/ * *@type {SyncHook<[Iterable<Module>]>} * /
			beforeModuleIds: new SyncHook(["modules"]),
			/ * *@type {SyncHook<[Iterable<Module>]>} * /
			moduleIds: new SyncHook(["modules"]),
			/ * *@type {SyncHook<[Iterable<Module>]>} * /
			optimizeModuleIds: new SyncHook(["modules"]),
			/ * *@type {SyncHook<[Iterable<Module>]>} * /
			afterOptimizeModuleIds: new SyncHook(["modules"]),

			/ * *@type {SyncHook<[Iterable<Chunk>, any]>} * /
			reviveChunks: new SyncHook(["chunks"."records"]),
			/ * *@type {SyncHook<[Iterable<Chunk>]>} * /
			beforeChunkIds: new SyncHook(["chunks"]),
			/ * *@type {SyncHook<[Iterable<Chunk>]>} * /
			chunkIds: new SyncHook(["chunks"]),
			/ * *@type {SyncHook<[Iterable<Chunk>]>} * /
			optimizeChunkIds: new SyncHook(["chunks"]),
			/ * *@type {SyncHook<[Iterable<Chunk>]>} * /
			afterOptimizeChunkIds: new SyncHook(["chunks"]),

			/ * *@type {SyncHook<[Iterable<Module>, any]>} * /
			recordModules: new SyncHook(["modules"."records"]),
			/ * *@type {SyncHook<[Iterable<Chunk>, any]>} * /
			recordChunks: new SyncHook(["chunks"."records"]),

			/ * *@type {SyncHook<[Iterable<Module>]>} * /
			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<SyncHook<[Partial<NormalizedStatsOptions>, CreateStatsOptionsContext]>>} * /
			statsPreset: new HookMap(() = > new SyncHook(["options"."context")),/ * *@type {SyncHook<[Partial<NormalizedStatsOptions>, 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() {
				returngetNormalModuleLoader(); }}); }}Copy the code

There is so much code that I won’t go through it all, but let’s focus on how plug-ins are defined internally.