Skip to content

Commit bc95879

Browse files
authored
perf: short circuit remapping detection on recursive symlink (gakonst#1225)
1 parent a656830 commit bc95879

File tree

1 file changed

+31
-3
lines changed

1 file changed

+31
-3
lines changed

ethers-solc/src/remappings.rs

+31-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::utils;
12
use serde::{Deserialize, Serialize};
23
use std::{
34
collections::{hash_map::Entry, HashMap},
@@ -172,13 +173,15 @@ impl Remapping {
172173

173174
let dir = dir.as_ref();
174175
let is_inside_node_modules = dir.ends_with("node_modules");
176+
175177
// iterate over all dirs that are children of the root
176178
for dir in walkdir::WalkDir::new(dir)
177179
.follow_links(true)
178180
.min_depth(1)
179181
.max_depth(1)
180182
.into_iter()
181-
.filter_map(std::result::Result::ok)
183+
.filter_entry(|e| !is_hidden(e))
184+
.filter_map(Result::ok)
182185
.filter(|e| e.file_type().is_dir())
183186
{
184187
let depth1_dir = dir.path();
@@ -487,6 +490,11 @@ fn is_lib_dir(dir: &Path) -> bool {
487490
.unwrap_or_default()
488491
}
489492

493+
/// Returns true if the file is _hidden_
494+
fn is_hidden(entry: &walkdir::DirEntry) -> bool {
495+
entry.file_name().to_str().map(|s| s.starts_with('.')).unwrap_or(false)
496+
}
497+
490498
/// Finds all remappings in the directory recursively
491499
fn find_remapping_candidates(
492500
current_dir: &Path,
@@ -506,8 +514,8 @@ fn find_remapping_candidates(
506514
.min_depth(1)
507515
.max_depth(1)
508516
.into_iter()
509-
.filter_map(std::result::Result::ok)
510-
.filter(|entry| !entry.file_name().to_str().map(|s| s.starts_with('.')).unwrap_or(false))
517+
.filter_entry(|e| !is_hidden(e))
518+
.filter_map(Result::ok)
511519
{
512520
let entry: walkdir::DirEntry = entry;
513521

@@ -518,6 +526,26 @@ fn find_remapping_candidates(
518526
{
519527
is_candidate = true;
520528
} else if entry.file_type().is_dir() {
529+
// if the dir is a symlink to a parent dir we short circuit here
530+
// `walkdir` will catch symlink loops, but this check prevents that we end up scanning a
531+
// workspace like
532+
// ```text
533+
// my-package/node_modules
534+
// ├── dep/node_modules
535+
// ├── symlink to `my-package`
536+
// ```
537+
if entry.path_is_symlink() {
538+
if let Ok(target) = utils::canonicalize(entry.path()) {
539+
// the symlink points to a parent dir of the current window
540+
if open.components().count() > target.components().count() &&
541+
utils::common_ancestor(open, &target).is_some()
542+
{
543+
// short-circuiting
544+
return Vec::new()
545+
}
546+
}
547+
}
548+
521549
let subdir = entry.path();
522550
// we skip commonly used subdirs that should not be searched for recursively
523551
if !(subdir.ends_with("tests") || subdir.ends_with("test") || subdir.ends_with("demo"))

0 commit comments

Comments
 (0)