Skip to content

Commit f50011a

Browse files
refactor: native watcher to make codes clear (#11282)
* refactor: native watcher to make codes clear
1 parent 42f2adb commit f50011a

File tree

11 files changed

+508
-542
lines changed

11 files changed

+508
-542
lines changed

crates/rspack_binding_api/src/native_watcher.rs

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
use std::boxed::Box;
1+
use std::{boxed::Box, path::PathBuf};
22

33
use napi::bindgen_prelude::*;
44
use napi_derive::*;
5-
use rspack_fs::{FsWatcher, FsWatcherIgnored, FsWatcherOptions, PathUpdater};
5+
use rspack_fs::{FsWatcher, FsWatcherIgnored, FsWatcherOptions};
6+
use rspack_paths::ArcPath;
67
use rspack_regex::RspackRegex;
78

89
type JsWatcherIgnored = Either3<String, Vec<String>, RspackRegex>;
@@ -86,29 +87,14 @@ impl NativeWatcher {
8687
let js_event_handler = JsEventHandler::new(callback)?;
8788
let js_event_handler_undelayed = JsEventHandlerUndelayed::new(callback_undelayed)?;
8889

89-
let file_updater = PathUpdater {
90-
added: files.0,
91-
removed: files.1,
92-
};
93-
94-
let directories_updater = PathUpdater {
95-
added: directories.0,
96-
removed: directories.1,
97-
};
98-
99-
let missing_updater = PathUpdater {
100-
added: missing.0,
101-
removed: missing.1,
102-
};
103-
10490
reference.share_with(env, |native_watcher| {
10591
napi::bindgen_prelude::spawn(async move {
10692
native_watcher
10793
.watcher
10894
.watch(
109-
file_updater,
110-
directories_updater,
111-
missing_updater,
95+
to_tuple_path_iterator(files),
96+
to_tuple_path_iterator(directories),
97+
to_tuple_path_iterator(missing),
11298
Box::new(js_event_handler),
11399
Box::new(js_event_handler_undelayed),
114100
)
@@ -147,6 +133,15 @@ impl NativeWatcher {
147133
}
148134
}
149135

136+
fn to_tuple_path_iterator(
137+
tuple: (Vec<String>, Vec<String>),
138+
) -> (impl Iterator<Item = ArcPath>, impl Iterator<Item = ArcPath>) {
139+
(
140+
tuple.0.into_iter().map(|s| ArcPath::from(PathBuf::from(s))),
141+
tuple.1.into_iter().map(|s| ArcPath::from(PathBuf::from(s))),
142+
)
143+
}
144+
150145
struct JsEventHandler {
151146
inner: napi::threadsafe_function::ThreadsafeFunction<
152147
NativeWatchResult,
@@ -177,8 +172,8 @@ impl JsEventHandler {
177172
impl rspack_fs::EventAggregateHandler for JsEventHandler {
178173
fn on_event_handle(
179174
&self,
180-
changed_files: std::collections::HashSet<String>,
181-
deleted_files: std::collections::HashSet<String>,
175+
changed_files: rspack_util::fx_hash::FxHashSet<String>,
176+
deleted_files: rspack_util::fx_hash::FxHashSet<String>,
182177
) {
183178
let changed_files_vec: Vec<String> = changed_files.into_iter().collect();
184179
let deleted_files_vec: Vec<String> = deleted_files.into_iter().collect();

crates/rspack_fs/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ pub use read::ReadableFileSystem;
33

44
mod watcher;
55
pub use watcher::{
6-
EventAggregateHandler, EventHandler, FsWatcher, FsWatcherIgnored, FsWatcherOptions, PathUpdater,
6+
EventAggregateHandler, EventHandler, FsWatcher, FsWatcherIgnored, FsWatcherOptions,
77
};
88

99
mod write;

crates/rspack_fs/src/watcher/analyzer/directories.rs

Lines changed: 38 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use rspack_paths::ArcPath;
33
use rspack_util::fx_hash::FxHashSet as HashSet;
44

55
use super::{Analyzer, WatchPattern};
6-
use crate::watcher::path_manager::PathAccessor;
6+
use crate::watcher::paths::PathAccessor;
77

88
/// `WatcherDirectoriesAnalyzer` analyzes the path register and determines
99
///
@@ -66,33 +66,29 @@ impl WatcherDirectoriesAnalyzer {
6666
#[cfg(test)]
6767
mod tests {
6868
use super::*;
69-
use crate::watcher::path_manager::{PathManager, PathUpdater};
69+
use crate::watcher::paths::PathManager;
7070

7171
#[test]
7272
fn test_find_watch_directories() {
7373
let current_dir = std::env::current_dir().expect("Failed to get current directory");
7474
let path_manager = PathManager::default();
75-
let file_updater = PathUpdater {
76-
added: vec![
77-
current_dir.join("Cargo.toml").to_string_lossy().to_string(),
78-
current_dir.join("src/lib.rs").to_string_lossy().to_string(),
79-
],
80-
removed: vec![],
81-
};
82-
83-
let directory_updater = PathUpdater {
84-
added: vec![current_dir.join("src").to_string_lossy().to_string()],
85-
removed: vec![],
86-
};
87-
88-
let missing_updater = PathUpdater {
89-
added: vec![],
90-
removed: vec![],
91-
};
92-
93-
path_manager
94-
.update_paths(file_updater, directory_updater, missing_updater)
95-
.unwrap();
75+
let files = (
76+
vec![
77+
current_dir.join("Cargo.toml").into(),
78+
current_dir.join("src/lib.rs").into(),
79+
]
80+
.into_iter(),
81+
vec![].into_iter(),
82+
);
83+
84+
let dirs = (
85+
vec![current_dir.join("src").into()].into_iter(),
86+
vec![].into_iter(),
87+
);
88+
89+
let missing = (vec![].into_iter(), vec![].into_iter());
90+
91+
path_manager.update(files, dirs, missing).unwrap();
9692
let analyzer = WatcherDirectoriesAnalyzer::default();
9793
let watch_patterns = analyzer.analyze(path_manager.access());
9894

@@ -115,33 +111,25 @@ mod tests {
115111
let dir_0 = ArcPath::from(current_dir.join("src"));
116112

117113
let path_manager = PathManager::default();
118-
let file_updater = PathUpdater {
119-
added: vec![
120-
current_dir.join("Cargo.toml").to_string_lossy().to_string(),
121-
current_dir
122-
.join("src/a/b/c/d.rs")
123-
.to_string_lossy()
124-
.to_string(),
125-
],
126-
removed: vec![],
127-
};
128-
let directory_updater = PathUpdater {
129-
added: vec![
130-
current_dir.join("src").to_string_lossy().to_string(),
131-
current_dir
132-
.join("src/b/c/d/e")
133-
.to_string_lossy()
134-
.to_string(),
135-
],
136-
removed: vec![],
137-
};
138-
let missing_updater = PathUpdater {
139-
added: vec![],
140-
removed: vec![],
141-
};
142-
path_manager
143-
.update_paths(file_updater, directory_updater, missing_updater)
144-
.unwrap();
114+
let files = (
115+
vec![
116+
current_dir.join("Cargo.toml").into(),
117+
current_dir.join("src/a/b/c/d.rs").into(),
118+
]
119+
.into_iter(),
120+
vec![].into_iter(),
121+
);
122+
let dirs = (
123+
vec![
124+
current_dir.join("src").into(),
125+
current_dir.join("src/b/c/d/e").into(),
126+
]
127+
.into_iter(),
128+
vec![].into_iter(),
129+
);
130+
let missing = (vec![].into_iter(), vec![].into_iter());
131+
132+
path_manager.update(files, dirs, missing).unwrap();
145133

146134
let analyzer = WatcherDirectoriesAnalyzer::default();
147135
let watch_patterns = analyzer.analyze(path_manager.access());

crates/rspack_fs/src/watcher/analyzer/mod.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,18 @@
1-
use crate::watcher::{WatchPattern, path_manager::PathAccessor};
1+
use crate::watcher::{WatchPattern, paths::PathAccessor};
22

33
mod directories;
44
mod root;
55

6-
/// The `Analyzer` trait defines an interface for analyzing a [`PathRegister`]
7-
/// and producing a set of [`WatchTarget`]s to be watched by the file system watcher.
8-
///
9-
/// Implementors of this trait should provide logic to determine which paths
10-
/// should be watched, and with what recursive mode, based on the current state
11-
/// of the path register.
12-
///
13-
/// The trait is bounded by `Default` to allow easy instantiation.
6+
/// The `Analyzer` trait defines an interface for analyzing a [`PathAccessor`]
7+
/// and producing a set of [`WatchPattern`]s to be watched by the file system watcher.
148
pub(crate) trait Analyzer: Default {
159
/// Analyze the given [`PathRegister`] and return a list of [`WatchTarget`]s.
1610
///
1711
/// # Arguments
18-
/// * `register` - The path register containing all paths to consider.
12+
/// * `path_accessor` - A reference to a [`PathAccessor`] that provides access to the current state of paths, directories, and missing paths.
1913
///
2014
/// # Returns
21-
/// A vector of [`WatchTarget`]s representing the paths and their watch modes.
15+
/// A vector of [`WatchPattern`]s representing the paths and their watch modes.
2216
fn analyze<'a>(&self, path_accessor: PathAccessor<'a>) -> Vec<WatchPattern>;
2317
}
2418

crates/rspack_fs/src/watcher/analyzer/root.rs

Lines changed: 41 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,32 @@
11
#![allow(unused)]
2+
use std::ops::Deref;
3+
24
use dashmap::DashSet as HashSet;
35
use rspack_paths::ArcPath;
46
use rspack_util::fx_hash::FxDashMap as HashMap;
57

68
use super::{Analyzer, WatchPattern};
7-
use crate::watcher::path_manager::PathAccessor;
9+
use crate::watcher::paths::PathAccessor;
810

911
#[derive(Default)]
12+
/// The `WatcherRootAnalyzer` is an implementation of the `Analyzer` trait that
13+
/// analyzes the root directory of the file system and determines the common root
14+
/// path to be watched.
1015
pub struct WatcherRootAnalyzer {
1116
path_tree: PathTree,
1217
}
1318

1419
impl Analyzer for WatcherRootAnalyzer {
1520
fn analyze<'a>(&self, path_accessor: PathAccessor<'a>) -> Vec<WatchPattern> {
16-
let incremental_files = path_accessor.incremental_files();
17-
let incremental_directories = path_accessor.incremental_directories();
18-
let incremental_missing = path_accessor.incremental_missing();
19-
20-
for added in incremental_files.added().iter() {
21-
self.path_tree.add_path(&added);
22-
}
21+
let (_, added_files, removed_files) = path_accessor.files();
22+
let (_, added_directories, removed_directories) = path_accessor.directories();
23+
let (_, added_missing, removed_missing) = path_accessor.missing();
2324

24-
for removed in incremental_files.removed().iter() {
25-
self.path_tree.remove_path(&removed);
26-
}
27-
28-
for added in incremental_directories.added().iter() {
29-
self.path_tree.add_path(&added);
30-
}
31-
for removed in incremental_directories.removed().iter() {
32-
self.path_tree.remove_path(&removed);
33-
}
34-
for added in incremental_missing.added().iter() {
35-
self.path_tree.add_path(&added);
36-
}
37-
for removed in incremental_missing.removed().iter() {
38-
self.path_tree.remove_path(&removed);
39-
}
25+
self.path_tree.update_paths(added_files, removed_files);
26+
self
27+
.path_tree
28+
.update_paths(added_directories, removed_directories);
29+
self.path_tree.update_paths(added_missing, removed_missing);
4030

4131
let common_root = self.path_tree.find_common_root();
4232

@@ -80,6 +70,15 @@ impl PathTree {
8070
}
8171
}
8272

73+
pub fn update_paths(&self, added_paths: &HashSet<ArcPath>, removed_paths: &HashSet<ArcPath>) {
74+
for added in added_paths.iter() {
75+
self.add_path(added.deref());
76+
}
77+
for removed in removed_paths.iter() {
78+
self.remove_path(removed.deref());
79+
}
80+
}
81+
8382
pub fn add_path(&self, path: &ArcPath) {
8483
self.inner.entry(path.clone()).or_default();
8584
self.add_path_recursive(path);
@@ -153,7 +152,7 @@ mod tests {
153152
use rspack_paths::ArcPath;
154153

155154
use super::*;
156-
use crate::watcher::path_manager::{PathManager, PathUpdater};
155+
use crate::watcher::paths::PathManager;
157156

158157
#[test]
159158
fn test_find_watch_root() {
@@ -163,27 +162,10 @@ mod tests {
163162
let dir_0 = ArcPath::from(current_dir.clone());
164163
let dir_1 = ArcPath::from(current_dir.join("src"));
165164
let path_manager = PathManager::default();
166-
let file_updater = PathUpdater {
167-
added: vec![
168-
file_0.to_string_lossy().to_string(),
169-
file_1.to_string_lossy().to_string(),
170-
],
171-
removed: vec![],
172-
};
173-
let directory_updater = PathUpdater {
174-
added: vec![
175-
dir_0.to_string_lossy().to_string(),
176-
dir_1.to_string_lossy().to_string(),
177-
],
178-
removed: vec![],
179-
};
180-
let missing_updater = PathUpdater {
181-
added: vec![],
182-
removed: vec![],
183-
};
184-
path_manager
185-
.update_paths(file_updater, directory_updater, missing_updater)
186-
.unwrap();
165+
let files = (vec![file_0, file_1].into_iter(), vec![].into_iter());
166+
let dirs = (vec![dir_0, dir_1].into_iter(), vec![].into_iter());
167+
let missing = (vec![].into_iter(), vec![].into_iter());
168+
path_manager.update(files, dirs, missing).unwrap();
187169

188170
let analyzer = WatcherRootAnalyzer::default();
189171
let watch_patterns = analyzer.analyze(path_manager.access());
@@ -198,38 +180,19 @@ mod tests {
198180
let current_dir = std::env::current_dir().expect("Failed to get current directory");
199181

200182
let path_manager = PathManager::default();
201-
let file_updater = PathUpdater {
202-
added: vec![],
203-
removed: vec![],
204-
};
205-
let directory_updater = PathUpdater {
206-
added: vec![],
207-
removed: vec![],
208-
};
209-
let missing_updater = PathUpdater {
210-
added: vec![
211-
current_dir
212-
.join("_missing")
213-
.join("a")
214-
.to_string_lossy()
215-
.to_string(),
216-
current_dir
217-
.join("_missing")
218-
.join("b")
219-
.to_string_lossy()
220-
.to_string(),
221-
current_dir
222-
.join("_missing")
223-
.join("c.js")
224-
.to_string_lossy()
225-
.to_string(),
226-
],
227-
removed: vec![],
228-
};
229-
230-
path_manager
231-
.update_paths(file_updater, directory_updater, missing_updater)
232-
.unwrap();
183+
let files = (vec![].into_iter(), vec![].into_iter());
184+
let dirs = (vec![].into_iter(), vec![].into_iter());
185+
let missing = (
186+
vec![
187+
current_dir.join("_missing").join("a").into(),
188+
current_dir.join("_missing").join("b").into(),
189+
current_dir.join("_missing").join("c.js").into(),
190+
]
191+
.into_iter(),
192+
vec![].into_iter(),
193+
);
194+
195+
path_manager.update(files, dirs, missing).unwrap();
233196

234197
let analyzer = WatcherRootAnalyzer::default();
235198
let watch_patterns = analyzer.analyze(path_manager.access());

0 commit comments

Comments
 (0)