Skip to main content
The Compiler module is the main engine that creates a compilation instance with all the options passed through the webpack configuration. It extends Tapable and provides lifecycle hooks.

Accessing Compiler Hooks

Compiler hooks are accessed in your plugin’s apply method:
class MyPlugin {
  apply(compiler) {
    compiler.hooks.run.tap('MyPlugin', (compiler) => {
      console.log('The webpack build process is starting');
    });
  }
}

Hook Types Reference

Each hook is an instance of a Tapable hook class. The type determines how you can tap into it:
  • SyncHook - Use .tap() only
  • SyncBailHook - Use .tap(), can return value to exit early
  • AsyncSeriesHook - Use .tap(), .tapAsync(), or .tapPromise()
  • AsyncParallelHook - Use .tap(), .tapAsync(), or .tapPromise() (runs in parallel)

Lifecycle Hooks

initialize

SyncHook<[]>
Called when the compiler is initialized.
compiler.hooks.initialize.tap('MyPlugin', () => {
  console.log('Compiler initialized');
});

environment

SyncHook<[]>
Called after the environment is set up.
compiler.hooks.environment.tap('MyPlugin', () => {
  // Prepare environment
});

afterEnvironment

SyncHook<[]>
Called after environment setup is complete.
compiler.hooks.afterEnvironment.tap('MyPlugin', () => {
  // Environment is ready
});

afterPlugins

SyncHook<[Compiler]>
Called after all plugins from the configuration have been applied.
compiler.hooks.afterPlugins.tap('MyPlugin', (compiler) => {
  console.log('All plugins applied');
});

afterResolvers

SyncHook<[Compiler]>
Called after the resolvers are set up.
compiler.hooks.afterResolvers.tap('MyPlugin', (compiler) => {
  // Modify resolvers if needed
});

entryOption

SyncBailHook<[string, Entry]>
Called after the entry configuration has been processed. Return true to prevent default entry processing.
compiler.hooks.entryOption.tap('MyPlugin', (context, entry) => {
  // Custom entry handling
});

Build Lifecycle Hooks

beforeRun

AsyncSeriesHook<[Compiler]>
Called before the compiler starts running. Perfect for cleanup or setup tasks.
compiler.hooks.beforeRun.tapAsync('MyPlugin', (compiler, callback) => {
  // Prepare for build
  callback();
});
Example from source (lib/Compiler.js:607):
this.hooks.beforeRun.callAsync(this, (err) => {
  if (err) return finalCallback(err);
  this.hooks.run.callAsync(this, (err) => {
    // Continue build process
  });
});

run

AsyncSeriesHook<[Compiler]>
Called when the compiler starts running.
compiler.hooks.run.tapPromise('MyPlugin', async (compiler) => {
  console.log('Build started');
});

watchRun

AsyncSeriesHook<[Compiler]>
Called when watch mode is triggered. Receives the compiler instance.
compiler.hooks.watchRun.tapAsync('MyPlugin', (compiler, callback) => {
  console.log('Watch compilation started');
  // Access changed files
  const changedFiles = compiler.modifiedFiles;
  callback();
});

Compilation Hooks

beforeCompile

AsyncSeriesHook<[CompilationParams]>
Called before compilation parameters are created.
compiler.hooks.beforeCompile.tapAsync(
  'MyPlugin',
  (params, callback) => {
    // params.normalModuleFactory
    // params.contextModuleFactory
    callback();
  }
);

compile

SyncHook<[CompilationParams]>
Called immediately before a new compilation is created.
compiler.hooks.compile.tap('MyPlugin', (params) => {
  console.log('Creating new compilation');
});

thisCompilation

SyncHook<[Compilation, CompilationParams]>
Called when a compilation is created, before compilation hook.
compiler.hooks.thisCompilation.tap(
  'MyPlugin',
  (compilation, params) => {
    // Called before compilation hook
  }
);

compilation

SyncHook<[Compilation, CompilationParams]>
Called when a compilation is created. This is your main entry point for compilation-level hooks.
compiler.hooks.compilation.tap(
  'MyPlugin',
  (compilation, { normalModuleFactory }) => {
    // Access compilation hooks
    compilation.hooks.buildModule.tap('MyPlugin', (module) => {
      // Module is being built
    });
  }
);
Example: DefinePlugin (lib/DefinePlugin.js:388):
compiler.hooks.compilation.tap(
  'DefinePlugin',
  (compilation, { normalModuleFactory }) => {
    const handler = (parser) => {
      // Walk definitions and hook into parser
    };
    normalModuleFactory.hooks.parser
      .for('javascript/auto')
      .tap('DefinePlugin', handler);
  }
);

make

