Skip to main content

turbopack_core/resolve/
plugin.rs

1use anyhow::Result;
2use turbo_rcstr::RcStr;
3use turbo_tasks::{ReadRef, ResolvedVc, Vc};
4use turbo_tasks_fs::{FileSystemPath, glob::Glob};
5
6use crate::{
7    reference_type::ReferenceType,
8    resolve::{ResolveResultOption, parse::Request},
9};
10
11/// A condition which determines if the hooks of a resolve plugin gets called.
12///
13/// The glob is read at construction time and stored as a `ReadRef`, so `matches` is a pure
14/// sync function. `serialization = "skip"` because serializing a `ReadRef` is wasteful and
15/// recomputing this is very cheap.
16#[turbo_tasks::value(serialization = "skip")]
17pub enum AfterResolvePluginCondition {
18    Glob {
19        root: FileSystemPath,
20        glob: ReadRef<Glob>,
21    },
22    // these variants are used by utoo
23    Always,
24    Never,
25}
26
27#[turbo_tasks::value_impl]
28impl AfterResolvePluginCondition {
29    #[turbo_tasks::function]
30    pub async fn new_with_glob(root: FileSystemPath, glob: ResolvedVc<Glob>) -> Result<Vc<Self>> {
31        let glob = glob.await?;
32        Ok(AfterResolvePluginCondition::Glob { root, glob }.cell())
33    }
34}
35
36impl AfterResolvePluginCondition {
37    /// Test whether `fs_path` matches this condition.
38    pub fn matches(&self, fs_path: &FileSystemPath) -> bool {
39        match self {
40            AfterResolvePluginCondition::Glob { root, glob } => {
41                root.get_path_to(fs_path).is_some_and(|p| glob.matches(p))
42            }
43            AfterResolvePluginCondition::Always => true,
44            AfterResolvePluginCondition::Never => false,
45        }
46    }
47}
48
49/// A condition which determines if the hooks of a resolve plugin gets called.
50#[turbo_tasks::value(shared, serialization = "skip")]
51pub enum BeforeResolvePluginCondition {
52    Request(ReadRef<Glob>),
53    Modules(ReadRef<Vec<RcStr>>),
54    // These are used by utoo
55    Always,
56    Never,
57}
58
59#[turbo_tasks::value_impl]
60impl BeforeResolvePluginCondition {
61    #[turbo_tasks::function]
62    pub async fn from_modules(modules: ResolvedVc<Vec<RcStr>>) -> Result<Vc<Self>> {
63        Ok(BeforeResolvePluginCondition::Modules(modules.await?).cell())
64    }
65
66    #[turbo_tasks::function]
67    pub async fn from_request_glob(glob: ResolvedVc<Glob>) -> Result<Vc<Self>> {
68        Ok(BeforeResolvePluginCondition::Request(glob.await?).cell())
69    }
70}
71
72impl BeforeResolvePluginCondition {
73    /// Test whether `request` matches this condition.
74    pub fn matches(&self, request: &Request) -> bool {
75        match self {
76            BeforeResolvePluginCondition::Request(glob) => match request.request() {
77                Some(request) => glob.matches(request.as_str()),
78                None => false,
79            },
80            BeforeResolvePluginCondition::Modules(modules) => {
81                if let Request::Module { module, .. } = request {
82                    modules.iter().any(|m| module.is_match(m))
83                } else {
84                    false
85                }
86            }
87            BeforeResolvePluginCondition::Always => true,
88            BeforeResolvePluginCondition::Never => false,
89        }
90    }
91}
92
93#[turbo_tasks::value_trait]
94pub trait BeforeResolvePlugin {
95    fn before_resolve_condition(&self) -> Vc<BeforeResolvePluginCondition>;
96
97    #[turbo_tasks::function]
98    fn before_resolve(
99        self: Vc<Self>,
100        lookup_path: FileSystemPath,
101        reference_type: ReferenceType,
102        request: Vc<Request>,
103    ) -> Vc<ResolveResultOption>;
104}
105
106#[turbo_tasks::value_trait]
107pub trait AfterResolvePlugin {
108    /// A condition which determines if the hooks gets called.
109    fn after_resolve_condition(&self) -> Vc<AfterResolvePluginCondition>;
110
111    /// This hook gets called when a full filepath has been resolved and the
112    /// condition matches. If a value is returned it replaces the resolve
113    /// result.
114    #[turbo_tasks::function]
115    fn after_resolve(
116        self: Vc<Self>,
117        fs_path: FileSystemPath,
118        lookup_path: FileSystemPath,
119        reference_type: ReferenceType,
120        request: Vc<Request>,
121    ) -> Vc<ResolveResultOption>;
122}