Skip to main content

turbopack_ecmascript/manifest/
chunk_asset.rs

1use anyhow::Result;
2use turbo_rcstr::{RcStr, rcstr};
3use turbo_tasks::{ResolvedVc, TryJoinIterExt, Vc};
4use turbopack_core::{
5    chunk::{
6        ChunkableModule, ChunkingContext, ChunkingContextExt, availability_info::AvailabilityInfo,
7    },
8    ident::AssetIdent,
9    module::{Module, ModuleSideEffects},
10    module_graph::{
11        ModuleGraph, chunk_group_info::ChunkGroup, module_batch::ChunkableModuleOrBatch,
12    },
13    output::OutputAssetsWithReferenced,
14    reference::{ModuleReferences, SingleOutputAssetReference},
15};
16
17use super::chunk_item::ManifestChunkItem;
18use crate::chunk::{EcmascriptChunkPlaceable, EcmascriptExports};
19
20/// The manifest module is deferred until requested by the manifest loader
21/// item when the dynamic `import()` expression is reached.
22///
23/// Its responsibility is to generate a Promise that will resolve only after
24/// all the necessary chunks needed by the dynamic import are loaded by the client.
25///
26/// Splitting the dynamic import into a quickly generate-able manifest loader
27/// item and a slow-to-generate manifest chunk allows for faster incremental
28/// compilation. The traversal won't be performed until the dynamic import is
29/// actually reached, instead of eagerly as part of the chunk that the dynamic
30/// import appears in.
31#[turbo_tasks::value(shared)]
32pub struct ManifestAsyncModule {
33    pub inner: ResolvedVc<Box<dyn ChunkableModule>>,
34    pub module_graph: ResolvedVc<ModuleGraph>,
35    pub chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
36    pub availability_info: AvailabilityInfo,
37}
38
39#[turbo_tasks::value_impl]
40impl ManifestAsyncModule {
41    #[turbo_tasks::function]
42    pub fn new(
43        module: ResolvedVc<Box<dyn ChunkableModule>>,
44        module_graph: ResolvedVc<ModuleGraph>,
45        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
46        availability_info: AvailabilityInfo,
47    ) -> Vc<Self> {
48        Self::cell(ManifestAsyncModule {
49            inner: module,
50            module_graph,
51            chunking_context,
52            availability_info,
53        })
54    }
55
56    #[turbo_tasks::function]
57    pub(super) fn chunk_group(&self) -> Vc<OutputAssetsWithReferenced> {
58        self.chunking_context.chunk_group_assets(
59            self.inner.ident(),
60            ChunkGroup::Async(ResolvedVc::upcast(self.inner)),
61            *self.module_graph,
62            self.availability_info,
63        )
64    }
65
66    #[turbo_tasks::function]
67    pub async fn manifest_chunk_group(
68        self: ResolvedVc<Self>,
69    ) -> Result<Vc<OutputAssetsWithReferenced>> {
70        let this = self.await?;
71        if let Some(chunk_items) = this.availability_info.available_modules() {
72            let inner_module = ResolvedVc::upcast(this.inner);
73            let batches = this
74                .module_graph
75                .module_batches(this.chunking_context.batching_config())
76                .await?;
77            let module_or_batch = batches.get_entry(inner_module).await?;
78            if let Some(chunkable_module_or_batch) =
79                ChunkableModuleOrBatch::from_module_or_batch(module_or_batch)
80                && *chunk_items.get(chunkable_module_or_batch.into()).await?
81            {
82                return Ok(OutputAssetsWithReferenced {
83                    assets: ResolvedVc::cell(vec![]),
84                    referenced_assets: ResolvedVc::cell(vec![]),
85                    references: ResolvedVc::cell(vec![]),
86                }
87                .cell());
88            }
89        }
90        Ok(this.chunking_context.chunk_group_assets(
91            self.ident(),
92            ChunkGroup::Async(ResolvedVc::upcast(self)),
93            *this.module_graph,
94            this.availability_info,
95        ))
96    }
97
98    #[turbo_tasks::function]
99    pub fn module_ident(&self) -> Vc<AssetIdent> {
100        self.inner.ident()
101    }
102
103    #[turbo_tasks::function]
104    pub async fn content_ident(&self) -> Result<Vc<AssetIdent>> {
105        let mut ident = self.inner.ident();
106        if let Some(available_modules) = self.availability_info.available_modules() {
107            ident = ident.with_modifier(available_modules.hash().await?.to_string().into());
108        }
109        Ok(ident)
110    }
111}
112
113fn manifest_chunk_reference_description() -> RcStr {
114    rcstr!("manifest chunk")
115}
116
117#[turbo_tasks::value_impl]
118impl Module for ManifestAsyncModule {
119    #[turbo_tasks::function]
120    fn ident(&self) -> Vc<AssetIdent> {
121        self.inner
122            .ident()
123            .with_modifier(manifest_chunk_reference_description())
124    }
125
126    #[turbo_tasks::function]
127    fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
128        Vc::cell(None)
129    }
130
131    #[turbo_tasks::function]
132    async fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
133        let assets = self.chunk_group().expand_all_assets().await?;
134
135        Ok(Vc::cell(
136            assets
137                .into_iter()
138                .copied()
139                .map(|chunk| async move {
140                    Ok(ResolvedVc::upcast(
141                        SingleOutputAssetReference::new(
142                            *chunk,
143                            manifest_chunk_reference_description(),
144                        )
145                        .to_resolved()
146                        .await?,
147                    ))
148                })
149                .try_join()
150                .await?,
151        ))
152    }
153
154    #[turbo_tasks::function]
155    fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
156        ModuleSideEffects::SideEffectFree.cell()
157    }
158}
159
160#[turbo_tasks::value_impl]
161impl ChunkableModule for ManifestAsyncModule {
162    #[turbo_tasks::function]
163    fn as_chunk_item(
164        self: ResolvedVc<Self>,
165        _module_graph: Vc<ModuleGraph>,
166        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
167    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
168        Vc::upcast(
169            ManifestChunkItem {
170                chunking_context,
171                manifest: self,
172            }
173            .cell(),
174        )
175    }
176}
177
178#[turbo_tasks::value_impl]
179impl EcmascriptChunkPlaceable for ManifestAsyncModule {
180    #[turbo_tasks::function]
181    fn get_exports(&self) -> Vc<EcmascriptExports> {
182        EcmascriptExports::Value.cell()
183    }
184}