or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

core-resolution.mdfile-system.mdindex.mdmodern-features.mdplugin-system.mdresolver-factory.md
tile.json

plugin-system.mddocs/

Plugin System

Extensive plugin system with 29 built-in plugins for handling aliases, modern package features, and custom resolution logic using tapable hooks.

Capabilities

Core Plugin Classes

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();
    });
  }
}

Plugin Categories

Path Resolution Plugins

/**
 * 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;
}

Module Resolution Plugins

/**
 * 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;
}

Modern Package Features

/**
 * 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;
}

File System Plugins

/**
 * 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;
}

Custom Plugin Development

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"
        }
      }
    })
  ]
});

Available Built-in Plugins

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 logging

Types

type 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;
}