1use std::{future::Future, hash::Hash, ops::Deref};
2
3use anyhow::Result;
4use bincode::{Decode, Encode};
5use either::Either;
6use rustc_hash::FxHashMap;
7use smallvec::{SmallVec, smallvec};
8use turbo_tasks::{
9 FxIndexMap, ReadRef, ResolvedVc, TryFlatJoinIterExt, TryJoinIterExt, Vc, trace::TraceRawVcs,
10};
11
12use crate::{
13 chunk::{ChunkItemWithAsyncModuleInfo, ChunkType, ChunkableModule, ChunkingContext},
14 module_graph::{
15 ModuleGraph,
16 async_module_info::AsyncModulesInfo,
17 chunk_group_info::RoaringBitmapWrapper,
18 module_batch::{ChunkableModuleBatchGroup, ChunkableModuleOrBatch, ModuleBatch},
19 },
20};
21
22pub async fn attach_async_info_to_chunkable_module(
28 module: ResolvedVc<Box<dyn ChunkableModule>>,
29 async_module_info: Vc<AsyncModulesInfo>,
30 module_graph: Vc<ModuleGraph>,
31 chunking_context: Vc<Box<dyn ChunkingContext>>,
32) -> Result<ChunkItemWithAsyncModuleInfo> {
33 let general_module = ResolvedVc::upcast(module);
34 let async_info = if async_module_info.is_async(general_module).await? {
35 Some(
36 module_graph
37 .referenced_async_modules(*general_module)
38 .to_resolved()
39 .await?,
40 )
41 } else {
42 None
43 };
44 let chunk_item = module
45 .as_chunk_item(module_graph, chunking_context)
46 .to_resolved()
47 .await?;
48 let chunk_type = chunk_item
49 .into_trait_ref()
50 .await?
51 .ty()
52 .to_resolved()
53 .await?;
54 Ok(ChunkItemWithAsyncModuleInfo {
55 chunk_item,
56 chunk_type,
57 module: Some(module),
58 async_info,
59 })
60}
61
62#[turbo_tasks::task_input]
63#[derive(Debug, Clone, PartialEq, Eq, Hash, TraceRawVcs, Encode, Decode)]
64pub enum ChunkItemOrBatchWithAsyncModuleInfo {
65 ChunkItem(ChunkItemWithAsyncModuleInfo),
66 Batch(ResolvedVc<ChunkItemBatchWithAsyncModuleInfo>),
67}
68
69type ChunkItemOrBatchWithAsyncModuleInfoByChunkType = Either<
70 ChunkItemBatchWithAsyncModuleInfoByChunkTypeData,
71 ReadRef<ChunkItemBatchWithAsyncModuleInfoByChunkType>,
72>;
73
74#[turbo_tasks::value(transparent)]
75pub struct ChunkItemOrBatchWithAsyncModuleInfos(Vec<ChunkItemOrBatchWithAsyncModuleInfo>);
76
77impl ChunkItemOrBatchWithAsyncModuleInfo {
78 pub async fn from_chunkable_module_or_batch(
79 chunkable_module_or_batch: ChunkableModuleOrBatch,
80 async_module_info: Vc<AsyncModulesInfo>,
81 module_graph: Vc<ModuleGraph>,
82 chunking_context: Vc<Box<dyn ChunkingContext>>,
83 ) -> Result<Option<Self>> {
84 Ok(match chunkable_module_or_batch {
85 ChunkableModuleOrBatch::Module(module) => Some(Self::ChunkItem(
86 attach_async_info_to_chunkable_module(
87 module,
88 async_module_info,
89 module_graph,
90 chunking_context,
91 )
92 .await?,
93 )),
94 ChunkableModuleOrBatch::Batch(batch) => Some(Self::Batch(
95 ChunkItemBatchWithAsyncModuleInfo::from_module_batch(
96 *batch,
97 module_graph,
98 chunking_context,
99 )
100 .to_resolved()
101 .await?,
102 )),
103 ChunkableModuleOrBatch::None(_) => None,
104 })
105 }
106
107 pub async fn split_by_chunk_type(
108 &self,
109 ) -> Result<ChunkItemOrBatchWithAsyncModuleInfoByChunkType> {
110 Ok(match self {
111 Self::ChunkItem(item) => {
112 Either::Left(smallvec![(item.chunk_type, Self::ChunkItem(*item))])
113 }
114 Self::Batch(batch) => Either::Right(batch.split_by_chunk_type().await?),
115 })
116 }
117}
118
119#[turbo_tasks::value]
120#[derive(Debug, Clone, Hash)]
121pub struct ChunkItemBatchWithAsyncModuleInfo {
122 pub chunk_items: Vec<ChunkItemWithAsyncModuleInfo>,
123 pub chunk_groups: Option<RoaringBitmapWrapper>,
124}
125
126#[turbo_tasks::value_impl]
127impl ChunkItemBatchWithAsyncModuleInfo {
128 #[turbo_tasks::function]
129 pub fn new(chunk_items: Vec<ChunkItemWithAsyncModuleInfo>) -> Vc<Self> {
130 Self {
131 chunk_items,
132 chunk_groups: None,
133 }
134 .cell()
135 }
136
137 #[turbo_tasks::function]
138 pub async fn from_module_batch(
139 batch: Vc<ModuleBatch>,
140 module_graph: Vc<ModuleGraph>,
141 chunking_context: Vc<Box<dyn ChunkingContext>>,
142 ) -> Result<Vc<Self>> {
143 let async_module_info = module_graph.async_module_info();
144 let batch = batch.await?;
145 let chunk_items = batch
146 .modules
147 .iter()
148 .map(|module| {
149 attach_async_info_to_chunkable_module(
150 *module,
151 async_module_info,
152 module_graph,
153 chunking_context,
154 )
155 })
156 .try_join()
157 .await?;
158 Ok(Self {
159 chunk_items,
160 chunk_groups: batch.chunk_groups.clone(),
161 }
162 .cell())
163 }
164
165 #[turbo_tasks::function]
166 pub async fn split_by_chunk_type(
167 self: Vc<Self>,
168 ) -> Result<Vc<ChunkItemBatchWithAsyncModuleInfoByChunkType>> {
169 let this = self.await?;
170 let mut iter = this.chunk_items.iter().enumerate();
171 let Some((_, first)) = iter.next() else {
172 return Ok(Vc::cell(SmallVec::new()));
173 };
174 let chunk_type = first.chunk_type;
175 for (i, item) in iter.by_ref() {
176 let ty = item.chunk_type;
177 if ty != chunk_type {
178 let mut map = FxIndexMap::default();
179 map.insert(chunk_type, this.chunk_items[..i].to_vec());
180 map.insert(ty, vec![*item]);
181 for (_, item) in iter {
182 map.entry(item.chunk_type).or_default().push(*item);
183 }
184 return Ok(Vc::cell(
185 map.into_iter()
186 .map(|(ty, chunk_items)| {
187 let item = if chunk_items.len() == 1 {
188 ChunkItemOrBatchWithAsyncModuleInfo::ChunkItem(
189 chunk_items.into_iter().next().unwrap(),
190 )
191 } else {
192 ChunkItemOrBatchWithAsyncModuleInfo::Batch(
193 Self {
194 chunk_items,
195 chunk_groups: this.chunk_groups.clone(),
196 }
197 .resolved_cell(),
198 )
199 };
200 (ty, item)
201 })
202 .collect(),
203 ));
204 }
205 }
206 Ok(Vc::cell(smallvec![(
207 chunk_type,
208 ChunkItemOrBatchWithAsyncModuleInfo::Batch(self.to_resolved().await?)
209 )]))
210 }
211}
212
213type ChunkItemBatchWithAsyncModuleInfoByChunkTypeData = SmallVec<
214 [(
215 ResolvedVc<Box<dyn ChunkType>>,
216 ChunkItemOrBatchWithAsyncModuleInfo,
217 ); 1],
218>;
219
220#[turbo_tasks::value(transparent)]
221pub struct ChunkItemBatchWithAsyncModuleInfoByChunkType(
222 ChunkItemBatchWithAsyncModuleInfoByChunkTypeData,
223);
224
225type ChunkItemBatchGroupByChunkTypeT = SmallVec<
226 [(
227 ResolvedVc<Box<dyn ChunkType>>,
228 ResolvedVc<ChunkItemBatchGroup>,
229 ); 1],
230>;
231
232#[turbo_tasks::value(transparent)]
233pub struct ChunkItemBatchGroupByChunkType(ChunkItemBatchGroupByChunkTypeT);
234
235#[turbo_tasks::value(transparent)]
236pub struct ChunkItemBatchGroups(Vec<ResolvedVc<ChunkItemBatchGroup>>);
237
238#[turbo_tasks::value]
239pub struct ChunkItemBatchGroup {
240 pub items: Vec<ChunkItemOrBatchWithAsyncModuleInfo>,
241 pub chunk_groups: RoaringBitmapWrapper,
242}
243
244#[turbo_tasks::value_impl]
245impl ChunkItemBatchGroup {
246 #[turbo_tasks::function]
247 pub async fn from_module_batch_group(
248 batch_group: Vc<ChunkableModuleBatchGroup>,
249 module_graph: Vc<ModuleGraph>,
250 chunking_context: Vc<Box<dyn ChunkingContext>>,
251 ) -> Result<Vc<Self>> {
252 let async_module_info = module_graph.async_module_info();
253 let batch_group = batch_group.await?;
254 let items = batch_group
255 .items
256 .iter()
257 .map(|&batch| {
258 ChunkItemOrBatchWithAsyncModuleInfo::from_chunkable_module_or_batch(
259 batch,
260 async_module_info,
261 module_graph,
262 chunking_context,
263 )
264 })
265 .try_flat_join()
266 .await?;
267 Ok(Self {
268 items,
269 chunk_groups: batch_group.chunk_groups.clone(),
270 }
271 .cell())
272 }
273
274 #[turbo_tasks::function]
275 pub async fn split_by_chunk_type(self: Vc<Self>) -> Result<Vc<ChunkItemBatchGroupByChunkType>> {
276 let this = self.await?;
277 let mut map: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
279 for item in &this.items {
280 let split = item.split_by_chunk_type().await?;
281 for (ty, value) in split.iter() {
282 map.entry(*ty).or_default().push(value.clone());
283 }
284 }
285 let result = if map.len() == 1 {
286 let (ty, _) = map.into_iter().next().unwrap();
287 smallvec![(ty, self.to_resolved().await?)]
288 } else {
289 map.into_iter()
290 .map(|(ty, items)| {
291 (
292 ty,
293 ChunkItemBatchGroup {
294 items,
295 chunk_groups: this.chunk_groups.clone(),
296 }
297 .resolved_cell(),
298 )
299 })
300 .collect()
301 };
302 Ok(Vc::cell(result))
303 }
304}
305
306pub async fn batch_info<'a, BatchGroup, Item, Info, BatchGroupInfo, A, B>(
307 batch_groups: &[ResolvedVc<BatchGroup>],
308 items: &[Item],
309 get_batch_group_info: impl Fn(Vc<BatchGroup>) -> A + Send + 'a,
310 get_item_info: impl Fn(&Item) -> B + Send + 'a,
311) -> Result<Vec<Info>>
312where
313 A: Future<Output = Result<BatchGroupInfo>> + Send + 'a,
314 B: Future<Output = Result<Info>> + Send + 'a,
315 BatchGroup: Send,
316 Item: Send + Eq + Hash,
317 BatchGroupInfo: Deref<Target = FxHashMap<Item, Info>> + Send,
318 Info: Clone + Send,
319{
320 let batch_group_info: Vec<BatchGroupInfo> = batch_groups
321 .iter()
322 .map(|&batch_group| get_batch_group_info(*batch_group))
323 .try_join()
324 .await?;
325 let batch_group_info = batch_group_info
326 .iter()
327 .flat_map(|info| info.iter())
328 .collect::<FxHashMap<_, _>>();
329 items
330 .iter()
331 .map(async |item| {
332 Ok(if let Some(&info) = batch_group_info.get(item) {
333 info.clone()
334 } else {
335 get_item_info(item).await?
336 })
337 })
338 .try_join()
339 .await
340}