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(Vc::upcast(self), request, options, reference_type)
70    }
71
72    async fn resolve_options(
73        self: Vc<Self>,
74        reference_type: ReferenceType,
75    ) -> Result<Vc<ResolveOptions>> {
76        Ok(self
77            .asset_context()
78            .resolve_options(self.origin_path().await?.clone_value(), reference_type))
79    }
80
81    fn with_transition(self: ResolvedVc<Self>, transition: RcStr) -> Vc<Box<dyn ResolveOrigin>> {
82        Vc::upcast(
83            ResolveOriginWithTransition {
84                previous: ResolvedVc::upcast(self),
85                transition,
86            }
87            .cell(),
88        )
89    }
90}
91
92async fn resolve_asset(
93    resolve_origin: Vc<Box<dyn ResolveOrigin>>,
94    request: Vc<Request>,
95    options: Vc<ResolveOptions>,
96    reference_type: ReferenceType,
97) -> Result<Vc<ModuleResolveResult>> {
98    if let Some(asset) = *resolve_origin.get_inner_asset(request).await? {
99        return Ok(*ModuleResolveResult::module(asset));
100    }
101    Ok(resolve_origin
102        .asset_context()
103        .resolve()
104        .await?
105        .resolve_asset(
106            resolve_origin.origin_path().await?.clone_value(),
107            request.resolve().await?,
108            options.resolve().await?,
109            reference_type,
110        ))
111}
112
113/// A resolve origin for some path and context without additional modifications.
114#[turbo_tasks::value]
115pub struct PlainResolveOrigin {
116    asset_context: ResolvedVc<Box<dyn AssetContext>>,
117    origin_path: FileSystemPath,
118}
119
120#[turbo_tasks::value_impl]
121impl PlainResolveOrigin {
122    #[turbo_tasks::function]
123    pub fn new(
124        asset_context: ResolvedVc<Box<dyn AssetContext>>,
125        origin_path: FileSystemPath,
126    ) -> Vc<Self> {
127        PlainResolveOrigin {
128            asset_context,
129            origin_path,
130        }
131        .cell()
132    }
133}
134
135#[turbo_tasks::value_impl]
136impl ResolveOrigin for PlainResolveOrigin {
137    #[turbo_tasks::function]
138    fn origin_path(&self) -> Vc<FileSystemPath> {
139        self.origin_path.clone().cell()
140    }
141
142    #[turbo_tasks::function]
143    fn asset_context(&self) -> Vc<Box<dyn AssetContext>> {
144        *self.asset_context
145    }
146}
147
148/// Wraps a ResolveOrigin to add a transition.
149#[turbo_tasks::value]
150struct ResolveOriginWithTransition {
151    previous: ResolvedVc<Box<dyn ResolveOrigin>>,
152    transition: RcStr,
153}
154
155#[turbo_tasks::value_impl]
156impl ResolveOrigin for ResolveOriginWithTransition {
157    #[turbo_tasks::function]
158    fn origin_path(&self) -> Vc<FileSystemPath> {
159        self.previous.origin_path()
160    }
161
162    #[turbo_tasks::function]
163    fn asset_context(&self) -> Vc<Box<dyn AssetContext>> {
164        self.previous
165            .asset_context()
166            .with_transition(self.transition.clone())
167    }
168
169    #[turbo_tasks::function]
170    fn get_inner_asset(&self, request: Vc<Request>) -> Vc<OptionModule> {
171        self.previous.get_inner_asset(request)
172    }
173}