Awesome
<p align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/Boshen/oxc-assets/main/preview-dark-transparent.png" width="600"> <img alt="OXC Logo" src="https://raw.githubusercontent.com/Boshen/oxc-assets/main/preview-white.png" width="600"> </picture> </p> <div align="center"> </div>Oxc Resolver
Rust port of enhanced-resolve.
- released on crates.io and npm.
- built-in tsconfig-paths-webpack-plugin
- support extending tsconfig defined in
tsconfig.extends
- support paths alias defined in
tsconfig.compilerOptions.paths
- support project references defined
tsconfig.references
- support template variable ${configDir} for substitution of config files directory path
- support extending tsconfig defined in
- supports in-memory file system via the
FileSystem
trait - contains
tracing
instrumentation
Usage
The following usages apply to both Rust and Node.js; the code snippets are written in JavaScript.
To handle the exports
field in package.json
, ESM and CJS need to be differentiated.
ESM
defaultConditions is the conditional environment name array, ["node", "import"].
This means when the caller is an ESM import (import "module"
), resolve options should be
{
"conditionNames": ["node", "import"]
}
CJS
LOAD_PACKAGE_EXPORTS(X, DIR)
- let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(DIR/NAME), "." + SUBPATH,
package.json
"exports", ["node", "require"]) defined in the ESM resolver.
This means when the caller is a CJS require (require("module")
), resolve options should be
{
"conditionNames": ["node", "require"]
}
Cache
To support both CJS and ESM with the same cache:
const esmResolver = new ResolverFactory({
conditionNames: ["node", "import"]
});
const cjsResolver = esmResolver.cloneWithOptions({
conditionNames: ["node", "require"]
});
Browser Field
From this non-standard spec:
The
browser
field is provided to JavaScript bundlers or component tools when packaging modules for client side use.
The option is
{
"aliasFields": ["browser"]
}
Main Field
{
"mainFields": ["module", "main"]
}
Quoting esbuild's documentation:
main
- This is the standard field for all packages that are meant to be used with node. The name main is hard-coded in to node's module resolution logic itself. Because it's intended for use with node, it's reasonable to expect that the file path in this field is a CommonJS-style module.module
- This field came from a proposal for how to integrate ECMAScript modules into node. Because of this, it's reasonable to expect that the file path in this field is an ECMAScript-style module. This proposal wasn't adopted by node (node uses "type": "module" instead) but it was adopted by major bundlers because ECMAScript-style modules lead to better tree shaking, or dead code removal.browser
- This field came from a proposal that allows bundlers to replace node-specific files or modules with their browser-friendly versions. It lets you specify an alternate browser-specific entry point. Note that it is possible for a package to use both the browser and module field together (see the note below).
Errors & Trouble Shooting
Error: Package subpath '.' is not defined by "exports" in
- occurs when resolving withoutconditionNames
.
Options
The options are aligned with enhanced-resolve.
Field | Default | Description |
---|---|---|
alias | [] | A list of module alias configurations or an object which maps key to value |
aliasFields | [] | A list of alias fields in description files |
extensionAlias | {} | An object which maps extension to extension aliases |
conditionNames | [] | A list of exports field condition names |
descriptionFiles | ["package.json"] | A list of description files to read from |
enforceExtension | false | Enforce that a extension from extensions must be used |
exportsFields | ["exports"] | A list of exports fields in description files |
extensions | [".js", ".json", ".node"] | A list of extensions which should be tried for files |
fallback | [] | Same as alias , but only used if default resolving fails |
fileSystem | The file system which should be used | |
fullySpecified | false | Request passed to resolve is already fully specified and extensions or main files are not resolved for it (they are still resolved for internal requests) |
mainFields | ["main"] | A list of main fields in description files |
mainFiles | ["index"] | A list of main files in directories |
modules | ["node_modules"] | A list of directories to resolve modules from, can be absolute path or folder name |
resolveToContext | false | Resolve to a context instead of a file |
preferRelative | false | Prefer to resolve module requests as relative request and fallback to resolving as module |
preferAbsolute | false | Prefer to resolve server-relative urls as absolute paths before falling back to resolve in roots |
restrictions | [] | A list of resolve restrictions |
roots | [] | A list of root paths |
symlinks | true | Whether to resolve symlinks to their symlinked location |
Unimplemented Options
Field | Default | Description |
---|---|---|
cachePredicate | function() { return true }; | A function which decides whether a request should be cached or not. An object is passed to the function with path and request properties. |
cacheWithContext | true | If unsafe cache is enabled, includes request.context in the cache key |
plugins | [] | A list of additional resolve plugins which should be applied |
resolver | undefined | A prepared Resolver to which the plugins are attached |
unsafeCache | false | Use this cache object to unsafely cache the successful requests |
Debugging
The following environment variable emits tracing information for the oxc_resolver::resolve
function.
e.g.
2024-06-11T07:12:20.003537Z DEBUG oxc_resolver: options: ResolveOptions { ... }, path: "...", specifier: "...", ret: "..."
at /path/to/oxc_resolver-1.8.1/src/lib.rs:212
in oxc_resolver::resolve with path: "...", specifier: "..."
The input values are options
, path
and specifier
, the returned value is ret
.
NAPI
OXC_LOG=DEBUG your_program
Rolldown
RD_LOG='oxc_resolver' rolldown build
Rspack
RSPACK_PROFILE='TRACE=filter=oxc_resolver=trace&layer=logger' rspack build
Test
Tests are ported from
- enhanced-resolve
- tsconfig-path and parcel-resolver for tsconfig-paths
Test cases are located in ./src/tests
, fixtures are located in ./tests
- alias.test.js
- browserField.test.js
- dependencies.test.js
- exportsField.test.js
- extension-alias.test.js
- extensions.test.js
- fallback.test.js
- fullSpecified.test.js
- identifier.test.js (see unit test in
crates/oxc_resolver/src/request.rs
) - importsField.test.js
- incorrect-description-file.test.js (need to add ctx.fileDependencies)
- missing.test.js
- path.test.js (see unit test in
crates/oxc_resolver/src/path.rs
) - plugins.test.js
- pnp.test.js
- resolve.test.js
- restrictions.test.js (partially done, regex is not supported yet)
- roots.test.js
- scoped-packages.test.js
- simple.test.js
- symlink.test.js
Irrelevant tests
- CachedInputFileSystem.test.js
- SyncAsyncFileSystemDecorator.test.js
- forEachBail.test.js
- getPaths.test.js
- pr-53.test.js
- unsafe-cache.test.js
- yield.test.js
Sponsored By
<p align="center"> <a href="https://github.com/sponsors/Boshen"> <img src="https://raw.githubusercontent.com/Boshen/sponsors/main/sponsors.svg" alt="My sponsors" /> </a> </p>📖 License
oxc_resolver
is free and open-source software licensed under the MIT License.
Oxc partially copies code from the following projects.
Project | License |
---|---|
webpack/enhanced-resolve | MIT |
dividab/tsconfig-paths | MIT |
parcel-bundler/parcel | MIT |
tmccombs/json-comments-rs | Apache 2.0 |