AsyncParallelHook<[Compilation]>
Called when compilation starts. This is where entries are processed.
compiler.hooks.make.tapAsync('MyPlugin', (compilation, callback) => {
  // Add entries or process modules
  callback();
});

finishMake

AsyncSeriesHook<[Compilation]>
Called when make phase finishes.
compiler.hooks.finishMake.tapAsync('MyPlugin', (compilation, callback) => {
  // Make phase complete
  callback();
});

afterCompile

AsyncSeriesHook<[Compilation]>
Called after compilation finishes.
compiler.hooks.afterCompile.tapAsync('MyPlugin', (compilation, callback) => {
  // Compilation complete, before sealing
  callback();
});

Module Factory Hooks

normalModuleFactory

SyncHook<[NormalModuleFactory]>
Called after creating a NormalModuleFactory.
compiler.hooks.normalModuleFactory.tap('MyPlugin', (factory) => {
  factory.hooks.parser.for('javascript/auto').tap('MyPlugin', (parser) => {
    // Customize parser
  });
});

contextModuleFactory

SyncHook<[ContextModuleFactory]>
Called after creating a ContextModuleFactory.
compiler.hooks.contextModuleFactory.tap('MyPlugin', (factory) => {
  // Customize context module creation
});

Emit Hooks

shouldEmit

SyncBailHook<[Compilation], boolean | void>
Return false to prevent emitting assets.
compiler.hooks.shouldEmit.tap('MyPlugin', (compilation) => {
  // Return false to skip emit
  if (compilation.errors.length > 0) {
    return false;
  }
});

emit

AsyncSeriesHook<[Compilation]>
Called immediately before assets are emitted to output directory.
compiler.hooks.emit.tapAsync('MyPlugin', (compilation, callback) => {
  // Modify assets before writing to disk
  for (const filename in compilation.assets) {
    console.log(`Emitting ${filename}`);
  }
  callback();
});

assetEmitted

AsyncSeriesHook<[string, AssetEmittedInfo]>
Called when an asset has been written to disk.
compiler.hooks.assetEmitted.tap(
  'MyPlugin',
  (file, { content, outputPath, targetPath }) => {
    console.log(`Asset emitted: ${file}`);
  }
);

afterEmit

AsyncSeriesHook<[Compilation]>
Called after assets have been emitted.
compiler.hooks.afterEmit.tapAsync('MyPlugin', (compilation, callback) => {
  // All assets written to disk
  callback();
});

Completion Hooks

done

AsyncSeriesHook<[Stats]>
Called when compilation completes successfully.
compiler.hooks.done.tap('MyPlugin', (stats) => {
  console.log('Build completed');
  console.log(stats.toString());
});

afterDone

SyncHook<[Stats]>
Called after done hook completes.
compiler.hooks.afterDone.tap('MyPlugin', (stats) => {
  // Final cleanup
});

failed

SyncHook<[Error]>
Called when compilation fails.
compiler.hooks.failed.tap('MyPlugin', (error) => {
  console.error('Build failed:', error);
});

Watch Mode Hooks

invalid

SyncHook<[string | null, number]>
Called when a watched file changes.
compiler.hooks.invalid.tap('MyPlugin', (filename, changeTime) => {
  console.log(`File changed: ${filename}`);
});

watchClose

SyncHook<[]>
Called when watch mode stops.
compiler.hooks.watchClose.tap('MyPlugin', () => {
  console.log('Watch mode stopped');
});

Other Hooks

shutdown

AsyncSeriesHook<[]>
Called when compiler is shutting down.
compiler.hooks.shutdown.tapAsync('MyPlugin', (callback) => {
  // Cleanup resources
  callback();
});

infrastructureLog

SyncBailHook<[string, string, any[]], true | void>
Custom infrastructure logging.
compiler.hooks.infrastructureLog.tap(
  'MyPlugin',
  (origin, type, args) => {
    // Custom logging
  }
);

Complete Example

class MyCompilerPlugin {
  apply(compiler) {
    // Setup phase
    compiler.hooks.environment.tap('MyPlugin', () => {
      console.log('Environment setup');
    });

    // Before build
    compiler.hooks.beforeRun.tapAsync('MyPlugin', (compiler, callback) => {
      console.log('Preparing to build');
      callback();
    });

    // During compilation
    compiler.hooks.compilation.tap('MyPlugin', (compilation) => {
      compilation.hooks.buildModule.tap('MyPlugin', (module) => {
        console.log('Building module:', module.identifier());
      });
    });

    // After emit
    compiler.hooks.afterEmit.tapAsync('MyPlugin', (compilation, callback) => {
      console.log('Assets emitted');
      callback();
    });

    // Build complete
    compiler.hooks.done.tap('MyPlugin', (stats) => {
      console.log('Build completed in', stats.endTime - stats.startTime, 'ms');
    });
  }
}

See Also