Skip to main content
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
string
required
Type of resolver to get. Common types:
  • 'normal' - Standard module resolution
  • 'loader' - Loader resolution
  • 'context' - Context module resolution
resolveOptions
ResolveOptions
Optional resolve configuration options.
return
Resolver
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
required
Context object (usually an empty object {}).
path
string
required
The directory from which to resolve.
request
string
required
The module request to resolve (e.g., ‘lodash’, ’./utils’, ’@/components’).
resolveContext
object
required
Resolve context object (usually an empty object {}).
callback
function
required
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
ResolveOptions
required
Options to override or add to the existing resolver configuration.
return
Resolver
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