Skip to main content

turbopack_core/reference/
mod.rs

1use std::collections::HashSet;
2
3use anyhow::Result;
4use bincode::{Decode, Encode};
5use smallvec::SmallVec;
6use turbo_rcstr::RcStr;
7use turbo_tasks::{
8    NonLocalValue, ResolvedVc, TryFlatJoinIterExt, TryJoinIterExt, ValueToString, Vc,
9    debug::ValueDebugFormat,
10};
11
12use crate::{
13    chunk::{ChunkingType, TracedMode},
14    issue::IssueSource,
15    module::{Module, Modules},
16    output::{
17        ExpandOutputAssetsInput, ExpandedOutputAssets, OutputAsset, OutputAssets,
18        expand_output_assets,
19    },
20    raw_module::RawModule,
21    resolve::{BindingUsage, ExportUsage, ImportUsage, ModuleResolveResult},
22};
23pub mod source_map;
24
25pub use source_map::SourceMapReference;
26
27/// A reference to one or multiple [Module]s, [OutputAsset]s or other special
28/// things.
29///
30/// [Module]: crate::module::Module
31/// [OutputAsset]: crate::output::OutputAsset
32#[turbo_tasks::value_trait]
33pub trait ModuleReference: ValueToString {
34    #[turbo_tasks::function]
35    fn resolve_reference(self: Vc<Self>) -> Vc<ModuleResolveResult>;
36    // TODO think about different types
37    // fn kind(&self) -> Vc<AssetReferenceType>;
38
39    fn chunking_type(&self) -> Option<ChunkingType>;
40
41    fn binding_usage(&self) -> BindingUsage {
42        BindingUsage::default()
43    }
44
45    fn source(&self) -> Option<IssueSource> {
46        None
47    }
48}
49
50/// A [`ModuleReference`] created by tracing a dynamic filesystem access (e.g.
51/// `fs.readFileSync`, `path.join`) for the purpose of Node File Tracing.
52#[turbo_tasks::value_trait]
53pub trait DynamicTraceReference: ModuleReference {
54    /// The name of the function/call that created this reference (e.g.
55    /// `fs.readFileSync`). Used to name the offending call in tracing
56    /// diagnostics so the suggested fix refers to the actual call rather than an
57    /// example.
58    fn origin_fn_name(&self) -> RcStr;
59}
60
61/// Multiple [ModuleReference]s
62#[turbo_tasks::value(transparent)]
63pub struct ModuleReferences(Vec<ResolvedVc<Box<dyn ModuleReference>>>);
64
65#[turbo_tasks::value_impl]
66impl ModuleReferences {
67    /// An empty list of [ModuleReference]s
68    #[turbo_tasks::function]
69    pub fn empty() -> Vc<Self> {
70        Vc::cell(Vec::new())
71    }
72}
73
74#[turbo_tasks::value]
75#[derive(ValueToString)]
76#[value_to_string(self.description)]
77pub struct SingleChunkableModuleReference {
78    asset: ResolvedVc<Box<dyn Module>>,
79    description: RcStr,
80    export: ExportUsage,
81}
82
83#[turbo_tasks::value_impl]
84impl SingleChunkableModuleReference {
85    #[turbo_tasks::function]
86    pub async fn new(
87        asset: ResolvedVc<Box<dyn Module>>,
88        description: RcStr,
89        export: Vc<ExportUsage>,
90    ) -> Result<Vc<Self>> {
91        Ok(Self::cell(SingleChunkableModuleReference {
92            asset,
93            description,
94            export: export.owned().await?,
95        }))
96    }
97}
98
99#[turbo_tasks::value_impl]
100impl ModuleReference for SingleChunkableModuleReference {
101    #[turbo_tasks::function]
102    fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
103        *ModuleResolveResult::module(self.asset)
104    }
105
106    fn chunking_type(&self) -> Option<ChunkingType> {
107        Some(ChunkingType::Parallel {
108            inherit_async: true,
109            hoisted: false,
110        })
111    }
112
113    fn binding_usage(&self) -> BindingUsage {
114        BindingUsage {
115            import: ImportUsage::TopLevel,
116            export: self.export.clone(),
117        }
118    }
119}
120
121/// Aggregates all [Module]s referenced by an [Module]. [ModuleReference]
122/// This does not include transitively references [Module]s, but it includes
123/// primary and secondary [Module]s referenced.
124///
125/// [Module]: crate::module::Module
126#[turbo_tasks::function]
127pub async fn referenced_modules_and_affecting_sources(
128    module: Vc<Box<dyn Module>>,
129    include_binding_usage: bool,
130) -> Result<Vc<ModulesWithRefData>> {
131    let modules = module
132        .references()
133        .await?
134        .iter()
135        .map(async |reference| {
136            let trait_ref = reference.into_trait_ref().await?;
137            let resolve_result = reference.resolve_reference().await?;
138            if let Some(chunking_type) = &trait_ref.chunking_type() {
139                let mut modules: SmallVec<[_; 2]> =
140                    resolve_result.primary_modules_raw_iter().collect();
141                modules.extend(
142                    resolve_result
143                        .affecting_sources_iter()
144                        .map(async |source| {
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(async |reference| 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, NonLocalValue, Encode, Decode)]
225pub struct ResolvedReference {
226    pub chunking_type: ChunkingType,
227    pub binding_usage: BindingUsage,
228    pub modules: SmallVec<[ResolvedVc<Box<dyn Module>>; 2]>,
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(async |reference| {
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}