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::OutputAssets,
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 chunks(&self) -> Vc<OutputAssets> {
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_chunks(self: ResolvedVc<Self>) -> Result<Vc<OutputAssets>> {
69        let this = self.await?;
70        if let Some(chunk_items) = this.availability_info.available_modules() {
71            let inner_module = ResolvedVc::upcast(this.inner);
72            let batches = this
73                .module_graph
74                .module_batches(this.chunking_context.batching_config())
75                .await?;
76            let module_or_batch = batches.get_entry(inner_module).await?;
77            if let Some(chunkable_module_or_batch) =
78                ChunkableModuleOrBatch::from_module_or_batch(module_or_batch)
79                && *chunk_items.get(chunkable_module_or_batch).await?
80            {
81                return Ok(Vc::cell(vec![]));
82            }
83        }
84        Ok(this.chunking_context.chunk_group_assets(
85            self.ident(),
86            ChunkGroup::Async(ResolvedVc::upcast(self)),
87            *this.module_graph,
88            this.availability_info,
89        ))
90    }
91
92    #[turbo_tasks::function]
93    pub fn module_ident(&self) -> Vc<AssetIdent> {
94        self.inner.ident()
95    }
96
97    #[turbo_tasks::function]
98    pub async fn content_ident(&self) -> Result<Vc<AssetIdent>> {
99        let mut ident = self.inner.ident();
100        if let Some(available_modules) = self.availability_info.available_modules() {
101            ident = ident.with_modifier(available_modules.hash().await?.to_string().into());
102        }
103        Ok(ident)
104    }
105}
106
107fn manifest_chunk_reference_description() -> RcStr {
108    rcstr!("manifest chunk")
109}
110
111#[turbo_tasks::value_impl]
112impl Module for ManifestAsyncModule {
113    #[turbo_tasks::function]
114    fn ident(&self) -> Vc<AssetIdent> {
115        self.inner
116            .ident()
117            .with_modifier(manifest_chunk_reference_description())
118    }
119
120    #[turbo_tasks::function]
121    async fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
122        let chunks = self.chunks();
123
124        Ok(Vc::cell(
125            chunks
126                .await?
127                .iter()
128                .copied()
129                .map(|chunk| async move {
130                    Ok(ResolvedVc::upcast(
131                        SingleOutputAssetReference::new(
132                            *chunk,
133                            manifest_chunk_reference_description(),
134                        )
135                        .to_resolved()
136                        .await?,
137                    ))
138                })
139                .try_join()
140                .await?,
141        ))
142    }
143}
144
145#[turbo_tasks::value_impl]
146impl Asset for ManifestAsyncModule {
147    #[turbo_tasks::function]
148    fn content(&self) -> Vc<AssetContent> {
149        panic!("content() should not be called");
150    }
151}
152
153#[turbo_tasks::value_impl]
154impl ChunkableModule for ManifestAsyncModule {
155    #[turbo_tasks::function]
156    fn as_chunk_item(
157        self: ResolvedVc<Self>,
158        _module_graph: Vc<ModuleGraph>,
159        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
160    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
161        Vc::upcast(
162            ManifestChunkItem {
163                chunking_context,
164                manifest: self,
165            }
166            .cell(),
167        )
168    }
169}
170
171#[turbo_tasks::value_impl]
172impl EcmascriptChunkPlaceable for ManifestAsyncModule {
173    #[turbo_tasks::function]
174    fn get_exports(&self) -> Vc<EcmascriptExports> {
175        EcmascriptExports::Value.cell()
176    }
177}