diff --git a/examples/resolver.rs b/examples/resolver.rs index 454698a3..e77aa053 100644 --- a/examples/resolver.rs +++ b/examples/resolver.rs @@ -1,15 +1,18 @@ +///! See documentation at use std::{env, path::PathBuf}; use oxc_resolver::{AliasValue, ResolveOptions, Resolver}; fn main() { - // Path to directory, must be in absolute path. - let path = env::args().nth(1).expect("path"); + let path = PathBuf::from(env::args().nth(1).expect("path")); + + assert!(path.is_dir(), "{path:?} must be a directory that will be resolved against."); + assert!(path.is_absolute(), "{path:?} must be an absolute path.",); + let specifier = env::args().nth(2).expect("specifier"); - let path = PathBuf::from(path).canonicalize().unwrap(); println!("path: {path:?}"); - println!("request: {specifier}"); + println!("specifier: {specifier}"); let options = ResolveOptions { alias_fields: vec![vec!["browser".into()]], diff --git a/src/lib.rs b/src/lib.rs index d8506c66..22ca5215 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,19 +1,39 @@ //! # Oxc Resolver //! -//! Node.js Module Resolution. +//! Node.js [CommonJS][cjs] and [ECMAScript][esm] Module Resolution. //! -//! All configuration options are aligned with [enhanced-resolve]. +//! A module resolution is the process of finding the file referenced by a module specifier in +//! `import "specifier"` or `require("specifier")`. +//! +//! All [configuration options](ResolveOptions) are aligned with webpack's [enhanced-resolve]. +//! +//! ## Terminology +//! +//! ### Specifier +//! +//! For [CommonJS modules][cjs], +//! the specifier is the string passed to the `require` function. e.g. `"id"` in `require("id")`. +//! +//! For [ECMAScript modules][esm], +//! the specifier of an `import` statement is the string after the `from` keyword, +//! e.g. `'specifier'` in `import 'specifier'` or `import { sep } from 'specifier'`. +//! Specifiers are also used in export from statements, and as the argument to an `import()` expression. +//! +//! This is also named "request" in some places. //! //! ## References: //! -//! * Algorithm adapted from Node.js [CommonJS Module Resolution Algorithm] and [ECMAScript Module Resolution Algorithm] -//! * Tests are ported from [enhanced-resolve] -//! * Some code adapted from [parcel-resolver] +//! * Algorithm adapted from Node.js [CommonJS Module Resolution Algorithm] and [ECMAScript Module Resolution Algorithm]. +//! * Tests are ported from [enhanced-resolve]. +//! * Some code is adapted from [parcel-resolver]. +//! * The documentation is copied from [webpack's resolve configuration](https://webpack.js.org/configuration/resolve). //! //! [enhanced-resolve]: https://github.com/webpack/enhanced-resolve //! [CommonJS Module Resolution Algorithm]: https://nodejs.org/api/modules.html#all-together //! [ECMAScript Module Resolution Algorithm]: https://nodejs.org/api/esm.html#resolution-algorithm-specification //! [parcel-resolver]: https://github.com/parcel-bundler/parcel/blob/v2/packages/utils/node-resolver-rs +//! [cjs]: https://nodejs.org/api/modules.html +//! [esm]: https://nodejs.org/api/esm.html //! //! ## Example //! @@ -70,6 +90,7 @@ use nodejs_package_json::{ImportExportField, ImportExportKey, ImportExportMap}; type ResolveResult = Result, ResolveError>; +/// Context returned from the [Resolver::resolve_with_context] API #[derive(Debug, Default, Clone)] pub struct ResolveContext { /// Files that was found on file system @@ -127,11 +148,13 @@ impl ResolverGeneric { self.cache.clear(); } - /// Resolve `specifier` at an absolute `path` + /// Resolve `specifier` at an absolute `path`. /// /// A specifier is the string passed to require or import, i.e. `require("specifier")` or `import "specifier"`. /// - /// The path must be an **absolute** path where the specifier is resolved from. + /// `path` must be an **absolute** path to a directory where the specifier is resolved against. + /// For CommonJS modules, it is the `__dirname` variable that contains the absolute path to the folder containing current module. + /// For ECMAScript modules, it is the value of `import.meta.url`. /// /// # Errors /// diff --git a/src/options.rs b/src/options.rs index 6b393531..24bed14b 100644 --- a/src/options.rs +++ b/src/options.rs @@ -14,10 +14,17 @@ pub struct ResolveOptions { pub tsconfig: Option, /// Create aliases to import or require certain modules more easily. + /// + /// An alias is used to replace a whole path or part of a path. + /// For example, to alias a commonly used `src/` folders: `vec![("@/src"), vec![AliasValue::Path("/path/to/src")]]` + /// /// A trailing $ can also be added to the given object's keys to signify an exact match. + /// + /// See [webpack's `resolve.alias` documentation](https://webpack.js.org/configuration/resolve/#resolvealias) for a list of use cases. pub alias: Alias, /// A list of alias fields in description files. + /// /// Specify a field, such as `browser`, to be parsed according to [this specification](https://github.com/defunctzombie/package-browser-field-spec). /// Can be a path to json object such as `["path", "to", "exports"]`. /// @@ -25,6 +32,7 @@ pub struct ResolveOptions { pub alias_fields: Vec>, /// Condition names for exports field which defines entry points of a package. + /// /// The key order in the exports field is significant. During condition matching, earlier entries have higher priority and take precedence over later entries. /// /// Default `[]` @@ -51,6 +59,7 @@ pub struct ResolveOptions { pub enforce_extension: EnforceExtension, /// A list of exports fields in description files. + /// /// Can be a path to a JSON object such as `["path", "to", "exports"]`. /// /// Default `[["exports"]]`. @@ -62,9 +71,12 @@ pub struct ResolveOptions { pub extension_alias: Vec<(String, Vec)>, /// Attempt to resolve these extensions in order. + /// /// If multiple files share the same name but have different extensions, /// will resolve the one with the extension listed first in the array and skip the rest. /// + /// All extensions must begin with a leading dot. + /// /// Default `[".js", ".json", ".node"]` pub extensions: Vec, @@ -330,6 +342,10 @@ impl ResolveOptions { } pub(crate) fn sanitize(mut self) -> Self { + debug_assert!( + self.extensions.iter().filter(|e| !e.is_empty()).all(|e| e.starts_with('.')), + "All extensions must start with a leading dot" + ); // Set `enforceExtension` to `true` when [ResolveOptions::extensions] contains an empty string. // See if self.enforce_extension == EnforceExtension::Auto {