Overview
The Oxc Resolver is a high-performance module resolution library that implements Node.js-style module resolution. It’s used by build tools, bundlers, and linters to resolve import and require statements to actual file paths.
Node.js Compatible Full implementation of Node.js module resolution algorithm.
Enhanced Features TypeScript paths, exports/imports fields, and custom conditions.
High Performance Optimized with caching and efficient algorithms.
Well-Tested Passes enhanced-resolve test suite.
The resolver is available as a standalone package oxc-resolver and is also used internally by oxlint’s import plugin.
Features
Resolution Algorithms
Node.js Resolution : Classic node_modules traversal
Package Exports : Support for exports field in package.json
Package Imports : Support for imports field (subpath imports)
TypeScript Paths : Resolve using tsconfig.json path mappings
Conditional Exports : Resolve based on conditions (import, require, node, browser, etc.)
File Type Support
JavaScript: .js, .mjs, .cjs
TypeScript: .ts, .tsx, .mts, .cts
JSON: .json
CSS: .css, .scss, .sass, .less
Other: Custom extensions
Advanced Features
Browser Field : Support for browser-specific entry points
Aliases : Configure custom path aliases
Extension Resolution : Automatic extension resolution
Directory Indexes : Resolve directories to index files
Symlink Resolution : Handle symbolic links correctly
Yarn PnP : Support for Yarn Plug’n’Play
Node.js API
Installation
Basic Usage
import { ResolverFactory } from 'oxc-resolver' ;
// Create resolver
const resolver = new ResolverFactory ();
// Resolve a module
const result = resolver . sync ( 'lodash' , '/path/to/project' );
if ( result . error ) {
console . error ( 'Resolution failed:' , result . error );
} else {
console . log ( 'Resolved to:' , result . path );
// Resolved to: /path/to/project/node_modules/lodash/lodash.js
}
Resolver Options
interface ResolverOptions {
/** Condition names for conditional exports */
conditionNames ?: string [];
/** Alias mappings */
alias ?: Record < string , string | string []>;
/** Extensions to try */
extensions ?: string [];
/** Main fields to check in package.json */
mainFields ?: string [];
/** Main files to check (index.js, etc) */
mainFiles ?: string [];
/** Module directories to search (node_modules, etc) */
modules ?: string [];
/** Enable TypeScript path resolution */
tsconfig ?: TsConfigOptions ;
/** Enable symlinks */
symlinks ?: boolean ;
/** Prefer relative imports */
preferRelative ?: boolean ;
/** Enable browser field */
browser ?: boolean ;
}
import { ResolverFactory } from 'oxc-resolver' ;
const resolver = new ResolverFactory ({
// Extensions to try
extensions: [ '.ts' , '.tsx' , '.js' , '.jsx' , '.json' ],
// Condition names for exports field
conditionNames: [ 'import' , 'require' , 'node' , 'default' ],
// Main fields in package.json
mainFields: [ 'module' , 'main' ],
// Aliases
alias: {
'@' : './src' ,
'@components' : './src/components' ,
'@utils' : './src/utils'
},
// Enable TypeScript resolution
tsconfig: {
configFile: './tsconfig.json' ,
references: 'auto' // Follow project references
}
});
const result = resolver . sync ( '@components/Button' , '/path/to/project' );
Async Resolution
const result = await resolver . async ( 'react' , '/path/to/project' );
if ( result . path ) {
console . log ( 'Resolved to:' , result . path );
}
TypeScript Path Resolution
// tsconfig.json
{
"compilerOptions" : {
"baseUrl" : "." ,
"paths" : {
"@/*" : [ "src/*" ],
"@components/*" : [ "src/components/*" ],
"@utils/*" : [ "src/utils/*" ]
}
}
}
// Code
const resolver = new ResolverFactory ({
tsconfig: {
configFile: './tsconfig.json'
}
});
const result = resolver . sync ( '@/lib/helper' , '/path/to/project' );
// Resolves to: /path/to/project/src/lib/helper.ts
Rust API
Adding to Cargo.toml
[ dependencies ]
oxc_resolver = "11"
Basic Usage
use oxc_resolver :: { Resolver , ResolveOptions };
use std :: path :: Path ;
fn main () {
// Create resolver with default options
let resolver = Resolver :: default ();
// Resolve a module
let path = Path :: new ( "/path/to/project" );
let result = resolver . resolve ( path , "lodash" );
match result {
Ok ( resolved ) => {
println! ( "Resolved to: {:?}" , resolved . path ());
}
Err ( error ) => {
eprintln! ( "Resolution failed: {:?}" , error );
}
}
}
Resolver Options
use oxc_resolver :: { Resolver , ResolveOptions };
let options = ResolveOptions {
extensions : vec! [
".ts" . into (),
".tsx" . into (),
".js" . into (),
".jsx" . into (),
],
condition_names : vec! [
"import" . into (),
"require" . into (),
"node" . into (),
"default" . into (),
],
main_fields : vec! [ "module" . into (), "main" . into ()],
alias : vec! [
( "@" . into (), vec! [ "./src" . into ()]),
( "@components" . into (), vec! [ "./src/components" . into ()]),
],
tsconfig : Some ( TsconfigOptions {
config_file : "./tsconfig.json" . into (),
references : TsconfigReferences :: Auto ,
}),
.. ResolveOptions :: default ()
};
let resolver = Resolver :: new ( options );
Caching
use oxc_resolver :: { Resolver , ResolveOptions , ResolverCache };
// Create resolver with cache
let cache = ResolverCache :: default ();
let resolver = Resolver :: new_with_cache ( ResolveOptions :: default (), cache );
// Subsequent resolutions will use cache
let result1 = resolver . resolve ( path , "lodash" );
let result2 = resolver . resolve ( path , "lodash" ); // Uses cached result
Resolution Examples
Node Modules Resolution
// Resolve from node_modules
resolver . sync ( 'react' , '/app/src' )
// → /app/node_modules/react/index.js
resolver . sync ( 'lodash/isEmpty' , '/app/src' )
// → /app/node_modules/lodash/isEmpty.js
Relative Imports
// Relative paths
resolver . sync ( './utils/helper' , '/app/src/components' )
// → /app/src/components/utils/helper.js
resolver . sync ( '../lib/api' , '/app/src/components' )
// → /app/src/lib/api.js
Package Exports
{
"name" : "my-package" ,
"exports" : {
"." : {
"import" : "./dist/esm/index.js" ,
"require" : "./dist/cjs/index.js" ,
"types" : "./dist/types/index.d.ts"
},
"./utils" : {
"import" : "./dist/esm/utils.js" ,
"require" : "./dist/cjs/utils.js"
}
}
}
// With conditionNames: ['import']
resolver . sync ( 'my-package' , '/app' )
// → /app/node_modules/my-package/dist/esm/index.js
resolver . sync ( 'my-package/utils' , '/app' )
// → /app/node_modules/my-package/dist/esm/utils.js
TypeScript Paths
// tsconfig.json
{
"compilerOptions" : {
"baseUrl" : "." ,
"paths" : {
"@/*" : [ "src/*" ],
"@components/*" : [ "src/components/*" ]
}
}
}
// Resolution
resolver . sync ( '@/utils/helper' , '/app' )
// → /app/src/utils/helper.ts
resolver . sync ( '@components/Button' , '/app' )
// → /app/src/components/Button.tsx
Alias Resolution
const resolver = new ResolverFactory ({
alias: {
'@' : './src' ,
'@lib' : './src/lib' ,
'vendor' : './vendor/packages'
}
});
resolver . sync ( '@/index' , '/app' )
// → /app/src/index.js
resolver . sync ( '@lib/utils' , '/app' )
// → /app/src/lib/utils.js
Browser Field
{
"name" : "my-package" ,
"main" : "./index.js" ,
"browser" : {
"./index.js" : "./browser.js" ,
"fs" : false
}
}
const resolver = new ResolverFactory ({
browser: true
});
resolver . sync ( 'my-package' , '/app' )
// → /app/node_modules/my-package/browser.js
resolver . sync ( 'fs' , '/app' )
// → false (ignored in browser)
Use Cases
Bundlers
Linters
Dependency Analysis
Module Analysis
// webpack/rollup/vite plugin
function resolvePlugin () {
const resolver = new ResolverFactory ({
extensions: [ '.ts' , '.tsx' , '.js' , '.jsx' ],
tsconfig: { configFile: './tsconfig.json' }
});
return {
name: 'resolve' ,
resolveId ( source , importer ) {
const result = resolver . sync ( source , importer );
return result . path ;
}
};
}
// Check if imports resolve correctly
import { ResolverFactory } from 'oxc-resolver' ;
function validateImport ( importPath , currentFile ) {
const resolver = new ResolverFactory ();
const result = resolver . sync ( importPath , currentFile );
if ( result . error ) {
console . error ( `Cannot resolve: ${ importPath } ` );
return false ;
}
return true ;
}
// Build dependency graph
function analyzeDependencies ( entryFile ) {
const resolver = new ResolverFactory ();
const deps = new Set ();
function traverse ( file ) {
const imports = extractImports ( file );
for ( const imp of imports ) {
const result = resolver . sync ( imp , file );
if ( result . path && ! deps . has ( result . path )) {
deps . add ( result . path );
traverse ( result . path );
}
}
}
traverse ( entryFile );
return deps ;
}
// Find all files importing a module
function findImporters ( targetModule , sourceFiles ) {
const resolver = new ResolverFactory ();
const importers = [];
for ( const file of sourceFiles ) {
const imports = extractImports ( file );
for ( const imp of imports ) {
const result = resolver . sync ( imp , file );
if ( result . path === targetModule ) {
importers . push ( file );
break ;
}
}
}
return importers ;
}
Integration
With oxlint
The resolver is used internally by oxlint’s import plugin:
{
"plugins" : [ "import" ],
"rules" : {
"import/no-unresolved" : "error"
}
}
import { ResolverFactory } from 'oxc-resolver' ;
class MyBundler {
private resolver : ResolverFactory ;
constructor ( config ) {
this . resolver = new ResolverFactory ({
extensions: config . extensions ,
alias: config . alias ,
tsconfig: config . tsconfig
});
}
resolve ( specifier : string , from : string ) {
return this . resolver . sync ( specifier , from );
}
}
Caching
The resolver implements aggressive caching:
Resolution Cache : Cache resolved paths
File System Cache : Cache file existence checks
Package.json Cache : Cache package.json parsing
TSConfig Cache : Cache tsconfig.json parsing
Benchmarks
Comparison with enhanced-resolve:
Operation oxc-resolver enhanced-resolve Cold resolve ~1ms ~5ms Cached resolve ~0.01ms ~0.1ms 10,000 resolves ~100ms ~800ms
Comparison
vs enhanced-resolve (webpack)
Feature oxc-resolver enhanced-resolve Speed Fast Slower Node.js Resolution Yes Yes Exports Field Yes Yes TypeScript Paths Native Via plugin Caching Built-in Manual
vs Node.js require.resolve
Feature oxc-resolver require.resolve Exports Field Yes Yes (Node 12.7+) TypeScript Paths Yes No Conditions Configurable Limited Aliases Yes No
Resources
GitHub Repository Source code (part of oxc monorepo)
npm Package Standalone npm package
Documentation Rust API documentation
FAQ
Does it work with Yarn PnP?
Yes, the resolver has experimental support for Yarn Plug’n’Play resolution.
Can I use it with webpack?
Yes, though webpack has enhanced-resolve built-in. You might use oxc-resolver for custom plugins or tools.
Does it support subpath exports?
Yes, full support for package.json exports field including subpath exports and conditional exports.
How do I debug resolution failures?
Enable debug logging or check the error message in the result. Common issues: missing files, incorrect aliases, or misconfigured tsconfig paths.
Is it compatible with Node.js resolution?
Yes, it implements the full Node.js resolution algorithm including all edge cases.