turbopack_ecmascript/async_chunk/
module.rs1use anyhow::{Result, bail};
2use indoc::formatdoc;
3use tracing::Instrument;
4use turbo_rcstr::rcstr;
5use turbo_tasks::{ResolvedVc, TryJoinIterExt, ValueToString, Vc};
6use turbopack_core::{
7 chunk::{
8 AsyncModuleInfo, ChunkData, ChunkableModule, ChunkingContext, ChunkingContextExt,
9 ChunksData, ModuleChunkItemIdExt, availability_info::AvailabilityInfo,
10 },
11 ident::AssetIdent,
12 module::{Module, ModuleSideEffects},
13 module_graph::{
14 ModuleGraph, chunk_group_info::ChunkGroupKey, module_batch::ChunkableModuleOrBatch,
15 },
16 output::OutputAssetsWithReferenced,
17 reference::ModuleReferences,
18};
19
20use crate::{
21 chunk::{
22 EcmascriptChunkItemContent, EcmascriptChunkItemOptions, EcmascriptChunkPlaceable,
23 EcmascriptExports, data::EcmascriptChunkData, ecmascript_chunk_item,
24 },
25 runtime_functions::{TURBOPACK_EXPORT_VALUE, TURBOPACK_LOAD},
26 utils::{StringifyJs, StringifyModuleId},
27};
28
29#[turbo_tasks::value]
32pub struct AsyncLoaderModule {
33 pub inner: ResolvedVc<Box<dyn ChunkableModule>>,
34 pub chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
35 pub availability_info: AvailabilityInfo,
36}
37
38#[turbo_tasks::value_impl]
39impl AsyncLoaderModule {
40 #[turbo_tasks::function]
41 pub fn new(
42 module: ResolvedVc<Box<dyn ChunkableModule>>,
43 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
44 availability_info: AvailabilityInfo,
45 ) -> Vc<Self> {
46 Self::cell(AsyncLoaderModule {
47 inner: module,
48 chunking_context,
49 availability_info,
50 })
51 }
52
53 #[turbo_tasks::function]
54 pub async fn asset_ident_for(module: Vc<Box<dyn ChunkableModule>>) -> Result<Vc<AssetIdent>> {
55 Ok(module
56 .ident()
57 .owned()
58 .await?
59 .with_modifier(rcstr!("async loader"))
60 .into_vc())
61 }
62
63 #[turbo_tasks::function]
64 pub(super) async fn chunk_group(
65 &self,
66 module_graph: Vc<ModuleGraph>,
67 ) -> Result<Vc<OutputAssetsWithReferenced>> {
68 let inner_module = ResolvedVc::upcast(self.inner);
69 let batches = module_graph
70 .module_batches(self.chunking_context.batching_config())
71 .await?;
72 let module_or_batch = batches.get_entry(inner_module).await?;
73 if let Some(chunkable_module_or_batch) =
74 ChunkableModuleOrBatch::from_module_or_batch(module_or_batch)
75 && self
76 .availability_info
77 .is_available(module_graph.to_resolved().await?, chunkable_module_or_batch)
78 .await?
79 {
80 return Ok(OutputAssetsWithReferenced {
81 assets: ResolvedVc::cell(vec![]),
82 referenced_assets: ResolvedVc::cell(vec![]),
83 references: ResolvedVc::cell(vec![]),
84 }
85 .cell());
86 }
87 Ok(self.chunking_context.chunk_group_assets(
88 self.inner.ident(),
89 ChunkGroupKey::Async(ResolvedVc::upcast(self.inner)),
90 module_graph,
91 self.availability_info,
92 ))
93 }
94
95 #[turbo_tasks::function]
96 async fn chunks_data(self: Vc<Self>, module_graph: Vc<ModuleGraph>) -> Result<Vc<ChunksData>> {
97 let this = self.await?;
98 let span = tracing::info_span!(
99 "compute async chunks",
100 name = self.ident().to_string().await?.as_str()
101 );
102 async move {
103 Ok(ChunkData::from_assets(
104 this.chunking_context.output_root().owned().await?,
105 *self.chunk_group(module_graph).await?.assets,
106 ))
107 }
108 .instrument(span)
109 .await
110 }
111}
112
113#[turbo_tasks::value_impl]
114impl Module for AsyncLoaderModule {
115 #[turbo_tasks::function]
116 fn ident(&self) -> Vc<AssetIdent> {
117 Self::asset_ident_for(*self.inner)
118 }
119
120 #[turbo_tasks::function]
121 fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
122 Vc::cell(None)
123 }
124
125 #[turbo_tasks::function]
126 async fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
127 bail!("AsyncLoaderModule::references should never be called")
128 }
129
130 #[turbo_tasks::function]
131 fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
132 ModuleSideEffects::SideEffectFree.cell()
133 }
134}
135
136#[turbo_tasks::value_impl]
137impl ChunkableModule for AsyncLoaderModule {
138 #[turbo_tasks::function]
139 fn as_chunk_item(
140 self: ResolvedVc<Self>,
141 module_graph: ResolvedVc<ModuleGraph>,
142 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
143 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
144 ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
145 }
146}
147
148#[turbo_tasks::value_impl]
149impl EcmascriptChunkPlaceable for AsyncLoaderModule {
150 #[turbo_tasks::function]
151 fn get_exports(&self) -> Vc<EcmascriptExports> {
152 EcmascriptExports::Value.cell()
153 }
154
155 #[turbo_tasks::function]
156 async fn chunk_item_content(
157 self: Vc<Self>,
158 chunking_context: Vc<Box<dyn ChunkingContext>>,
159 module_graph: Vc<ModuleGraph>,
160 _async_module_info: Option<Vc<AsyncModuleInfo>>,
161 estimated: bool,
162 ) -> Result<Vc<EcmascriptChunkItemContent>> {
163 let options = EcmascriptChunkItemOptions {
164 supports_arrow_functions: *chunking_context
165 .environment()
166 .runtime_versions()
167 .supports_arrow_functions()
168 .await?,
169 ..Default::default()
170 };
171
172 if estimated {
173 let code = formatdoc! {
174 r#"
175 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
176 return Promise.all([].map((chunk) => {TURBOPACK_LOAD}(chunk))).then(() => {{}});
177 }});
178 "#,
179 };
180 return Ok(EcmascriptChunkItemContent {
181 inner_code: code.into(),
182 options,
183 ..Default::default()
184 }
185 .cell());
186 }
187
188 let this = self.await?;
189
190 let id = if let Some(placeable) =
191 ResolvedVc::try_downcast::<Box<dyn EcmascriptChunkPlaceable>>(this.inner)
192 {
193 Some(placeable.chunk_item_id(chunking_context).await?)
194 } else {
195 None
196 };
197 let id = id.as_ref();
198
199 let chunks_data = self.chunks_data(module_graph).await?;
200 let chunks_data = chunks_data.iter().try_join().await?;
201 let chunks_data: Vec<_> = chunks_data
202 .iter()
203 .map(|chunk_data| EcmascriptChunkData::new(chunk_data))
204 .collect();
205
206 let code = match (id, chunks_data.is_empty()) {
207 (Some(id), true) => {
208 formatdoc! {
209 r#"
210 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
211 return Promise.resolve().then(() => {{
212 return parentImport({id});
213 }});
214 }});
215 "#,
216 id = StringifyModuleId(id),
217 }
218 }
219 (Some(id), false) => {
220 formatdoc! {
221 r#"
222 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
223 return Promise.all({chunks:#}.map((chunk) => {TURBOPACK_LOAD}(chunk))).then(() => {{
224 return parentImport({id});
225 }});
226 }});
227 "#,
228 chunks = StringifyJs(&chunks_data),
229 id = StringifyModuleId(id),
230 }
231 }
232 (None, true) => {
233 formatdoc! {
234 r#"
235 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
236 return Promise.resolve();
237 }});
238 "#,
239 }
240 }
241 (None, false) => {
242 formatdoc! {
243 r#"
244 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
245 return Promise.all({chunks:#}.map((chunk) => {TURBOPACK_LOAD}(chunk))).then(() => {{}});
246 }});
247 "#,
248 chunks = StringifyJs(&chunks_data),
249 }
250 }
251 };
252
253 Ok(EcmascriptChunkItemContent {
254 inner_code: code.into(),
255 options,
256 ..Default::default()
257 }
258 .cell())
259 }
260
261 #[turbo_tasks::function]
262 async fn chunk_item_content_ident(
263 self: Vc<Self>,
264 _chunking_context: Vc<Box<dyn ChunkingContext>>,
265 module_graph: Vc<ModuleGraph>,
266 ) -> Result<Vc<AssetIdent>> {
267 let this = self.await?;
268
269 let nested_async_availability = this
270 .chunking_context
271 .is_nested_async_availability_enabled()
272 .await?;
273
274 let availability_ident = if *nested_async_availability {
275 Some(
276 self.chunks_data(module_graph)
277 .hash()
278 .await?
279 .to_string()
280 .into(),
281 )
282 } else {
283 this.availability_info.ident().await?
284 };
285
286 Ok(if let Some(availability_ident) = availability_ident {
287 self.ident()
288 .owned()
289 .await?
290 .with_modifier(availability_ident)
291 .into_vc()
292 } else {
293 self.ident()
294 })
295 }
296
297 #[turbo_tasks::function]
298 fn chunk_item_output_assets(
299 self: Vc<Self>,
300 _chunking_context: Vc<Box<dyn ChunkingContext>>,
301 module_graph: Vc<ModuleGraph>,
302 ) -> Vc<OutputAssetsWithReferenced> {
303 self.chunk_group(module_graph)
304 }
305}