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 AssetSuffix, ChunkGroupResult, ChunkGroupType, ChunkingConfig, ChunkingConfigs,
31 ChunkingContext, ChunkingContextExt, EntryChunkGroupResult, MangleType, MinifyType,
32 SourceMapSourceType, SourceMapsType, UnusedReferences, UrlBehavior,
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};
48
49#[turbo_tasks::value(shared, operation)]
51#[derive(Debug, Clone, Hash, Ord, PartialOrd, DeterministicHash, Serialize, ValueToString)]
52#[serde(untagged)]
53pub enum ModuleId {
54 Number(u64),
55 String(RcStr),
56}
57
58impl Display for ModuleId {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 match self {
61 ModuleId::Number(i) => write!(f, "{i}"),
62 ModuleId::String(s) => write!(f, "{s}"),
63 }
64 }
65}
66
67impl ModuleId {
68 pub fn parse(id: &str) -> Result<ModuleId> {
69 Ok(match id.parse::<u64>() {
70 Ok(i) => ModuleId::Number(i),
71 Err(_) => ModuleId::String(id.into()),
72 })
73 }
74}
75
76#[turbo_tasks::value(transparent, shared)]
78pub struct ModuleIds(Vec<ModuleId>);
79
80#[turbo_tasks::value_trait]
82pub trait ChunkableModule: Module {
83 #[turbo_tasks::function]
84 fn as_chunk_item(
85 self: Vc<Self>,
86 module_graph: Vc<ModuleGraph>,
87 chunking_context: Vc<Box<dyn ChunkingContext>>,
88 ) -> Vc<Box<dyn ChunkItem>>;
89}
90
91#[turbo_tasks::value(transparent)]
92pub struct ChunkableModules(Vec<ResolvedVc<Box<dyn ChunkableModule>>>);
93
94#[turbo_tasks::value_impl]
95impl ChunkableModules {
96 #[turbo_tasks::function]
97 pub fn interned(modules: Vec<ResolvedVc<Box<dyn ChunkableModule>>>) -> Vc<Self> {
98 Vc::cell(modules)
99 }
100}
101
102#[turbo_tasks::value_trait]
107pub trait MergeableModule: Module {
108 #[turbo_tasks::function]
111 fn is_mergeable(self: Vc<Self>) -> Vc<bool> {
112 Vc::cell(true)
113 }
114
115 #[turbo_tasks::function]
121 fn merge(
122 self: Vc<Self>,
123 modules: Vc<MergeableModulesExposed>,
124 entry_points: Vc<MergeableModules>,
125 ) -> Vc<Box<dyn ChunkableModule>>;
126}
127#[turbo_tasks::value(transparent)]
128pub struct MergeableModules(Vec<ResolvedVc<Box<dyn MergeableModule>>>);
129
130#[turbo_tasks::value_impl]
131impl MergeableModules {
132 #[turbo_tasks::function]
133 pub fn interned(modules: Vec<ResolvedVc<Box<dyn MergeableModule>>>) -> Vc<Self> {
134 Vc::cell(modules)
135 }
136}
137
138#[derive(
140 Copy, Clone, Debug, PartialEq, Eq, TraceRawVcs, NonLocalValue, TaskInput, Hash, Encode, Decode,
141)]
142pub enum MergeableModuleExposure {
143 None,
146 Internal,
149 External,
152}
153
154#[turbo_tasks::value(transparent)]
155pub struct MergeableModulesExposed(
156 Vec<(
157 ResolvedVc<Box<dyn MergeableModule>>,
158 MergeableModuleExposure,
159 )>,
160);
161
162#[turbo_tasks::value_impl]
163impl MergeableModulesExposed {
164 #[turbo_tasks::function]
165 pub fn interned(
166 modules: Vec<(
167 ResolvedVc<Box<dyn MergeableModule>>,
168 MergeableModuleExposure,
169 )>,
170 ) -> Vc<Self> {
171 Vc::cell(modules)
172 }
173}
174
175#[turbo_tasks::value(transparent)]
176pub struct Chunks(Vec<ResolvedVc<Box<dyn Chunk>>>);
177
178#[turbo_tasks::value_impl]
179impl Chunks {
180 #[turbo_tasks::function]
182 pub fn empty() -> Vc<Self> {
183 Vc::cell(vec![])
184 }
185}
186
187#[turbo_tasks::value_trait]
191pub trait Chunk: OutputAssetsReference {
192 #[turbo_tasks::function]
193 fn ident(self: Vc<Self>) -> Vc<AssetIdent>;
194 #[turbo_tasks::function]
195 fn chunking_context(self: Vc<Self>) -> Vc<Box<dyn ChunkingContext>>;
196 #[turbo_tasks::function]
201 fn chunk_items(self: Vc<Self>) -> Vc<ChunkItems> {
202 ChunkItems(vec![]).cell()
203 }
204}
205
206#[turbo_tasks::value(shared)]
209#[derive(Default)]
210pub struct OutputChunkRuntimeInfo {
211 pub included_ids: Option<ResolvedVc<ModuleIds>>,
212 pub excluded_ids: Option<ResolvedVc<ModuleIds>>,
213 pub module_chunks: Option<ResolvedVc<OutputAssets>>,
217 pub placeholder_for_future_extensions: (),
218}
219
220#[turbo_tasks::value_impl]
221impl OutputChunkRuntimeInfo {
222 #[turbo_tasks::function]
223 pub fn empty() -> Vc<Self> {
224 Self::default().cell()
225 }
226}
227
228#[turbo_tasks::value_trait]
229pub trait OutputChunk: Asset {
230 #[turbo_tasks::function]
231 fn runtime_info(self: Vc<Self>) -> Vc<OutputChunkRuntimeInfo>;
232}
233
234#[derive(
237 Debug,
238 Clone,
239 Hash,
240 TraceRawVcs,
241 Serialize,
242 Deserialize,
243 Eq,
244 PartialEq,
245 ValueDebugFormat,
246 NonLocalValue,
247 Encode,
248 Decode,
249)]
250pub enum ChunkingType {
251 Parallel {
253 inherit_async: bool,
256 hoisted: bool,
259 },
260 Async,
263 Isolated {
267 _ty: ChunkGroupType,
268 merge_tag: Option<RcStr>,
269 },
270 Shared {
274 inherit_async: bool,
275 merge_tag: Option<RcStr>,
276 },
277 Traced,
279}
280
281impl Display for ChunkingType {
282 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
283 match self {
284 ChunkingType::Parallel {
285 inherit_async,
286 hoisted,
287 } => {
288 write!(
289 f,
290 "Parallel(inherit_async: {inherit_async}, hoisted: {hoisted})",
291 )
292 }
293 ChunkingType::Async => write!(f, "Async"),
294 ChunkingType::Isolated {
295 _ty,
296 merge_tag: Some(merge_tag),
297 } => {
298 write!(f, "Isolated(merge_tag: {merge_tag})")
299 }
300 ChunkingType::Isolated {
301 _ty,
302 merge_tag: None,
303 } => {
304 write!(f, "Isolated")
305 }
306 ChunkingType::Shared {
307 inherit_async,
308 merge_tag: Some(merge_tag),
309 } => {
310 write!(
311 f,
312 "Shared(inherit_async: {inherit_async}, merge_tag: {merge_tag})"
313 )
314 }
315 ChunkingType::Shared {
316 inherit_async,
317 merge_tag: None,
318 } => {
319 write!(f, "Shared(inherit_async: {inherit_async})")
320 }
321 ChunkingType::Traced => write!(f, "Traced"),
322 }
323 }
324}
325
326impl ChunkingType {
327 pub fn is_inherit_async(&self) -> bool {
328 matches!(
329 self,
330 ChunkingType::Parallel {
331 inherit_async: true,
332 ..
333 } | ChunkingType::Shared {
334 inherit_async: true,
335 ..
336 }
337 )
338 }
339
340 pub fn is_parallel(&self) -> bool {
341 matches!(self, ChunkingType::Parallel { .. })
342 }
343
344 pub fn is_merged(&self) -> bool {
345 matches!(
346 self,
347 ChunkingType::Isolated {
348 merge_tag: Some(_),
349 ..
350 } | ChunkingType::Shared {
351 merge_tag: Some(_),
352 ..
353 }
354 )
355 }
356
357 pub fn without_inherit_async(&self) -> Self {
358 match self {
359 ChunkingType::Parallel { hoisted, .. } => ChunkingType::Parallel {
360 hoisted: *hoisted,
361 inherit_async: false,
362 },
363 ChunkingType::Async => ChunkingType::Async,
364 ChunkingType::Isolated { _ty, merge_tag } => ChunkingType::Isolated {
365 _ty: *_ty,
366 merge_tag: merge_tag.clone(),
367 },
368 ChunkingType::Shared {
369 inherit_async: _,
370 merge_tag,
371 } => ChunkingType::Shared {
372 inherit_async: false,
373 merge_tag: merge_tag.clone(),
374 },
375 ChunkingType::Traced => ChunkingType::Traced,
376 }
377 }
378}
379
380#[turbo_tasks::value(transparent)]
381pub struct ChunkingTypeOption(Option<ChunkingType>);
382
383pub struct ChunkGroupContent {
384 pub chunkable_items: Vec<ChunkableModuleOrBatch>,
385 pub batch_groups: Vec<ResolvedVc<ModuleBatchGroup>>,
386 pub async_modules: FxIndexSet<ResolvedVc<Box<dyn ChunkableModule>>>,
387 pub traced_modules: FxIndexSet<ResolvedVc<Box<dyn Module>>>,
388 pub availability_info: AvailabilityInfo,
389}
390
391#[turbo_tasks::value_trait]
392pub trait ChunkItem: OutputAssetsReference {
393 #[turbo_tasks::function]
397 fn asset_ident(self: Vc<Self>) -> Vc<AssetIdent>;
398
399 #[turbo_tasks::function]
404 fn content_ident(self: Vc<Self>) -> Vc<AssetIdent> {
405 self.asset_ident()
406 }
407
408 #[turbo_tasks::function]
410 fn ty(self: Vc<Self>) -> Vc<Box<dyn ChunkType>>;
411
412 #[turbo_tasks::function]
415 fn module(self: Vc<Self>) -> Vc<Box<dyn Module>>;
416
417 #[turbo_tasks::function]
418 fn chunking_context(self: Vc<Self>) -> Vc<Box<dyn ChunkingContext>>;
419}
420
421#[turbo_tasks::value_trait]
422pub trait ChunkType: ValueToString {
423 #[turbo_tasks::function]
425 fn is_style(self: Vc<Self>) -> Vc<bool>;
426
427 #[turbo_tasks::function]
429 fn chunk(
430 &self,
431 chunking_context: Vc<Box<dyn ChunkingContext>>,
432 chunk_items: Vec<ChunkItemOrBatchWithAsyncModuleInfo>,
433 batch_groups: Vec<ResolvedVc<ChunkItemBatchGroup>>,
434 ) -> Vc<Box<dyn Chunk>>;
435
436 #[turbo_tasks::function]
437 fn chunk_item_size(
438 &self,
439 chunking_context: Vc<Box<dyn ChunkingContext>>,
440 chunk_item: Vc<Box<dyn ChunkItem>>,
441 async_module_info: Option<Vc<AsyncModuleInfo>>,
442 ) -> Vc<usize>;
443}
444
445pub fn round_chunk_item_size(size: usize) -> usize {
446 let a = size.next_power_of_two();
447 size & (a | (a >> 1) | (a >> 2))
448}
449
450#[turbo_tasks::value(transparent)]
451pub struct ChunkItems(pub Vec<ResolvedVc<Box<dyn ChunkItem>>>);
452
453#[turbo_tasks::value]
454pub struct AsyncModuleInfo {
455 pub referenced_async_modules: AutoSet<ResolvedVc<Box<dyn Module>>>,
456}
457
458#[turbo_tasks::value_impl]
459impl AsyncModuleInfo {
460 #[turbo_tasks::function]
461 pub fn new(referenced_async_modules: Vec<ResolvedVc<Box<dyn Module>>>) -> Result<Vc<Self>> {
462 Ok(Self {
463 referenced_async_modules: referenced_async_modules.into_iter().collect(),
464 }
465 .cell())
466 }
467}
468
469#[derive(
470 Debug, Clone, PartialEq, Eq, Hash, TraceRawVcs, TaskInput, NonLocalValue, Encode, Decode,
471)]
472pub struct ChunkItemWithAsyncModuleInfo {
473 pub chunk_item: ResolvedVc<Box<dyn ChunkItem>>,
474 pub module: Option<ResolvedVc<Box<dyn ChunkableModule>>>,
475 pub async_info: Option<ResolvedVc<AsyncModuleInfo>>,
476}
477
478#[turbo_tasks::value(transparent)]
479pub struct ChunkItemsWithAsyncModuleInfo(Vec<ChunkItemWithAsyncModuleInfo>);
480
481pub trait ChunkItemExt {
482 fn id(self: Vc<Self>) -> impl Future<Output = Result<ModuleId>> + Send;
484}
485
486impl<T> ChunkItemExt for T
487where
488 T: Upcast<Box<dyn ChunkItem>> + Send,
489{
490 async fn id(self: Vc<Self>) -> Result<ModuleId> {
492 let chunk_item = Vc::upcast_non_strict(self);
493 chunk_item
494 .chunking_context()
495 .chunk_item_id_strategy()
496 .await?
497 .get_id(chunk_item)
498 .await
499 }
500}
501
502pub trait ModuleChunkItemIdExt {
503 fn chunk_item_id(
505 self: Vc<Self>,
506 chunking_context: Vc<Box<dyn ChunkingContext>>,
507 ) -> impl Future<Output = Result<ModuleId>> + Send;
508}
509impl<T> ModuleChunkItemIdExt for T
510where
511 T: Upcast<Box<dyn Module>> + Send,
512{
513 async fn chunk_item_id(
514 self: Vc<Self>,
515 chunking_context: Vc<Box<dyn ChunkingContext>>,
516 ) -> Result<ModuleId> {
517 chunking_context
518 .chunk_item_id_strategy()
519 .await?
520 .get_id_from_module(Vc::upcast_non_strict(self))
521 .await
522 }
523}
524
525#[cfg(test)]
526mod tests {
527 use super::*;
528
529 #[test]
530 fn test_round_chunk_item_size() {
531 assert_eq!(round_chunk_item_size(0), 0);
532 assert_eq!(round_chunk_item_size(1), 1);
533 assert_eq!(round_chunk_item_size(2), 2);
534 assert_eq!(round_chunk_item_size(3), 3);
535 assert_eq!(round_chunk_item_size(4), 4);
536 assert_eq!(round_chunk_item_size(5), 4);
537 assert_eq!(round_chunk_item_size(6), 6);
538 assert_eq!(round_chunk_item_size(7), 6);
539 assert_eq!(round_chunk_item_size(8), 8);
540 assert_eq!(round_chunk_item_size(49000), 32_768);
541 assert_eq!(round_chunk_item_size(50000), 49_152);
542
543 assert_eq!(changes_in_range(0..1000), 19);
544 assert_eq!(changes_in_range(1000..2000), 2);
545 assert_eq!(changes_in_range(2000..3000), 1);
546
547 assert_eq!(changes_in_range(3000..10000), 4);
548
549 fn changes_in_range(range: std::ops::Range<usize>) -> usize {
550 let len = range.len();
551 let mut count = 0;
552 for i in range {
553 let a = round_chunk_item_size(i);
554 assert!(a >= i * 2 / 3);
555 assert!(a <= i);
556 let b = round_chunk_item_size(i + 1);
557
558 if a == b {
559 count += 1;
560 }
561 }
562 len - count
563 }
564 }
565}