turbopack_ecmascript/manifest/
chunk_asset.rs1use anyhow::Result;
2use indoc::formatdoc;
3use turbo_rcstr::{RcStr, rcstr};
4use turbo_tasks::{ResolvedVc, TryJoinIterExt, Vc};
5use turbopack_core::{
6 chunk::{
7 AsyncModuleInfo, ChunkData, ChunkableModule, ChunkingContext, ChunkingContextExt,
8 ChunksData, HmrChunkListSource, availability_info::AvailabilityInfo,
9 },
10 ident::AssetIdent,
11 module::{Module, ModuleSideEffects},
12 module_graph::{
13 ModuleGraph, chunk_group_info::ChunkGroupKey, module_batch::ChunkableModuleOrBatch,
14 },
15 output::{OutputAssets, OutputAssetsWithReferenced},
16};
17
18use crate::{
19 async_chunk::proxy::{LazyCompilationProxyModule, lazy_compilation_state},
20 chunk::{
21 EcmascriptChunkItemContent, EcmascriptChunkPlaceable, EcmascriptExports,
22 data::EcmascriptChunkData, ecmascript_chunk_item,
23 },
24 runtime_functions::TURBOPACK_EXPORT_VALUE,
25 utils::StringifyJs,
26};
27
28#[turbo_tasks::value(shared)]
40pub struct ManifestAsyncModule {
41 pub inner: ResolvedVc<Box<dyn ChunkableModule>>,
42 pub module_graph: ResolvedVc<ModuleGraph>,
43 pub chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
44 pub availability_info: AvailabilityInfo,
45}
46
47#[turbo_tasks::value_impl]
48impl ManifestAsyncModule {
49 #[turbo_tasks::function]
50 pub fn new(
51 module: ResolvedVc<Box<dyn ChunkableModule>>,
52 module_graph: ResolvedVc<ModuleGraph>,
53 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
54 availability_info: AvailabilityInfo,
55 ) -> Vc<Self> {
56 Self::cell(ManifestAsyncModule {
57 inner: module,
58 module_graph,
59 chunking_context,
60 availability_info,
61 })
62 }
63
64 #[turbo_tasks::function]
65 pub(super) fn chunk_group(&self) -> Vc<OutputAssetsWithReferenced> {
66 self.chunking_context.chunk_group_assets(
67 self.inner.ident(),
68 ChunkGroupKey::Async(ResolvedVc::upcast(self.inner)),
69 *self.module_graph,
70 self.availability_info,
71 )
72 }
73
74 #[turbo_tasks::function]
75 pub async fn manifest_chunk_group(
76 self: ResolvedVc<Self>,
77 ) -> Result<Vc<OutputAssetsWithReferenced>> {
78 let this = self.await?;
79 let inner_module = ResolvedVc::upcast(this.inner);
80 let batches = this
81 .module_graph
82 .module_batches(this.chunking_context.batching_config())
83 .await?;
84 let module_or_batch = batches.get_entry(inner_module).await?;
85 if let Some(chunkable_module_or_batch) =
86 ChunkableModuleOrBatch::from_module_or_batch(module_or_batch)
87 && this
88 .availability_info
89 .is_available(this.module_graph, chunkable_module_or_batch)
90 .await?
91 {
92 return Ok(OutputAssetsWithReferenced {
93 assets: ResolvedVc::cell(vec![]),
94 referenced_assets: ResolvedVc::cell(vec![]),
95 references: ResolvedVc::cell(vec![]),
96 }
97 .cell());
98 }
99 let chunk_item = self.as_chunk_item(*this.module_graph, *this.chunking_context);
100 let chunk = this
101 .chunking_context
102 .standalone_chunk(chunk_item)
103 .to_resolved()
104 .await?;
105 Ok(OutputAssetsWithReferenced {
106 assets: ResolvedVc::cell(vec![chunk]),
107 referenced_assets: ResolvedVc::cell(vec![]),
108 references: ResolvedVc::cell(vec![]),
109 }
110 .cell())
111 }
112
113 #[turbo_tasks::function]
116 async fn hmr_chunk_list(self: Vc<Self>) -> Result<Vc<OutputAssets>> {
117 let this = self.await?;
118 let ident = self.ident();
119 let ident = if let Some(availability_ident) = this.availability_info.ident().await? {
123 ident
124 .owned()
125 .await?
126 .with_modifier(availability_ident)
127 .into_vc()
128 } else {
129 ident
130 };
131 Ok(this.chunking_context.hmr_chunk_list(
132 ident,
133 *self.chunk_group().await?.assets,
134 HmrChunkListSource::Dynamic,
135 ))
136 }
137
138 #[turbo_tasks::function]
139 pub fn module_ident(&self) -> Vc<AssetIdent> {
140 self.inner.ident()
141 }
142
143 #[turbo_tasks::function]
144 pub async fn content_ident(&self) -> Result<Vc<AssetIdent>> {
145 let ident = self.inner.ident();
146 Ok(
147 if let Some(availability_ident) = self.availability_info.ident().await? {
148 ident
149 .owned()
150 .await?
151 .with_modifier(availability_ident)
152 .into_vc()
153 } else {
154 ident
155 },
156 )
157 }
158
159 #[turbo_tasks::function]
160 async fn chunks_data(self: Vc<Self>) -> Result<Vc<ChunksData>> {
161 let this = self.await?;
162 Ok(ChunkData::from_assets(
163 this.chunking_context.output_root().owned().await?,
164 self.chunk_group()
165 .await?
166 .assets
167 .concatenate(self.hmr_chunk_list()),
168 ))
169 }
170}
171
172fn manifest_chunk_reference_description() -> RcStr {
173 rcstr!("manifest chunk")
174}
175
176#[turbo_tasks::value_impl]
177impl Module for ManifestAsyncModule {
178 #[turbo_tasks::function]
179 async fn ident(&self) -> Result<Vc<AssetIdent>> {
180 let ident = self
181 .inner
182 .ident()
183 .owned()
184 .await?
185 .with_modifier(manifest_chunk_reference_description());
186 let Some(proxy) = ResolvedVc::try_downcast_type::<LazyCompilationProxyModule>(self.inner)
191 else {
192 return Ok(ident.into_vc());
193 };
194 Ok(ident
195 .rename_as(&format!("*.{}.js", proxy.await?.key))
196 .into_vc())
197 }
198
199 #[turbo_tasks::function]
200 fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
201 Vc::cell(None)
202 }
203
204 #[turbo_tasks::function]
205 fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
206 ModuleSideEffects::SideEffectFree.cell()
207 }
208}
209
210#[turbo_tasks::value_impl]
211impl ChunkableModule for ManifestAsyncModule {
212 #[turbo_tasks::function]
213 fn as_chunk_item(
214 self: ResolvedVc<Self>,
215 module_graph: ResolvedVc<ModuleGraph>,
216 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
217 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
218 ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
219 }
220}
221
222#[turbo_tasks::value_impl]
223impl EcmascriptChunkPlaceable for ManifestAsyncModule {
224 #[turbo_tasks::function]
225 fn get_exports(&self) -> Vc<EcmascriptExports> {
226 EcmascriptExports::Value.cell()
227 }
228
229 #[turbo_tasks::function]
230 async fn chunk_item_content(
231 self: Vc<Self>,
232 _chunking_context: Vc<Box<dyn ChunkingContext>>,
233 _module_graph: Vc<ModuleGraph>,
234 _async_module_info: Option<Vc<AsyncModuleInfo>>,
235 _estimated: bool,
236 ) -> Result<Vc<EcmascriptChunkItemContent>> {
237 let chunks_data = self.chunks_data().await?;
238 let chunks_data = chunks_data.iter().try_join().await?;
239 let chunks_data: Vec<_> = chunks_data
240 .iter()
241 .map(|chunk_data| EcmascriptChunkData::new(chunk_data))
242 .collect();
243
244 let code = formatdoc! {
245 r#"
246 {TURBOPACK_EXPORT_VALUE}({:#});
247 "#,
248 StringifyJs(&chunks_data)
249 };
250
251 Ok(EcmascriptChunkItemContent {
252 inner_code: code.into(),
253 ..Default::default()
254 }
255 .cell())
256 }
257
258 #[turbo_tasks::function]
259 fn chunk_item_content_ident(
260 self: Vc<Self>,
261 _chunking_context: Vc<Box<dyn ChunkingContext>>,
262 _module_graph: Vc<ModuleGraph>,
263 ) -> Vc<AssetIdent> {
264 self.content_ident()
265 }
266
267 #[turbo_tasks::function]
268 async fn chunk_item_output_assets(
269 self: Vc<Self>,
270 _chunking_context: Vc<Box<dyn ChunkingContext>>,
271 _module_graph: Vc<ModuleGraph>,
272 ) -> Result<Vc<OutputAssetsWithReferenced>> {
273 if let Some(proxy) =
275 ResolvedVc::try_downcast_type::<LazyCompilationProxyModule>(self.await?.inner)
276 && !lazy_compilation_state(proxy.await?.key.clone())
277 .await?
278 .is_active()
279 {
280 return Ok(self.chunk_group());
281 }
282 Ok(self
283 .chunk_group()
284 .concatenate(OutputAssetsWithReferenced::from_assets(
285 self.hmr_chunk_list(),
286 )))
287 }
288}