turbopack_ecmascript/manifest/
chunk_asset.rs

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