Skip to main content

turbopack_core/reference/
mod.rs

1use std::collections::HashSet;
2
3use anyhow::Result;
4use bincode::{Decode, Encode};
5use turbo_rcstr::RcStr;
6use turbo_tasks::{
7    NonLocalValue, ResolvedVc, TryFlatJoinIterExt, TryJoinIterExt, ValueToString, Vc,
8    debug::ValueDebugFormat, trace::TraceRawVcs,
9};
10
11use crate::{
12    chunk::{ChunkingType, TracedMode},
13    issue::IssueSource,
14    module::{Module, Modules},
15    output::{
16        ExpandOutputAssetsInput, ExpandedOutputAssets, OutputAsset, OutputAssets,
17        expand_output_assets,
18    },
19    raw_module::RawModule,
20    resolve::{BindingUsage, ExportUsage, ImportUsage, ModuleResolveResult},
21};
22pub mod source_map;
23
24pub use source_map::SourceMapReference;
25
26/// A reference to one or multiple [Module]s, [OutputAsset]s or other special
27/// things.
28///
29/// [Module]: crate::module::Module
30/// [OutputAsset]: crate::output::OutputAsset
31#[turbo_tasks::value_trait]
32pub trait ModuleReference: ValueToString {
33    #[turbo_tasks::function]
34    fn resolve_reference(self: Vc<Self>) -> Vc<ModuleResolveResult>;
35    // TODO think about different types
36    // fn kind(&self) -> Vc<AssetReferenceType>;
37
38    fn chunking_type(&self) -> Option<ChunkingType>;
39
40    fn binding_usage(&self) -> BindingUsage {
41        BindingUsage::default()
42    }
43
44    fn source(&self) -> Option<IssueSource> {
45        None
46    }
47}
48
49/// Multiple [ModuleReference]s
50#[turbo_tasks::value(transparent)]
51pub struct ModuleReferences(Vec<ResolvedVc<Box<dyn ModuleReference>>>);
52
53#[turbo_tasks::value_impl]
54impl ModuleReferences {
55    /// An empty list of [ModuleReference]s
56    #[turbo_tasks::function]
57    pub fn empty() -> Vc<Self> {
58        Vc::cell(Vec::new())
59    }
60}
61
62#[turbo_tasks::value]
63#[derive(ValueToString)]
64#[value_to_string(self.description)]
65pub struct SingleChunkableModuleReference {
66    asset: ResolvedVc<Box<dyn Module>>,
67    description: RcStr,
68    export: ExportUsage,
69}
70
71#[turbo_tasks::value_impl]
72impl SingleChunkableModuleReference {
73    #[turbo_tasks::function]
74    pub async fn new(
75        asset: ResolvedVc<Box<dyn Module>>,
76        description: RcStr,
77        export: Vc<ExportUsage>,
78    ) -> Result<Vc<Self>> {
79        Ok(Self::cell(SingleChunkableModuleReference {
80            asset,
81            description,
82            export: export.owned().await?,
83        }))
84    }
85}
86
87#[turbo_tasks::value_impl]
88impl ModuleReference for SingleChunkableModuleReference {
89    #[turbo_tasks::function]
90    fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
91        *ModuleResolveResult::module(self.asset)
92    }
93
94    fn chunking_type(&self) -> Option<ChunkingType> {
95        Some(ChunkingType::Parallel {
96            inherit_async: true,
97            hoisted: false,
98        })
99    }
100
101    fn binding_usage(&self) -> BindingUsage {
102        BindingUsage {
103            import: ImportUsage::TopLevel,
104            export: self.export.clone(),
105        }
106    }
107}
108
109/// Aggregates all [Module]s referenced by an [Module]. [ModuleReference]
110/// This does not include transitively references [Module]s, but it includes
111/// primary and secondary [Module]s referenced.
112///
113/// [Module]: crate::module::Module
114#[turbo_tasks::function]
115pub async fn referenced_modules_and_affecting_sources(
116    module: Vc<Box<dyn Module>>,
117    include_binding_usage: bool,
118) -> Result<Vc<ModulesWithRefData>> {
119    let modules = module
120        .references()
121        .await?
122        .iter()
123        .map(|reference| async {
124            let trait_ref = reference.into_trait_ref().await?;
125            let resolve_result = reference.resolve_reference().await?;
126            if let Some(chunking_type) = &trait_ref.chunking_type() {
127                let mut modules = resolve_result
128                    .primary_modules_raw_iter()
129                    .collect::<Vec<_>>();
130                modules.extend(
131                    resolve_result
132                        .affecting_sources_iter()
133                        .map(|source| async move {
134                            Ok(ResolvedVc::upcast(
135                                RawModule::new(*source).to_resolved().await?,
136                            ))
137                        })
138                        .try_join()
139                        .await?,
140                );
141
142                let binding_usage = if include_binding_usage {
143                    trait_ref.binding_usage()
144                } else {
145                    BindingUsage::default()
146                };
147
148                return Ok(Some((
149                    *reference,
150                    ResolvedReference {
151                        chunking_type: chunking_type.clone(),
152                        binding_usage,
153                        modules,
154                    },
155                )));
156            }
157            Ok(None)
158        })
159        .try_flat_join()
160        .await?;
161    Ok(Vc::cell(modules))
162}
163
164#[turbo_tasks::value]
165#[derive(ValueToString)]
166#[value_to_string("traced {}", self.module.ident())]
167pub struct TracedModuleReference {
168    module: ResolvedVc<Box<dyn Module>>,
169    mode: TracedMode,
170}
171
172#[turbo_tasks::value_impl]
173impl ModuleReference for TracedModuleReference {
174    #[turbo_tasks::function]
175    fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
176        *ModuleResolveResult::module(self.module)
177    }
178
179    fn chunking_type(&self) -> Option<ChunkingType> {
180        Some(ChunkingType::Traced { mode: self.mode })
181    }
182}
183
184#[turbo_tasks::value_impl]
185impl TracedModuleReference {
186    #[turbo_tasks::function]
187    pub fn new(module: ResolvedVc<Box<dyn Module>>, mode: TracedMode) -> Vc<Self> {
188        Self::cell(TracedModuleReference { module, mode })
189    }
190}
191
192/// Aggregates all primary [`Module`]s referenced by an [`Module`]. This does not include
193/// transitively references [`Module`]s, only includes primary [`Module`]s referenced.
194///
195/// [`Module`]: crate::module::Module
196#[turbo_tasks::function]
197pub async fn primary_referenced_modules(module: Vc<Box<dyn Module>>) -> Result<Vc<Modules>> {
198    let mut set = HashSet::new();
199    let modules = module
200        .references()
201        .await?
202        .iter()
203        .map(|reference| async { reference.resolve_reference().await?.primary_modules().await })
204        .try_join()
205        .await?
206        .into_iter()
207        .flatten()
208        .filter(|&module| set.insert(module))
209        .collect();
210    Ok(Vc::cell(modules))
211}
212
213#[derive(Clone, Eq, PartialEq, ValueDebugFormat, TraceRawVcs, NonLocalValue, Encode, Decode)]
214pub struct ResolvedReference {
215    pub chunking_type: ChunkingType,
216    pub binding_usage: BindingUsage,
217    pub modules: Vec<ResolvedVc<Box<dyn Module>>>,
218}
219
220#[turbo_tasks::value(transparent)]
221pub struct ModulesWithRefData(Vec<(ResolvedVc<Box<dyn ModuleReference>>, ResolvedReference)>);
222
223/// Aggregates all primary [Module]s referenced by an [Module] via [ModuleReference]s with a
224/// non-empty chunking_type. This does not include transitively referenced [Module]s, only primary
225/// [Module]s referenced.
226///
227/// [Module]: crate::module::Module
228#[turbo_tasks::function]
229pub async fn primary_chunkable_referenced_modules(
230    module: ResolvedVc<Box<dyn Module>>,
231    include_traced: bool,
232    include_binding_usage: bool,
233) -> Result<Vc<ModulesWithRefData>> {
234    let modules = module
235        .references()
236        .await?
237        .iter()
238        .map(|reference| async {
239            let trait_ref = reference.into_trait_ref().await?;
240            if let Some(chunking_type) = &trait_ref.chunking_type() {
241                if !include_traced && chunking_type.is_traced() {
242                    return Ok(None);
243                }
244
245                let resolved = reference
246                    .resolve_reference()
247                    .await?
248                    .primary_modules()
249                    .await?;
250                let binding_usage = if include_binding_usage {
251                    trait_ref.binding_usage()
252                } else {
253                    BindingUsage::default()
254                };
255
256                return Ok(Some((
257                    *reference,
258                    ResolvedReference {
259                        chunking_type: chunking_type.clone(),
260                        binding_usage,
261                        modules: resolved,
262                    },
263                )));
264            }
265            Ok(None)
266        })
267        .try_flat_join()
268        .await?;
269    Ok(Vc::cell(modules))
270}
271
272/// Walks the asset graph from multiple assets and collect all referenced
273/// assets.
274#[turbo_tasks::function]
275pub async fn all_assets_from_entries(
276    entries: Vc<OutputAssets>,
277) -> Result<Vc<ExpandedOutputAssets>> {
278    Ok(Vc::cell(
279        expand_output_assets(
280            entries
281                .await?
282                .into_iter()
283                .map(ExpandOutputAssetsInput::Asset),
284            true,
285        )
286        .await?,
287    ))
288}
289
290/// Walks the asset graph from multiple assets and collect all referenced
291/// assets.
292#[turbo_tasks::function]
293pub async fn all_assets_from_entry(
294    entry: ResolvedVc<Box<dyn OutputAsset>>,
295) -> Result<Vc<ExpandedOutputAssets>> {
296    Ok(Vc::cell(
297        expand_output_assets(std::iter::once(ExpandOutputAssetsInput::Asset(entry)), true).await?,
298    ))
299}