Skip to content

Commit 80adaf3

Browse files
authored
perf: cache whether a path is node_modules or inside node_modules (oxc-project#490)
1 parent 6684a64 commit 80adaf3

3 files changed

Lines changed: 30 additions & 5 deletions

File tree

src/cache.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ pub trait CachedPath: Sized {
6262

6363
fn parent(&self) -> Option<&Self>;
6464

65+
fn is_node_modules(&self) -> bool;
66+
67+
fn inside_node_modules(&self) -> bool;
68+
6569
/// Find package.json of a path by traversing parent directories.
6670
#[allow(clippy::type_complexity)]
6771
fn find_package_json<C: Cache<Cp = Self>>(

src/fs_cache.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,14 @@ impl<Fs: FileSystem> Cache for FsCache<Fs> {
6767
return entry.clone();
6868
}
6969
let parent = path.parent().map(|p| self.value(p));
70+
let is_node_modules = path.file_name().as_ref().is_some_and(|&name| name == "node_modules");
71+
let inside_node_modules =
72+
is_node_modules || parent.as_ref().is_some_and(|parent| parent.inside_node_modules);
7073
let cached_path = FsCachedPath(Arc::new(CachedPathImpl::new(
7174
hash,
7275
path.to_path_buf().into_boxed_path(),
76+
is_node_modules,
77+
inside_node_modules,
7378
parent,
7479
)));
7580
paths.insert(cached_path.clone());
@@ -262,18 +267,28 @@ pub struct CachedPathImpl {
262267
hash: u64,
263268
path: Box<Path>,
264269
parent: Option<FsCachedPath>,
270+
is_node_modules: bool,
271+
inside_node_modules: bool,
265272
meta: OnceLock<Option<FileMetadata>>,
266273
canonicalized: OnceLock<Result<FsCachedPath, ResolveError>>,
267274
canonicalizing: AtomicU64,
268275
package_json: OnceLock<Option<(FsCachedPath, Arc<PackageJsonSerde>)>>,
269276
}
270277

271278
impl CachedPathImpl {
272-
const fn new(hash: u64, path: Box<Path>, parent: Option<FsCachedPath>) -> Self {
279+
fn new(
280+
hash: u64,
281+
path: Box<Path>,
282+
is_node_modules: bool,
283+
inside_node_modules: bool,
284+
parent: Option<FsCachedPath>,
285+
) -> Self {
273286
Self {
274287
hash,
275288
path,
276289
parent,
290+
is_node_modules,
291+
inside_node_modules,
277292
meta: OnceLock::new(),
278293
canonicalized: OnceLock::new(),
279294
canonicalizing: AtomicU64::new(0),
@@ -303,6 +318,14 @@ impl CachedPath for FsCachedPath {
303318
self.0.parent.as_ref()
304319
}
305320

321+
fn is_node_modules(&self) -> bool {
322+
self.is_node_modules
323+
}
324+
325+
fn inside_node_modules(&self) -> bool {
326+
self.inside_node_modules
327+
}
328+
306329
/// Find package.json of a path by traversing parent directories.
307330
///
308331
/// # Errors

src/lib.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,13 +287,11 @@ impl<C: Cache> ResolverGeneric<C> {
287287
// Algorithm:
288288
// Find `node_modules/package/package.json`
289289
// or the first package.json if the path is not inside node_modules.
290-
// TODO(perf): cache whether a directory is inside `node_modules`?
291-
let inside_node_modules = iter::successors(Some(cached_path), |cp| cp.parent())
292-
.any(|cp| cp.path().ends_with("node_modules"));
290+
let inside_node_modules = cached_path.inside_node_modules();
293291
if inside_node_modules {
294292
let mut last = None;
295293
for cp in iter::successors(Some(cached_path), |cp| cp.parent()) {
296-
if cp.path().ends_with("node_modules") {
294+
if cp.is_node_modules() {
297295
break;
298296
}
299297
if self.cache.is_dir(cp, ctx) {

0 commit comments

Comments
 (0)