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