turbopack_core/reference/
mod.rs

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