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,
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).await?
82            {
83                return Ok(OutputAssetsWithReferenced {
84                    assets: ResolvedVc::cell(vec![]),
85                    referenced_assets: 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    async fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
128        let assets = self.chunk_group().all_assets().await?;
129
130        Ok(Vc::cell(
131            assets
132                .into_iter()
133                .copied()
134                .map(|chunk| async move {
135                    Ok(ResolvedVc::upcast(
136                        SingleOutputAssetReference::new(
137                            *chunk,
138                            manifest_chunk_reference_description(),
139                        )
140                        .to_resolved()
141                        .await?,
142                    ))
143                })
144                .try_join()
145                .await?,
146        ))
147    }
148}
149
150#[turbo_tasks::value_impl]
151impl Asset for ManifestAsyncModule {
152    #[turbo_tasks::function]
153    fn content(&self) -> Vc<AssetContent> {
154        panic!("content() should not be called");
155    }
156}
157
158#[turbo_tasks::value_impl]
159impl ChunkableModule for ManifestAsyncModule {
160    #[turbo_tasks::function]
161    fn as_chunk_item(
162        self: ResolvedVc<Self>,
163        _module_graph: Vc<ModuleGraph>,
164        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
165    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
166        Vc::upcast(
167            ManifestChunkItem {
168                chunking_context,
169                manifest: self,
170            }
171            .cell(),
172        )
173    }
174}
175
176#[turbo_tasks::value_impl]
177impl EcmascriptChunkPlaceable for ManifestAsyncModule {
178    #[turbo_tasks::function]
179    fn get_exports(&self) -> Vc<EcmascriptExports> {
180        EcmascriptExports::Value.cell()
181    }
182}