diff --git a/src/cache.rs b/src/cache.rs index 8d54f362..76cff20f 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -24,25 +24,61 @@ use crate::{ #[derive(Default)] pub struct Cache { pub(crate) fs: Fs, + exact_paths: DashMap, BuildHasherDefault>, paths: DashSet>, + use_exact_paths: bool, tsconfigs: DashMap, BuildHasherDefault>, } impl Cache { - pub fn new(fs: Fs) -> Self { + pub fn new(fs: Fs, use_exact_paths: bool) -> Self { Self { fs, + exact_paths: DashMap::default(), paths: DashSet::default(), + use_exact_paths, tsconfigs: DashMap::default(), } } pub fn clear(&self) { + self.exact_paths.clear(); self.paths.clear(); self.tsconfigs.clear(); } pub fn value(&self, path: &Path) -> CachedPath { + if self.use_exact_paths { + return self.value_exact(path); + } + self.value_semantic(path) + } + + fn value_exact(&self, path: &Path) -> CachedPath { + let hash = { + let mut hasher = FxHasher::default(); + path.as_os_str().hash(&mut hasher); + hasher.finish() + }; + if let Some(cache_entry) = self.exact_paths.get(&hash).and_then(|cache_entries| { + cache_entries + .iter() + .find(|cache_entry| cache_entry.path().as_os_str() == path.as_os_str()) + .cloned() + }) { + return cache_entry; + } + let parent = path.parent().map(|p| self.value_exact(p)); + let data = CachedPath(Arc::new(CachedPathImpl::new( + hash, + path.to_path_buf().into_boxed_path(), + parent, + ))); + self.exact_paths.entry(hash).or_default().push(data.clone()); + data + } + + fn value_semantic(&self, path: &Path) -> CachedPath { let hash = { let mut hasher = FxHasher::default(); path.hash(&mut hasher); @@ -51,7 +87,7 @@ impl Cache { if let Some(cache_entry) = self.paths.get((hash, path).borrow() as &dyn CacheKey) { return cache_entry.clone(); } - let parent = path.parent().map(|p| self.value(p)); + let parent = path.parent().map(|p| self.value_semantic(p)); let data = CachedPath(Arc::new(CachedPathImpl::new( hash, path.to_path_buf().into_boxed_path(), diff --git a/src/file_system.rs b/src/file_system.rs index feaeaa92..ea932ac7 100644 --- a/src/file_system.rs +++ b/src/file_system.rs @@ -9,6 +9,8 @@ use cfg_if::cfg_if; use pnp::fs::LruZipCache; #[cfg(all(feature = "yarn_pnp", not(target_arch = "wasm32")))] use pnp::fs::{VPath, VPathInfo, ZipCache}; +#[cfg(not(target_arch = "wasm32"))] +use tokio::runtime::{Handle, RuntimeFlavor}; /// File System abstraction used for `ResolverGeneric` #[async_trait::async_trait] @@ -144,6 +146,11 @@ impl FileSystemOs { } } +#[cfg(not(target_arch = "wasm32"))] +fn use_direct_fs_calls() -> bool { + Handle::try_current().is_ok_and(|handle| handle.runtime_flavor() == RuntimeFlavor::CurrentThread) +} + #[cfg(not(target_arch = "wasm32"))] #[async_trait::async_trait] impl FileSystem for FileSystemOs { @@ -159,6 +166,10 @@ impl FileSystem for FileSystemOs { } }} + if use_direct_fs_calls() { + return fs::read(path); + } + tokio::fs::read(path).await } @@ -174,6 +185,9 @@ impl FileSystem for FileSystemOs { } } } + if use_direct_fs_calls() { + return fs::read_to_string(path); + } tokio::fs::read_to_string(path).await } @@ -197,10 +211,18 @@ impl FileSystem for FileSystemOs { } } + if use_direct_fs_calls() { + return fs::metadata(path).map(FileMetadata::from); + } + tokio::fs::metadata(path).await.map(FileMetadata::from) } async fn symlink_metadata(&self, path: &Path) -> io::Result { + if use_direct_fs_calls() { + return fs::symlink_metadata(path).map(FileMetadata::from); + } + tokio::fs::symlink_metadata(path) .await .map(FileMetadata::from) diff --git a/src/lib.rs b/src/lib.rs index 6ca6ec6d..967918a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -137,9 +137,20 @@ impl Default for ResolverGeneric { impl ResolverGeneric { pub fn new(options: ResolveOptions) -> Self { + let options = options.sanitize(); + let use_exact_paths = { + #[cfg(feature = "yarn_pnp")] + { + !options.enable_pnp + } + #[cfg(not(feature = "yarn_pnp"))] + { + true + } + }; Self { - options: options.sanitize(), - cache: Arc::new(Cache::new(Fs::default())), + options, + cache: Arc::new(Cache::new(Fs::default(), use_exact_paths)), #[cfg(feature = "yarn_pnp")] pnp_manifest: Arc::new(arc_swap::ArcSwapOption::empty()), #[cfg(feature = "yarn_pnp")] @@ -151,9 +162,20 @@ impl ResolverGeneric { impl ResolverGeneric { pub fn new_with_file_system(file_system: Fs, options: ResolveOptions) -> Self { + let options = options.sanitize(); + let use_exact_paths = { + #[cfg(feature = "yarn_pnp")] + { + !options.enable_pnp + } + #[cfg(not(feature = "yarn_pnp"))] + { + true + } + }; Self { - options: options.sanitize(), - cache: Arc::new(Cache::new(file_system)), + options, + cache: Arc::new(Cache::new(file_system, use_exact_paths)), #[cfg(feature = "yarn_pnp")] pnp_manifest: Arc::new(arc_swap::ArcSwapOption::empty()), #[cfg(feature = "yarn_pnp")]