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.
Parser hooks allow you to customize how webpack parses and analyzes your code. These hooks are primarily used for JavaScript/TypeScript files but can also apply to other module types.
Accessing Parser Hooks
Parser hooks are accessed through the NormalModuleFactory:
class MyPlugin {
apply(compiler) {
compiler.hooks.compilation.tap(
'MyPlugin',
(compilation, { normalModuleFactory }) => {
normalModuleFactory.hooks.parser
.for('javascript/auto')
.tap('MyPlugin', (parser) => {
// Access parser hooks here
});
}
);
}
}
Program-Level Hooks
program
SyncBailHook<[Program, Comment[]]>
Called when parsing begins with the AST root node.
parser.hooks.program.tap('MyPlugin', (ast, comments) => {
console.log('Parsing started');
console.log('Number of comments:', comments.length);
});
finish
SyncHook<[Program, Comment[]]>
Called when parsing finishes.
parser.hooks.finish.tap('MyPlugin', (ast, comments) => {
console.log('Parsing complete');
});
Statement Hooks
statement
SyncBailHook<[Statement]>
Called for every statement in the code.
parser.hooks.statement.tap('MyPlugin', (statement) => {
if (statement.type === 'IfStatement') {
console.log('Found if statement');
}
});
statementIf
SyncBailHook<[IfStatement]>
Called for if statements.
parser.hooks.statementIf.tap('MyPlugin', (statement) => {
console.log('If statement condition:', statement.test);
});
label
HookMap<SyncBailHook<[LabeledStatement]>>
Called for labeled statements.
parser.hooks.label.for('myLabel').tap('MyPlugin', (statement) => {
console.log('Found labeled statement');
});
Expression Hooks
expression
HookMap<SyncBailHook<[Expression]>>
Called when an expression matching the key is encountered. This is one of the most powerful hooks.
// Replace process.env.NODE_ENV with a constant
parser.hooks.expression
.for('process.env.NODE_ENV')
.tap('MyPlugin', (expr) => {
const dep = new ConstDependency(
JSON.stringify('production'),
expr.range
);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
return true; // Handled
});
Example: DefinePlugin (lib/DefinePlugin.js:628):
parser.hooks.expression.for(key).tap('DefinePlugin', (expr) => {
const strCode = toCode(
code,
parser,
compilation.valueCacheVersions,
key,
runtimeTemplate,
logger,
!parser.isAsiPosition(expr.range[0])
);
return toConstantDependency(parser, strCode)(expr);
});
expressionAnyMember
HookMap<SyncBailHook<[Expression]>>
Called for member expressions with any property.
parser.hooks.expressionAnyMember
.for('process.env')
.tap('MyPlugin', (expr) => {
// Handle process.env.ANYTHING
});
expressionConditionalOperator
SyncBailHook<[ConditionalExpression]>
Called for ternary operators.
parser.hooks.expressionConditionalOperator.tap(
'MyPlugin',
(expr) => {
console.log('Found ternary:', expr);
}
);
expressionLogicalOperator
SyncBailHook<[LogicalExpression]>
Called for logical operators (&&, ||, ??).
parser.hooks.expressionLogicalOperator.tap('MyPlugin', (expr) => {
if (expr.operator === '&&') {
console.log('Found AND operator');
}
});
Call Expression Hooks
call
HookMap<SyncBailHook<[CallExpression]>>
Called when a function call is encountered.
parser.hooks.call.for('require').tap('MyPlugin', (expr) => {
if (expr.arguments.length === 1) {
const arg = parser.evaluateExpression(expr.arguments[0]);
if (arg.isString()) {
console.log('Requiring:', arg.string);
}
}
});
Example: ProvidePlugin (lib/ProvidePlugin.js:90):
parser.hooks.call.for(name).tap('ProvidePlugin', (expr) => {
const dep = new ProvidedDependency(
request[0],
nameIdentifier,
request.slice(1),
expr.callee.range
);
dep.loc = expr.callee.loc;
parser.state.module.addDependency(dep);
parser.walkExpressions(expr.arguments);
return true;
});
callAnyMember
HookMap<SyncBailHook<[CallExpression]>>
Called for method calls on objects.
parser.hooks.callAnyMember
.for('console')
.tap('MyPlugin', (expr) => {
// Matches console.log(), console.error(), etc.
});
new
HookMap<SyncBailHook<[NewExpression]>>
Called for new expressions.
parser.hooks.new.for('Worker').tap('MyPlugin', (expr) => {
console.log('Creating new Worker');
});
Member Expression Hooks
member
HookMap<SyncBailHook<[MemberExpression]>>
Called for member access expressions.
parser.hooks.member
.for('module.exports')
.tap('MyPlugin', (expr) => {
console.log('Accessing module.exports');
});
memberChain
HookMap<SyncBailHook<[Expression, string[]]>>
Called for chained member expressions.
parser.hooks.memberChain
.for('process')
.tap('MyPlugin', (expr, members) => {
// members might be ['env', 'NODE_ENV']
console.log('Process chain:', members);
});
Import/Export Hooks
import
SyncBailHook<[Statement, ImportSource]>
Called for ES6 import statements.
parser.hooks.import.tap('MyPlugin', (statement, source) => {
console.log('Importing from:', source);
});
export
SyncBailHook<[Statement]>
Called for export statements.
parser.hooks.export.tap('MyPlugin', (statement) => {
console.log('Export statement found');
});
exportImport
SyncBailHook<[Statement, ImportSource]>
Called for export ... from statements.
parser.hooks.exportImport.tap('MyPlugin', (statement, source) => {
console.log('Re-exporting from:', source);
});
exportExpression
SyncBailHook<[Statement, Declaration]>
Called for export expressions.
parser.hooks.exportExpression.tap('MyPlugin', (statement, declaration) => {
console.log('Exporting expression');
});
exportSpecifier
SyncBailHook<[Statement, string, string, number]>
Called for each export specifier.
parser.hooks.exportSpecifier.tap(
'MyPlugin',
(statement, localName, exportedName, index) => {
console.log(`Exporting ${localName} as ${exportedName}`);
}
);
importSpecifier
SyncBailHook<[Statement, ImportSource, string, string]>
Called for each import specifier.
parser.hooks.importSpecifier.tap(
'MyPlugin',
(statement, source, importedName, localName) => {
console.log(`Importing ${importedName} as ${localName} from ${source}`);
}
);
Evaluation Hooks
evaluate
HookMap<SyncBailHook<[Expression]>>
Evaluate expressions at build time.
parser.hooks.evaluate
.for('Identifier')
.tap('MyPlugin', (expr) => {
if (expr.name === 'MY_CONSTANT') {
return new BasicEvaluatedExpression()
.setString('my value')
.setRange(expr.range);
}
});
evaluateTypeof
HookMap<SyncBailHook<[Expression]>>
Evaluate typeof expressions.
parser.hooks.evaluateTypeof
.for('MY_VAR')
.tap('MyPlugin', (expr) => {
return new BasicEvaluatedExpression()
.setString('object')
.setRange(expr.range);
});
evaluateIdentifier
HookMap<SyncBailHook<[Expression]>>
Evaluate identifiers.
parser.hooks.evaluateIdentifier
.for('MY_VAR')
.tap('MyPlugin', (expr) => {
return new BasicEvaluatedExpression()
.setBoolean(true)
.setRange(expr.range);
});
evaluateDefinedIdentifier
HookMap<SyncBailHook<[Expression]>>
Evaluate defined identifiers.
parser.hooks.evaluateDefinedIdentifier
.for('MY_VAR')
.tap('MyPlugin', (expr) => {
return new BasicEvaluatedExpression()
.setBoolean(true)
.setRange(expr.range);
});
Type Detection Hooks
typeof
HookMap<SyncBailHook<[Expression]>>
Customize typeof operator results.
parser.hooks.typeof
.for('MY_VAR')
.tap('MyPlugin', (expr) => {
return toConstantDependency(parser, JSON.stringify('string'))(expr);
});
Variable Hooks
varDeclaration
HookMap<SyncBailHook<[Declaration]>>
Called for variable declarations.
parser.hooks.varDeclaration
.for('myVar')
.tap('MyPlugin', (declaration) => {
console.log('Variable declared:', declaration);
});
pattern
HookMap<SyncBailHook<[Identifier]>>
Called for pattern matching in destructuring.
parser.hooks.pattern
.for('myVar')
.tap('MyPlugin', (identifier) => {
console.log('Pattern match:', identifier);
});
canRename
HookMap<SyncBailHook<[Expression]>>
Determines if an identifier can be renamed.
parser.hooks.canRename
.for('MY_VAR')
.tap('MyPlugin', () => {
return true; // Allow renaming
});
rename
HookMap<SyncBailHook<[Expression]>>
Rename an identifier.
parser.hooks.rename
.for('oldName')
.tap('MyPlugin', (expr) => {
return 'newName';
});
Assign Hooks
assign
HookMap<SyncBailHook<[AssignmentExpression]>>
Called for assignment expressions.
parser.hooks.assign
.for('module.exports')
.tap('MyPlugin', (expr) => {
console.log('Assigning to module.exports');
});
assignMemberChain
HookMap<SyncBailHook<[AssignmentExpression, string[]]>>
Called for chained member assignments.
parser.hooks.assignMemberChain
.for('module')
.tap('MyPlugin', (expr, members) => {
// members might be ['exports', 'default']
});
SyncBailHook<[MemberExpression]>
Called for import.meta expressions.
parser.hooks.importMeta.tap('MyPlugin', (expr) => {
console.log('import.meta accessed');
});
Complete Example: Environment Variables Plugin
const ConstDependency = require('webpack/lib/dependencies/ConstDependency');
class EnvironmentPlugin {
constructor(env) {
this.env = env;
}
apply(compiler) {
compiler.hooks.compilation.tap(
'EnvironmentPlugin',
(compilation, { normalModuleFactory }) => {
const handler = (parser) => {
// Handle process.env.VAR_NAME
parser.hooks.expressionAnyMember
.for('process.env')
.tap('EnvironmentPlugin', (expr) => {
if (expr.property.type === 'Identifier') {
const name = expr.property.name;
const value = this.env[name];
if (value !== undefined) {
const dep = new ConstDependency(
JSON.stringify(value),
expr.range
);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
return true;
}
}
});
// Handle typeof process.env.VAR_NAME
parser.hooks.typeof
.for('process.env')
.tap('EnvironmentPlugin', (expr) => {
// Return 'object' for process.env
const dep = new ConstDependency(
JSON.stringify('object'),
expr.range
);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
return true;
});
};
normalModuleFactory.hooks.parser
.for('javascript/auto')
.tap('EnvironmentPlugin', handler);
normalModuleFactory.hooks.parser
.for('javascript/dynamic')
.tap('EnvironmentPlugin', handler);
normalModuleFactory.hooks.parser
.for('javascript/esm')
.tap('EnvironmentPlugin', handler);
}
);
}
}
// Usage
new EnvironmentPlugin({
NODE_ENV: 'production',
API_URL: 'https://api.example.com'
});
Parser State
The parser maintains state during parsing:
parser.state.module // Current module being parsed
parser.state.compilation // Current compilation
parser.scope // Current scope information
Helper Functions
evaluateExpression
Evaluate an expression at build time:
const result = parser.evaluateExpression(expr);
if (result.isString()) {
console.log('String value:', result.string);
}
walkExpressions
Walk through an array of expressions:
parser.walkExpressions(node.arguments);
getNameForExpression
Get the name for an expression:
const nameInfo = parser.getNameForExpression(expr);
if (nameInfo) {
console.log('Name:', nameInfo.name);
}
See Also