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