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 chunk::availability_info::AvailabilityInfo,
40 ident::AssetIdent,
41 module::Module,
42 module_graph::{
43 ModuleGraph,
44 module_batch::{ChunkableModuleOrBatch, ModuleBatchGroup},
45 },
46 output::OutputAssets,
47 reference::ModuleReference,
48 resolve::ExportUsage,
49};
50
51#[turbo_tasks::value(shared, operation)]
53#[derive(Debug, Clone, Hash, Ord, PartialOrd, DeterministicHash)]
54#[serde(untagged)]
55pub enum ModuleId {
56 Number(u64),
57 String(RcStr),
58}
59
60impl Display for ModuleId {
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 match self {
63 ModuleId::Number(i) => write!(f, "{i}"),
64 ModuleId::String(s) => write!(f, "{s}"),
65 }
66 }
67}
68
69#[turbo_tasks::value_impl]
70impl ValueToString for ModuleId {
71 #[turbo_tasks::function]
72 fn to_string(&self) -> Vc<RcStr> {
73 Vc::cell(self.to_string().into())
74 }
75}
76
77impl ModuleId {
78 pub fn parse(id: &str) -> Result<ModuleId> {
79 Ok(match id.parse::<u64>() {
80 Ok(i) => ModuleId::Number(i),
81 Err(_) => ModuleId::String(id.into()),
82 })
83 }
84}
85
86#[turbo_tasks::value(transparent, shared)]
88pub struct ModuleIds(Vec<ResolvedVc<ModuleId>>);
89
90#[turbo_tasks::value_trait]
92pub trait ChunkableModule: Module + Asset {
93 #[turbo_tasks::function]
94 fn as_chunk_item(
95 self: Vc<Self>,
96 module_graph: Vc<ModuleGraph>,
97 chunking_context: Vc<Box<dyn ChunkingContext>>,
98 ) -> Vc<Box<dyn ChunkItem>>;
99}
100
101#[turbo_tasks::value(transparent)]
102pub struct ChunkableModules(Vec<ResolvedVc<Box<dyn ChunkableModule>>>);
103
104#[turbo_tasks::value_impl]
105impl ChunkableModules {
106 #[turbo_tasks::function]
107 pub fn interned(modules: Vec<ResolvedVc<Box<dyn ChunkableModule>>>) -> Vc<Self> {
108 Vc::cell(modules)
109 }
110}
111
112#[turbo_tasks::value_trait]
117pub trait MergeableModule: Module + Asset {
118 #[turbo_tasks::function]
121 fn is_mergeable(self: Vc<Self>) -> Vc<bool> {
122 Vc::cell(true)
123 }
124
125 #[turbo_tasks::function]
131 fn merge(
132 self: Vc<Self>,
133 modules: Vc<MergeableModulesExposed>,
134 entry_points: Vc<MergeableModules>,
135 ) -> Vc<Box<dyn ChunkableModule>>;
136}
137#[turbo_tasks::value(transparent)]
138pub struct MergeableModules(Vec<ResolvedVc<Box<dyn MergeableModule>>>);
139
140#[turbo_tasks::value_impl]
141impl MergeableModules {
142 #[turbo_tasks::function]
143 pub fn interned(modules: Vec<ResolvedVc<Box<dyn MergeableModule>>>) -> Vc<Self> {
144 Vc::cell(modules)
145 }
146}
147
148#[derive(
150 Copy,
151 Clone,
152 Debug,
153 PartialEq,
154 Eq,
155 Serialize,
156 Deserialize,
157 TraceRawVcs,
158 NonLocalValue,
159 TaskInput,
160 Hash,
161)]
162pub enum MergeableModuleExposure {
163 None,
166 Internal,
169 External,
172}
173
174#[turbo_tasks::value(transparent)]
175pub struct MergeableModulesExposed(
176 Vec<(
177 ResolvedVc<Box<dyn MergeableModule>>,
178 MergeableModuleExposure,
179 )>,
180);
181
182#[turbo_tasks::value_impl]
183impl MergeableModulesExposed {
184 #[turbo_tasks::function]
185 pub fn interned(
186 modules: Vec<(
187 ResolvedVc<Box<dyn MergeableModule>>,
188 MergeableModuleExposure,
189 )>,
190 ) -> Vc<Self> {
191 Vc::cell(modules)
192 }
193}
194
195#[turbo_tasks::value(transparent)]
196pub struct Chunks(Vec<ResolvedVc<Box<dyn Chunk>>>);
197
198#[turbo_tasks::value_impl]
199impl Chunks {
200 #[turbo_tasks::function]
202 pub fn empty() -> Vc<Self> {
203 Vc::cell(vec![])
204 }
205}
206
207#[turbo_tasks::value_trait]
211pub trait Chunk {
212 #[turbo_tasks::function]
213 fn ident(self: Vc<Self>) -> Vc<AssetIdent>;
214 #[turbo_tasks::function]
215 fn chunking_context(self: Vc<Self>) -> Vc<Box<dyn ChunkingContext>>;
216 #[turbo_tasks::function]
222 fn references(self: Vc<Self>) -> Vc<OutputAssets> {
223 OutputAssets::empty()
224 }
225
226 #[turbo_tasks::function]
227 fn chunk_items(self: Vc<Self>) -> Vc<ChunkItems> {
228 ChunkItems(vec![]).cell()
229 }
230}
231
232#[turbo_tasks::value(shared)]
235#[derive(Default)]
236pub struct OutputChunkRuntimeInfo {
237 pub included_ids: Option<ResolvedVc<ModuleIds>>,
238 pub excluded_ids: Option<ResolvedVc<ModuleIds>>,
239 pub module_chunks: Option<ResolvedVc<OutputAssets>>,
243 pub placeholder_for_future_extensions: (),
244}
245
246#[turbo_tasks::value_trait]
247pub trait OutputChunk: Asset {
248 #[turbo_tasks::function]
249 fn runtime_info(self: Vc<Self>) -> Vc<OutputChunkRuntimeInfo>;
250}
251
252#[derive(
255 Debug,
256 Clone,
257 Hash,
258 TraceRawVcs,
259 Serialize,
260 Deserialize,
261 Eq,
262 PartialEq,
263 ValueDebugFormat,
264 NonLocalValue,
265)]
266pub enum ChunkingType {
267 Parallel {
269 inherit_async: bool,
272 hoisted: bool,
275 },
276 Async,
279 Isolated {
283 _ty: ChunkGroupType,
284 merge_tag: Option<RcStr>,
285 },
286 Shared {
290 inherit_async: bool,
291 merge_tag: Option<RcStr>,
292 },
293 Traced,
295}
296
297impl Display for ChunkingType {
298 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
299 match self {
300 ChunkingType::Parallel {
301 inherit_async,
302 hoisted,
303 } => {
304 write!(
305 f,
306 "Parallel(inherit_async: {inherit_async}, hoisted: {hoisted})",
307 )
308 }
309 ChunkingType::Async => write!(f, "Async"),
310 ChunkingType::Isolated {
311 _ty,
312 merge_tag: Some(merge_tag),
313 } => {
314 write!(f, "Isolated(merge_tag: {merge_tag})")
315 }
316 ChunkingType::Isolated {
317 _ty,
318 merge_tag: None,
319 } => {
320 write!(f, "Isolated")
321 }
322 ChunkingType::Shared {
323 inherit_async,
324 merge_tag: Some(merge_tag),
325 } => {
326 write!(
327 f,
328 "Shared(inherit_async: {inherit_async}, merge_tag: {merge_tag})"
329 )
330 }
331 ChunkingType::Shared {
332 inherit_async,
333 merge_tag: None,
334 } => {
335 write!(f, "Shared(inherit_async: {inherit_async})")
336 }
337 ChunkingType::Traced => write!(f, "Traced"),
338 }
339 }
340}
341
342impl ChunkingType {
343 pub fn is_inherit_async(&self) -> bool {
344 matches!(
345 self,
346 ChunkingType::Parallel {
347 inherit_async: true,
348 ..
349 } | ChunkingType::Shared {
350 inherit_async: true,
351 ..
352 }
353 )
354 }
355
356 pub fn is_parallel(&self) -> bool {
357 matches!(self, ChunkingType::Parallel { .. })
358 }
359
360 pub fn is_merged(&self) -> bool {
361 matches!(
362 self,
363 ChunkingType::Isolated {
364 merge_tag: Some(_),
365 ..
366 } | ChunkingType::Shared {
367 merge_tag: Some(_),
368 ..
369 }
370 )
371 }
372
373 pub fn without_inherit_async(&self) -> Self {
374 match self {
375 ChunkingType::Parallel { hoisted, .. } => ChunkingType::Parallel {
376 hoisted: *hoisted,
377 inherit_async: false,
378 },
379 ChunkingType::Async => ChunkingType::Async,
380 ChunkingType::Isolated { _ty, merge_tag } => ChunkingType::Isolated {
381 _ty: *_ty,
382 merge_tag: merge_tag.clone(),
383 },
384 ChunkingType::Shared {
385 inherit_async: _,
386 merge_tag,
387 } => ChunkingType::Shared {
388 inherit_async: false,
389 merge_tag: merge_tag.clone(),
390 },
391 ChunkingType::Traced => ChunkingType::Traced,
392 }
393 }
394}
395
396#[turbo_tasks::value(transparent)]
397pub struct ChunkingTypeOption(Option<ChunkingType>);
398
399#[turbo_tasks::value_trait]
406pub trait ChunkableModuleReference: ModuleReference + ValueToString {
407 #[turbo_tasks::function]
408 fn chunking_type(self: Vc<Self>) -> Vc<ChunkingTypeOption> {
409 Vc::cell(Some(ChunkingType::Parallel {
410 inherit_async: false,
411 hoisted: false,
412 }))
413 }
414
415 #[turbo_tasks::function]
416 fn export_usage(self: Vc<Self>) -> Vc<ExportUsage> {
417 ExportUsage::all()
418 }
419}
420
421pub struct ChunkGroupContent {
422 pub chunkable_items: FxIndexSet<ChunkableModuleOrBatch>,
423 pub batch_groups: FxIndexSet<ResolvedVc<ModuleBatchGroup>>,
424 pub async_modules: FxIndexSet<ResolvedVc<Box<dyn ChunkableModule>>>,
425 pub traced_modules: FxIndexSet<ResolvedVc<Box<dyn Module>>>,
426 pub availability_info: AvailabilityInfo,
427}
428
429#[turbo_tasks::value_trait]
430pub trait ChunkItem {
431 #[turbo_tasks::function]
435 fn asset_ident(self: Vc<Self>) -> Vc<AssetIdent>;
436 #[turbo_tasks::function]
441 fn content_ident(self: Vc<Self>) -> Vc<AssetIdent> {
442 self.asset_ident()
443 }
444 #[turbo_tasks::function]
446 fn references(self: Vc<Self>) -> Vc<OutputAssets> {
447 OutputAssets::empty()
448 }
449
450 #[turbo_tasks::function]
452 fn ty(self: Vc<Self>) -> Vc<Box<dyn ChunkType>>;
453
454 #[turbo_tasks::function]
457 fn module(self: Vc<Self>) -> Vc<Box<dyn Module>>;
458
459 #[turbo_tasks::function]
460 fn chunking_context(self: Vc<Self>) -> Vc<Box<dyn ChunkingContext>>;
461}
462
463#[turbo_tasks::value_trait]
464pub trait ChunkType: ValueToString {
465 #[turbo_tasks::function]
467 fn is_style(self: Vc<Self>) -> Vc<bool>;
468
469 #[turbo_tasks::function]
471 fn chunk(
472 &self,
473 chunking_context: Vc<Box<dyn ChunkingContext>>,
474 chunk_items: Vec<ChunkItemOrBatchWithAsyncModuleInfo>,
475 batch_groups: Vec<ResolvedVc<ChunkItemBatchGroup>>,
476 referenced_output_assets: Vc<OutputAssets>,
477 ) -> Vc<Box<dyn Chunk>>;
478
479 #[turbo_tasks::function]
480 fn chunk_item_size(
481 &self,
482 chunking_context: Vc<Box<dyn ChunkingContext>>,
483 chunk_item: Vc<Box<dyn ChunkItem>>,
484 async_module_info: Option<Vc<AsyncModuleInfo>>,
485 ) -> Vc<usize>;
486}
487
488pub fn round_chunk_item_size(size: usize) -> usize {
489 let a = size.next_power_of_two();
490 size & (a | (a >> 1) | (a >> 2))
491}
492
493#[turbo_tasks::value(transparent)]
494pub struct ChunkItems(pub Vec<ResolvedVc<Box<dyn ChunkItem>>>);
495
496#[turbo_tasks::value]
497pub struct AsyncModuleInfo {
498 pub referenced_async_modules: AutoSet<ResolvedVc<Box<dyn Module>>>,
499}
500
501#[turbo_tasks::value_impl]
502impl AsyncModuleInfo {
503 #[turbo_tasks::function]
504 pub fn new(referenced_async_modules: Vec<ResolvedVc<Box<dyn Module>>>) -> Result<Vc<Self>> {
505 Ok(Self {
506 referenced_async_modules: referenced_async_modules.into_iter().collect(),
507 }
508 .cell())
509 }
510}
511
512#[derive(
513 Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, TraceRawVcs, TaskInput, NonLocalValue,
514)]
515pub struct ChunkItemWithAsyncModuleInfo {
516 pub chunk_item: ResolvedVc<Box<dyn ChunkItem>>,
517 pub module: Option<ResolvedVc<Box<dyn ChunkableModule>>>,
518 pub async_info: Option<ResolvedVc<AsyncModuleInfo>>,
519}
520
521#[turbo_tasks::value(transparent)]
522pub struct ChunkItemsWithAsyncModuleInfo(Vec<ChunkItemWithAsyncModuleInfo>);
523
524pub trait ChunkItemExt {
525 fn id(self: Vc<Self>) -> Vc<ModuleId>;
527}
528
529impl<T> ChunkItemExt for T
530where
531 T: Upcast<Box<dyn ChunkItem>>,
532{
533 fn id(self: Vc<Self>) -> Vc<ModuleId> {
535 let chunk_item = Vc::upcast(self);
536 chunk_item.chunking_context().chunk_item_id(chunk_item)
537 }
538}
539
540pub trait ModuleChunkItemIdExt {
541 fn chunk_item_id(
543 self: Vc<Self>,
544 chunking_context: Vc<Box<dyn ChunkingContext>>,
545 ) -> Vc<ModuleId>;
546}
547impl<T> ModuleChunkItemIdExt for T
548where
549 T: Upcast<Box<dyn Module>>,
550{
551 fn chunk_item_id(
552 self: Vc<Self>,
553 chunking_context: Vc<Box<dyn ChunkingContext>>,
554 ) -> Vc<ModuleId> {
555 chunking_context.chunk_item_id_from_module(Vc::upcast(self))
556 }
557}
558
559#[cfg(test)]
560mod tests {
561 use super::*;
562
563 #[test]
564 fn test_round_chunk_item_size() {
565 assert_eq!(round_chunk_item_size(0), 0);
566 assert_eq!(round_chunk_item_size(1), 1);
567 assert_eq!(round_chunk_item_size(2), 2);
568 assert_eq!(round_chunk_item_size(3), 3);
569 assert_eq!(round_chunk_item_size(4), 4);
570 assert_eq!(round_chunk_item_size(5), 4);
571 assert_eq!(round_chunk_item_size(6), 6);
572 assert_eq!(round_chunk_item_size(7), 6);
573 assert_eq!(round_chunk_item_size(8), 8);
574 assert_eq!(round_chunk_item_size(49000), 32_768);
575 assert_eq!(round_chunk_item_size(50000), 49_152);
576
577 assert_eq!(changes_in_range(0..1000), 19);
578 assert_eq!(changes_in_range(1000..2000), 2);
579 assert_eq!(changes_in_range(2000..3000), 1);
580
581 assert_eq!(changes_in_range(3000..10000), 4);
582
583 fn changes_in_range(range: std::ops::Range<usize>) -> usize {
584 let len = range.len();
585 let mut count = 0;
586 for i in range {
587 let a = round_chunk_item_size(i);
588 assert!(a >= i * 2 / 3);
589 assert!(a <= i);
590 let b = round_chunk_item_size(i + 1);
591
592 if a == b {
593 count += 1;
594 }
595 }
596 len - count
597 }
598 }
599}