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