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