turbopack_ecmascript/async_chunk/
chunk_item.rs1use anyhow::Result;
2use indoc::formatdoc;
3use tracing::Instrument;
4use turbo_tasks::{ResolvedVc, TryJoinIterExt, ValueToString, Vc};
5use turbopack_core::{
6 chunk::{
7 AsyncModuleInfo, ChunkData, ChunkItem, ChunkType, ChunkingContext, ChunkingContextExt,
8 ChunksData, ModuleChunkItemIdExt,
9 },
10 ident::AssetIdent,
11 module::Module,
12 module_graph::{
13 ModuleGraph, chunk_group_info::ChunkGroup, module_batch::ChunkableModuleOrBatch,
14 },
15 output::{OutputAssetsReference, OutputAssetsWithReferenced},
16};
17
18use crate::{
19 async_chunk::module::AsyncLoaderModule,
20 chunk::{
21 EcmascriptChunkItem, EcmascriptChunkItemContent, EcmascriptChunkPlaceable,
22 EcmascriptChunkType, data::EcmascriptChunkData,
23 },
24 runtime_functions::{TURBOPACK_EXPORT_VALUE, TURBOPACK_LOAD},
25 utils::{StringifyJs, StringifyModuleId},
26};
27
28#[turbo_tasks::value(shared)]
29pub struct AsyncLoaderChunkItem {
30 pub module: ResolvedVc<AsyncLoaderModule>,
31 pub module_graph: ResolvedVc<ModuleGraph>,
32 pub chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
33}
34
35#[turbo_tasks::value_impl]
36impl AsyncLoaderChunkItem {
37 #[turbo_tasks::function]
38 pub(super) async fn chunk_group(&self) -> Result<Vc<OutputAssetsWithReferenced>> {
39 let module = self.module.await?;
40 if let Some(chunk_items) = module.availability_info.available_modules() {
41 let inner_module = ResolvedVc::upcast(module.inner);
42 let batches = self
43 .module_graph
44 .module_batches(self.chunking_context.batching_config())
45 .await?;
46 let module_or_batch = batches.get_entry(inner_module).await?;
47 if let Some(chunkable_module_or_batch) =
48 ChunkableModuleOrBatch::from_module_or_batch(module_or_batch)
49 && *chunk_items.get(chunkable_module_or_batch.into()).await?
50 {
51 return Ok(OutputAssetsWithReferenced {
52 assets: ResolvedVc::cell(vec![]),
53 referenced_assets: ResolvedVc::cell(vec![]),
54 references: ResolvedVc::cell(vec![]),
55 }
56 .cell());
57 }
58 }
59 Ok(self.chunking_context.chunk_group_assets(
60 module.inner.ident(),
61 ChunkGroup::Async(ResolvedVc::upcast(module.inner)),
62 *self.module_graph,
63 module.availability_info,
64 ))
65 }
66
67 #[turbo_tasks::function]
68 async fn chunks_data(self: Vc<Self>) -> Result<Vc<ChunksData>> {
69 let this = self.await?;
70 let span = tracing::info_span!(
71 "compute async chunks",
72 name = this.module.ident().to_string().await?.as_str()
73 );
74 async move {
75 Ok(ChunkData::from_assets(
76 this.chunking_context.output_root().owned().await?,
77 *self.chunk_group().await?.assets,
78 ))
79 }
80 .instrument(span)
81 .await
82 }
83}
84
85#[turbo_tasks::value_impl]
86impl EcmascriptChunkItem for AsyncLoaderChunkItem {
87 #[turbo_tasks::function]
88 async fn content(self: Vc<Self>) -> Result<Vc<EcmascriptChunkItemContent>> {
89 let this = self.await?;
90 let module = this.module.await?;
91
92 let id = if let Some(placeable) =
93 ResolvedVc::try_downcast::<Box<dyn EcmascriptChunkPlaceable>>(module.inner)
94 {
95 Some(placeable.chunk_item_id(*this.chunking_context).await?)
96 } else {
97 None
98 };
99 let id = id.as_deref();
100
101 let chunks_data = self.chunks_data().await?;
102 let chunks_data = chunks_data.iter().try_join().await?;
103 let chunks_data: Vec<_> = chunks_data
104 .iter()
105 .map(|chunk_data| EcmascriptChunkData::new(chunk_data))
106 .collect();
107
108 let code = match (id, chunks_data.is_empty()) {
109 (Some(id), true) => {
110 formatdoc! {
111 r#"
112 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
113 return Promise.resolve().then(() => {{
114 return parentImport({id});
115 }});
116 }});
117 "#,
118 id = StringifyModuleId(id),
119 }
120 }
121 (Some(id), false) => {
122 formatdoc! {
123 r#"
124 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
125 return Promise.all({chunks:#}.map((chunk) => {TURBOPACK_LOAD}(chunk))).then(() => {{
126 return parentImport({id});
127 }});
128 }});
129 "#,
130 chunks = StringifyJs(&chunks_data),
131 id = StringifyModuleId(id),
132 }
133 }
134 (None, true) => {
135 formatdoc! {
136 r#"
137 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
138 return Promise.resolve();
139 }});
140 "#,
141 }
142 }
143 (None, false) => {
144 formatdoc! {
145 r#"
146 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
147 return Promise.all({chunks:#}.map((chunk) => {TURBOPACK_LOAD}(chunk))).then(() => {{}});
148 }});
149 "#,
150 chunks = StringifyJs(&chunks_data),
151 }
152 }
153 };
154
155 Ok(EcmascriptChunkItemContent {
156 inner_code: code.into(),
157 ..Default::default()
158 }
159 .cell())
160 }
161
162 #[turbo_tasks::function]
163 fn content_with_async_module_info(
164 self: Vc<Self>,
165 _async_module_info: Option<Vc<AsyncModuleInfo>>,
166 estimated: bool,
167 ) -> Vc<EcmascriptChunkItemContent> {
168 if estimated {
169 let code = formatdoc! {
170 r#"
171 {TURBOPACK_EXPORT_VALUE}((parentImport) => {{
172 return Promise.all([].map((chunk) => {TURBOPACK_LOAD}(chunk))).then(() => {{}});
173 }});
174 "#,
175 };
176 EcmascriptChunkItemContent {
177 inner_code: code.into(),
178 ..Default::default()
179 }
180 .cell()
181 } else {
182 self.content()
183 }
184 }
185}
186
187#[turbo_tasks::value_impl]
188impl OutputAssetsReference for AsyncLoaderChunkItem {
189 #[turbo_tasks::function]
190 fn references(self: Vc<Self>) -> Vc<OutputAssetsWithReferenced> {
191 self.chunk_group()
192 }
193}
194
195#[turbo_tasks::value_impl]
196impl ChunkItem for AsyncLoaderChunkItem {
197 #[turbo_tasks::function]
198 fn asset_ident(&self) -> Vc<AssetIdent> {
199 self.module.ident()
200 }
201
202 #[turbo_tasks::function]
203 async fn content_ident(self: Vc<Self>) -> Result<Vc<AssetIdent>> {
204 let mut ident = self.module().ident();
205
206 let this = self.await?;
207
208 let nested_async_availability = this
209 .chunking_context
210 .is_nested_async_availability_enabled()
211 .await?;
212
213 let availability_ident = if *nested_async_availability {
214 Some(self.chunks_data().hash().await?.to_string().into())
215 } else {
216 this.module.await?.availability_info.ident().await?
217 };
218
219 if let Some(availability_ident) = availability_ident {
220 ident = ident.with_modifier(availability_ident)
221 }
222
223 Ok(ident)
224 }
225
226 #[turbo_tasks::function]
227 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
228 *self.chunking_context
229 }
230
231 #[turbo_tasks::function]
232 async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
233 Ok(Vc::upcast(
234 Vc::<EcmascriptChunkType>::default().resolve().await?,
235 ))
236 }
237
238 #[turbo_tasks::function]
239 fn module(&self) -> Vc<Box<dyn Module>> {
240 *ResolvedVc::upcast(self.module)
241 }
242}