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