Skip to main content

rspack_core/cache/
snapshot.rs

1use rspack_cacheable::{cacheable, utils::PortablePath, with::As};
2use rspack_regex::RspackRegex;
3
4/// Use string or regex to match path
5#[cacheable]
6#[derive(Debug, Clone, Hash)]
7pub enum PathMatcher {
8  String(#[cacheable(with=As<PortablePath>)] String),
9  Regexp(RspackRegex),
10}
11
12impl PathMatcher {
13  fn try_match(&self, path: &str) -> bool {
14    match self {
15      Self::String(string) => path.contains(string),
16      Self::Regexp(regex) => regex.test(path),
17    }
18  }
19}
20
21/// Snapshot options
22#[cacheable]
23#[derive(Debug, Clone, Hash)]
24pub struct SnapshotOptions {
25  /// immutable paths, snapshot will ignore them
26  immutable_paths: Vec<PathMatcher>,
27  /// unmanaged paths, snapshot will use compile time strategy even if
28  /// them are in managed_paths
29  unmanaged_paths: Vec<PathMatcher>,
30  /// managed_paths, snapshot will use lib version strategy
31  managed_paths: Vec<PathMatcher>,
32  dependencies: SnapshotStrategyOptions,
33  context_dependencies: SnapshotStrategyOptions,
34}
35
36impl Default for SnapshotOptions {
37  fn default() -> Self {
38    Self {
39      immutable_paths: Default::default(),
40      unmanaged_paths: Default::default(),
41      managed_paths: Default::default(),
42      dependencies: SnapshotStrategyOptions::hash_and_timestamp(),
43      context_dependencies: SnapshotStrategyOptions::timestamp(),
44    }
45  }
46}
47
48#[cacheable]
49#[derive(Debug, Clone, Copy, Hash)]
50pub struct SnapshotStrategyOptions {
51  pub hash: bool,
52  pub timestamp: bool,
53}
54
55impl SnapshotStrategyOptions {
56  pub const fn new(hash: bool, timestamp: bool) -> Self {
57    Self { hash, timestamp }
58  }
59
60  pub const fn hash() -> Self {
61    Self::new(true, false)
62  }
63
64  pub const fn timestamp() -> Self {
65    Self::new(false, true)
66  }
67
68  pub const fn hash_and_timestamp() -> Self {
69    Self::new(true, true)
70  }
71}
72
73impl Default for SnapshotStrategyOptions {
74  fn default() -> Self {
75    Self::timestamp()
76  }
77}
78
79impl SnapshotOptions {
80  pub fn new(
81    immutable_paths: Vec<PathMatcher>,
82    unmanaged_paths: Vec<PathMatcher>,
83    managed_paths: Vec<PathMatcher>,
84  ) -> Self {
85    Self {
86      immutable_paths,
87      unmanaged_paths,
88      managed_paths,
89      ..Default::default()
90    }
91  }
92
93  pub fn dependencies_strategy(&self) -> SnapshotStrategyOptions {
94    self.dependencies
95  }
96
97  pub fn context_dependencies_strategy(&self) -> SnapshotStrategyOptions {
98    self.context_dependencies
99  }
100
101  pub fn is_immutable_path(&self, path_str: &str) -> bool {
102    for item in &self.immutable_paths {
103      if item.try_match(path_str) {
104        return true;
105      }
106    }
107    false
108  }
109
110  pub fn is_managed_path(&self, path_str: &str) -> bool {
111    for item in &self.unmanaged_paths {
112      if item.try_match(path_str) {
113        return false;
114      }
115    }
116
117    for item in &self.managed_paths {
118      if item.try_match(path_str) {
119        return true;
120      }
121    }
122    false
123  }
124}
125
126#[cfg(test)]
127mod tests {
128  use rspack_regex::RspackRegex;
129
130  use super::{PathMatcher, SnapshotOptions};
131
132  #[test]
133  fn should_path_matcher_works() {
134    let matcher = PathMatcher::String("abc".into());
135    assert!(matcher.try_match("aabcc"));
136    assert!(matcher.try_match("abccd"));
137    assert!(matcher.try_match("xxabc"));
138    assert!(!matcher.try_match("aadcc"));
139
140    let matcher = PathMatcher::Regexp(RspackRegex::new("[0-9]").unwrap());
141    assert!(matcher.try_match("aa0cc"));
142    assert!(matcher.try_match("3cc"));
143    assert!(!matcher.try_match("abc"));
144  }
145
146  #[test]
147  fn should_snapshot_options_works() {
148    let options = SnapshotOptions::new(
149      vec![
150        PathMatcher::String("constant".into()),
151        PathMatcher::Regexp(RspackRegex::new("global/[A-Z]+").unwrap()),
152      ],
153      vec![
154        PathMatcher::String("node_modules/test1".into()),
155        PathMatcher::Regexp(RspackRegex::new("test_modules/test.+").unwrap()),
156      ],
157      vec![
158        PathMatcher::String("node_modules".into()),
159        PathMatcher::Regexp(RspackRegex::new("test_modules/.+").unwrap()),
160      ],
161    );
162
163    assert!(options.is_immutable_path("/root/project/constant/var.js"));
164    assert!(options.is_immutable_path("/root/project/constant1/var.js"));
165    assert!(options.is_immutable_path("/root/project/1constant/var.js"));
166
167    assert!(options.is_immutable_path("/root/project/global/NAME.js"));
168    assert!(options.is_immutable_path("/root/project/global/Name.js"));
169    assert!(!options.is_immutable_path("/root/project/global/var.js"));
170
171    assert!(options.is_managed_path("/root/project/node_modules/var.js"));
172    assert!(!options.is_managed_path("/root/project/node_modules/test1/var.js"));
173
174    assert!(options.is_managed_path("/root/project/test_modules/var.js"));
175    assert!(!options.is_managed_path("/root/project/test_modules/test1/var.js"));
176  }
177}