turbopack_core/resolve/
origin.rs

1use std::future::Future;
2
3use anyhow::Result;
4use turbo_rcstr::RcStr;
5use turbo_tasks::{ResolvedVc, Upcast, Value, Vc};
6use turbo_tasks_fs::FileSystemPath;
7
8use super::{ModuleResolveResult, options::ResolveOptions, parse::Request};
9use crate::{context::AssetContext, module::OptionModule, reference_type::ReferenceType};
10
11/// A location where resolving can occur from. It carries some meta information
12/// that are needed for resolving from here.
13#[turbo_tasks::value_trait]
14pub trait ResolveOrigin {
15    /// The origin path where resolving starts. This is pointing to a file,
16    /// since that might be needed to infer custom resolving options for that
17    /// specific file. But usually only the directory is relevant for the real
18    /// resolving.
19    fn origin_path(self: Vc<Self>) -> Vc<FileSystemPath>;
20
21    /// The AssetContext that carries the configuration for building that
22    /// subgraph.
23    fn asset_context(self: Vc<Self>) -> Vc<Box<dyn AssetContext>>;
24
25    /// Get an inner asset form this origin that doesn't require resolving but
26    /// is directly attached
27    fn get_inner_asset(self: Vc<Self>, request: Vc<Request>) -> Vc<OptionModule> {
28        let _ = request;
29        Vc::cell(None)
30    }
31}
32
33// TODO it would be nice if these methods can be moved to the trait to allow
34// overriding it, but currently we explicitly disallow it due to the way
35// transitions work. Maybe transitions should be decorators on ResolveOrigin?
36pub trait ResolveOriginExt: Send {
37    /// Resolve to an asset from that origin. Custom resolve options can be
38    /// passed. Otherwise provide `origin.resolve_options()` unmodified.
39    fn resolve_asset(
40        self: Vc<Self>,
41        request: Vc<Request>,
42        options: Vc<ResolveOptions>,
43        reference_type: Value<ReferenceType>,
44    ) -> impl Future<Output = Result<Vc<ModuleResolveResult>>> + Send;
45
46    /// Get the resolve options that apply for this origin.
47    fn resolve_options(self: Vc<Self>, reference_type: Value<ReferenceType>) -> Vc<ResolveOptions>;
48
49    /// Adds a transition that is used for resolved assets.
50    fn with_transition(self: ResolvedVc<Self>, transition: RcStr) -> Vc<Box<dyn ResolveOrigin>>;
51}
52
53impl<T> ResolveOriginExt for T
54where
55    T: ResolveOrigin + Upcast<Box<dyn ResolveOrigin>>,
56{
57    fn resolve_asset(
58        self: Vc<Self>,
59        request: Vc<Request>,
60        options: Vc<ResolveOptions>,
61        reference_type: Value<ReferenceType>,
62    ) -> impl Future<Output = Result<Vc<ModuleResolveResult>>> + Send {
63        resolve_asset(Vc::upcast(self), request, options, reference_type)
64    }
65
66    fn resolve_options(self: Vc<Self>, reference_type: Value<ReferenceType>) -> Vc<ResolveOptions> {
67        self.asset_context()
68            .resolve_options(self.origin_path(), reference_type)
69    }
70
71    fn with_transition(self: ResolvedVc<Self>, transition: RcStr) -> Vc<Box<dyn ResolveOrigin>> {
72        Vc::upcast(
73            ResolveOriginWithTransition {
74                previous: ResolvedVc::upcast(self),
75                transition,
76            }
77            .cell(),
78        )
79    }
80}
81
82async fn resolve_asset(
83    resolve_origin: Vc<Box<dyn ResolveOrigin>>,
84    request: Vc<Request>,
85    options: Vc<ResolveOptions>,
86    reference_type: Value<ReferenceType>,
87) -> Result<Vc<ModuleResolveResult>> {
88    if let Some(asset) = *resolve_origin.get_inner_asset(request).await? {
89        return Ok(*ModuleResolveResult::module(asset));
90    }
91    Ok(resolve_origin
92        .asset_context()
93        .resolve()
94        .await?
95        .resolve_asset(
96            resolve_origin.origin_path().resolve().await?,
97            request.resolve().await?,
98            options.resolve().await?,
99            reference_type,
100        ))
101}
102
103/// A resolve origin for some path and context without additional modifications.
104#[turbo_tasks::value]
105pub struct PlainResolveOrigin {
106    asset_context: ResolvedVc<Box<dyn AssetContext>>,
107    origin_path: ResolvedVc<FileSystemPath>,
108}
109
110#[turbo_tasks::value_impl]
111impl PlainResolveOrigin {
112    #[turbo_tasks::function]
113    pub fn new(
114        asset_context: ResolvedVc<Box<dyn AssetContext>>,
115        origin_path: ResolvedVc<FileSystemPath>,
116    ) -> Vc<Self> {
117        PlainResolveOrigin {
118            asset_context,
119            origin_path,
120        }
121        .cell()
122    }
123}
124
125#[turbo_tasks::value_impl]
126impl ResolveOrigin for PlainResolveOrigin {
127    #[turbo_tasks::function]
128    fn origin_path(&self) -> Vc<FileSystemPath> {
129        *self.origin_path
130    }
131
132    #[turbo_tasks::function]
133    fn asset_context(&self) -> Vc<Box<dyn AssetContext>> {
134        *self.asset_context
135    }
136}
137
138/// Wraps a ResolveOrigin to add a transition.
139#[turbo_tasks::value]
140struct ResolveOriginWithTransition {
141    previous: ResolvedVc<Box<dyn ResolveOrigin>>,
142    transition: RcStr,
143}
144
145#[turbo_tasks::value_impl]
146impl ResolveOrigin for ResolveOriginWithTransition {
147    #[turbo_tasks::function]
148    fn origin_path(&self) -> Vc<FileSystemPath> {
149        self.previous.origin_path()
150    }
151
152    #[turbo_tasks::function]
153    fn asset_context(&self) -> Vc<Box<dyn AssetContext>> {
154        self.previous
155            .asset_context()
156            .with_transition(self.transition.clone())
157    }
158
159    #[turbo_tasks::function]
160    fn get_inner_asset(&self, request: Vc<Request>) -> Vc<OptionModule> {
161        self.previous.get_inner_asset(request)
162    }
163}