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, hash::Hash};
12
13use anyhow::{Result, bail};
14use auto_hash_map::AutoSet;
15use bincode::{Decode, Encode};
16use serde::{Deserialize, Serialize};
17use turbo_rcstr::RcStr;
18use turbo_tasks::{
19 FxIndexSet, NonLocalValue, ReadRef, 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, available_modules::AvailableModulesSet},
40 ident::AssetIdent,
41 module::Module,
42 module_graph::{
43 ModuleGraph,
44 module_batch::{ChunkableModuleOrBatch, ModuleBatchGroup},
45 },
46 output::{OutputAssets, OutputAssetsReference},
47};
48
49#[derive(
50 Debug,
51 TaskInput,
52 Clone,
53 Copy,
54 PartialEq,
55 Eq,
56 Hash,
57 TraceRawVcs,
58 DeterministicHash,
59 NonLocalValue,
60 Encode,
61 Decode,
62)]
63pub enum ContentHashing {
64 Direct {
68 length: u8,
71 },
72}
73
74#[turbo_tasks::value(shared)]
75#[derive(Debug, Default, Clone, Copy, Hash, Serialize, Deserialize, TaskInput)]
76#[serde(rename_all = "kebab-case")]
77pub enum CrossOrigin {
78 #[default]
79 None,
80 Anonymous,
81 UseCredentials,
82}
83
84impl CrossOrigin {
85 pub fn as_str(self) -> Option<&'static str> {
86 match self {
87 Self::None => None,
88 Self::Anonymous => Some("anonymous"),
89 Self::UseCredentials => Some("use-credentials"),
90 }
91 }
92}
93
94impl TryFrom<Option<&str>> for CrossOrigin {
95 type Error = anyhow::Error;
96
97 fn try_from(value: Option<&str>) -> Result<Self> {
98 match value {
99 None => Ok(Self::None),
100 Some("anonymous") => Ok(Self::Anonymous),
101 Some("use-credentials") => Ok(Self::UseCredentials),
102 Some(value) => bail!(
103 "invalid crossOrigin value `{value}`; supported values are `anonymous` and \
104 `use-credentials`"
105 ),
106 }
107 }
108}
109
110#[turbo_tasks::value(shared, operation)]
112#[derive(Debug, Clone, Hash, Ord, PartialOrd, DeterministicHash, Serialize, ValueToString)]
113#[serde(untagged)]
114pub enum ModuleId {
115 Number(u64),
116 String(RcStr),
117}
118
119impl Display for ModuleId {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 match self {
122 ModuleId::Number(i) => write!(f, "{i}"),
123 ModuleId::String(s) => write!(f, "{s}"),
124 }
125 }
126}
127
128impl ModuleId {
129 pub fn parse(id: &str) -> Result<ModuleId> {
130 Ok(match id.parse::<u64>() {
131 Ok(i) => ModuleId::Number(i),
132 Err(_) => ModuleId::String(id.into()),
133 })
134 }
135}
136
137#[turbo_tasks::value(transparent, shared)]
139pub struct ModuleIds(Vec<ModuleId>);
140
141#[turbo_tasks::value_trait]
143pub trait ChunkableModule: Module {
144 #[turbo_tasks::function]
145 fn as_chunk_item(
146 self: Vc<Self>,
147 module_graph: Vc<ModuleGraph>,
148 chunking_context: Vc<Box<dyn ChunkingContext>>,
149 ) -> Vc<Box<dyn ChunkItem>>;
150}
151
152#[turbo_tasks::value_trait]
157pub trait MergeableModule: Module {
158 #[turbo_tasks::function]
161 fn is_mergeable(self: Vc<Self>) -> Vc<bool> {
162 Vc::cell(true)
163 }
164
165 #[turbo_tasks::function]
171 fn merge(
172 self: Vc<Self>,
173 modules: Vc<MergeableModulesExposed>,
174 entry_points: Vc<MergeableModules>,
175 ) -> Vc<Box<dyn ChunkableModule>>;
176}
177#[turbo_tasks::value(transparent)]
178pub struct MergeableModules(Vec<ResolvedVc<Box<dyn MergeableModule>>>);
179
180#[turbo_tasks::value_impl]
181impl MergeableModules {
182 #[turbo_tasks::function]
183 pub fn interned(modules: Vec<ResolvedVc<Box<dyn MergeableModule>>>) -> Vc<Self> {
184 Vc::cell(modules)
185 }
186}
187
188#[derive(
190 Copy, Clone, Debug, PartialEq, Eq, TraceRawVcs, NonLocalValue, TaskInput, Hash, Encode, Decode,
191)]
192pub enum MergeableModuleExposure {
193 None,
196 Internal,
199 External,
202}
203
204#[turbo_tasks::value(transparent)]
205pub struct MergeableModulesExposed(
206 Vec<(
207 ResolvedVc<Box<dyn MergeableModule>>,
208 MergeableModuleExposure,
209 )>,
210);
211
212#[turbo_tasks::value_impl]
213impl MergeableModulesExposed {
214 #[turbo_tasks::function]
215 pub fn interned(
216 modules: Vec<(
217 ResolvedVc<Box<dyn MergeableModule>>,
218 MergeableModuleExposure,
219 )>,
220 ) -> Vc<Self> {
221 Vc::cell(modules)
222 }
223}
224
225#[turbo_tasks::value(transparent)]
226pub struct Chunks(Vec<ResolvedVc<Box<dyn Chunk>>>);
227
228#[turbo_tasks::value_impl]
229impl Chunks {
230 #[turbo_tasks::function]
231 pub fn empty() -> Vc<Self> {
232 Vc::cell(vec![])
233 }
234}
235
236#[turbo_tasks::value_trait]
243pub trait Chunk: OutputAssetsReference {
244 #[turbo_tasks::function]
245 fn ident(self: Vc<Self>) -> Vc<AssetIdent>;
246
247 #[turbo_tasks::function]
248 fn chunking_context(self: Vc<Self>) -> Vc<Box<dyn ChunkingContext>>;
249
250 #[turbo_tasks::function]
251 fn chunk_items(self: Vc<Self>) -> Vc<ChunkItems> {
252 ChunkItems(vec![]).cell()
253 }
254}
255
256#[turbo_tasks::value(shared)]
259#[derive(Default)]
260pub struct OutputChunkRuntimeInfo {
261 pub included_ids: Option<ResolvedVc<ModuleIds>>,
262 pub excluded_ids: Option<ResolvedVc<ModuleIds>>,
263 pub module_chunks: Option<ResolvedVc<OutputAssets>>,
267 pub placeholder_for_future_extensions: (),
268}
269
270#[turbo_tasks::value_impl]
271impl OutputChunkRuntimeInfo {
272 #[turbo_tasks::function]
273 pub fn empty() -> Vc<Self> {
274 Self::default().cell()
275 }
276}
277
278#[turbo_tasks::value_trait]
279pub trait OutputChunk: Asset {
280 #[turbo_tasks::function]
281 fn runtime_info(self: Vc<Self>) -> Vc<OutputChunkRuntimeInfo>;
282}
283
284#[derive(
286 Debug,
287 Clone,
288 Copy,
289 Hash,
290 TraceRawVcs,
291 Serialize,
292 Deserialize,
293 Eq,
294 PartialEq,
295 ValueDebugFormat,
296 NonLocalValue,
297 Encode,
298 Decode,
299 TaskInput,
300)]
301pub enum TracedMode {
302 Entry,
304 Transitive,
307}
308
309impl Display for TracedMode {
310 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
311 match self {
312 TracedMode::Entry => write!(f, "Entry"),
313 TracedMode::Transitive => write!(f, "Transitive"),
314 }
315 }
316}
317
318#[derive(
321 Debug,
322 Clone,
323 Hash,
324 TraceRawVcs,
325 Serialize,
326 Deserialize,
327 Eq,
328 PartialEq,
329 ValueDebugFormat,
330 NonLocalValue,
331 Encode,
332 Decode,
333)]
334pub enum ChunkingType {
335 Parallel {
337 inherit_async: bool,
340 hoisted: bool,
343 },
344 Async,
347 Isolated {
351 _ty: ChunkGroupType,
352 merge_tag: Option<RcStr>,
353 },
354 Shared {
358 inherit_async: bool,
359 merge_tag: Option<RcStr>,
360 },
361 Traced {
365 mode: TracedMode,
367 },
368}
369
370impl Display for ChunkingType {
371 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
372 match self {
373 ChunkingType::Parallel {
374 inherit_async,
375 hoisted,
376 } => {
377 write!(
378 f,
379 "Parallel(inherit_async: {inherit_async}, hoisted: {hoisted})",
380 )
381 }
382 ChunkingType::Async => write!(f, "Async"),
383 ChunkingType::Isolated {
384 _ty,
385 merge_tag: Some(merge_tag),
386 } => {
387 write!(f, "Isolated(merge_tag: {merge_tag})")
388 }
389 ChunkingType::Isolated {
390 _ty,
391 merge_tag: None,
392 } => {
393 write!(f, "Isolated")
394 }
395 ChunkingType::Shared {
396 inherit_async,
397 merge_tag: Some(merge_tag),
398 } => {
399 write!(
400 f,
401 "Shared(inherit_async: {inherit_async}, merge_tag: {merge_tag})"
402 )
403 }
404 ChunkingType::Shared {
405 inherit_async,
406 merge_tag: None,
407 } => {
408 write!(f, "Shared(inherit_async: {inherit_async})")
409 }
410 ChunkingType::Traced { mode } => write!(f, "Traced(mode: {mode})"),
411 }
412 }
413}
414
415impl ChunkingType {
416 pub fn is_inherit_async(&self) -> bool {
417 matches!(
418 self,
419 ChunkingType::Parallel {
420 inherit_async: true,
421 ..
422 } | ChunkingType::Shared {
423 inherit_async: true,
424 ..
425 }
426 )
427 }
428
429 pub fn is_parallel(&self) -> bool {
430 matches!(self, ChunkingType::Parallel { .. })
431 }
432
433 pub fn is_traced(&self) -> bool {
434 matches!(self, ChunkingType::Traced { .. })
435 }
436
437 pub fn is_merged(&self) -> bool {
438 matches!(
439 self,
440 ChunkingType::Isolated {
441 merge_tag: Some(_),
442 ..
443 } | ChunkingType::Shared {
444 merge_tag: Some(_),
445 ..
446 }
447 )
448 }
449
450 pub fn without_inherit_async(&self) -> Self {
451 match self {
452 ChunkingType::Parallel { hoisted, .. } => ChunkingType::Parallel {
453 hoisted: *hoisted,
454 inherit_async: false,
455 },
456 ChunkingType::Async => ChunkingType::Async,
457 ChunkingType::Isolated { _ty, merge_tag } => ChunkingType::Isolated {
458 _ty: *_ty,
459 merge_tag: merge_tag.clone(),
460 },
461 ChunkingType::Shared {
462 inherit_async: _,
463 merge_tag,
464 } => ChunkingType::Shared {
465 inherit_async: false,
466 merge_tag: merge_tag.clone(),
467 },
468 ChunkingType::Traced { mode } => ChunkingType::Traced { mode: *mode },
469 }
470 }
471}
472
473#[turbo_tasks::value(cell = "new")]
474pub struct ChunkGroupContentInner {
475 pub chunkable_items: Vec<ChunkableModuleOrBatch>,
476 pub batch_groups: Vec<ResolvedVc<ModuleBatchGroup>>,
477 #[bincode(with = "turbo_bincode::indexset")]
478 pub async_modules: FxIndexSet<ResolvedVc<Box<dyn ChunkableModule>>>,
479 pub available_modules: ResolvedVc<AvailableModulesSet>,
480}
481
482pub struct ChunkGroupContent {
483 pub inner: ReadRef<ChunkGroupContentInner>,
484 pub availability_info: AvailabilityInfo,
485}
486
487#[turbo_tasks::value_trait]
488pub trait ChunkItem: OutputAssetsReference {
489 #[turbo_tasks::function]
493 fn asset_ident(self: Vc<Self>) -> Vc<AssetIdent>;
494
495 #[turbo_tasks::function]
500 fn content_ident(self: Vc<Self>) -> Vc<AssetIdent> {
501 self.asset_ident()
502 }
503
504 fn ty(&self) -> Vc<Box<dyn ChunkType>>;
506
507 #[turbo_tasks::function]
510 fn module(self: Vc<Self>) -> Vc<Box<dyn Module>>;
511
512 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>>;
513}
514
515#[turbo_tasks::value_trait]
516pub trait ChunkType: ValueToString {
517 #[turbo_tasks::function]
519 fn is_style(self: Vc<Self>) -> Vc<bool>;
520
521 #[turbo_tasks::function]
523 fn chunk(
524 &self,
525 chunking_context: Vc<Box<dyn ChunkingContext>>,
526 chunk_items: Vec<ChunkItemOrBatchWithAsyncModuleInfo>,
527 batch_groups: Vec<ResolvedVc<ChunkItemBatchGroup>>,
528 ) -> Vc<Box<dyn Chunk>>;
529
530 #[turbo_tasks::function]
531 fn chunk_item_size(
532 &self,
533 chunking_context: Vc<Box<dyn ChunkingContext>>,
534 chunk_item: Vc<Box<dyn ChunkItem>>,
535 async_module_info: Option<Vc<AsyncModuleInfo>>,
536 ) -> Vc<usize>;
537}
538
539pub fn round_chunk_item_size(size: usize) -> usize {
540 let a = size.next_power_of_two();
541 size & (a | (a >> 1) | (a >> 2))
542}
543
544#[turbo_tasks::value(transparent)]
545pub struct ChunkItems(pub Vec<ResolvedVc<Box<dyn ChunkItem>>>);
546
547#[turbo_tasks::value]
548pub struct AsyncModuleInfo {
549 pub referenced_async_modules: AutoSet<ResolvedVc<Box<dyn Module>>>,
550}
551
552#[turbo_tasks::value_impl]
553impl AsyncModuleInfo {
554 #[turbo_tasks::function]
555 pub fn new(referenced_async_modules: Vec<ResolvedVc<Box<dyn Module>>>) -> Result<Vc<Self>> {
556 Ok(Self {
557 referenced_async_modules: referenced_async_modules.into_iter().collect(),
558 }
559 .cell())
560 }
561}
562
563#[derive(
564 Debug, Clone, Copy, PartialEq, Eq, Hash, TraceRawVcs, TaskInput, NonLocalValue, Encode, Decode,
565)]
566pub struct ChunkItemWithAsyncModuleInfo {
567 pub chunk_item: ResolvedVc<Box<dyn ChunkItem>>,
568 pub chunk_type: ResolvedVc<Box<dyn ChunkType>>,
569 pub module: Option<ResolvedVc<Box<dyn ChunkableModule>>>,
570 pub async_info: Option<ResolvedVc<AsyncModuleInfo>>,
571}
572
573pub trait ChunkItemExt {
574 fn id(self: Vc<Self>) -> impl Future<Output = Result<ModuleId>> + Send;
576}
577
578impl<T> ChunkItemExt for T
579where
580 T: Upcast<Box<dyn ChunkItem>> + Send,
581{
582 async fn id(self: Vc<Self>) -> Result<ModuleId> {
584 let chunk_item = Vc::upcast_non_strict(self);
585 chunk_item
586 .into_trait_ref()
587 .await?
588 .chunking_context()
589 .chunk_item_id_strategy()
590 .await?
591 .get_id(chunk_item)
592 .await
593 }
594}
595
596pub trait ModuleChunkItemIdExt {
597 fn chunk_item_id(
599 self: Vc<Self>,
600 chunking_context: Vc<Box<dyn ChunkingContext>>,
601 ) -> impl Future<Output = Result<ModuleId>> + Send;
602}
603impl<T> ModuleChunkItemIdExt for T
604where
605 T: Upcast<Box<dyn Module>> + Send,
606{
607 async fn chunk_item_id(
608 self: Vc<Self>,
609 chunking_context: Vc<Box<dyn ChunkingContext>>,
610 ) -> Result<ModuleId> {
611 chunking_context
612 .chunk_item_id_strategy()
613 .await?
614 .get_id_from_module(Vc::upcast_non_strict(self))
615 .await
616 }
617}
618
619#[cfg(test)]
620mod tests {
621 use super::*;
622
623 #[test]
624 fn test_round_chunk_item_size() {
625 assert_eq!(round_chunk_item_size(0), 0);
626 assert_eq!(round_chunk_item_size(1), 1);
627 assert_eq!(round_chunk_item_size(2), 2);
628 assert_eq!(round_chunk_item_size(3), 3);
629 assert_eq!(round_chunk_item_size(4), 4);
630 assert_eq!(round_chunk_item_size(5), 4);
631 assert_eq!(round_chunk_item_size(6), 6);
632 assert_eq!(round_chunk_item_size(7), 6);
633 assert_eq!(round_chunk_item_size(8), 8);
634 assert_eq!(round_chunk_item_size(49000), 32_768);
635 assert_eq!(round_chunk_item_size(50000), 49_152);
636
637 assert_eq!(changes_in_range(0..1000), 19);
638 assert_eq!(changes_in_range(1000..2000), 2);
639 assert_eq!(changes_in_range(2000..3000), 1);
640
641 assert_eq!(changes_in_range(3000..10000), 4);
642
643 fn changes_in_range(range: std::ops::Range<usize>) -> usize {
644 let len = range.len();
645 let mut count = 0;
646 for i in range {
647 let a = round_chunk_item_size(i);
648 assert!(a >= i * 2 / 3);
649 assert!(a <= i);
650 let b = round_chunk_item_size(i + 1);
651
652 if a == b {
653 count += 1;
654 }
655 }
656 len - count
657 }
658 }
659}