Skip to content

Commit 50cee74

Browse files
authored
perf: resolve each local file's path and test status once (#497)
1 parent 71471a8 commit 50cee74

1 file changed

Lines changed: 71 additions & 51 deletions

File tree

rs-lib/src/mappings.rs

Lines changed: 71 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright 2018-2024 the Deno authors. MIT license.
22

3+
use std::borrow::Cow;
34
use std::cell::RefCell;
45
use std::collections::HashMap;
56
use std::collections::HashSet;
@@ -49,51 +50,50 @@ impl Mappings {
4950
) -> Result<Self> {
5051
let mut mappings = HashMap::new();
5152
let mut mapped_filepaths_no_ext = HashSet::new();
53+
let local_files = LocalFile::resolve_all(specifiers)?;
5254
// the main files keep their paths relative to the main files' root so that
5355
// where the tests happen to live doesn't shift the distributed code
54-
let main_specifiers = specifiers
55-
.local
56-
.iter()
57-
.filter(|s| !specifiers.test_modules.contains(s))
58-
.cloned()
59-
.collect::<Vec<_>>();
60-
let main_base_dir = if main_specifiers.is_empty() {
61-
get_base_dir(&specifiers.local)?
62-
} else {
63-
get_base_dir(&main_specifiers)?
56+
let main_base_dir = {
57+
let mut main_dirs = local_files
58+
.iter()
59+
.filter(|f| !f.is_test)
60+
.filter_map(|f| f.file_path.parent())
61+
.peekable();
62+
if main_dirs.peek().is_some() {
63+
get_base_dir(main_dirs)?
64+
} else {
65+
get_base_dir(local_files.iter().filter_map(|f| f.file_path.parent()))?
66+
}
6467
};
6568
// the tests may be in a directory outside the main files' root, so they get
6669
// a root that encompasses both
67-
let mut test_base_dir_candidates = vec![main_base_dir.clone()];
68-
for specifier in specifiers
69-
.local
70-
.iter()
71-
.filter(|s| specifiers.test_modules.contains(s))
72-
{
73-
let file_path = url_to_file_path(specifier)?;
74-
test_base_dir_candidates.push(file_path.parent().unwrap().to_path_buf());
75-
}
76-
let test_base_dir = get_common_dir(test_base_dir_candidates);
70+
let test_base_dir = get_common_dir(
71+
std::iter::once(main_base_dir.as_path()).chain(
72+
local_files
73+
.iter()
74+
.filter(|f| f.is_test)
75+
.filter_map(|f| f.file_path.parent()),
76+
),
77+
);
7778
ensure_nonempty_base_dir(&test_base_dir)?;
7879
let mut root_local_dirs = HashSet::new();
7980

80-
for specifier in specifiers.local.iter() {
81-
let file_path = url_to_file_path(specifier)?;
82-
let base_dir = if specifiers.test_modules.contains(specifier) {
81+
for local_file in local_files.iter() {
82+
let base_dir = if local_file.is_test {
8383
&test_base_dir
8484
} else {
8585
&main_base_dir
8686
};
8787
let relative_file_path =
88-
file_path.strip_prefix(base_dir).map_err(|_| {
88+
local_file.file_path.strip_prefix(base_dir).map_err(|_| {
8989
anyhow::anyhow!(
9090
"Error stripping prefix of {} with base {}",
91-
file_path.display(),
91+
local_file.file_path.display(),
9292
base_dir.display()
9393
)
9494
})?;
9595
mappings.insert(
96-
specifier.clone(),
96+
local_file.specifier.clone(),
9797
get_mapped_file_path(
9898
MediaType::from_path(relative_file_path),
9999
relative_file_path,
@@ -217,6 +217,33 @@ impl Mappings {
217217
}
218218
}
219219

220+
/// A local file with its path and test status resolved once up front, since
221+
/// both are needed several times while working out the output paths.
222+
struct LocalFile<'a> {
223+
specifier: &'a ModuleSpecifier,
224+
file_path: PathBuf,
225+
is_test: bool,
226+
}
227+
228+
impl<'a> LocalFile<'a> {
229+
pub fn resolve_all(specifiers: &'a Specifiers) -> Result<Vec<Self>> {
230+
if specifiers.local.is_empty() {
231+
bail!("Did not find any local files. Specifying only remote files is not currently supported.");
232+
}
233+
specifiers
234+
.local
235+
.iter()
236+
.map(|specifier| {
237+
Ok(Self {
238+
specifier,
239+
file_path: url_to_file_path(specifier)?,
240+
is_test: specifiers.test_modules.contains(specifier),
241+
})
242+
})
243+
.collect()
244+
}
245+
}
246+
220247
/// Takes a group of remote specifiers for the provided base directory
221248
/// and gets their output paths.
222249
fn remote_specifiers_to_paths<'a>(
@@ -525,18 +552,10 @@ fn is_banned_segment_char(c: char) -> bool {
525552
matches!(c, '/' | '\\') || is_banned_path_char(c)
526553
}
527554

528-
fn get_base_dir(specifiers: &[ModuleSpecifier]) -> Result<PathBuf> {
529-
if specifiers.is_empty() {
530-
bail!("Did not find any local files. Specifying only remote files is not currently supported.");
531-
}
532-
let base_dir = get_common_dir(
533-
specifiers
534-
.iter()
535-
.map(|specifier| {
536-
Ok(url_to_file_path(specifier)?.parent().unwrap().into())
537-
})
538-
.collect::<Result<Vec<PathBuf>>>()?,
539-
);
555+
fn get_base_dir<'a>(
556+
dirs: impl IntoIterator<Item = &'a Path>,
557+
) -> Result<PathBuf> {
558+
let base_dir = get_common_dir(dirs);
540559
ensure_nonempty_base_dir(&base_dir)?;
541560
Ok(base_dir)
542561
}
@@ -548,17 +567,19 @@ fn ensure_nonempty_base_dir(base_dir: &Path) -> Result<()> {
548567
Ok(())
549568
}
550569

551-
fn get_common_dir(dirs: impl IntoIterator<Item = PathBuf>) -> PathBuf {
570+
fn get_common_dir<'a>(dirs: impl IntoIterator<Item = &'a Path>) -> PathBuf {
552571
// todo(dsherret): should maybe error on windows when the files
553572
// span different drives...
554573
let mut dirs = dirs.into_iter();
555-
let Some(mut base_dir) = dirs.next() else {
574+
let Some(first_dir) = dirs.next() else {
556575
return PathBuf::new();
557576
};
577+
// only allocates when the directories actually diverge
578+
let mut base_dir = Cow::Borrowed(first_dir);
558579
for parent_dir in dirs {
559580
if base_dir != parent_dir {
560-
if base_dir.starts_with(&parent_dir) {
561-
base_dir = parent_dir;
581+
if base_dir.starts_with(parent_dir) {
582+
base_dir = Cow::Borrowed(parent_dir);
562583
} else {
563584
let mut final_path = PathBuf::new();
564585
for (a, b) in base_dir.components().zip(parent_dir.components()) {
@@ -568,11 +589,11 @@ fn get_common_dir(dirs: impl IntoIterator<Item = PathBuf>) -> PathBuf {
568589
break;
569590
}
570591
}
571-
base_dir = final_path;
592+
base_dir = Cow::Owned(final_path);
572593
}
573594
}
574595
}
575-
base_dir
596+
base_dir.into_owned()
576597
}
577598

578599
#[cfg(test)]
@@ -609,13 +630,12 @@ mod test {
609630
);
610631

611632
fn run_test(urls: Vec<&str>, expected: &str) {
612-
let result = get_base_dir(
613-
&urls
614-
.into_iter()
615-
.map(|u| ModuleSpecifier::parse(u).unwrap())
616-
.collect::<Vec<_>>(),
617-
)
618-
.unwrap();
633+
let file_paths = urls
634+
.into_iter()
635+
.map(|u| url_to_file_path(&ModuleSpecifier::parse(u).unwrap()).unwrap())
636+
.collect::<Vec<_>>();
637+
let result =
638+
get_base_dir(file_paths.iter().filter_map(|p| p.parent())).unwrap();
619639
assert_eq!(result, PathBuf::from(expected));
620640
}
621641
}

0 commit comments

Comments
 (0)