Skip to main content

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