turbopack_core/resolve/
origin.rs

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