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