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, OutputAssetsReference},
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: OutputAssetsReference {
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]
222 fn chunk_items(self: Vc<Self>) -> Vc<ChunkItems> {
223 ChunkItems(vec![]).cell()
224 }
225}
226
227#[turbo_tasks::value(shared)]
230#[derive(Default)]
231pub struct OutputChunkRuntimeInfo {
232 pub included_ids: Option<ResolvedVc<ModuleIds>>,
233 pub excluded_ids: Option<ResolvedVc<ModuleIds>>,
234 pub module_chunks: Option<ResolvedVc<OutputAssets>>,
238 pub placeholder_for_future_extensions: (),
239}
240
241#[turbo_tasks::value_impl]
242impl OutputChunkRuntimeInfo {
243 #[turbo_tasks::function]
244 pub fn empty() -> Vc<Self> {
245 Self::default().cell()
246 }
247}
248
249#[turbo_tasks::value_trait]
250pub trait OutputChunk: Asset {
251 #[turbo_tasks::function]
252 fn runtime_info(self: Vc<Self>) -> Vc<OutputChunkRuntimeInfo>;
253}
254
255#[derive(
258 Debug,
259 Clone,
260 Hash,
261 TraceRawVcs,
262 Serialize,
263 Deserialize,
264 Eq,
265 PartialEq,
266 ValueDebugFormat,
267 NonLocalValue,
268)]
269pub enum ChunkingType {
270 Parallel {
272 inherit_async: bool,
275 hoisted: bool,
278 },
279 Async,
282 Isolated {
286 _ty: ChunkGroupType,
287 merge_tag: Option<RcStr>,
288 },
289 Shared {
293 inherit_async: bool,
294 merge_tag: Option<RcStr>,
295 },
296 Traced,
298}
299
300impl Display for ChunkingType {
301 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
302 match self {
303 ChunkingType::Parallel {
304 inherit_async,
305 hoisted,
306 } => {
307 write!(
308 f,
309 "Parallel(inherit_async: {inherit_async}, hoisted: {hoisted})",
310 )
311 }
312 ChunkingType::Async => write!(f, "Async"),
313 ChunkingType::Isolated {
314 _ty,
315 merge_tag: Some(merge_tag),
316 } => {
317 write!(f, "Isolated(merge_tag: {merge_tag})")
318 }
319 ChunkingType::Isolated {
320 _ty,
321 merge_tag: None,
322 } => {
323 write!(f, "Isolated")
324 }
325 ChunkingType::Shared {
326 inherit_async,
327 merge_tag: Some(merge_tag),
328 } => {
329 write!(
330 f,
331 "Shared(inherit_async: {inherit_async}, merge_tag: {merge_tag})"
332 )
333 }
334 ChunkingType::Shared {
335 inherit_async,
336 merge_tag: None,
337 } => {
338 write!(f, "Shared(inherit_async: {inherit_async})")
339 }
340 ChunkingType::Traced => write!(f, "Traced"),
341 }
342 }
343}
344
345impl ChunkingType {
346 pub fn is_inherit_async(&self) -> bool {
347 matches!(
348 self,
349 ChunkingType::Parallel {
350 inherit_async: true,
351 ..
352 } | ChunkingType::Shared {
353 inherit_async: true,
354 ..
355 }
356 )
357 }
358
359 pub fn is_parallel(&self) -> bool {
360 matches!(self, ChunkingType::Parallel { .. })
361 }
362
363 pub fn is_merged(&self) -> bool {
364 matches!(
365 self,
366 ChunkingType::Isolated {
367 merge_tag: Some(_),
368 ..
369 } | ChunkingType::Shared {
370 merge_tag: Some(_),
371 ..
372 }
373 )
374 }
375
376 pub fn without_inherit_async(&self) -> Self {
377 match self {
378 ChunkingType::Parallel { hoisted, .. } => ChunkingType::Parallel {
379 hoisted: *hoisted,
380 inherit_async: false,
381 },
382 ChunkingType::Async => ChunkingType::Async,
383 ChunkingType::Isolated { _ty, merge_tag } => ChunkingType::Isolated {
384 _ty: *_ty,
385 merge_tag: merge_tag.clone(),
386 },
387 ChunkingType::Shared {
388 inherit_async: _,
389 merge_tag,
390 } => ChunkingType::Shared {
391 inherit_async: false,
392 merge_tag: merge_tag.clone(),
393 },
394 ChunkingType::Traced => ChunkingType::Traced,
395 }
396 }
397}
398
399#[turbo_tasks::value(transparent)]
400pub struct ChunkingTypeOption(Option<ChunkingType>);
401
402#[turbo_tasks::value_trait]
409pub trait ChunkableModuleReference: ModuleReference + ValueToString {
410 #[turbo_tasks::function]
411 fn chunking_type(self: Vc<Self>) -> Vc<ChunkingTypeOption> {
412 Vc::cell(Some(ChunkingType::Parallel {
413 inherit_async: false,
414 hoisted: false,
415 }))
416 }
417
418 #[turbo_tasks::function]
419 fn export_usage(self: Vc<Self>) -> Vc<ExportUsage> {
420 ExportUsage::all()
421 }
422}
423
424pub struct ChunkGroupContent {
425 pub chunkable_items: Vec<ChunkableModuleOrBatch>,
426 pub batch_groups: Vec<ResolvedVc<ModuleBatchGroup>>,
427 pub async_modules: FxIndexSet<ResolvedVc<Box<dyn ChunkableModule>>>,
428 pub traced_modules: FxIndexSet<ResolvedVc<Box<dyn Module>>>,
429 pub availability_info: AvailabilityInfo,
430}
431
432#[turbo_tasks::value_trait]
433pub trait ChunkItem: OutputAssetsReference {
434 #[turbo_tasks::function]
438 fn asset_ident(self: Vc<Self>) -> Vc<AssetIdent>;
439
440 #[turbo_tasks::function]
445 fn content_ident(self: Vc<Self>) -> Vc<AssetIdent> {
446 self.asset_ident()
447 }
448
449 #[turbo_tasks::function]
451 fn ty(self: Vc<Self>) -> Vc<Box<dyn ChunkType>>;
452
453 #[turbo_tasks::function]
456 fn module(self: Vc<Self>) -> Vc<Box<dyn Module>>;
457
458 #[turbo_tasks::function]
459 fn chunking_context(self: Vc<Self>) -> Vc<Box<dyn ChunkingContext>>;
460}
461
462#[turbo_tasks::value_trait]
463pub trait ChunkType: ValueToString {
464 #[turbo_tasks::function]
466 fn is_style(self: Vc<Self>) -> Vc<bool>;
467
468 #[turbo_tasks::function]
470 fn chunk(
471 &self,
472 chunking_context: Vc<Box<dyn ChunkingContext>>,
473 chunk_items: Vec<ChunkItemOrBatchWithAsyncModuleInfo>,
474 batch_groups: Vec<ResolvedVc<ChunkItemBatchGroup>>,
475 ) -> Vc<Box<dyn Chunk>>;
476
477 #[turbo_tasks::function]
478 fn chunk_item_size(
479 &self,
480 chunking_context: Vc<Box<dyn ChunkingContext>>,
481 chunk_item: Vc<Box<dyn ChunkItem>>,
482 async_module_info: Option<Vc<AsyncModuleInfo>>,
483 ) -> Vc<usize>;
484}
485
486pub fn round_chunk_item_size(size: usize) -> usize {
487 let a = size.next_power_of_two();
488 size & (a | (a >> 1) | (a >> 2))
489}
490
491#[turbo_tasks::value(transparent)]
492pub struct ChunkItems(pub Vec<ResolvedVc<Box<dyn ChunkItem>>>);
493
494#[turbo_tasks::value]
495pub struct AsyncModuleInfo {
496 pub referenced_async_modules: AutoSet<ResolvedVc<Box<dyn Module>>>,
497}
498
499#[turbo_tasks::value_impl]
500impl AsyncModuleInfo {
501 #[turbo_tasks::function]
502 pub fn new(referenced_async_modules: Vec<ResolvedVc<Box<dyn Module>>>) -> Result<Vc<Self>> {
503 Ok(Self {
504 referenced_async_modules: referenced_async_modules.into_iter().collect(),
505 }
506 .cell())
507 }
508}
509
510#[derive(
511 Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, TraceRawVcs, TaskInput, NonLocalValue,
512)]
513pub struct ChunkItemWithAsyncModuleInfo {
514 pub chunk_item: ResolvedVc<Box<dyn ChunkItem>>,
515 pub module: Option<ResolvedVc<Box<dyn ChunkableModule>>>,
516 pub async_info: Option<ResolvedVc<AsyncModuleInfo>>,
517}
518
519#[turbo_tasks::value(transparent)]
520pub struct ChunkItemsWithAsyncModuleInfo(Vec<ChunkItemWithAsyncModuleInfo>);
521
522pub trait ChunkItemExt {
523 fn id(self: Vc<Self>) -> Vc<ModuleId>;
525}
526
527impl<T> ChunkItemExt for T
528where
529 T: Upcast<Box<dyn ChunkItem>>,
530{
531 fn id(self: Vc<Self>) -> Vc<ModuleId> {
533 let chunk_item = Vc::upcast_non_strict(self);
534 chunk_item.chunking_context().chunk_item_id(chunk_item)
535 }
536}
537
538pub trait ModuleChunkItemIdExt {
539 fn chunk_item_id(
541 self: Vc<Self>,
542 chunking_context: Vc<Box<dyn ChunkingContext>>,
543 ) -> Vc<ModuleId>;
544}
545impl<T> ModuleChunkItemIdExt for T
546where
547 T: Upcast<Box<dyn Module>>,
548{
549 fn chunk_item_id(
550 self: Vc<Self>,
551 chunking_context: Vc<Box<dyn ChunkingContext>>,
552 ) -> Vc<ModuleId> {
553 chunking_context.chunk_item_id_from_module(Vc::upcast_non_strict(self))
554 }
555}
556
557#[cfg(test)]
558mod tests {
559 use super::*;
560
561 #[test]
562 fn test_round_chunk_item_size() {
563 assert_eq!(round_chunk_item_size(0), 0);
564 assert_eq!(round_chunk_item_size(1), 1);
565 assert_eq!(round_chunk_item_size(2), 2);
566 assert_eq!(round_chunk_item_size(3), 3);
567 assert_eq!(round_chunk_item_size(4), 4);
568 assert_eq!(round_chunk_item_size(5), 4);
569 assert_eq!(round_chunk_item_size(6), 6);
570 assert_eq!(round_chunk_item_size(7), 6);
571 assert_eq!(round_chunk_item_size(8), 8);
572 assert_eq!(round_chunk_item_size(49000), 32_768);
573 assert_eq!(round_chunk_item_size(50000), 49_152);
574
575 assert_eq!(changes_in_range(0..1000), 19);
576 assert_eq!(changes_in_range(1000..2000), 2);
577 assert_eq!(changes_in_range(2000..3000), 1);
578
579 assert_eq!(changes_in_range(3000..10000), 4);
580
581 fn changes_in_range(range: std::ops::Range<usize>) -> usize {
582 let len = range.len();
583 let mut count = 0;
584 for i in range {
585 let a = round_chunk_item_size(i);
586 assert!(a >= i * 2 / 3);
587 assert!(a <= i);
588 let b = round_chunk_item_size(i + 1);
589
590 if a == b {
591 count += 1;
592 }
593 }
594 len - count
595 }
596 }
597}