Skip to content

Commit f2adad2

Browse files
committed
feat(core): update additional project directories handling
Refine workspace file handling and path utilities to properly support additional project directories throughout the native and TypeScript layers.
1 parent ad89f73 commit f2adad2

File tree

11 files changed

+195
-42
lines changed

11 files changed

+195
-42
lines changed

packages/nx/src/hasher/hash-plan-inspector.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export class HashPlanInspector {
4444
false
4545
);
4646
this.inspector = new NativeHashPlanInspector(
47+
workspaceRoot,
4748
externalReferences.allWorkspaceFiles,
4849
this.projectGraphRef,
4950
externalReferences.projectFiles

packages/nx/src/native/index.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export declare class FileLock {
4343
}
4444

4545
export declare class HashPlanInspector {
46-
constructor(allWorkspaceFiles: ExternalObject<Array<FileData>>, projectGraph: ExternalObject<ProjectGraph>, projectFileMap: ExternalObject<Record<string, Array<FileData>>>)
46+
constructor(workspaceRoot: string, allWorkspaceFiles: ExternalObject<Array<FileData>>, projectGraph: ExternalObject<ProjectGraph>, projectFileMap: ExternalObject<Record<string, Array<FileData>>>)
4747
inspect(hashPlans: ExternalObject<Record<string, Array<HashInstruction>>>): Record<string, string[]>
4848
}
4949

@@ -136,7 +136,7 @@ export declare class Watcher {
136136
export declare class WorkspaceContext {
137137
workspaceRoot: string
138138
constructor(workspaceRoot: string, cacheDir: string)
139-
getWorkspaceFiles(projectRootMap: Record<string, string>): NxWorkspaceFiles
139+
getWorkspaceFiles(additionalProjectDirectories: Array<string>, projectRootMap: Record<string, string>): NxWorkspaceFiles
140140
glob(globs: Array<string>, exclude?: Array<string> | undefined | null): Array<string>
141141
/**
142142
* Performs multiple glob pattern matches against workspace files in parallel

packages/nx/src/native/tasks/hash_plan_inspector.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::collections::HashMap;
99

1010
#[napi]
1111
pub struct HashPlanInspector {
12+
workspace_root: String,
1213
all_workspace_files: External<Vec<FileData>>,
1314
project_graph: External<ProjectGraph>,
1415
project_file_map: External<HashMap<String, Vec<FileData>>>,
@@ -18,11 +19,13 @@ pub struct HashPlanInspector {
1819
impl HashPlanInspector {
1920
#[napi(constructor)]
2021
pub fn new(
22+
workspace_root: String,
2123
all_workspace_files: External<Vec<FileData>>,
2224
project_graph: External<ProjectGraph>,
2325
project_file_map: External<HashMap<String, Vec<FileData>>>,
2426
) -> Self {
2527
Self {
28+
workspace_root,
2629
all_workspace_files,
2730
project_graph,
2831
project_file_map,
@@ -58,6 +61,7 @@ impl HashPlanInspector {
5861
.ok_or_else(|| anyhow!("project {} not found", project_name))?;
5962

6063
let files = collect_project_files(
64+
&self.workspace_root,
6165
project_name,
6266
&project.root,
6367
file_sets,

packages/nx/src/native/tasks/hashers/hash_project_files.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
1-
use std::collections::HashMap;
2-
31
use anyhow::*;
2+
use std::collections::HashMap;
3+
use std::path::PathBuf;
44
use tracing::{trace, trace_span};
55

66
use crate::native::glob::build_glob_set;
77
use crate::native::types::FileData;
8+
use crate::native::utils::path::get_relative_path;
89

910
pub fn hash_project_files(
11+
workspace_root: &String,
1012
project_name: &str,
1113
project_root: &str,
1214
file_sets: &[String],
1315
project_file_map: &HashMap<String, Vec<FileData>>,
1416
) -> Result<String> {
1517
let _span = trace_span!("hash_project_files", project_name).entered();
16-
let collected_files =
17-
collect_project_files(project_name, project_root, file_sets, project_file_map)?;
18+
let collected_files = collect_project_files(
19+
workspace_root,
20+
project_name,
21+
project_root,
22+
file_sets,
23+
project_file_map,
24+
)?;
1825
trace!("collected_files: {:?}", collected_files.len());
1926
let mut hasher = xxhash_rust::xxh3::Xxh3::new();
2027
for file in collected_files {
@@ -26,6 +33,7 @@ pub fn hash_project_files(
2633

2734
/// base function that should be testable (to make sure that we're getting the proper files back)
2835
pub fn collect_project_files<'a>(
36+
workspace_root: &String,
2937
project_name: &str,
3038
project_root: &str,
3139
file_sets: &[String],
@@ -52,7 +60,15 @@ pub fn collect_project_files<'a>(
5260
let now = std::time::Instant::now();
5361
let hashes = files
5462
.iter()
55-
.filter(|file| glob_set.is_match(&file.file))
63+
.filter(|file| {
64+
let path = if PathBuf::from(&file.file).is_absolute() {
65+
&get_relative_path(workspace_root, &file.file)
66+
} else {
67+
&file.file
68+
};
69+
trace!("{} - {}", path, file.hash);
70+
glob_set.is_match(path)
71+
})
5672
.collect::<Vec<_>>();
5773
trace!("hash_files for {}: {:?}", project_name, now.elapsed());
5874
Ok(hashes)

packages/nx/src/native/tasks/task_hasher.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ impl TaskHasher {
220220
.get(project_name)
221221
.ok_or_else(|| anyhow!("project {} not found", project_name))?;
222222
let hashed_project_files = hash_project_files(
223+
&self.workspace_root,
223224
project_name,
224225
&project.root,
225226
file_sets,

packages/nx/src/native/utils/path.rs

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::native::{types::FileData, utils::normalize_trait::Normalize};
2-
use std::path::{Path, PathBuf};
2+
use std::path::{Component, Path, PathBuf};
33

44
impl Normalize for Path {
55
fn to_normalized_string(&self) -> String {
@@ -37,6 +37,83 @@ pub fn get_child_files<P: AsRef<Path>>(directory: P, files: Vec<FileData>) -> Ve
3737
.collect()
3838
}
3939

40+
pub fn join_paths(base: &Path, relative: &str) -> PathBuf {
41+
let mut path = base.to_path_buf();
42+
43+
let relative_path = Path::new(relative);
44+
45+
if relative_path.is_absolute() {
46+
return relative_path.to_path_buf();
47+
}
48+
49+
for component in relative_path.components() {
50+
match component {
51+
Component::ParentDir => {
52+
path.pop();
53+
}
54+
Component::CurDir => {}
55+
Component::Normal(c) => path.push(c),
56+
_ => {}
57+
}
58+
}
59+
60+
path
61+
}
62+
63+
/// Get the relative path from `from` to `to`.
64+
///
65+
/// # Example
66+
/// ```
67+
/// // From /a/b/c to /a/d/e returns "../../d/e"
68+
/// // From /a/b to /a/b/c returns "c"
69+
/// // From /a/b/c to /a/b/c returns "."
70+
/// ```
71+
pub fn get_relative_path<P: AsRef<Path>>(from: P, to: P) -> String {
72+
let from_path = from.as_ref();
73+
let to_path = to.as_ref();
74+
75+
// Normalize both paths
76+
let from_components: Vec<&std::ffi::OsStr> = from_path
77+
.components()
78+
.filter_map(|c| match c {
79+
Component::Normal(n) => Some(n.as_ref()),
80+
_ => None,
81+
})
82+
.collect();
83+
84+
let to_components: Vec<&std::ffi::OsStr> = to_path
85+
.components()
86+
.filter_map(|c| match c {
87+
Component::Normal(n) => Some(n.as_ref()),
88+
_ => None,
89+
})
90+
.collect();
91+
92+
// Find the common prefix length
93+
let common_len = from_components
94+
.iter()
95+
.zip(to_components.iter())
96+
.take_while(|(a, b)| a == b)
97+
.count();
98+
99+
// Calculate how many parent directories we need
100+
let ups = from_components.len() - common_len;
101+
102+
// Build the relative path
103+
let mut result = vec!["..".to_string(); ups];
104+
result.extend(
105+
to_components[common_len..]
106+
.iter()
107+
.map(|c| c.to_string_lossy().to_string()),
108+
);
109+
110+
if result.is_empty() {
111+
".".to_string()
112+
} else {
113+
result.join("/")
114+
}
115+
}
116+
40117
#[cfg(test)]
41118
mod test {
42119
use super::*;
@@ -70,4 +147,32 @@ mod test {
70147
let child_files = get_child_files(&directory, files);
71148
assert_eq!(child_files, ["foo/bar", "foo/baz", "foo/child/bar",]);
72149
}
150+
151+
#[test]
152+
fn should_get_relative_path_sibling() {
153+
assert_eq!(get_relative_path("a/b/c", "a/d/e"), "../../d/e");
154+
}
155+
156+
#[test]
157+
fn should_get_relative_path_child() {
158+
assert_eq!(get_relative_path("a/b", "a/b/c"), "c");
159+
}
160+
161+
#[test]
162+
fn should_get_relative_path_parent() {
163+
assert_eq!(get_relative_path("a/b/c/d", "a/b"), "../..");
164+
}
165+
166+
#[test]
167+
fn should_get_relative_path_same() {
168+
assert_eq!(get_relative_path("a/b/c", "a/b/c"), ".");
169+
}
170+
171+
#[test]
172+
fn should_get_relative_path_deeply_nested() {
173+
assert_eq!(
174+
get_relative_path("a/b/c/d/e", "a/x/y/z"),
175+
"../../../../x/y/z"
176+
);
177+
}
73178
}

packages/nx/src/native/workspace/additional_project_directories.rs

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,12 @@
1-
use std::path::{Path, PathBuf, Component};
1+
use std::path::PathBuf;
22

33
use crate::native::glob::glob_files::glob_files;
44
use crate::native::types::FileData;
5-
use crate::native::walker::{nx_walker, NxFile};
5+
use crate::native::utils::path::join_paths;
6+
use crate::native::walker::{NxFile, nx_walker};
67
use rayon::prelude::*;
78
use xxhash_rust::xxh3;
89

9-
fn join_paths(base: &Path, relative: &str) -> PathBuf {
10-
let mut path = base.to_path_buf();
11-
12-
for component in Path::new(relative).components() {
13-
match component {
14-
Component::ParentDir => {
15-
path.pop();
16-
}
17-
Component::CurDir => {}
18-
Component::Normal(c) => path.push(c),
19-
_ => {}
20-
}
21-
}
22-
23-
path
24-
}
25-
2610
pub fn get_files_in_additional_project_directories(
2711
workspace_root: String,
2812
additional_project_directories: Vec<String>,
@@ -45,7 +29,8 @@ pub fn multi_glob_in_additional_project_directories(
4529
globs: Vec<String>,
4630
exclude: Option<Vec<String>>,
4731
) -> napi::Result<Vec<Vec<String>>> {
48-
let files = get_files_in_additional_project_directories(workspace_root, additional_project_directories);
32+
let files =
33+
get_files_in_additional_project_directories(workspace_root, additional_project_directories);
4934

5035
globs
5136
.iter()
@@ -59,9 +44,10 @@ pub fn multi_glob_in_additional_project_directories(
5944
hash: String::new(),
6045
})
6146
.collect();
62-
let globbed_files: Vec<String> = glob_files(&file_data, vec![glob.clone()], exclude.clone())?
63-
.map(|f| f.file.to_owned())
64-
.collect();
47+
let globbed_files: Vec<String> =
48+
glob_files(&file_data, vec![glob.clone()], exclude.clone())?
49+
.map(|f| f.file.to_owned())
50+
.collect();
6551
result.extend(globbed_files);
6652
}
6753
Ok(result)
@@ -75,7 +61,8 @@ pub fn multi_hash_glob_in_additional_project_directories(
7561
additional_project_directories: Vec<String>,
7662
glob_groups: Vec<Vec<String>>,
7763
) -> napi::Result<Vec<String>> {
78-
let files = get_files_in_additional_project_directories(workspace_root, additional_project_directories);
64+
let files =
65+
get_files_in_additional_project_directories(workspace_root, additional_project_directories);
7966

8067
glob_groups
8168
.iter()

packages/nx/src/native/workspace/context.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::native::hasher::{hash_file, hash_file_path};
12
use std::collections::{HashMap, HashSet};
23
use std::mem;
34
use std::ops::Deref;
@@ -15,7 +16,10 @@ use crate::native::workspace::files_hashing::{full_files_hash, selective_files_h
1516
use crate::native::workspace::types::{
1617
FileMap, NxWorkspaceFilesExternals, ProjectFiles, UpdatedWorkspaceFiles,
1718
};
18-
use crate::native::workspace::{types::NxWorkspaceFiles, workspace_files};
19+
use crate::native::workspace::{
20+
additional_project_directories::get_files_in_additional_project_directories,
21+
types::NxWorkspaceFiles, workspace_files,
22+
};
1923
use napi::bindgen_prelude::External;
2024
use rayon::prelude::*;
2125
use tracing::{trace, warn};
@@ -217,9 +221,26 @@ impl WorkspaceContext {
217221
#[napi]
218222
pub fn get_workspace_files(
219223
&self,
224+
additional_project_directories: Vec<String>,
220225
project_root_map: HashMap<String, String>,
221226
) -> anyhow::Result<NxWorkspaceFiles> {
222-
workspace_files::get_files(project_root_map, self.all_file_data())
227+
let mut file_data = self.all_file_data();
228+
229+
// Gather files from additional project directories
230+
let additional_files = get_files_in_additional_project_directories(
231+
self.workspace_root.clone(),
232+
additional_project_directories,
233+
);
234+
235+
file_data.extend(additional_files.into_iter().flatten().map(|file| {
236+
let path = file.full_path;
237+
238+
let hash = hash_file_path(path.clone()).expect("file could not be hashed");
239+
240+
FileData { file: path, hash }
241+
}));
242+
243+
workspace_files::get_files(&self.workspace_root_path, project_root_map, file_data)
223244
.map_err(anyhow::Error::from)
224245
}
225246

packages/nx/src/native/workspace/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ use crate::native::workspace::types::NxWorkspaceFilesExternals;
33
use napi::bindgen_prelude::External;
44
use std::collections::HashMap;
55

6+
pub mod additional_project_directories;
67
pub mod context;
78
mod errors;
89
mod files_archive;
910
mod files_hashing;
1011
pub mod types;
1112
pub mod workspace_files;
12-
pub mod additional_project_directories;
1313

1414
#[napi]
1515
// should only be used in tests to transfer the file map from the JS world to the Rust world

0 commit comments

Comments
 (0)