turbopack_core/
condition.rs

1use bincode::{Decode, Encode};
2use turbo_tasks::{NonLocalValue, trace::TraceRawVcs};
3use turbo_tasks_fs::FileSystemPath;
4
5#[derive(Debug, Clone, TraceRawVcs, PartialEq, Eq, NonLocalValue, Encode, Decode)]
6pub enum ContextCondition {
7    All(Vec<ContextCondition>),
8    Any(Vec<ContextCondition>),
9    Not(Box<ContextCondition>),
10    InDirectory(String),
11    /// The same as `InDirectory("node_modules"), but as this is the most common case, we handle it
12    /// directly.`
13    InNodeModules,
14    InPath(FileSystemPath),
15}
16
17impl ContextCondition {
18    /// Creates a condition that matches if all of the given conditions match.
19    pub fn all(conditions: Vec<ContextCondition>) -> ContextCondition {
20        ContextCondition::All(conditions)
21    }
22
23    /// Creates a condition that matches if any of the given conditions match.
24    pub fn any(conditions: Vec<ContextCondition>) -> ContextCondition {
25        ContextCondition::Any(conditions)
26    }
27
28    /// Creates a condition that matches if the given condition does not match.
29    #[allow(clippy::should_implement_trait)]
30    pub fn not(condition: ContextCondition) -> ContextCondition {
31        ContextCondition::Not(Box::new(condition))
32    }
33
34    /// Returns true if the condition matches the context.
35    pub fn matches(&self, path: &FileSystemPath) -> bool {
36        match self {
37            ContextCondition::All(conditions) => conditions.iter().all(|c| c.matches(path)),
38            ContextCondition::Any(conditions) => conditions.iter().any(|c| c.matches(path)),
39            ContextCondition::Not(condition) => !condition.matches(path),
40            ContextCondition::InPath(other_path) => path.is_inside_or_equal_ref(other_path),
41            ContextCondition::InNodeModules => is_in_directory(path, "node_modules"),
42            ContextCondition::InDirectory(dir) => {
43                // `dir` must be a substring and bracketd by either `'/'` or the end of the path.
44                is_in_directory(path, dir)
45            }
46        }
47    }
48}
49
50fn is_in_directory(path: &FileSystemPath, dir: &str) -> bool {
51    if let Some(pos) = path.path.find(dir) {
52        let end = pos + dir.len();
53        (pos == 0 || path.path.as_bytes()[pos - 1] == b'/')
54            && (end == path.path.len() || path.path.as_bytes()[end] == b'/')
55    } else {
56        false
57    }
58}