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::ChunkGroup, 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 if let Some(chunk_items) = self.availability_info.available_modules() {
69 let inner_module = ResolvedVc::upcast(self.inner);
70 let batches = module_graph
71 .module_batches(self.chunking_context.batching_config())
72 .await?;
73 let module_or_batch = batches.get_entry(inner_module).await?;
74 if let Some(chunkable_module_or_batch) =
75 ChunkableModuleOrBatch::from_module_or_batch(module_or_batch)
76 && *chunk_items.get(chunkable_module_or_batch.into()).await?
77 {
78 return Ok(OutputAssetsWithReferenced {
79 assets: ResolvedVc::cell(vec![]),
80 referenced_assets: ResolvedVc::cell(vec![]),
81 references: ResolvedVc::cell(vec![]),
82 }
83 .cell());
84 }
85 }
86 Ok(self.chunking_context.chunk_group_assets(
87 self.inner.ident(),
88 ChunkGroup::Async(ResolvedVc::upcast(self.inner)),
89 module_graph,
90 self.availability_info,
91 ))
92 }
93
94 #[turbo_tasks::function]
95 async fn chunks_data(self: Vc<Self>, module_graph: Vc<ModuleGraph>) -> Result<Vc<ChunksData>> {
96 let this = self.await?;
97 let span = tracing::info_span!(
98 "compute async chunks",
99 name = self.ident().to_string().await?.as_str()
100 );
101 async move {
102 Ok(ChunkData::from_assets(
103 this.chunking_context.output_root().owned().await?,
104 *self.chunk_group(module_graph).await?.assets,
105 ))
106 }
107 .instrument(span)
108 .await
109 }
110}
111
112#[turbo_tasks::value_impl]
113impl Module for AsyncLoaderModule {
114 #[turbo_tasks::function]
115 fn ident(&self) -> Vc<AssetIdent> {
116 Self::asset_ident_for(*self.inner)
117 }
118
119 #[turbo_tasks::function]
120 fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
121 Vc::cell(None)
122 }
123
124 #[turbo_tasks::function]
125 async fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
126 bail!("AsyncLoaderModule::references should never be called")
127 }
128
129 #[turbo_tasks::function]
130 fn side_effects(self: Vc<Self>) -> Vc<ModuleSideEffects> {
131 ModuleSideEffects::SideEffectFree.cell()
132 }
133}
134
135#[turbo_tasks::value_impl]
136impl ChunkableModule for AsyncLoaderModule {
137 #[turbo_tasks::function]
138 fn as_chunk_item(
139 self: ResolvedVc<Self>,
140 module_graph: ResolvedVc<ModuleGraph>,
141 chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
142 ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
143 ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
144 }
145}
146
147#[turbo_tasks::value_impl]
148impl EcmascriptChunkPlaceable for AsyncLoaderModule {
149 #[turbo_tasks::function]
150 fn get_exports(&self) -> Vc<EcmascriptExports> {
151 EcmascriptExports::Value.cell()
152 }
153
154 #[turbo_tasks::function]
155 async fn chunk_item_content(
156 self: Vc<Self>,
157 chunking_context: Vc<Box<dyn ChunkingContext>>,
158 module_graph: Vc<ModuleGraph>,
159 _async_module_info: Option<Vc<AsyncModuleInfo>>,
160 estimated: bool,
161 ) -> Result<Vc<EcmascriptChunkItemContent>> {
162 let options = EcmascriptChunkItemOptions {
163 supports_arrow_functions: *chunking_context
164 .environment()
165 .runtime_versions()
166 .supports_arrow_functions()
167 .await?,
168 ..Default::default()
169 };
170
171 if estimated {
172 let code = formatdoc! {
173 r#"
174 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
175 return Promise.all([].map((chunk) => {TURBOPACK_LOAD}(chunk))).then(() => {{}});
176 }});
177 "#,
178 };
179 return Ok(EcmascriptChunkItemContent {
180 inner_code: code.into(),
181 options,
182 ..Default::default()
183 }
184 .cell());
185 }
186
187 let this = self.await?;
188
189 let id = if let Some(placeable) =
190 ResolvedVc::try_downcast::<Box<dyn EcmascriptChunkPlaceable>>(this.inner)
191 {
192 Some(placeable.chunk_item_id(chunking_context).await?)
193 } else {
194 None
195 };
196 let id = id.as_ref();
197
198 let chunks_data = self.chunks_data(module_graph).await?;
199 let chunks_data = chunks_data.iter().try_join().await?;
200 let chunks_data: Vec<_> = chunks_data
201 .iter()
202 .map(|chunk_data| EcmascriptChunkData::new(chunk_data))
203 .collect();
204
205 let code = match (id, chunks_data.is_empty()) {
206 (Some(id), true) => {
207 formatdoc! {
208 r#"
209 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
210 return Promise.resolve().then(() => {{
211 return parentImport({id});
212 }});
213 }});
214 "#,
215 id = StringifyModuleId(id),
216 }
217 }
218 (Some(id), false) => {
219 formatdoc! {
220 r#"
221 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
222 return Promise.all({chunks:#}.map((chunk) => {TURBOPACK_LOAD}(chunk))).then(() => {{
223 return parentImport({id});
224 }});
225 }});
226 "#,
227 chunks = StringifyJs(&chunks_data),
228 id = StringifyModuleId(id),
229 }
230 }
231 (None, true) => {
232 formatdoc! {
233 r#"
234 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
235 return Promise.resolve();
236 }});
237 "#,
238 }
239 }
240 (None, false) => {
241 formatdoc! {
242 r#"
243 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
244 return Promise.all({chunks:#}.map((chunk) => {TURBOPACK_LOAD}(chunk))).then(() => {{}});
245 }});
246 "#,
247 chunks = StringifyJs(&chunks_data),
248 }
249 }
250 };
251
252 Ok(EcmascriptChunkItemContent {
253 inner_code: code.into(),
254 options,
255 ..Default::default()
256 }
257 .cell())
258 }
259
260 #[turbo_tasks::function]
261 async fn chunk_item_content_ident(
262 self: Vc<Self>,
263 _chunking_context: Vc<Box<dyn ChunkingContext>>,
264 module_graph: Vc<ModuleGraph>,
265 ) -> Result<Vc<AssetIdent>> {
266 let this = self.await?;
267
268 let nested_async_availability = this
269 .chunking_context
270 .is_nested_async_availability_enabled()
271 .await?;
272
273 let availability_ident = if *nested_async_availability {
274 Some(
275 self.chunks_data(module_graph)
276 .hash()
277 .await?
278 .to_string()
279 .into(),
280 )
281 } else {
282 this.availability_info.ident().await?
283 };
284
285 Ok(if let Some(availability_ident) = availability_ident {
286 self.ident()
287 .owned()
288 .await?
289 .with_modifier(availability_ident)
290 .into_vc()
291 } else {
292 self.ident()
293 })
294 }
295
296 #[turbo_tasks::function]
297 fn chunk_item_output_assets(
298 self: Vc<Self>,
299 _chunking_context: Vc<Box<dyn ChunkingContext>>,
300 module_graph: Vc<ModuleGraph>,
301 ) -> Vc<OutputAssetsWithReferenced> {
302 self.chunk_group(module_graph)
303 }
304}