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