From cac4e734615dc701ba5a3d9cd0f48ffdada97260 Mon Sep 17 00:00:00 2001 From: Boshen Date: Mon, 17 Jul 2023 15:16:08 +0800 Subject: [PATCH] feat(resolver): implement browser field (#561) --- crates/oxc_resolver/README.md | 3 +- crates/oxc_resolver/src/error.rs | 28 ++++++- crates/oxc_resolver/src/file_system.rs | 29 +++++-- crates/oxc_resolver/src/lib.rs | 80 +++++++++++------- crates/oxc_resolver/src/package_json.rs | 84 +++++++++++++------ crates/oxc_resolver/src/request.rs | 6 +- .../enhanced_resolve/test/browser_field.rs | 70 ++++++++++++++++ .../enhanced_resolve/test/extension_alias.rs | 8 +- .../tests/enhanced_resolve/test/extensions.rs | 37 ++++---- .../tests/enhanced_resolve/test/mod.rs | 1 + .../tests/enhanced_resolve/test/resolve.rs | 1 + .../enhanced_resolve/test/scoped_packages.rs | 8 +- .../tests/enhanced_resolve/test/simple.rs | 14 ++-- 13 files changed, 263 insertions(+), 106 deletions(-) create mode 100644 crates/oxc_resolver/tests/enhanced_resolve/test/browser_field.rs diff --git a/crates/oxc_resolver/README.md b/crates/oxc_resolver/README.md index 926634c66..0fbc8ff1c 100644 --- a/crates/oxc_resolver/README.md +++ b/crates/oxc_resolver/README.md @@ -3,6 +3,7 @@ ## TODO - [ ] use `thiserror` for better error messages +- [ ] copy API documentation from webpack https://webpack.js.org/configuration/resolve/#resolve #### Resolver Options @@ -41,7 +42,7 @@ Tests ported from [enhanced-resolve](https://github.com/webpack/enhanced-resolve - [ ] CachedInputFileSystem.test.js - [ ] SyncAsyncFileSystemDecorator.test.js - [ ] alias.test.js -- [ ] browserField.test.js +- [x] browserField.test.js (reading the browser field is currently static - not read from the `browserField` option) - [ ] dependencies.test.js - [ ] exportsField.test.js - [x] extension-alias.test.js diff --git a/crates/oxc_resolver/src/error.rs b/crates/oxc_resolver/src/error.rs index e1d2bf694..43a918c04 100644 --- a/crates/oxc_resolver/src/error.rs +++ b/crates/oxc_resolver/src/error.rs @@ -1,16 +1,36 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use crate::request::RequestError; -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum ResolveError { - NotFound, + /// Path not found + NotFound(Box), + + /// Ignored path + /// + /// Derived from ignored path (false value) from browser field in package.json + /// ```json + /// { + /// "browser": { + /// "./module": false + /// } + /// } + /// ``` + /// See + Ignored(Box), + + /// The provided path request cannot be parsed Request(RequestError), + + /// All of the aliased extension are not found ExtensionAlias, + + /// JSON parse error JSON(JSONError), } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct JSONError { pub path: PathBuf, pub message: String, diff --git a/crates/oxc_resolver/src/file_system.rs b/crates/oxc_resolver/src/file_system.rs index bb63b71d1..c0c951d8a 100644 --- a/crates/oxc_resolver/src/file_system.rs +++ b/crates/oxc_resolver/src/file_system.rs @@ -1,10 +1,9 @@ -use std::{ - fs, - path::{Path, PathBuf}, -}; +use std::{fs, path::Path, sync::Arc}; use dashmap::DashMap; +use crate::{package_json::PackageJson, ResolveError}; + #[derive(Debug, Clone, Copy)] pub struct FileMetadata { is_file: bool, @@ -13,23 +12,35 @@ pub struct FileMetadata { /// [File System](https://doc.rust-lang.org/stable/std/fs/) with caching #[derive(Default)] pub struct FileSystem { - cache: DashMap>, + cache: DashMap, Option>, + package_json_cache: DashMap, Result, ResolveError>>, } impl FileSystem { /// - pub fn metadata>(&self, path: P) -> Option { - let path = path.as_ref(); + pub fn metadata(&self, path: &Path) -> Option { if let Some(result) = self.cache.get(path) { return *result; } let file_metadata = fs::metadata(path).ok().map(|metadata| FileMetadata { is_file: metadata.is_file() }); - self.cache.insert(path.to_path_buf(), file_metadata); + self.cache.insert(path.to_path_buf().into_boxed_path(), file_metadata); file_metadata } - pub fn is_file>(&self, path: P) -> bool { + pub fn is_file(&self, path: &Path) -> bool { self.metadata(path).is_some_and(|m| m.is_file) } + + pub fn read_package_json(&self, path: &Path) -> Result, ResolveError> { + if let Some(result) = self.package_json_cache.get(path) { + return result.value().clone(); + } + let package_json_string = fs::read_to_string(path).unwrap(); + let result = PackageJson::parse(path.to_path_buf(), &package_json_string) + .map(Arc::new) + .map_err(|error| ResolveError::from_serde_json_error(path.to_path_buf(), &error)); + self.package_json_cache.insert(path.to_path_buf().into_boxed_path(), result.clone()); + result + } } diff --git a/crates/oxc_resolver/src/lib.rs b/crates/oxc_resolver/src/lib.rs index d33aaee48..fe659dea0 100644 --- a/crates/oxc_resolver/src/lib.rs +++ b/crates/oxc_resolver/src/lib.rs @@ -1,8 +1,10 @@ //! # Oxc Resolver //! -//! Tests ported from [enhanced-resolve](https://github.com/webpack/enhanced-resolve). +//! ## References: //! -//! Algorithm from . +//! * Tests ported from [enhanced-resolve](https://github.com/webpack/enhanced-resolve) +//! * Algorithm adapted from [Node.js Module Resolution Algorithm](https://nodejs.org/api/modules.html#all-together) and [cjs loader](https://github.com/nodejs/node/blob/main/lib/internal/modules/cjs/loader.js) +//! * Some code adapted from [parcel-resolver](https://github.com/parcel-bundler/parcel/blob/v2/packages/utils/node-resolver-rs) mod error; mod file_system; @@ -13,7 +15,6 @@ mod request; use std::{ ffi::OsStr, - fs, path::{Path, PathBuf}, }; @@ -23,7 +24,6 @@ pub use crate::{ }; use crate::{ file_system::FileSystem, - package_json::PackageJson, path::PathUtil, request::{Request, RequestPath}, }; @@ -80,7 +80,7 @@ impl Resolver { /// /// # Errors /// - /// * Will return `Err` for [ResolveError] + /// * See [ResolveError] pub fn resolve>( &self, path: P, @@ -114,6 +114,9 @@ impl Resolver { } // 3. If X begins with './' or '/' or '../' RequestPath::Relative(relative_path) => { + if let Some(path) = self.load_package_self(path, relative_path)? { + return Ok(path); + } let path = path.normalize_with(relative_path); // a. LOAD_AS_FILE(Y + X) if !relative_path.ends_with('/') { @@ -126,7 +129,7 @@ impl Resolver { return Ok(path); } // c. THROW "not found" - return Err(ResolveError::NotFound); + return Err(ResolveError::NotFound(path.into_boxed_path())); } // 4. If X begins with '#' // a. LOAD_PACKAGE_IMPORTS(X, dirname(Y)) @@ -135,6 +138,9 @@ impl Resolver { } } // 5. LOAD_PACKAGE_SELF(X, dirname(Y)) + if let Some(path) = self.load_package_self(path, request_str)? { + return Ok(path); + } // 6. LOAD_NODE_MODULES(X, dirname(Y)) if let Some(path) = self.load_node_modules(path, request_str)? { return Ok(path); @@ -143,7 +149,7 @@ impl Resolver { return Ok(path); } // 7. THROW "not found" - Err(ResolveError::NotFound) + Err(ResolveError::NotFound(path.to_path_buf().into_boxed_path())) } #[allow(clippy::unnecessary_wraps)] @@ -170,15 +176,12 @@ impl Resolver { } #[allow(clippy::unnecessary_wraps)] - fn load_index(&self, path: &Path, package_json: Option<&PackageJson>) -> ResolveState { + fn load_index(&self, path: &Path) -> ResolveState { // 1. If X/index.js is a file, load X/index.js as JavaScript text. STOP // 2. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP // 3. If X/index.node is a file, load X/index.node as binary addon. STOP for extension in &self.options.extensions { - let mut index_path = path.join("index").with_extension(extension); - if let Some(resolved_path) = package_json.and_then(|p| p.resolve(&index_path)) { - index_path = resolved_path; - } + let index_path = path.join("index").with_extension(extension); if self.fs.is_file(&index_path) { return Ok(Some(index_path)); } @@ -191,9 +194,7 @@ impl Resolver { let package_json_path = path.join("package.json"); if self.fs.is_file(&package_json_path) { // a. Parse X/package.json, and look for "main" field. - let package_json_string = fs::read_to_string(&package_json_path).unwrap(); - let package_json = PackageJson::parse(package_json_path.clone(), &package_json_string) - .map_err(|error| ResolveError::from_serde_json_error(package_json_path, &error))?; + let package_json = self.fs.read_package_json(&package_json_path)?; // b. If "main" is a falsy value, GOTO 2. if let Some(main_field) = &package_json.main { // c. let M = X + (json main field) @@ -203,33 +204,31 @@ impl Resolver { return Ok(Some(path)); } // e. LOAD_INDEX(M) - if let Some(path) = self.load_index(&main_field_path, Some(&package_json))? { + if let Some(path) = self.load_index(&main_field_path)? { return Ok(Some(path)); } // f. LOAD_INDEX(X) DEPRECATED // g. THROW "not found" - return Err(ResolveError::NotFound); + return Err(ResolveError::NotFound(main_field_path.into_boxed_path())); } - // 2. LOAD_INDEX(X) - self.load_index(path, Some(&package_json)) - } else { - // 2. LOAD_INDEX(X) - self.load_index(path, None) } + // 2. LOAD_INDEX(X) + self.load_index(path) } fn load_node_modules(&self, start: &Path, request_str: &str) -> ResolveState { - const NODE_MODULES: &str = "node_modules"; // 1. let DIRS = NODE_MODULES_PATHS(START) - let dirs = start - .ancestors() - .filter(|path| path.file_name().is_some_and(|name| name != NODE_MODULES)); + let dirs = Self::node_module_paths(start); // 2. for each DIR in DIRS: - for dir in dirs { - let node_module_path = dir.join(NODE_MODULES); + for node_module_path in dirs { + let node_module_path = node_module_path.join(request_str); + for main_file in &self.options.main_files { + if let Some(path) = self.load_package_self(&node_module_path, main_file)? { + return Ok(Some(path)); + } + } // a. LOAD_PACKAGE_EXPORTS(X, DIR) // b. LOAD_AS_FILE(DIR/X) - let node_module_path = node_module_path.join(request_str); if !request_str.ends_with('/') { if let Some(path) = self.load_as_file(&node_module_path)? { return Ok(Some(path)); @@ -243,6 +242,29 @@ impl Resolver { Ok(None) } + fn node_module_paths(path: &Path) -> impl Iterator + '_ { + path.ancestors() + .filter(|path| path.file_name().is_some_and(|name| name != "node_modules")) + .map(|path| path.join("node_modules")) + } + + fn load_package_self(&self, path: &Path, request_str: &str) -> ResolveState { + for dir in path.ancestors() { + let package_json_path = dir.join("package.json"); + if self.fs.is_file(&package_json_path) { + let package_json = self.fs.read_package_json(&package_json_path)?; + if let Some(request_str) = + package_json.resolve_request(path, request_str, &self.options.extensions)? + { + let request = Request::parse(request_str).map_err(ResolveError::Request)?; + // TODO: Do we need to pass query and fragment? + return self.require(dir, &request).map(Some); + } + } + } + Ok(None) + } + /// Given an extension alias map `{".js": [".ts", "js"]}`, /// load the mapping instead of the provided extension /// diff --git a/crates/oxc_resolver/src/package_json.rs b/crates/oxc_resolver/src/package_json.rs index 831bbe75a..5c3bf743d 100644 --- a/crates/oxc_resolver/src/package_json.rs +++ b/crates/oxc_resolver/src/package_json.rs @@ -5,52 +5,88 @@ use std::{ use serde::Deserialize; -use crate::path::PathUtil; +use crate::{path::PathUtil, ResolveError}; +// TODO: allocate everything into an arena or SoA #[derive(Debug, Deserialize)] -pub struct PackageJson<'a> { +pub struct PackageJson { #[serde(skip)] pub path: PathBuf, - pub main: Option<&'a str>, - pub browser: Option>, + pub main: Option, + pub browser: Option, } #[derive(Debug, Deserialize)] #[serde(untagged)] -pub enum BrowserField<'a> { - String(&'a str), - Map(HashMap<&'a str, &'a str>), +pub enum BrowserField { + String(String), + Map(HashMap), } -impl<'a> PackageJson<'a> { - pub fn parse(path: PathBuf, json: &'a str) -> Result, serde_json::Error> { - let mut package_json: PackageJson = serde_json::from_str(json)?; +impl PackageJson { + pub fn parse(path: PathBuf, json: &str) -> Result { + let mut package_json: Self = serde_json::from_str(json)?; package_json.path = path; Ok(package_json) } - pub fn resolve(&self, path: &Path) -> Option { - // TODO: return ResolveError if the provided `alias_fields` is not `browser` for future - // proof - let browser_field = self.browser.as_ref()?; - match browser_field { - BrowserField::Map(map) => { + /// Resolve the request string for this package.json by looking at the `browser` field. + /// + /// # Errors + /// + /// * Returns [ResolveError::Ignored] for `"path": false` in `browser` field. + pub fn resolve_request( + &self, + path: &Path, + request_str: &str, + extensions: &[String], + ) -> Result, ResolveError> { + // TODO: return ResolveError if the provided `alias_fields` is not `browser` for future proof + match self.browser.as_ref() { + Some(BrowserField::Map(map)) => { for (key, value) in map { - let resolved_path = self.resolve_browser_field(key, value, path); - if resolved_path.is_some() { - return resolved_path; + if let Some(resolved_str) = + self.resolve_browser_field(path, key, value, request_str, extensions)? + { + return Ok(Some(resolved_str)); } } - None + Ok(None) } // TODO: implement - BrowserField::String(_) => None, + _ => Ok(None), } } - fn resolve_browser_field(&self, key: &str, value: &str, path: &Path) -> Option { + // TODO: refactor this mess + fn resolve_browser_field<'a>( + &'a self, + start: &Path, + key: &str, + value: &'a serde_json::Value, + request_str: &str, + extensions: &[String], + ) -> Result, ResolveError> { let directory = self.path.parent().unwrap(); // `unwrap`: this is a path to package.json, parent is its containing directory - // TODO: cache this join - (directory.join(key).normalize() == path).then(|| directory.join(value).normalize()) + let right = directory.join(key).normalize(); + let left = start.join(request_str).normalize(); + if key == request_str + || extensions + .iter() + .any(|ext| Path::new(request_str).with_extension(ext) == Path::new(key)) + || right == left + || extensions.iter().any(|ext| left.with_extension(ext) == right) + { + if let serde_json::Value::String(value) = value { + return Ok(Some(value.as_str())); + } + + // key match without string value, i.e. `"path": false` for ignore + let directory = self.path.parent().unwrap(); // `unwrap`: this is a path to package.json, parent is its containing directory + let path_key = directory.join(key).normalize(); + return Err(ResolveError::Ignored(path_key.into_boxed_path())); + } + + Ok(None) } } diff --git a/crates/oxc_resolver/src/request.rs b/crates/oxc_resolver/src/request.rs index e66d50326..b877c1757 100644 --- a/crates/oxc_resolver/src/request.rs +++ b/crates/oxc_resolver/src/request.rs @@ -1,16 +1,16 @@ -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum RequestError { Empty, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct Request<'a> { pub path: RequestPath<'a>, pub query: Option<&'a str>, pub fragment: Option<&'a str>, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum RequestPath<'a> { /// `/path` Absolute(&'a str), diff --git a/crates/oxc_resolver/tests/enhanced_resolve/test/browser_field.rs b/crates/oxc_resolver/tests/enhanced_resolve/test/browser_field.rs new file mode 100644 index 000000000..bacda192d --- /dev/null +++ b/crates/oxc_resolver/tests/enhanced_resolve/test/browser_field.rs @@ -0,0 +1,70 @@ +//! + +use std::path::PathBuf; + +use oxc_resolver::{ResolveError, ResolveOptions, Resolver}; + +fn fixture() -> PathBuf { + super::fixture().join("browser-module") +} + +#[test] +fn ignore() { + let f = fixture(); + + let options = ResolveOptions { + alias_fields: vec!["browser".into(), "innerBrowser1".into(), "innerBrowser2".into()], + ..ResolveOptions::default() + }; + + let resolver = Resolver::new(options); + + #[rustfmt::skip] + let data = [ + (f.clone(), "./lib/ignore", f.join("lib/ignore.js")), + (f.clone(), "./lib/ignore.js", f.join("lib/ignore.js")), + (f.join("lib"), "./ignore", f.join("lib/ignore.js")), + (f.join("lib"), "./ignore.js", f.join("lib/ignore.js")), + ]; + + for (path, request, expected) in data { + let resolution = resolver.resolve(&path, request); + let expected = ResolveError::Ignored(expected.into()); + assert_eq!(resolution, Err(expected), "{path:?} {request}"); + } +} + +#[test] +fn replace_file() -> Result<(), ResolveError> { + let f = fixture(); + + let options = + ResolveOptions { alias_fields: vec!["browser".into()], ..ResolveOptions::default() }; + + let resolver = Resolver::new(options); + + #[rustfmt::skip] + let data = [ + ("should replace a file 1", f.clone(), "./lib/replaced", f.join("lib/browser.js")), + ("should replace a file 2", f.clone(), "./lib/replaced.js", f.join("lib/browser.js")), + ("should replace a file 3", f.join("lib"), "./replaced", f.join("lib/browser.js")), + ("should replace a file 4", f.join("lib"), "./replaced.js", f.join("lib/browser.js")), + ("should replace a module with a file 1", f.clone(), "module-a", f.join("browser/module-a.js")), + ("should replace a module with a file 2", f.join("lib"), "module-a", f.join("browser/module-a.js")), + ("should replace a module with a module 1", f.clone(), "module-b", f.join("node_modules/module-c.js")), + ("should replace a module with a module 2", f.join("lib"), "module-b", f.join("node_modules/module-c.js")), + // TODO: resolve `innerBrowser1` field in `browser-module/pakckage.json` + // ("should resolve in nested property 1", f.clone(), "./lib/main1.js", f.join("lib/main.js")), + // TODO: resolve `innerBrowser2` field in `browser-module/pakckage.json` + // ("should resolve in nested property 2", f.clone(), "./lib/main2.js", f.join("lib/browser.js")), + ("should check only alias field properties", f.clone(), "./toString", f.join("lib/toString.js")), + ]; + + for (comment, path, request, expected) in data { + let resolution = resolver.resolve(&path, request)?; + let resolved_path = resolution.path(); + assert_eq!(resolved_path, expected, "{comment} {path:?} {request}"); + } + + Ok(()) +} diff --git a/crates/oxc_resolver/tests/enhanced_resolve/test/extension_alias.rs b/crates/oxc_resolver/tests/enhanced_resolve/test/extension_alias.rs index e0bcb35ff..8d3661c27 100644 --- a/crates/oxc_resolver/tests/enhanced_resolve/test/extension_alias.rs +++ b/crates/oxc_resolver/tests/enhanced_resolve/test/extension_alias.rs @@ -10,7 +10,9 @@ fn fixture() -> PathBuf { #[test] fn extension_alias() -> Result<(), ResolveError> { - let options = ResolveOptions { + let f = fixture(); + + let resolver = Resolver::new(ResolveOptions { extensions: vec![".js".into()], main_files: vec!["index.js".into()], extension_alias: vec![ @@ -18,9 +20,7 @@ fn extension_alias() -> Result<(), ResolveError> { (".mjs".into(), vec![".mts".into()]), ], ..ResolveOptions::default() - }; - let resolver = Resolver::new(options); - let f = fixture(); + }); #[rustfmt::skip] let pass = [ diff --git a/crates/oxc_resolver/tests/enhanced_resolve/test/extensions.rs b/crates/oxc_resolver/tests/enhanced_resolve/test/extensions.rs index f12f3e0ef..cf9857520 100644 --- a/crates/oxc_resolver/tests/enhanced_resolve/test/extensions.rs +++ b/crates/oxc_resolver/tests/enhanced_resolve/test/extensions.rs @@ -10,43 +10,38 @@ fn fixture() -> PathBuf { #[test] fn extensions() -> Result<(), ResolveError> { - let fixture = fixture(); + let f = fixture(); - let options = ResolveOptions { + let resolver = Resolver::new(ResolveOptions { extensions: vec![".ts".into(), ".js".into()], ..ResolveOptions::default() - }; - - let resolver = Resolver::new(options); + }); + #[rustfmt::skip] let pass = [ ("should resolve according to order of provided extensions", "./foo", "foo.ts"), - ( - "should resolve according to order of provided extensions (dir index)", - "./dir", - "dir/index.ts", - ), + ("should resolve according to order of provided extensions (dir index)", "./dir", "dir/index.ts"), ("should resolve according to main field in module root", ".", "index.js"), ("should resolve single file module before directory", "module", "node_modules/module.js"), - ( - "should resolve trailing slash directory before single file", - "module/", - "node_modules/module/index.ts", - ), + ("should resolve trailing slash directory before single file", "module/", "node_modules/module/index.ts"), ]; for (comment, request, expected_path) in pass { - let resolution = resolver.resolve(&fixture, request)?; + let resolution = resolver.resolve(&f, request)?; let path = resolution.path().canonicalize().unwrap(); - let expected = fixture.join(expected_path).canonicalize().unwrap(); + let expected = f.join(expected_path).canonicalize().unwrap(); assert_eq!(path, expected, "{comment} {request} {expected_path}"); } - let fail = [("not resolve to file when request has a trailing slash (relative)", "./foo.js/")]; + #[rustfmt::skip] + let fail = [ + ("not resolve to file when request has a trailing slash (relative)", "./foo.js/", f.join("foo.js")) + ]; - for (comment, request) in fail { - let resolution = resolver.resolve(&fixture, request); - assert!(resolution.is_err(), "{comment} {request} {resolution:?}"); + for (comment, request, expected_error) in fail { + let resolution = resolver.resolve(&f, request); + let error = ResolveError::NotFound(expected_error.into_boxed_path()); + assert_eq!(resolution, Err(error), "{comment} {request} {resolution:?}"); } Ok(()) diff --git a/crates/oxc_resolver/tests/enhanced_resolve/test/mod.rs b/crates/oxc_resolver/tests/enhanced_resolve/test/mod.rs index 01225e418..21b53e972 100644 --- a/crates/oxc_resolver/tests/enhanced_resolve/test/mod.rs +++ b/crates/oxc_resolver/tests/enhanced_resolve/test/mod.rs @@ -1,3 +1,4 @@ +mod browser_field; mod extension_alias; mod extensions; mod incorrect_description_file; diff --git a/crates/oxc_resolver/tests/enhanced_resolve/test/resolve.rs b/crates/oxc_resolver/tests/enhanced_resolve/test/resolve.rs index ea8d116eb..cc3c00a19 100644 --- a/crates/oxc_resolver/tests/enhanced_resolve/test/resolve.rs +++ b/crates/oxc_resolver/tests/enhanced_resolve/test/resolve.rs @@ -5,6 +5,7 @@ use oxc_resolver::{ResolveError, Resolver}; #[test] fn resolve() -> Result<(), ResolveError> { let f = super::fixture(); + let resolver = Resolver::default(); let main1_js_path = f.join("main1.js").to_string_lossy().to_string(); diff --git a/crates/oxc_resolver/tests/enhanced_resolve/test/scoped_packages.rs b/crates/oxc_resolver/tests/enhanced_resolve/test/scoped_packages.rs index a686c63d1..97f37c74d 100644 --- a/crates/oxc_resolver/tests/enhanced_resolve/test/scoped_packages.rs +++ b/crates/oxc_resolver/tests/enhanced_resolve/test/scoped_packages.rs @@ -12,10 +12,10 @@ fn fixture() -> PathBuf { fn scoped_packages() -> Result<(), ResolveError> { let f = fixture(); - let options = - ResolveOptions { alias_fields: vec!["browser".into()], ..ResolveOptions::default() }; - - let resolver = Resolver::new(options); + let resolver = Resolver::new(ResolveOptions { + alias_fields: vec!["browser".into()], + ..ResolveOptions::default() + }); #[rustfmt::skip] let pass = [ diff --git a/crates/oxc_resolver/tests/enhanced_resolve/test/simple.rs b/crates/oxc_resolver/tests/enhanced_resolve/test/simple.rs index 983ecbc6e..ee659d5b8 100644 --- a/crates/oxc_resolver/tests/enhanced_resolve/test/simple.rs +++ b/crates/oxc_resolver/tests/enhanced_resolve/test/simple.rs @@ -6,21 +6,21 @@ use oxc_resolver::{ResolveError, Resolver}; #[test] fn simple() -> Result<(), ResolveError> { + // mimic `enhanced-resolve/test/simple.test.js` + let f = env::current_dir().unwrap().join("tests/enhanced_resolve/test/"); + let resolver = Resolver::default(); - // mimic `enhanced-resolve/test/simple.test.js` - let dirname = env::current_dir().unwrap().join("tests/enhanced_resolve/test/"); - let data = [ - ("direct", dirname.clone(), "../lib/index"), - ("as directory", dirname.clone(), ".."), - ("as module", dirname.join("../../").canonicalize().unwrap(), "./enhanced_resolve"), + ("direct", f.clone(), "../lib/index"), + ("as directory", f.clone(), ".."), + ("as module", f.join("../../").canonicalize().unwrap(), "./enhanced_resolve"), ]; for (comment, path, request) in data { let resolution = resolver.resolve(&path, request)?; let resolved_path = resolution.path().canonicalize().unwrap(); - let expected = dirname.join("../lib/index.js").canonicalize().unwrap(); + let expected = f.join("../lib/index.js").canonicalize().unwrap(); assert_eq!(resolved_path, expected, "{comment} {path:?} {request}"); }