turbopack_ecmascript/manifest/
chunk_asset.rs

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