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