Extensive plugin system with 29 built-in plugins for handling aliases, modern package features, and custom resolution logic using tapable hooks.
Built-in plugins that can be used directly or as examples for custom plugins.
/**
* Plugin that clones basename for path resolution
*/
class CloneBasenamePlugin {
constructor(source: string, target: string);
apply(resolver: Resolver): void;
}
/**
* Plugin providing detailed logging during resolution
*/
class LogInfoPlugin {
constructor(source: string);
apply(resolver: Resolver): void;
}Usage Examples:
const { CloneBasenamePlugin, LogInfoPlugin } = require("enhanced-resolve");
// Create resolver with plugins
const resolve = require("enhanced-resolve").create({
extensions: [".js", ".json"],
plugins: [
new LogInfoPlugin("resolve"),
new CloneBasenamePlugin("resolve", "internal-resolve")
]
});
// Custom plugin example
class CustomPlugin {
constructor(options) {
this.options = options;
}
apply(resolver) {
const target = resolver.ensureHook("custom-target");
resolver.getHook("resolve").tapAsync("CustomPlugin", (request, resolveContext, callback) => {
// Custom resolution logic
if (request.request.startsWith("custom:")) {
const newRequest = {
...request,
request: request.request.slice(7) // Remove "custom:" prefix
};
resolver.doResolve(target, newRequest, "custom protocol", resolveContext, callback);
return;
}
callback();
});
}
}/**
* Handles module aliases and path mapping
*/
class AliasPlugin {
constructor(source: string, options: AliasOptions, target: string);
apply(resolver: Resolver): void;
}
/**
* Processes alias fields from package.json
*/
class AliasFieldPlugin {
constructor(source: string, field: string, target: string);
apply(resolver: Resolver): void;
}
/**
* Maps file extensions to other extensions
*/
class ExtensionAliasPlugin {
constructor(source: string, options: ExtensionAliasOptions, target: string);
apply(resolver: Resolver): void;
}
/**
* Appends suffixes to resolve requests
*/
class AppendPlugin {
constructor(source: string, appending: string, target: string);
apply(resolver: Resolver): void;
}/**
* Searches for modules in hierarchical directory structure
*/
class ModulesInHierarchicalDirectoriesPlugin {
constructor(source: string, directories: string[], target: string);
apply(resolver: Resolver): void;
}
/**
* Searches for modules in root directories
*/
class ModulesInRootPlugin {
constructor(source: string, path: string, target: string);
apply(resolver: Resolver): void;
}
/**
* Handles self-referencing packages via exports field
*/
class SelfReferencePlugin {
constructor(source: string, pkg: JsonObject, target: string);
apply(resolver: Resolver): void;
}/**
* Processes package.json exports field
*/
class ExportsFieldPlugin {
constructor(source: string, conditionNames: Set<string>, fieldNamePath: string[], target: string);
apply(resolver: Resolver): void;
}
/**
* Processes package.json imports field
*/
class ImportsFieldPlugin {
constructor(source: string, conditionNames: Set<string>, fieldNamePath: string[], target: string);
apply(resolver: Resolver): void;
}/**
* Checks if files exist on the file system
*/
class FileExistsPlugin {
constructor(source: string, target: string);
apply(resolver: Resolver): void;
}
/**
* Checks if directories exist on the file system
*/
class DirectoryExistsPlugin {
constructor(source: string, target: string);
apply(resolver: Resolver): void;
}
/**
* Handles symbolic link resolution
*/
class SymlinkPlugin {
constructor(source: string, target: string);
apply(resolver: Resolver): void;
}Framework for creating custom plugins using tapable hooks.
/**
* Base plugin interface
*/
interface Plugin {
apply(resolver: Resolver): void;
}
/**
* Resolver with hook system
*/
class Resolver {
hooks: {
resolve: AsyncSeriesBailHook;
result: AsyncSeriesHook;
resolveStep: SyncHook;
noResolve: SyncHook;
};
/**
* Ensure a hook exists
*/
ensureHook(name: string): AsyncSeriesBailHook;
/**
* Get a named hook
*/
getHook(name: string): AsyncSeriesBailHook;
/**
* Perform resolution with hook
*/
doResolve(
hook: AsyncSeriesBailHook,
request: ResolveRequest,
message: string,
resolveContext: ResolveContext,
callback: (err?: Error, result?: ResolveRequest) => void
): void;
}Custom Plugin Examples:
// Plugin that adds custom protocol support
class ProtocolPlugin {
constructor(protocol, resolver) {
this.protocol = protocol;
this.resolver = resolver;
}
apply(resolver) {
const target = resolver.ensureHook("resolve");
resolver.getHook("resolve").tapAsync("ProtocolPlugin", (request, resolveContext, callback) => {
if (request.request.startsWith(this.protocol + ":")) {
const newRequest = {
...request,
request: this.resolver(request.request.slice(this.protocol.length + 1))
};
resolver.doResolve(target, newRequest, `${this.protocol} protocol`, resolveContext, callback);
return;
}
callback();
});
}
}
// Plugin that adds conditional resolution based on environment
class EnvironmentPlugin {
constructor(conditions) {
this.conditions = conditions;
}
apply(resolver) {
resolver.getHook("resolve").tapAsync("EnvironmentPlugin", (request, resolveContext, callback) => {
const env = process.env.NODE_ENV || "development";
if (this.conditions[env]) {
const condition = this.conditions[env];
if (condition.alias && condition.alias[request.request]) {
const newRequest = {
...request,
request: condition.alias[request.request]
};
resolver.doResolve(resolver.ensureHook("resolve"), newRequest,
`environment ${env}`, resolveContext, callback);
return;
}
}
callback();
});
}
}
// Usage
const resolve = require("enhanced-resolve").create({
plugins: [
new ProtocolPlugin("virtual", (path) => `/virtual/${path}`),
new EnvironmentPlugin({
development: {
alias: {
"lodash": "lodash/fp"
}
},
production: {
alias: {
"lodash": "lodash-es"
}
}
})
]
});Complete list of built-in plugins and their purposes:
// Path Resolution
class AliasPlugin {} // Module aliases
class AliasFieldPlugin {} // Package.json alias fields
class ExtensionAliasPlugin {} // Extension mapping
class AppendPlugin {} // Append suffixes
// Module Resolution
class ModulesInHierarchicalDirectoriesPlugin {} // node_modules traversal
class ModulesInRootPlugin {} // Root directory modules
class SelfReferencePlugin {} // Self-referencing packages
// Modern Package Features
class ExportsFieldPlugin {} // package.json exports
class ImportsFieldPlugin {} // package.json imports
// File System
class FileExistsPlugin {} // File existence checks
class DirectoryExistsPlugin {} // Directory existence checks
class SymlinkPlugin {} // Symlink handling
// Package.json Processing
class DescriptionFilePlugin {} // Read package.json
class MainFieldPlugin {} // Process main fields
class UseFilePlugin {} // Use specific files
// Request Processing
class ParsePlugin {} // Parse requests
class JoinRequestPlugin {} // Join request parts
class JoinRequestPartPlugin {} // Join specific parts
// Flow Control
class ConditionalPlugin {} // Conditional logic
class NextPlugin {} // Pass to next hook
class TryNextPlugin {} // Try next on failure
class ResultPlugin {} // Handle results
// Advanced Features
class PnpPlugin {} // Yarn Plug'n'Play
class UnsafeCachePlugin {} // Result caching
class RestrictionsPlugin {} // Path restrictions
class RootsPlugin {} // Root path handling
class CloneBasenamePlugin {} // Clone basename
class LogInfoPlugin {} // Detailed loggingtype Plugin =
| undefined
| null
| false
| ""
| 0
| { apply: (resolver: Resolver) => void }
| ((resolver: Resolver) => void);
interface AsyncSeriesBailHook<T, R> {
tapAsync(name: string, fn: (...args: [...T, (err?: Error, result?: R) => void]) => void): void;
tap(name: string, fn: (...args: T) => R | void): void;
callAsync(...args: [...T, (err?: Error, result?: R) => void]): void;
}
interface AsyncSeriesHook<T> {
tapAsync(name: string, fn: (...args: [...T, (err?: Error) => void]) => void): void;
tap(name: string, fn: (...args: T) => void): void;
callAsync(...args: [...T, (err?: Error) => void]): void;
}
interface SyncHook<T> {
tap(name: string, fn: (...args: T) => void): void;
call(...args: T): void;
}