1pub mod availability_info;
2pub mod available_modules;
3pub mod chunk_group;
4pub mod chunk_id_strategy;
5pub(crate) mod chunk_item_batch;
6pub mod chunking;
7pub(crate) mod chunking_context;
8pub(crate) mod data;
9pub(crate) mod evaluate;
10
11use std::fmt::Display;
12
13use anyhow::Result;
14use auto_hash_map::AutoSet;
15use bincode::{Decode, Encode};
16use serde::{Deserialize, Serialize};
17use turbo_rcstr::RcStr;
18use turbo_tasks::{
19 FxIndexSet, NonLocalValue, ResolvedVc, TaskInput, Upcast, ValueToString, Vc,
20 debug::ValueDebugFormat, trace::TraceRawVcs,
21};
22use turbo_tasks_hash::DeterministicHash;
23
24pub use crate::chunk::{
25 chunk_item_batch::{
26 ChunkItemBatchGroup, ChunkItemBatchWithAsyncModuleInfo,
27 ChunkItemOrBatchWithAsyncModuleInfo, batch_info,
28 },
29 chunking_context::{
30 ChunkGroupResult, ChunkGroupType, ChunkingConfig, ChunkingConfigs, ChunkingContext,
31 ChunkingContextExt, EntryChunkGroupResult, MangleType, MinifyType, SourceMapSourceType,
32 SourceMapsType, UnusedReferences,
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, OutputAssetsReference},
47 reference::ModuleReference,
48 resolve::BindingUsage,
49};
50
51#[turbo_tasks::value(shared, operation)]
53#[derive(Debug, Clone, Hash, Ord, PartialOrd, DeterministicHash, Serialize)]
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<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, Clone, Debug, PartialEq, Eq, TraceRawVcs, NonLocalValue, TaskInput, Hash, Encode, Decode,
151)]
152pub enum MergeableModuleExposure {
153 None,
156 Internal,
159 External,
162}
163
164#[turbo_tasks::value(transparent)]
165pub struct MergeableModulesExposed(
166 Vec<(
167 ResolvedVc<Box<dyn MergeableModule>>,
168 MergeableModuleExposure,
169 )>,
170);
171
172#[turbo_tasks::value_impl]
173impl MergeableModulesExposed {
174 #[turbo_tasks::function]
175 pub fn interned(
176 modules: Vec<(
177 ResolvedVc<Box<dyn MergeableModule>>,
178 MergeableModuleExposure,
179 )>,
180 ) -> Vc<Self> {
181 Vc::cell(modules)
182 }
183}
184
185#[turbo_tasks::value(transparent)]
186pub struct Chunks(Vec<ResolvedVc<Box<dyn Chunk>>>);
187
188#[turbo_tasks::value_impl]
189impl Chunks {
190 #[turbo_tasks::function]
192 pub fn empty() -> Vc<Self> {
193 Vc::cell(vec![])
194 }
195}
196
197#[turbo_tasks::value_trait]
201pub trait Chunk: OutputAssetsReference {
202 #[turbo_tasks::function]
203 fn ident(self: Vc<Self>) -> Vc<AssetIdent>;
204 #[turbo_tasks::function]
205 fn chunking_context(self: Vc<Self>) -> Vc<Box<dyn ChunkingContext>>;
206 #[turbo_tasks::function]
211 fn chunk_items(self: Vc<Self>) -> Vc<ChunkItems> {
212 ChunkItems(vec![]).cell()
213 }
214}
215
216#[turbo_tasks::value(shared)]
219#[derive(Default)]
220pub struct OutputChunkRuntimeInfo {
221 pub included_ids: Option<ResolvedVc<ModuleIds>>,
222 pub excluded_ids: Option<ResolvedVc<ModuleIds>>,
223 pub module_chunks: Option<ResolvedVc<OutputAssets>>,
227 pub placeholder_for_future_extensions: (),
228}
229
230#[turbo_tasks::value_impl]
231impl OutputChunkRuntimeInfo {
232 #[turbo_tasks::function]
233 pub fn empty() -> Vc<Self> {
234 Self::default().cell()
235 }
236}
237
238#[turbo_tasks::value_trait]
239pub trait OutputChunk: Asset {
240 #[turbo_tasks::function]
241 fn runtime_info(self: Vc<Self>) -> Vc<OutputChunkRuntimeInfo>;
242}
243
244#[derive(
247 Debug,
248 Clone,
249 Hash,
250 TraceRawVcs,
251 Serialize,
252 Deserialize,
253 Eq,
254 PartialEq,
255 ValueDebugFormat,
256 NonLocalValue,
257 Encode,
258 Decode,
259)]
260pub enum ChunkingType {
261 Parallel {
263 inherit_async: bool,
266 hoisted: bool,
269 },
270 Async,
273 Isolated {
277 _ty: ChunkGroupType,
278 merge_tag: Option<RcStr>,
279 },
280 Shared {
284 inherit_async: bool,
285 merge_tag: Option<RcStr>,
286 },
287 Traced,
289}
290
291impl Display for ChunkingType {
292 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293 match self {
294 ChunkingType::Parallel {
295 inherit_async,
296 hoisted,
297 } => {
298 write!(
299 f,
300 "Parallel(inherit_async: {inherit_async}, hoisted: {hoisted})",
301 )
302 }
303 ChunkingType::Async => write!(f, "Async"),
304 ChunkingType::Isolated {
305 _ty,
306 merge_tag: Some(merge_tag),
307 } => {
308 write!(f, "Isolated(merge_tag: {merge_tag})")
309 }
310 ChunkingType::Isolated {
311 _ty,
312 merge_tag: None,
313 } => {
314 write!(f, "Isolated")
315 }
316 ChunkingType::Shared {
317 inherit_async,
318 merge_tag: Some(merge_tag),
319 } => {
320 write!(
321 f,
322 "Shared(inherit_async: {inherit_async}, merge_tag: {merge_tag})"
323 )
324 }
325 ChunkingType::Shared {
326 inherit_async,
327 merge_tag: None,
328 } => {
329 write!(f, "Shared(inherit_async: {inherit_async})")
330 }
331 ChunkingType::Traced => write!(f, "Traced"),
332 }
333 }
334}
335
336impl ChunkingType {
337 pub fn is_inherit_async(&self) -> bool {
338 matches!(
339 self,
340 ChunkingType::Parallel {
341 inherit_async: true,
342 ..
343 } | ChunkingType::Shared {
344 inherit_async: true,
345 ..
346 }
347 )
348 }
349
350 pub fn is_parallel(&self) -> bool {
351 matches!(self, ChunkingType::Parallel { .. })
352 }
353
354 pub fn is_merged(&self) -> bool {
355 matches!(
356 self,
357 ChunkingType::Isolated {
358 merge_tag: Some(_),
359 ..
360 } | ChunkingType::Shared {
361 merge_tag: Some(_),
362 ..
363 }
364 )
365 }
366
367 pub fn without_inherit_async(&self) -> Self {
368 match self {
369 ChunkingType::Parallel { hoisted, .. } => ChunkingType::Parallel {
370 hoisted: *hoisted,
371 inherit_async: false,
372 },
373 ChunkingType::Async => ChunkingType::Async,
374 ChunkingType::Isolated { _ty, merge_tag } => ChunkingType::Isolated {
375 _ty: *_ty,
376 merge_tag: merge_tag.clone(),
377 },
378 ChunkingType::Shared {
379 inherit_async: _,
380 merge_tag,
381 } => ChunkingType::Shared {
382 inherit_async: false,
383 merge_tag: merge_tag.clone(),
384 },
385 ChunkingType::Traced => ChunkingType::Traced,
386 }
387 }
388}
389
390#[turbo_tasks::value(transparent)]
391pub struct ChunkingTypeOption(Option<ChunkingType>);
392
393#[turbo_tasks::value_trait]
400pub trait ChunkableModuleReference: ModuleReference + ValueToString {
401 #[turbo_tasks::function]
402 fn chunking_type(self: Vc<Self>) -> Vc<ChunkingTypeOption> {
403 Vc::cell(Some(ChunkingType::Parallel {
404 inherit_async: false,
405 hoisted: false,
406 }))
407 }
408
409 #[turbo_tasks::function]
410 fn binding_usage(self: Vc<Self>) -> Vc<BindingUsage> {
411 BindingUsage::all()
412 }
413}
414
415pub struct ChunkGroupContent {
416 pub chunkable_items: Vec<ChunkableModuleOrBatch>,
417 pub batch_groups: Vec<ResolvedVc<ModuleBatchGroup>>,
418 pub async_modules: FxIndexSet<ResolvedVc<Box<dyn ChunkableModule>>>,
419 pub traced_modules: FxIndexSet<ResolvedVc<Box<dyn Module>>>,
420 pub availability_info: AvailabilityInfo,
421}
422
423#[turbo_tasks::value_trait]
424pub trait ChunkItem: OutputAssetsReference {
425 #[turbo_tasks::function]
429 fn asset_ident(self: Vc<Self>) -> Vc<AssetIdent>;
430
431 #[turbo_tasks::function]
436 fn content_ident(self: Vc<Self>) -> Vc<AssetIdent> {
437 self.asset_ident()
438 }
439
440 #[turbo_tasks::function]
442 fn ty(self: Vc<Self>) -> Vc<Box<dyn ChunkType>>;
443
444 #[turbo_tasks::function]
447 fn module(self: Vc<Self>) -> Vc<Box<dyn Module>>;
448
449 #[turbo_tasks::function]
450 fn chunking_context(self: Vc<Self>) -> Vc<Box<dyn ChunkingContext>>;
451}
452
453#[turbo_tasks::value_trait]
454pub trait ChunkType: ValueToString {
455 #[turbo_tasks::function]
457 fn is_style(self: Vc<Self>) -> Vc<bool>;
458
459 #[turbo_tasks::function]
461 fn chunk(
462 &self,
463 chunking_context: Vc<Box<dyn ChunkingContext>>,
464 chunk_items: Vec<ChunkItemOrBatchWithAsyncModuleInfo>,
465 batch_groups: Vec<ResolvedVc<ChunkItemBatchGroup>>,
466 ) -> Vc<Box<dyn Chunk>>;
467
468 #[turbo_tasks::function]
469 fn chunk_item_size(
470 &self,
471 chunking_context: Vc<Box<dyn ChunkingContext>>,
472 chunk_item: Vc<Box<dyn ChunkItem>>,
473 async_module_info: Option<Vc<AsyncModuleInfo>>,
474 ) -> Vc<usize>;
475}
476
477pub fn round_chunk_item_size(size: usize) -> usize {
478 let a = size.next_power_of_two();
479 size & (a | (a >> 1) | (a >> 2))
480}
481
482#[turbo_tasks::value(transparent)]
483pub struct ChunkItems(pub Vec<ResolvedVc<Box<dyn ChunkItem>>>);
484
485#[turbo_tasks::value]
486pub struct AsyncModuleInfo {
487 pub referenced_async_modules: AutoSet<ResolvedVc<Box<dyn Module>>>,
488}
489
490#[turbo_tasks::value_impl]
491impl AsyncModuleInfo {
492 #[turbo_tasks::function]
493 pub fn new(referenced_async_modules: Vec<ResolvedVc<Box<dyn Module>>>) -> Result<Vc<Self>> {
494 Ok(Self {
495 referenced_async_modules: referenced_async_modules.into_iter().collect(),
496 }
497 .cell())
498 }
499}
500
501#[derive(
502 Debug, Clone, PartialEq, Eq, Hash, TraceRawVcs, TaskInput, NonLocalValue, Encode, Decode,
503)]
504pub struct ChunkItemWithAsyncModuleInfo {
505 pub chunk_item: ResolvedVc<Box<dyn ChunkItem>>,
506 pub module: Option<ResolvedVc<Box<dyn ChunkableModule>>>,
507 pub async_info: Option<ResolvedVc<AsyncModuleInfo>>,
508}
509
510#[turbo_tasks::value(transparent)]
511pub struct ChunkItemsWithAsyncModuleInfo(Vec<ChunkItemWithAsyncModuleInfo>);
512
513pub trait ChunkItemExt {
514 fn id(self: Vc<Self>) -> impl Future<Output = Result<ModuleId>> + Send;
516}
517
518impl<T> ChunkItemExt for T
519where
520 T: Upcast<Box<dyn ChunkItem>> + Send,
521{
522 async fn id(self: Vc<Self>) -> Result<ModuleId> {
524 let chunk_item = Vc::upcast_non_strict(self);
525 chunk_item
526 .chunking_context()
527 .chunk_item_id_strategy()
528 .await?
529 .get_id(chunk_item)
530 .await
531 }
532}
533
534pub trait ModuleChunkItemIdExt {
535 fn chunk_item_id(
537 self: Vc<Self>,
538 chunking_context: Vc<Box<dyn ChunkingContext>>,
539 ) -> impl Future<Output = Result<ModuleId>> + Send;
540}
541impl<T> ModuleChunkItemIdExt for T
542where
543 T: Upcast<Box<dyn Module>> + Send,
544{
545 async fn chunk_item_id(
546 self: Vc<Self>,
547 chunking_context: Vc<Box<dyn ChunkingContext>>,
548 ) -> Result<ModuleId> {
549 chunking_context
550 .chunk_item_id_strategy()
551 .await?
552 .get_id_from_module(Vc::upcast_non_strict(self))
553 .await
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}