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