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    /// Matches the glob against the resolved path relative to `root`. A `root` of `None` matches
19    /// resolved paths on any filesystem, relative to that filesystem's root.
20    Glob {
21        root: Option<FileSystemPath>,
22        glob: ReadRef<Glob>,
23    },
24    // these variants are used by utoo
25    Always,
26    Never,
27}
28
29#[turbo_tasks::value_impl]
30impl AfterResolvePluginCondition {
31    #[turbo_tasks::function]
32    pub async fn new_with_glob(
33        root: Option<FileSystemPath>,
34        glob: ResolvedVc<Glob>,
35    ) -> Result<Vc<Self>> {
36        let glob = glob.await?;
37        Ok(AfterResolvePluginCondition::Glob { root, glob }.cell())
38    }
39}
40
41impl AfterResolvePluginCondition {
42    /// Test whether `fs_path` matches this condition.
43    pub fn matches(&self, fs_path: &FileSystemPath) -> bool {
44        match self {
45            AfterResolvePluginCondition::Glob { root, glob } => {
46                let path = match root {
47                    Some(root) => root.get_path_to(fs_path),
48                    None => Some(&*fs_path.path),
49                };
50                path.is_some_and(|p| glob.matches(p))
51            }
52            AfterResolvePluginCondition::Always => true,
53            AfterResolvePluginCondition::Never => false,
54        }
55    }
56}
57
58/// A condition which determines if the hooks of a resolve plugin gets called.
59#[turbo_tasks::value(shared, serialization = "skip")]
60pub enum BeforeResolvePluginCondition {
61    Request(ReadRef<Glob>),
62    Modules(ReadRef<Vec<RcStr>>),
63    // These are used by utoo
64    Always,
65    Never,
66}
67
68#[turbo_tasks::value_impl]
69impl BeforeResolvePluginCondition {
70    #[turbo_tasks::function]
71    pub async fn from_modules(modules: ResolvedVc<Vec<RcStr>>) -> Result<Vc<Self>> {
72        Ok(BeforeResolvePluginCondition::Modules(modules.await?).cell())
73    }
74
75    #[turbo_tasks::function]
76    pub async fn from_request_glob(glob: ResolvedVc<Glob>) -> Result<Vc<Self>> {
77        Ok(BeforeResolvePluginCondition::Request(glob.await?).cell())
78    }
79}
80
81impl BeforeResolvePluginCondition {
82    /// Test whether `request` matches this condition.
83    pub fn matches(&self, request: &Request) -> bool {
84        match self {
85            BeforeResolvePluginCondition::Request(glob) => match request.request() {
86                Some(request) => glob.matches(request.as_str()),
87                None => false,
88            },
89            BeforeResolvePluginCondition::Modules(modules) => {
90                if let Request::Module { module, .. } = request {
91                    modules.iter().any(|m| module.is_match(m))
92                } else {
93                    false
94                }
95            }
96            BeforeResolvePluginCondition::Always => true,
97            BeforeResolvePluginCondition::Never => false,
98        }
99    }
100}
101
102#[turbo_tasks::value_trait]
103pub trait BeforeResolvePlugin {
104    fn before_resolve_condition(&self) -> Vc<BeforeResolvePluginCondition>;
105
106    #[turbo_tasks::function]
107    fn before_resolve(
108        self: Vc<Self>,
109        lookup_path: FileSystemPath,
110        reference_type: ReferenceType,
111        request: Vc<Request>,
112    ) -> Vc<ResolveResultOption>;
113}
114
115#[turbo_tasks::value_trait]
116pub trait AfterResolvePlugin {
117    /// A condition which determines if the hooks gets called.
118    fn after_resolve_condition(&self) -> Vc<AfterResolvePluginCondition>;
119
120    /// This hook gets called when a full filepath has been resolved and the
121    /// condition matches. If a value is returned it replaces the resolve
122    /// result.
123    #[turbo_tasks::function]
124    fn after_resolve(
125        self: Vc<Self>,
126        fs_path: FileSystemPath,
127        lookup_path: FileSystemPath,
128        reference_type: ReferenceType,
129        request: Vc<Request>,
130    ) -> Vc<ResolveResultOption>;
131}