The Resolver API provides webpack’s module resolution functionality, allowing you to resolve module paths programmatically and customize the resolution process.
ResolverFactory
The ResolverFactory is responsible for creating and caching resolver instances.
Accessing ResolverFactory
The ResolverFactory is available on the Compiler instance:
class MyPlugin {
apply(compiler) {
const resolverFactory = compiler.resolverFactory;
// Create or get a resolver
const resolver = resolverFactory.get('normal');
}
}
get()
Returns a resolver for the specified type.
resolverFactory.get(
type: string,
resolveOptions?: ResolveOptions
): Resolver
Type of resolver to get. Common types:
'normal' - Standard module resolution
'loader' - Loader resolution
'context' - Context module resolution
Optional resolve configuration options.
Resolver instance with resolve() method and other utilities.
Example:
const resolver = compiler.resolverFactory.get('normal', {
extensions: ['.js', '.json', '.ts'],
alias: {
'@': path.resolve(__dirname, 'src')
}
});
Resolver
A Resolver instance provides methods for resolving module paths.
resolve()
Resolves a module path.
resolver.resolve(
context: object,
path: string,
request: string,
resolveContext: object,
callback: (err: Error | null, result?: string, details?: object) => void
): void
Context object (usually an empty object {}).
The directory from which to resolve.
The module request to resolve (e.g., ‘lodash’, ’./utils’, ’@/components’).
Resolve context object (usually an empty object {}).
Callback invoked when resolution completes.Parameters:
err - Error if resolution failed, null otherwise
result - Resolved absolute path
details - Additional resolution details
Example:
const resolver = compiler.resolverFactory.get('normal');
resolver.resolve(
{},
process.cwd(),
'lodash',
{},
(err, result) => {
if (err) {
console.error('Failed to resolve:', err);
return;
}
console.log('Resolved to:', result);
// Output: /path/to/project/node_modules/lodash/lodash.js
}
);
withOptions()
Creates a new resolver with modified options.
resolver.withOptions(
options: Partial<ResolveOptions>
): Resolver
Options to override or add to the existing resolver configuration.
New resolver instance with merged options.
Example:
const baseResolver = compiler.resolverFactory.get('normal');
const tsResolver = baseResolver.withOptions({
extensions: ['.ts', '.tsx', '.js', '.jsx']
});
tsResolver.resolve({}, process.cwd(), './App', {}, (err, result) => {
console.log('Resolved TypeScript module:', result);
});
Resolve Options
Configuration options for customizing resolver behavior.
Common Options
const resolveOptions = {
// File extensions to try
extensions: ['.js', '.json', '.jsx', '.ts', '.tsx'],
// Main fields to try when resolving packages
mainFields: ['browser', 'module', 'main'],
// Main files to try when resolving directories
mainFiles: ['index'],
// Module directories to search
modules: ['node_modules'],
// Alias configuration
alias: {
'@': path.resolve(__dirname, 'src'),
'components': path.resolve(__dirname, 'src/components'),
'utils$': path.resolve(__dirname, 'src/utils/index.js')
},
// Fallback options when resolution fails
fallback: {
'crypto': require.resolve('crypto-browserify'),
'stream': require.resolve('stream-browserify')
},
// Symlink options
symlinks: true,
// Cache resolution results
unsafeCache: true,
// Resolve to context
resolveToContext: false,
// Enforce certain file extensions
enforceExtension: false,
// Description files (package.json)
descriptionFiles: ['package.json'],
// Custom conditions
conditionNames: ['webpack', 'production', 'development']
};
const resolver = compiler.resolverFactory.get('normal', resolveOptions);
Hooking into Resolution
The ResolverFactory provides hooks to customize the resolution process.
resolveOptions Hook
Called before a resolver is created, allowing you to modify resolve options.
compiler.resolverFactory.hooks.resolveOptions
.for('normal')
.tap('MyPlugin', (resolveOptions) => {
// Modify resolve options
return {
...resolveOptions,
extensions: [...resolveOptions.extensions, '.custom']
};
});
resolver Hook
Called after a resolver is created, allowing you to add plugins to it.
compiler.resolverFactory.hooks.resolver
.for('normal')
.tap('MyPlugin', (resolver, resolveOptions, userResolveOptions) => {
// Add custom plugin to resolver
resolver.hooks.result.tap('MyPlugin', (result) => {
console.log('Resolved:', result);
return result;
});
});
Practical Examples
Custom Module Resolution Plugin
class CustomResolverPlugin {
apply(compiler) {
compiler.hooks.compilation.tap(
'CustomResolverPlugin',
(compilation, { normalModuleFactory }) => {
normalModuleFactory.hooks.beforeResolve.tapAsync(
'CustomResolverPlugin',
(resolveData, callback) => {
const resolver = compiler.resolverFactory.get('normal');
// Custom resolution logic
if (resolveData.request.startsWith('custom:')) {
const customPath = resolveData.request.replace('custom:', './custom/');
resolver.resolve(
{},
resolveData.context,
customPath,
{},
(err, result) => {
if (err) return callback(err);
resolveData.request = result;
callback();
}
);
} else {
callback();
}
}
);
}
);
}
}
module.exports = CustomResolverPlugin;
Aliasing Plugin
class AliasPlugin {
constructor(aliases) {
this.aliases = aliases;
}
apply(compiler) {
compiler.resolverFactory.hooks.resolveOptions
.for('normal')
.tap('AliasPlugin', (resolveOptions) => {
return {
...resolveOptions,
alias: {
...resolveOptions.alias,
...this.aliases
}
};
});
}
}
module.exports = AliasPlugin;
Usage:
const AliasPlugin = require('./AliasPlugin');
module.exports = {
// ...
plugins: [
new AliasPlugin({
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils'),
'@api': path.resolve(__dirname, 'src/api')
})
]
};
Resolution Logger Plugin
class ResolutionLoggerPlugin {
apply(compiler) {
compiler.resolverFactory.hooks.resolver
.for('normal')
.tap('ResolutionLoggerPlugin', (resolver) => {
resolver.hooks.result.tap('ResolutionLoggerPlugin', (result) => {
console.log('Resolved:', {
path: result.path,
request: result.request,
context: result.context
});
});
});
}
}
module.exports = ResolutionLoggerPlugin;
Conditional Resolution Plugin
class ConditionalResolvePlugin {
apply(compiler) {
const isDevelopment = compiler.options.mode === 'development';
compiler.resolverFactory.hooks.resolveOptions
.for('normal')
.tap('ConditionalResolvePlugin', (resolveOptions) => {
return {
...resolveOptions,
alias: {
...resolveOptions.alias,
// Use mock API in development
'./api': isDevelopment
? './api.mock.js'
: './api.prod.js'
}
};
});
}
}
module.exports = ConditionalResolvePlugin;
Using Resolver Programmatically
const webpack = require('webpack');
const path = require('path');
// Create a simple compiler to get resolver
const compiler = webpack({
mode: 'development',
entry: './src/index.js'
});
// Get resolver
const resolver = compiler.resolverFactory.get('normal');
// Resolve multiple modules
const modulesToResolve = [
'react',
'lodash',
'./utils/helpers',
'@/components/App'
];
modulesToResolve.forEach(request => {
resolver.resolve(
{},
process.cwd(),
request,
{},
(err, result) => {
if (err) {
console.error(`Failed to resolve ${request}:`, err.message);
} else {
console.log(`${request} → ${result}`);
}
}
);
});
Advanced: Custom Resolver Plugin
Create a completely custom resolver for special file types:
class VirtualModuleResolverPlugin {
constructor(virtualModules) {
this.virtualModules = virtualModules;
}
apply(compiler) {
compiler.hooks.compilation.tap(
'VirtualModuleResolverPlugin',
(compilation, { normalModuleFactory }) => {
normalModuleFactory.hooks.beforeResolve.tap(
'VirtualModuleResolverPlugin',
(resolveData) => {
const request = resolveData.request;
if (this.virtualModules[request]) {
// Provide virtual module content
resolveData.request = `virtual-module://${request}`;
}
return resolveData;
}
);
normalModuleFactory.hooks.afterResolve.tap(
'VirtualModuleResolverPlugin',
(resolveData) => {
if (resolveData.request.startsWith('virtual-module://')) {
const moduleName = resolveData.request.replace('virtual-module://', '');
resolveData.createData.resource = resolveData.request;
resolveData.createData.loaders = [];
}
return resolveData;
}
);
}
);
}
}
module.exports = VirtualModuleResolverPlugin;
Usage:
module.exports = {
// ...
plugins: [
new VirtualModuleResolverPlugin({
'config': `module.exports = { apiUrl: 'https://api.example.com' };`,
'version': `module.exports = '1.0.0';`
})
]
};
See Also