1pub mod availability_info;
2pub mod available_modules;
3pub mod chunk_group;
4pub(crate) mod chunk_item_batch;
5pub mod chunking;
6pub(crate) mod chunking_context;
7pub(crate) mod containment_tree;
8pub(crate) mod data;
9pub(crate) mod evaluate;
10pub mod module_id_strategies;
11pub mod optimize;
12
13use std::fmt::Display;
14
15use anyhow::Result;
16use auto_hash_map::AutoSet;
17use serde::{Deserialize, Serialize};
18use turbo_rcstr::RcStr;
19use turbo_tasks::{
20 FxIndexSet, NonLocalValue, ResolvedVc, TaskInput, Upcast, ValueToString, Vc,
21 debug::ValueDebugFormat, trace::TraceRawVcs,
22};
23use turbo_tasks_hash::DeterministicHash;
24
25pub use self::{
26 chunk_item_batch::{
27 ChunkItemBatchGroup, ChunkItemBatchWithAsyncModuleInfo,
28 ChunkItemOrBatchWithAsyncModuleInfo, batch_info,
29 },
30 chunking_context::{
31 ChunkGroupResult, ChunkGroupType, ChunkingConfig, ChunkingConfigs, ChunkingContext,
32 ChunkingContextExt, EntryChunkGroupResult, MangleType, MinifyType, SourceMapsType,
33 },
34 data::{ChunkData, ChunkDataOption, ChunksData},
35 evaluate::{EvaluatableAsset, EvaluatableAssetExt, EvaluatableAssets},
36};
37use crate::{
38 asset::Asset,
39 ident::AssetIdent,
40 module::Module,
41 module_graph::{
42 ModuleGraph,
43 module_batch::{ChunkableModuleOrBatch, ModuleBatchGroup},
44 },
45 output::OutputAssets,
46 reference::ModuleReference,
47};
48
49#[turbo_tasks::value(shared, operation)]
51#[derive(Debug, Clone, Hash, Ord, PartialOrd, DeterministicHash)]
52#[serde(untagged)]
53pub enum ModuleId {
54 Number(u64),
55 String(RcStr),
56}
57
58impl Display for ModuleId {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 match self {
61 ModuleId::Number(i) => write!(f, "{i}"),
62 ModuleId::String(s) => write!(f, "{s}"),
63 }
64 }
65}
66
67#[turbo_tasks::value_impl]
68impl ValueToString for ModuleId {
69 #[turbo_tasks::function]
70 fn to_string(&self) -> Vc<RcStr> {
71 Vc::cell(self.to_string().into())
72 }
73}
74
75impl ModuleId {
76 pub fn parse(id: &str) -> Result<ModuleId> {
77 Ok(match id.parse::<u64>() {
78 Ok(i) => ModuleId::Number(i),
79 Err(_) => ModuleId::String(id.into()),
80 })
81 }
82}
83
84#[turbo_tasks::value(transparent, shared)]
86pub struct ModuleIds(Vec<ResolvedVc<ModuleId>>);
87
88#[turbo_tasks::value_trait]
90pub trait ChunkableModule: Module + Asset {
91 fn as_chunk_item(
92 self: Vc<Self>,
93 module_graph: Vc<ModuleGraph>,
94 chunking_context: Vc<Box<dyn ChunkingContext>>,
95 ) -> Vc<Box<dyn ChunkItem>>;
96}
97
98#[turbo_tasks::value(transparent)]
99pub struct ChunkableModules(Vec<ResolvedVc<Box<dyn ChunkableModule>>>);
100
101#[turbo_tasks::value_impl]
102impl ChunkableModules {
103 #[turbo_tasks::function]
104 pub fn interned(modules: Vec<ResolvedVc<Box<dyn ChunkableModule>>>) -> Vc<Self> {
105 Vc::cell(modules)
106 }
107}
108
109#[turbo_tasks::value(transparent)]
110pub struct Chunks(Vec<ResolvedVc<Box<dyn Chunk>>>);
111
112#[turbo_tasks::value_impl]
113impl Chunks {
114 #[turbo_tasks::function]
116 pub fn empty() -> Vc<Self> {
117 Vc::cell(vec![])
118 }
119}
120
121#[turbo_tasks::value_trait]
125pub trait Chunk {
126 fn ident(self: Vc<Self>) -> Vc<AssetIdent>;
127 fn chunking_context(self: Vc<Self>) -> Vc<Box<dyn ChunkingContext>>;
128 fn references(self: Vc<Self>) -> Vc<OutputAssets> {
134 OutputAssets::empty()
135 }
136
137 fn chunk_items(self: Vc<Self>) -> Vc<ChunkItems> {
138 ChunkItems(vec![]).cell()
139 }
140}
141
142#[turbo_tasks::value(shared)]
145#[derive(Default)]
146pub struct OutputChunkRuntimeInfo {
147 pub included_ids: Option<ResolvedVc<ModuleIds>>,
148 pub excluded_ids: Option<ResolvedVc<ModuleIds>>,
149 pub module_chunks: Option<ResolvedVc<OutputAssets>>,
153 pub placeholder_for_future_extensions: (),
154}
155
156#[turbo_tasks::value_trait]
157pub trait OutputChunk: Asset {
158 fn runtime_info(self: Vc<Self>) -> Vc<OutputChunkRuntimeInfo>;
159}
160
161#[derive(
164 Debug,
165 Clone,
166 Hash,
167 TraceRawVcs,
168 Serialize,
169 Deserialize,
170 Eq,
171 PartialEq,
172 ValueDebugFormat,
173 NonLocalValue,
174)]
175pub enum ChunkingType {
176 Parallel {
178 inherit_async: bool,
181 hoisted: bool,
184 },
185 Async,
188 Isolated {
192 _ty: ChunkGroupType,
193 merge_tag: Option<RcStr>,
194 },
195 Shared {
199 inherit_async: bool,
200 merge_tag: Option<RcStr>,
201 },
202 Traced,
204}
205
206impl ChunkingType {
207 pub fn is_inherit_async(&self) -> bool {
208 matches!(
209 self,
210 ChunkingType::Parallel {
211 inherit_async: true,
212 ..
213 } | ChunkingType::Shared {
214 inherit_async: true,
215 ..
216 }
217 )
218 }
219
220 pub fn is_parallel(&self) -> bool {
221 matches!(self, ChunkingType::Parallel { .. })
222 }
223
224 pub fn is_merged(&self) -> bool {
225 matches!(
226 self,
227 ChunkingType::Isolated {
228 merge_tag: Some(_),
229 ..
230 } | ChunkingType::Shared {
231 merge_tag: Some(_),
232 ..
233 }
234 )
235 }
236
237 pub fn without_inherit_async(&self) -> Self {
238 match self {
239 ChunkingType::Parallel { hoisted, .. } => ChunkingType::Parallel {
240 hoisted: *hoisted,
241 inherit_async: false,
242 },
243 ChunkingType::Async => ChunkingType::Async,
244 ChunkingType::Isolated { _ty, merge_tag } => ChunkingType::Isolated {
245 _ty: *_ty,
246 merge_tag: merge_tag.clone(),
247 },
248 ChunkingType::Shared {
249 inherit_async: _,
250 merge_tag,
251 } => ChunkingType::Shared {
252 inherit_async: false,
253 merge_tag: merge_tag.clone(),
254 },
255 ChunkingType::Traced => ChunkingType::Traced,
256 }
257 }
258}
259
260#[turbo_tasks::value(transparent)]
261pub struct ChunkingTypeOption(Option<ChunkingType>);
262
263#[turbo_tasks::value_trait]
270pub trait ChunkableModuleReference: ModuleReference + ValueToString {
271 fn chunking_type(self: Vc<Self>) -> Vc<ChunkingTypeOption> {
272 Vc::cell(Some(ChunkingType::Parallel {
273 inherit_async: false,
274 hoisted: false,
275 }))
276 }
277}
278
279#[derive(Default)]
280pub struct ChunkGroupContent {
281 pub chunkable_items: FxIndexSet<ChunkableModuleOrBatch>,
282 pub batch_groups: FxIndexSet<ResolvedVc<ModuleBatchGroup>>,
283 pub async_modules: FxIndexSet<ResolvedVc<Box<dyn ChunkableModule>>>,
284 pub traced_modules: FxIndexSet<ResolvedVc<Box<dyn Module>>>,
285}
286
287#[turbo_tasks::value_trait]
288pub trait ChunkItem {
289 fn asset_ident(self: Vc<Self>) -> Vc<AssetIdent>;
293 fn content_ident(self: Vc<Self>) -> Vc<AssetIdent> {
298 self.asset_ident()
299 }
300 fn references(self: Vc<Self>) -> Vc<OutputAssets> {
302 OutputAssets::empty()
303 }
304
305 fn ty(self: Vc<Self>) -> Vc<Box<dyn ChunkType>>;
307
308 fn module(self: Vc<Self>) -> Vc<Box<dyn Module>>;
311
312 fn chunking_context(self: Vc<Self>) -> Vc<Box<dyn ChunkingContext>>;
313}
314
315#[turbo_tasks::value_trait]
316pub trait ChunkType: ValueToString {
317 fn is_style(self: Vc<Self>) -> Vc<bool>;
319
320 fn chunk(
322 &self,
323 chunking_context: Vc<Box<dyn ChunkingContext>>,
324 chunk_items: Vec<ChunkItemOrBatchWithAsyncModuleInfo>,
325 batch_groups: Vec<ResolvedVc<ChunkItemBatchGroup>>,
326 referenced_output_assets: Vc<OutputAssets>,
327 ) -> Vc<Box<dyn Chunk>>;
328
329 fn chunk_item_size(
330 &self,
331 chunking_context: Vc<Box<dyn ChunkingContext>>,
332 chunk_item: Vc<Box<dyn ChunkItem>>,
333 async_module_info: Option<Vc<AsyncModuleInfo>>,
334 ) -> Vc<usize>;
335}
336
337pub fn round_chunk_item_size(size: usize) -> usize {
338 let a = size.next_power_of_two();
339 size & (a | (a >> 1) | (a >> 2))
340}
341
342#[turbo_tasks::value(transparent)]
343pub struct ChunkItems(pub Vec<ResolvedVc<Box<dyn ChunkItem>>>);
344
345#[turbo_tasks::value]
346pub struct AsyncModuleInfo {
347 pub referenced_async_modules: AutoSet<ResolvedVc<Box<dyn Module>>>,
348}
349
350#[turbo_tasks::value_impl]
351impl AsyncModuleInfo {
352 #[turbo_tasks::function]
353 pub async fn new(
354 referenced_async_modules: Vec<ResolvedVc<Box<dyn Module>>>,
355 ) -> Result<Vc<Self>> {
356 Ok(Self {
357 referenced_async_modules: referenced_async_modules.into_iter().collect(),
358 }
359 .cell())
360 }
361}
362
363#[derive(
364 Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, TraceRawVcs, TaskInput, NonLocalValue,
365)]
366pub struct ChunkItemWithAsyncModuleInfo {
367 pub chunk_item: ResolvedVc<Box<dyn ChunkItem>>,
368 pub module: Option<ResolvedVc<Box<dyn ChunkableModule>>>,
369 pub async_info: Option<ResolvedVc<AsyncModuleInfo>>,
370}
371
372#[turbo_tasks::value(transparent)]
373pub struct ChunkItemsWithAsyncModuleInfo(Vec<ChunkItemWithAsyncModuleInfo>);
374
375pub trait ChunkItemExt {
376 fn id(self: Vc<Self>) -> Vc<ModuleId>;
378}
379
380impl<T> ChunkItemExt for T
381where
382 T: Upcast<Box<dyn ChunkItem>>,
383{
384 fn id(self: Vc<Self>) -> Vc<ModuleId> {
386 let chunk_item = Vc::upcast(self);
387 chunk_item.chunking_context().chunk_item_id(chunk_item)
388 }
389}
390
391pub trait ModuleChunkItemIdExt {
392 fn chunk_item_id(
394 self: Vc<Self>,
395 chunking_context: Vc<Box<dyn ChunkingContext>>,
396 ) -> Vc<ModuleId>;
397}
398impl<T> ModuleChunkItemIdExt for T
399where
400 T: Upcast<Box<dyn Module>>,
401{
402 fn chunk_item_id(
403 self: Vc<Self>,
404 chunking_context: Vc<Box<dyn ChunkingContext>>,
405 ) -> Vc<ModuleId> {
406 chunking_context.chunk_item_id_from_module(Vc::upcast(self))
407 }
408}
409
410#[cfg(test)]
411mod tests {
412 use super::*;
413
414 #[test]
415 fn test_round_chunk_item_size() {
416 assert_eq!(round_chunk_item_size(0), 0);
417 assert_eq!(round_chunk_item_size(1), 1);
418 assert_eq!(round_chunk_item_size(2), 2);
419 assert_eq!(round_chunk_item_size(3), 3);
420 assert_eq!(round_chunk_item_size(4), 4);
421 assert_eq!(round_chunk_item_size(5), 4);
422 assert_eq!(round_chunk_item_size(6), 6);
423 assert_eq!(round_chunk_item_size(7), 6);
424 assert_eq!(round_chunk_item_size(8), 8);
425 assert_eq!(round_chunk_item_size(49000), 32_768);
426 assert_eq!(round_chunk_item_size(50000), 49_152);
427
428 assert_eq!(changes_in_range(0..1000), 19);
429 assert_eq!(changes_in_range(1000..2000), 2);
430 assert_eq!(changes_in_range(2000..3000), 1);
431
432 assert_eq!(changes_in_range(3000..10000), 4);
433
434 fn changes_in_range(range: std::ops::Range<usize>) -> usize {
435 let len = range.len();
436 let mut count = 0;
437 for i in range {
438 let a = round_chunk_item_size(i);
439 assert!(a >= i * 2 / 3);
440 assert!(a <= i);
441 let b = round_chunk_item_size(i + 1);
442
443 if a == b {
444 count += 1;
445 }
446 }
447 len - count
448 }
449 }
450}