diff --git a/crates/oxc_resolver/src/cache.rs b/crates/oxc_resolver/src/cache.rs index 1702849dc..a49493259 100644 --- a/crates/oxc_resolver/src/cache.rs +++ b/crates/oxc_resolver/src/cache.rs @@ -1,12 +1,11 @@ use once_cell::sync::OnceCell as OnceLock; use std::{ borrow::{Borrow, Cow}, - collections::VecDeque, convert::AsRef, hash::{BuildHasherDefault, Hash, Hasher}, io, ops::Deref, - path::{Component, Path, PathBuf}, + path::{Path, PathBuf}, sync::Arc, }; @@ -14,7 +13,8 @@ use dashmap::{DashMap, DashSet}; use rustc_hash::FxHasher; use crate::{ - package_json::PackageJson, FileMetadata, FileSystem, ResolveError, ResolveOptions, TsConfig, + package_json::PackageJson, path::PathUtil, FileMetadata, FileSystem, ResolveError, + ResolveOptions, TsConfig, }; #[derive(Default)] @@ -79,56 +79,6 @@ impl Cache { }) .map(|r| Arc::clone(r.value())) } - - // Code copied from parcel - // - fn canonicalize(&self, path: &Path) -> io::Result { - let mut ret = PathBuf::new(); - let mut seen_links = 0; - let mut queue = VecDeque::new(); - queue.push_back(path.to_path_buf()); - while let Some(cur_path) = queue.pop_front() { - let mut components = cur_path.components(); - for component in &mut components { - match component { - Component::Prefix(c) => ret.push(c.as_os_str()), - Component::RootDir => { - ret.push(component.as_os_str()); - } - Component::CurDir => {} - Component::ParentDir => { - ret.pop(); - } - Component::Normal(c) => { - ret.push(c); - let cached_path = self.value(&ret); - let Some(link) = cached_path.symlink(&self.fs)? else { - continue; - }; - seen_links += 1; - if seen_links > 32 { - return Err(io::Error::new( - io::ErrorKind::NotFound, - "Too many symlinks", - )); - } - if link.is_absolute() { - ret = PathBuf::new(); - } else { - ret.pop(); - } - let remaining = components.as_path(); - if !remaining.as_os_str().is_empty() { - queue.push_front(remaining.to_path_buf()); - } - queue.push_front(link); - break; - } - } - } - } - Ok(ret) - } } #[derive(Clone)] @@ -179,7 +129,7 @@ pub struct CachedPathImpl { parent: Option, meta: OnceLock>, symlink: OnceLock>, - canonicalized: OnceLock, + canonicalized: OnceLock>, node_modules: OnceLock>, package_json: OnceLock>>, } @@ -227,7 +177,7 @@ impl CachedPathImpl { .get_or_try_init(|| { if let Ok(symlink_metadata) = fs.symlink_metadata(&self.path) { if symlink_metadata.is_symlink { - return fs.read_link(self.path()).map(Some); + return fs.canonicalize(self.path()).map(Some); } } Ok(None) @@ -235,8 +185,22 @@ impl CachedPathImpl { .cloned() } - pub fn canonicalize(&self, cache: &Cache) -> io::Result { - self.canonicalized.get_or_try_init(|| cache.canonicalize(&self.path)).cloned() + pub fn realpath(&self, fs: &Fs) -> io::Result { + self.canonicalized + .get_or_try_init(|| { + if let Some(link) = self.symlink(fs)? { + return Ok(Some(link)); + } + if let Some(parent) = self.parent() { + let parent_path = parent.realpath(fs)?; + return Ok(Some( + parent_path.normalize_with(self.path.strip_prefix(&parent.path).unwrap()), + )); + }; + Ok(None) + }) + .cloned() + .map(|r| r.unwrap_or_else(|| self.path.clone().to_path_buf())) } pub fn module_directory( diff --git a/crates/oxc_resolver/src/file_system.rs b/crates/oxc_resolver/src/file_system.rs index 7fae5303d..5362b62fb 100644 --- a/crates/oxc_resolver/src/file_system.rs +++ b/crates/oxc_resolver/src/file_system.rs @@ -26,12 +26,12 @@ pub trait FileSystem: Default + Send + Sync { /// See [std::fs::symlink_metadata] fn symlink_metadata>(&self, path: P) -> io::Result; - /// See [std::fs::read_link] + /// See [std::fs::canonicalize] /// /// # Errors /// /// See [std::fs::read_link] - fn read_link>(&self, path: P) -> io::Result; + fn canonicalize>(&self, path: P) -> io::Result; } /// Metadata information about a file. @@ -71,7 +71,7 @@ impl FileSystem for FileSystemOs { fs::symlink_metadata(path).map(FileMetadata::from) } - fn read_link>(&self, path: P) -> io::Result { - fs::read_link(path).map(|p| dunce::simplified(&p).to_path_buf()) + fn canonicalize>(&self, path: P) -> io::Result { + dunce::canonicalize(path) } } diff --git a/crates/oxc_resolver/src/lib.rs b/crates/oxc_resolver/src/lib.rs index a77de64d4..ca6fe7fdd 100644 --- a/crates/oxc_resolver/src/lib.rs +++ b/crates/oxc_resolver/src/lib.rs @@ -511,7 +511,7 @@ impl ResolverGeneric { fn load_realpath(&self, cached_path: &CachedPath) -> Result { if self.options.symlinks { - cached_path.canonicalize(&self.cache).map_err(ResolveError::from) + cached_path.realpath(&self.cache.fs).map_err(ResolveError::from) } else { Ok(cached_path.to_path_buf()) } diff --git a/crates/oxc_resolver/src/tests/memory_fs.rs b/crates/oxc_resolver/src/tests/memory_fs.rs index b0a2a541a..186327061 100644 --- a/crates/oxc_resolver/src/tests/memory_fs.rs +++ b/crates/oxc_resolver/src/tests/memory_fs.rs @@ -66,7 +66,7 @@ impl FileSystem for MemoryFS { self.metadata(path) } - fn read_link>(&self, _path: P) -> io::Result { + fn canonicalize>(&self, _path: P) -> io::Result { Err(io::Error::new(io::ErrorKind::NotFound, "not a symlink")) } } diff --git a/crates/oxc_resolver/src/tests/symlink.rs b/crates/oxc_resolver/src/tests/symlink.rs index a91eb0c98..4e76aa08f 100644 --- a/crates/oxc_resolver/src/tests/symlink.rs +++ b/crates/oxc_resolver/src/tests/symlink.rs @@ -109,13 +109,8 @@ fn test() -> io::Result<()> { ]; for (comment, path, request) in pass { - let filename = resolver_with_symlinks.resolve(&path, request).map_or_else( - |err| { - panic!("{err:?} {comment} {path:?} {request}"); - }, - |r| r.full_path(), - ); - assert_eq!(filename, root.join("lib/index.js")); + let filename = resolver_with_symlinks.resolve(&path, request).map(|r| r.full_path()); + assert_eq!(filename, Ok(root.join("lib/index.js")), "{comment:?}"); let resolved_path = resolver_without_symlinks.resolve(&path, request).map(|r| r.full_path());