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/// A [`ModuleReference`] created by tracing a dynamic filesystem access (e.g.
50/// `fs.readFileSync`, `path.join`) for the purpose of Node File Tracing.
51#[turbo_tasks::value_trait]
52pub trait DynamicTraceReference: ModuleReference {
53    /// The name of the function/call that created this reference (e.g.
54    /// `fs.readFileSync`). Used to name the offending call in tracing
55    /// diagnostics so the suggested fix refers to the actual call rather than an
56    /// example.
57    fn origin_fn_name(&self) -> RcStr;
58}
59
60/// Multiple [ModuleReference]s
61#[turbo_tasks::value(transparent)]
62pub struct ModuleReferences(Vec<ResolvedVc<Box<dyn ModuleReference>>>);
63
64#[turbo_tasks::value_impl]
65impl ModuleReferences {
66    /// An empty list of [ModuleReference]s
67    #[turbo_tasks::function]
68    pub fn empty() -> Vc<Self> {
69        Vc::cell(Vec::new())
70    }
71}
72
73#[turbo_tasks::value]
74#[derive(ValueToString)]
75#[value_to_string(self.description)]
76pub struct SingleChunkableModuleReference {
77    asset: ResolvedVc<Box<dyn Module>>,
78    description: RcStr,
79    export: ExportUsage,
80}
81
82#[turbo_tasks::value_impl]
83impl SingleChunkableModuleReference {
84    #[turbo_tasks::function]
85    pub async fn new(
86        asset: ResolvedVc<Box<dyn Module>>,
87        description: RcStr,
88        export: Vc<ExportUsage>,
89    ) -> Result<Vc<Self>> {
90        Ok(Self::cell(SingleChunkableModuleReference {
91            asset,
92            description,
93            export: export.owned().await?,
94        }))
95    }
96}
97
98#[turbo_tasks::value_impl]
99impl ModuleReference for SingleChunkableModuleReference {
100    #[turbo_tasks::function]
101    fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
102        *ModuleResolveResult::module(self.asset)
103    }
104
105    fn chunking_type(&self) -> Option<ChunkingType> {
106        Some(ChunkingType::Parallel {
107            inherit_async: true,
108            hoisted: false,
109        })
110    }
111
112    fn binding_usage(&self) -> BindingUsage {
113        BindingUsage {
114            import: ImportUsage::TopLevel,
115            export: self.export.clone(),
116        }
117    }
118}
119
120/// Aggregates all [Module]s referenced by an [Module]. [ModuleReference]
121/// This does not include transitively references [Module]s, but it includes
122/// primary and secondary [Module]s referenced.
123///
124/// [Module]: crate::module::Module
125#[turbo_tasks::function]
126pub async fn referenced_modules_and_affecting_sources(
127    module: Vc<Box<dyn Module>>,
128    include_binding_usage: bool,
129) -> Result<Vc<ModulesWithRefData>> {
130    let modules = module
131        .references()
132        .await?
133        .iter()
134        .map(|reference| async {
135            let trait_ref = reference.into_trait_ref().await?;
136            let resolve_result = reference.resolve_reference().await?;
137            if let Some(chunking_type) = &trait_ref.chunking_type() {
138                let mut modules = resolve_result
139                    .primary_modules_raw_iter()
140                    .collect::<Vec<_>>();
141                modules.extend(
142                    resolve_result
143                        .affecting_sources_iter()
144                        .map(|source| async move {
145                            Ok(ResolvedVc::upcast(
146                                RawModule::new(*source).to_resolved().await?,
147                            ))
148                        })
149                        .try_join()
150                        .await?,
151                );
152
153                let binding_usage = if include_binding_usage {
154                    trait_ref.binding_usage()
155                } else {
156                    BindingUsage::default()
157                };
158
159                return Ok(Some((
160                    *reference,
161                    ResolvedReference {
162                        chunking_type: chunking_type.clone(),
163                        binding_usage,
164                        modules,
165                    },
166                )));
167            }
168            Ok(None)
169        })
170        .try_flat_join()
171        .await?;
172    Ok(Vc::cell(modules))
173}
174
175#[turbo_tasks::value]
176#[derive(ValueToString)]
177#[value_to_string("traced {}", self.module.ident())]
178pub struct TracedModuleReference {
179    module: ResolvedVc<Box<dyn Module>>,
180    mode: TracedMode,
181}
182
183#[turbo_tasks::value_impl]
184impl ModuleReference for TracedModuleReference {
185    #[turbo_tasks::function]
186    fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
187        *ModuleResolveResult::module(self.module)
188    }
189
190    fn chunking_type(&self) -> Option<ChunkingType> {
191        Some(ChunkingType::Traced { mode: self.mode })
192    }
193}
194
195#[turbo_tasks::value_impl]
196impl TracedModuleReference {
197    #[turbo_tasks::function]
198    pub fn new(module: ResolvedVc<Box<dyn Module>>, mode: TracedMode) -> Vc<Self> {
199        Self::cell(TracedModuleReference { module, mode })
200    }
201}
202
203/// Aggregates all primary [`Module`]s referenced by an [`Module`]. This does not include
204/// transitively references [`Module`]s, only includes primary [`Module`]s referenced.
205///
206/// [`Module`]: crate::module::Module
207#[turbo_tasks::function]
208pub async fn primary_referenced_modules(module: Vc<Box<dyn Module>>) -> Result<Vc<Modules>> {
209    let mut set = HashSet::new();
210    let modules = module
211        .references()
212        .await?
213        .iter()
214        .map(|reference| async { reference.resolve_reference().await?.primary_modules().await })
215        .try_join()
216        .await?
217        .into_iter()
218        .flatten()
219        .filter(|&module| set.insert(module))
220        .collect();
221    Ok(Vc::cell(modules))
222}
223
224#[derive(Clone, Eq, PartialEq, ValueDebugFormat, TraceRawVcs, NonLocalValue, Encode, Decode)]
225pub struct ResolvedReference {
226    pub chunking_type: ChunkingType,
227    pub binding_usage: BindingUsage,
228    pub modules: Vec<ResolvedVc<Box<dyn Module>>>,
229}
230
231#[turbo_tasks::value(transparent)]
232pub struct ModulesWithRefData(Vec<(ResolvedVc<Box<dyn ModuleReference>>, ResolvedReference)>);
233
234/// Aggregates all primary [Module]s referenced by an [Module] via [ModuleReference]s with a
235/// non-empty chunking_type. This does not include transitively referenced [Module]s, only primary
236/// [Module]s referenced.
237///
238/// [Module]: crate::module::Module
239#[turbo_tasks::function]
240pub async fn primary_chunkable_referenced_modules(
241    module: ResolvedVc<Box<dyn Module>>,
242    include_traced: bool,
243    include_binding_usage: bool,
244) -> Result<Vc<ModulesWithRefData>> {
245    let modules = module
246        .references()
247        .await?
248        .iter()
249        .map(|reference| async {
250            let trait_ref = reference.into_trait_ref().await?;
251            if let Some(chunking_type) = &trait_ref.chunking_type() {
252                if !include_traced && chunking_type.is_traced() {
253                    return Ok(None);
254                }
255
256                let resolved = reference
257                    .resolve_reference()
258                    .await?
259                    .primary_modules()
260                    .await?;
261                let binding_usage = if include_binding_usage {
262                    trait_ref.binding_usage()
263                } else {
264                    BindingUsage::default()
265                };
266
267                return Ok(Some((
268                    *reference,
269                    ResolvedReference {
270                        chunking_type: chunking_type.clone(),
271                        binding_usage,
272                        modules: resolved,
273                    },
274                )));
275            }
276            Ok(None)
277        })
278        .try_flat_join()
279        .await?;
280    Ok(Vc::cell(modules))
281}
282
283/// Walks the asset graph from multiple assets and collect all referenced
284/// assets.
285#[turbo_tasks::function]
286pub async fn all_assets_from_entries(
287    entries: Vc<OutputAssets>,
288) -> Result<Vc<ExpandedOutputAssets>> {
289    Ok(Vc::cell(
290        expand_output_assets(
291            entries
292                .await?
293                .into_iter()
294                .map(ExpandOutputAssetsInput::Asset),
295            true,
296        )
297        .await?,
298    ))
299}
300
301/// Walks the asset graph from multiple assets and collect all referenced
302/// assets.
303#[turbo_tasks::function]
304pub async fn all_assets_from_entry(
305    entry: ResolvedVc<Box<dyn OutputAsset>>,
306) -> Result<Vc<ExpandedOutputAssets>> {
307    Ok(Vc::cell(
308        expand_output_assets(std::iter::once(ExpandOutputAssetsInput::Asset(entry)), true).await?,
309    ))
310}