Documentation Index
Fetch the complete documentation index at: https://docs.webpack.js.org/llms.txt
Use this file to discover all available pages before exploring further.
Module factories are responsible for creating module instances in webpack. The two main factories are NormalModuleFactory and ContextModuleFactory.
NormalModuleFactory
The NormalModuleFactory creates normal modules (most JavaScript/TypeScript files, JSON, etc.).
Accessing NormalModuleFactory
class MyPlugin {
apply(compiler) {
compiler.hooks.compilation.tap(
'MyPlugin',
(compilation, { normalModuleFactory }) => {
// Access normalModuleFactory hooks
}
);
}
}
NormalModuleFactory Hooks
resolve
AsyncSeriesBailHook<[ResolveData]>
Called before resolving a module request.
normalModuleFactory.hooks.resolve.tap('MyPlugin', (resolveData) => {
console.log('Resolving:', resolveData.request);
console.log('Context:', resolveData.context);
});
factorize
AsyncSeriesBailHook<[ResolveData]>
Called before the module is created.
normalModuleFactory.hooks.factorize.tapAsync(
'MyPlugin',
(resolveData, callback) => {
// Custom module creation logic
callback();
}
);
beforeResolve
AsyncSeriesBailHook<[ResolveData]>
Called before module resolution begins. Return false to skip resolving.
normalModuleFactory.hooks.beforeResolve.tap('MyPlugin', (resolveData) => {
if (resolveData.request.includes('ignore-me')) {
return false; // Skip this module
}
});
afterResolve
AsyncSeriesBailHook<[ResolveData]>
Called after module is resolved.
normalModuleFactory.hooks.afterResolve.tap('MyPlugin', (resolveData) => {
console.log('Resolved to:', resolveData.createData.resource);
});
createModule
AsyncSeriesBailHook<[CreateData, ResolveData]>
Called when creating a module. Can return a custom module.
normalModuleFactory.hooks.createModule.tap(
'MyPlugin',
(createData, resolveData) => {
if (shouldUseCustomModule(createData)) {
return new MyCustomModule(createData);
}
}
);
module
SyncWaterfallHook<[Module, CreateData, ResolveData]>
Called after a module is created. Can modify or replace the module.
normalModuleFactory.hooks.module.tap(
'MyPlugin',
(module, createData, resolveData) => {
// Modify module
module.customData = 'my custom data';
return module;
}
);
createParser
HookMap<SyncBailHook<[ParserOptions], Parser>>
Create a custom parser for a module type.
normalModuleFactory.hooks.createParser
.for('javascript/auto')
.tap('MyPlugin', (parserOptions) => {
return new MyCustomParser(parserOptions);
});
parser
HookMap<SyncHook<[Parser, ParserOptions]>>
Access and customize the parser for a module type. This is where most parser customization happens.
normalModuleFactory.hooks.parser
.for('javascript/auto')
.tap('MyPlugin', (parser, parserOptions) => {
// Hook into parser events
parser.hooks.program.tap('MyPlugin', (ast) => {
console.log('Parsing JavaScript');
});
});
Example: DefinePlugin (lib/DefinePlugin.js:780):
normalModuleFactory.hooks.parser
.for('javascript/auto')
.tap('DefinePlugin', (parser) => {
// Define global constants
parser.hooks.expression.for('MY_VAR').tap('DefinePlugin', (expr) => {
return toConstantDependency(parser, JSON.stringify('value'))(expr);
});
});
createGenerator
HookMap<SyncBailHook<[GeneratorOptions], Generator>>
Create a custom generator for a module type.
normalModuleFactory.hooks.createGenerator
.for('javascript/auto')
.tap('MyPlugin', (generatorOptions) => {
return new MyCustomGenerator(generatorOptions);
});
generator
HookMap<SyncHook<[Generator, GeneratorOptions]>>
Access and customize the generator for a module type.
normalModuleFactory.hooks.generator
.for('javascript/auto')
.tap('MyPlugin', (generator, generatorOptions) => {
// Customize code generation
});
ContextModuleFactory
The ContextModuleFactory handles dynamic requires like require('./templates/' + name + '.js').
Accessing ContextModuleFactory
compiler.hooks.compilation.tap(
'MyPlugin',
(compilation, { contextModuleFactory }) => {
// Access contextModuleFactory hooks
}
);
ContextModuleFactory Hooks
beforeResolve
AsyncSeriesBailHook<[ResolveData]>
Called before context resolution.
contextModuleFactory.hooks.beforeResolve.tap('MyPlugin', (resolveData) => {
console.log('Context request:', resolveData.request);
});
afterResolve
AsyncSeriesBailHook<[ResolveData]>
Called after context resolution.
contextModuleFactory.hooks.afterResolve.tap('MyPlugin', (resolveData) => {
console.log('Context resolved:', resolveData.resource);
});
contextModuleFiles
SyncWaterfallHook<[string[]]>
Called with the list of files found in the context.
contextModuleFactory.hooks.contextModuleFiles.tap(
'MyPlugin',
(files) => {
console.log('Context files:', files);
// Can filter or modify files
return files.filter(f => !f.endsWith('.test.js'));
}
);
Parser Hooks (JavaScript)
When you access a parser through normalModuleFactory.hooks.parser, you get access to numerous hooks for customizing code parsing.
Common Parser Hooks
program
SyncBailHook<[Program, Comment[]]>
Called when parsing starts.
parser.hooks.program.tap('MyPlugin', (ast, comments) => {
console.log('Parsing program');
});
statement
SyncBailHook<[Statement]>
Called for each statement.
parser.hooks.statement.tap('MyPlugin', (statement) => {
if (statement.type === 'ImportDeclaration') {
console.log('Found import');
}
});
expression
HookMap<SyncBailHook<[Expression]>>
Called when an expression is encountered.
parser.hooks.expression.for('MY_GLOBAL').tap('MyPlugin', (expr) => {
// Replace MY_GLOBAL with a constant
const dep = new ConstDependency('"my value"', expr.range);
parser.state.module.addDependency(dep);
return true;
});
Example: ProvidePlugin (lib/ProvidePlugin.js:75):
parser.hooks.expression.for('$').tap('ProvidePlugin', (expr) => {
const dep = new ProvidedDependency(
'jquery',
'jquery',
[],
expr.range
);
parser.state.module.addDependency(dep);
return true;
});
call
HookMap<SyncBailHook<[CallExpression]>>
Called when a function call is encountered.
parser.hooks.call.for('require').tap('MyPlugin', (expr) => {
console.log('Found require call');
});
import
SyncBailHook<[Statement, ImportSource]>
Called for import statements.
parser.hooks.import.tap('MyPlugin', (statement, source) => {
console.log('Importing:', source);
});
Complete Example: Custom Module Type
class CustomModulePlugin {
apply(compiler) {
compiler.hooks.compilation.tap(
'CustomModulePlugin',
(compilation, { normalModuleFactory }) => {
// Register custom module type
normalModuleFactory.hooks.createParser
.for('custom/type')
.tap('CustomModulePlugin', (parserOptions) => {
return new CustomParser(parserOptions);
});
normalModuleFactory.hooks.createGenerator
.for('custom/type')
.tap('CustomModulePlugin', (generatorOptions) => {
return new CustomGenerator(generatorOptions);
});
// Customize parsing for JavaScript
normalModuleFactory.hooks.parser
.for('javascript/auto')
.tap('CustomModulePlugin', (parser) => {
// Hook into require calls
parser.hooks.call.for('customRequire').tap(
'CustomModulePlugin',
(expr) => {
if (expr.arguments.length !== 1) return;
const param = parser.evaluateExpression(expr.arguments[0]);
if (!param.isString()) return;
// Add custom dependency
const dep = new CustomDependency(
param.string,
expr.range
);
parser.state.module.addDependency(dep);
return true;
}
);
});
}
);
}
}
Complete Example: Provide Global Variables
class ProvideGlobalsPlugin {
constructor(definitions) {
this.definitions = definitions;
}
apply(compiler) {
compiler.hooks.compilation.tap(
'ProvideGlobalsPlugin',
(compilation, { normalModuleFactory }) => {
const handler = (parser) => {
for (const [name, modulePath] of Object.entries(this.definitions)) {
// Hook into identifier expressions
parser.hooks.expression.for(name).tap(
'ProvideGlobalsPlugin',
(expr) => {
const dep = new ProvidedDependency(
modulePath,
name,
[],
expr.range
);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
return true;
}
);
}
};
normalModuleFactory.hooks.parser
.for('javascript/auto')
.tap('ProvideGlobalsPlugin', handler);
normalModuleFactory.hooks.parser
.for('javascript/dynamic')
.tap('ProvideGlobalsPlugin', handler);
normalModuleFactory.hooks.parser
.for('javascript/esm')
.tap('ProvideGlobalsPlugin', handler);
}
);
}
}
// Usage
new ProvideGlobalsPlugin({
$: 'jquery',
_: 'lodash'
});
Module Types
Common module types you can hook into:
javascript/auto - Auto-detect ESM or CommonJS
javascript/dynamic - CommonJS
javascript/esm - ES Modules
json - JSON files
asset - Asset modules
asset/source - Source assets
asset/resource - Resource assets
asset/inline - Inline assets
webassembly/async - Async WebAssembly
webassembly/sync - Sync WebAssembly
css - CSS files
css/module - CSS Modules
ResolveData Structure
The ResolveData object contains:
interface ResolveData {
contextInfo: {
issuer: string; // Path of the importing module
compiler: string; // Compiler name
};
context: string; // Directory context
request: string; // The import request
dependencies: Dependency[];
createData: {
resource: string; // Resolved file path
loaders: LoaderItem[]; // Loaders to apply
type: string; // Module type
parser: Parser; // Parser instance
generator: Generator; // Generator instance
};
}
See Also