turbopack_core/reference/
mod.rs

1use std::collections::HashSet;
2
3use anyhow::Result;
4use turbo_rcstr::RcStr;
5use turbo_tasks::{
6    FxIndexSet, ResolvedVc, TryFlatJoinIterExt, TryJoinIterExt, ValueToString, Vc,
7    graph::{AdjacencyMap, GraphTraversal},
8};
9
10use crate::{
11    chunk::{ChunkableModuleReference, ChunkingType, ChunkingTypeOption},
12    module::{Module, Modules},
13    output::{OutputAsset, OutputAssets},
14    raw_module::RawModule,
15    resolve::{ModuleResolveResult, RequestKey},
16};
17pub mod source_map;
18
19pub use source_map::SourceMapReference;
20
21/// A reference to one or multiple [Module]s, [OutputAsset]s or other special
22/// things. There are a bunch of optional traits that can influence how these
23/// references are handled. e. g. [ChunkableModuleReference]
24///
25/// [Module]: crate::module::Module
26/// [OutputAsset]: crate::output::OutputAsset
27/// [ChunkableModuleReference]: crate::chunk::ChunkableModuleReference
28#[turbo_tasks::value_trait]
29pub trait ModuleReference: ValueToString {
30    fn resolve_reference(self: Vc<Self>) -> Vc<ModuleResolveResult>;
31    // TODO think about different types
32    // fn kind(&self) -> Vc<AssetReferenceType>;
33}
34
35/// Multiple [ModuleReference]s
36#[turbo_tasks::value(transparent)]
37pub struct ModuleReferences(Vec<ResolvedVc<Box<dyn ModuleReference>>>);
38
39#[turbo_tasks::value_impl]
40impl ModuleReferences {
41    /// An empty list of [ModuleReference]s
42    #[turbo_tasks::function]
43    pub fn empty() -> Vc<Self> {
44        Vc::cell(Vec::new())
45    }
46}
47
48/// A reference that always resolves to a single module.
49#[turbo_tasks::value]
50pub struct SingleModuleReference {
51    asset: ResolvedVc<Box<dyn Module>>,
52    description: ResolvedVc<RcStr>,
53}
54
55#[turbo_tasks::value_impl]
56impl ModuleReference for SingleModuleReference {
57    #[turbo_tasks::function]
58    fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
59        *ModuleResolveResult::module(self.asset)
60    }
61}
62
63#[turbo_tasks::value_impl]
64impl ValueToString for SingleModuleReference {
65    #[turbo_tasks::function]
66    fn to_string(&self) -> Vc<RcStr> {
67        *self.description
68    }
69}
70
71#[turbo_tasks::value_impl]
72impl SingleModuleReference {
73    /// Create a new [Vc<SingleModuleReference>] that resolves to the given
74    /// asset.
75    #[turbo_tasks::function]
76    pub fn new(asset: ResolvedVc<Box<dyn Module>>, description: ResolvedVc<RcStr>) -> Vc<Self> {
77        Self::cell(SingleModuleReference { asset, description })
78    }
79
80    /// The [Vc<Box<dyn Asset>>] that this reference resolves to.
81    #[turbo_tasks::function]
82    pub fn asset(&self) -> Vc<Box<dyn Module>> {
83        *self.asset
84    }
85}
86
87#[turbo_tasks::value]
88pub struct SingleChunkableModuleReference {
89    asset: ResolvedVc<Box<dyn Module>>,
90    description: ResolvedVc<RcStr>,
91}
92
93#[turbo_tasks::value_impl]
94impl SingleChunkableModuleReference {
95    #[turbo_tasks::function]
96    pub fn new(asset: ResolvedVc<Box<dyn Module>>, description: ResolvedVc<RcStr>) -> Vc<Self> {
97        Self::cell(SingleChunkableModuleReference { asset, description })
98    }
99}
100
101#[turbo_tasks::value_impl]
102impl ChunkableModuleReference for SingleChunkableModuleReference {
103    #[turbo_tasks::function]
104    fn chunking_type(self: Vc<Self>) -> Vc<ChunkingTypeOption> {
105        Vc::cell(Some(ChunkingType::Parallel {
106            inherit_async: true,
107            hoisted: false,
108        }))
109    }
110}
111
112#[turbo_tasks::value_impl]
113impl ModuleReference for SingleChunkableModuleReference {
114    #[turbo_tasks::function]
115    fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
116        *ModuleResolveResult::module(self.asset)
117    }
118}
119
120#[turbo_tasks::value_impl]
121impl ValueToString for SingleChunkableModuleReference {
122    #[turbo_tasks::function]
123    fn to_string(&self) -> Vc<RcStr> {
124        *self.description
125    }
126}
127
128/// A reference that always resolves to a single module.
129#[turbo_tasks::value]
130pub struct SingleOutputAssetReference {
131    asset: ResolvedVc<Box<dyn OutputAsset>>,
132    description: ResolvedVc<RcStr>,
133}
134
135#[turbo_tasks::value_impl]
136impl ModuleReference for SingleOutputAssetReference {
137    #[turbo_tasks::function]
138    fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
139        *ModuleResolveResult::output_asset(RequestKey::default(), self.asset)
140    }
141}
142
143#[turbo_tasks::value_impl]
144impl ValueToString for SingleOutputAssetReference {
145    #[turbo_tasks::function]
146    fn to_string(&self) -> Vc<RcStr> {
147        *self.description
148    }
149}
150
151#[turbo_tasks::value_impl]
152impl SingleOutputAssetReference {
153    /// Create a new [Vc<SingleOutputAssetReference>] that resolves to the given
154    /// asset.
155    #[turbo_tasks::function]
156    pub fn new(
157        asset: ResolvedVc<Box<dyn OutputAsset>>,
158        description: ResolvedVc<RcStr>,
159    ) -> Vc<Self> {
160        Self::cell(SingleOutputAssetReference { asset, description })
161    }
162
163    /// The [Vc<Box<dyn Asset>>] that this reference resolves to.
164    #[turbo_tasks::function]
165    pub fn asset(&self) -> Vc<Box<dyn OutputAsset>> {
166        *self.asset
167    }
168}
169
170/// Aggregates all [Module]s referenced by an [Module]. [ModuleReference]
171/// This does not include transitively references [Module]s, but it includes
172/// primary and secondary [Module]s referenced.
173///
174/// [Module]: crate::module::Module
175#[turbo_tasks::function]
176pub async fn referenced_modules_and_affecting_sources(
177    module: Vc<Box<dyn Module>>,
178) -> Result<Vc<Modules>> {
179    let references = module.references().await?;
180
181    let resolved_references = references
182        .iter()
183        .map(|r| r.resolve_reference())
184        .try_join()
185        .await?;
186    let mut modules = Vec::new();
187    for resolve_result in resolved_references {
188        modules.extend(resolve_result.primary_modules_raw_iter());
189        modules.extend(
190            resolve_result
191                .affecting_sources_iter()
192                .map(|source| async move {
193                    Ok(ResolvedVc::upcast(
194                        RawModule::new(*source).to_resolved().await?,
195                    ))
196                })
197                .try_join()
198                .await?,
199        );
200    }
201
202    let resolved_modules: FxIndexSet<_> = modules.into_iter().collect();
203
204    Ok(Vc::cell(resolved_modules.into_iter().collect()))
205}
206
207#[turbo_tasks::value]
208pub struct TracedModuleReference {
209    module: ResolvedVc<Box<dyn Module>>,
210}
211
212#[turbo_tasks::value_impl]
213impl ModuleReference for TracedModuleReference {
214    #[turbo_tasks::function]
215    fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
216        *ModuleResolveResult::module(self.module)
217    }
218}
219
220#[turbo_tasks::value_impl]
221impl ValueToString for TracedModuleReference {
222    #[turbo_tasks::function]
223    async fn to_string(&self) -> Result<Vc<RcStr>> {
224        Ok(Vc::cell(
225            format!("traced {}", self.module.ident().to_string().await?).into(),
226        ))
227    }
228}
229
230#[turbo_tasks::value_impl]
231impl ChunkableModuleReference for TracedModuleReference {
232    #[turbo_tasks::function]
233    fn chunking_type(&self) -> Vc<ChunkingTypeOption> {
234        Vc::cell(Some(ChunkingType::Traced))
235    }
236}
237
238#[turbo_tasks::value_impl]
239impl TracedModuleReference {
240    #[turbo_tasks::function]
241    pub fn new(module: ResolvedVc<Box<dyn Module>>) -> Vc<Self> {
242        Self::cell(TracedModuleReference { module })
243    }
244}
245
246/// Aggregates all primary [Module]s referenced by an [Module]. [AssetReference]
247/// This does not include transitively references [Module]s, only includes
248/// primary [Module]s referenced.
249///
250/// [Module]: crate::module::Module
251#[turbo_tasks::function]
252pub async fn primary_referenced_modules(module: Vc<Box<dyn Module>>) -> Result<Vc<Modules>> {
253    let mut set = HashSet::new();
254    let modules = module
255        .references()
256        .await?
257        .iter()
258        .map(|reference| async {
259            reference
260                .resolve_reference()
261                .resolve()
262                .await?
263                .primary_modules()
264                .owned()
265                .await
266        })
267        .try_join()
268        .await?
269        .into_iter()
270        .flatten()
271        .filter(|&module| set.insert(module))
272        .collect();
273    Ok(Vc::cell(modules))
274}
275
276type ModulesVec = Vec<ResolvedVc<Box<dyn Module>>>;
277#[turbo_tasks::value(transparent)]
278pub struct ModulesWithChunkingType(Vec<(ChunkingType, ModulesVec)>);
279
280/// Aggregates all primary [Module]s referenced by an [Module] via [ChunkableModuleReference]s.
281/// This does not include transitively references [Module]s, only includes
282/// primary [Module]s referenced.
283///
284/// [Module]: crate::module::Module
285#[turbo_tasks::function]
286pub async fn primary_chunkable_referenced_modules(
287    module: Vc<Box<dyn Module>>,
288    include_traced: bool,
289) -> Result<Vc<ModulesWithChunkingType>> {
290    let modules = module
291        .references()
292        .await?
293        .iter()
294        .map(|reference| async {
295            if let Some(reference) =
296                ResolvedVc::try_downcast::<Box<dyn ChunkableModuleReference>>(*reference)
297            {
298                if let Some(chunking_type) = &*reference.chunking_type().await? {
299                    if !include_traced && matches!(chunking_type, ChunkingType::Traced) {
300                        return Ok(None);
301                    }
302
303                    let resolved = reference
304                        .resolve_reference()
305                        .resolve()
306                        .await?
307                        .primary_modules()
308                        .owned()
309                        .await?;
310                    return Ok(Some((chunking_type.clone(), resolved)));
311                }
312            }
313            Ok(None)
314        })
315        .try_flat_join()
316        .await?;
317    Ok(Vc::cell(modules))
318}
319
320/// Walks the asset graph from multiple assets and collect all referenced
321/// assets.
322#[turbo_tasks::function]
323pub async fn all_assets_from_entries(entries: Vc<OutputAssets>) -> Result<Vc<OutputAssets>> {
324    Ok(Vc::cell(
325        AdjacencyMap::new()
326            .skip_duplicates()
327            .visit(
328                entries.await?.iter().copied().map(ResolvedVc::upcast),
329                get_referenced_assets,
330            )
331            .await
332            .completed()?
333            .into_inner()
334            .into_postorder_topological()
335            .collect(),
336    ))
337}
338
339/// Computes the list of all chunk children of a given chunk.
340pub async fn get_referenced_assets(
341    asset: ResolvedVc<Box<dyn OutputAsset>>,
342) -> Result<impl Iterator<Item = ResolvedVc<Box<dyn OutputAsset>>> + Send> {
343    Ok(asset
344        .references()
345        .await?
346        .iter()
347        .copied()
348        .collect::<Vec<_>>()
349        .into_iter())
350}