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(
287 Debug,
288 Clone,
289 Hash,
290 TraceRawVcs,
291 Serialize,
292 Deserialize,
293 Eq,
294 PartialEq,
295 ValueDebugFormat,
296 NonLocalValue,
297 Encode,
298 Decode,
299)]
300pub enum ChunkingType {
301 Parallel {
303 inherit_async: bool,
306 hoisted: bool,
309 },
310 Async,
313 Isolated {
317 _ty: ChunkGroupType,
318 merge_tag: Option<RcStr>,
319 },
320 Shared {
324 inherit_async: bool,
325 merge_tag: Option<RcStr>,
326 },
327 Traced,
329}
330
331impl Display for ChunkingType {
332 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
333 match self {
334 ChunkingType::Parallel {
335 inherit_async,
336 hoisted,
337 } => {
338 write!(
339 f,
340 "Parallel(inherit_async: {inherit_async}, hoisted: {hoisted})",
341 )
342 }
343 ChunkingType::Async => write!(f, "Async"),
344 ChunkingType::Isolated {
345 _ty,
346 merge_tag: Some(merge_tag),
347 } => {
348 write!(f, "Isolated(merge_tag: {merge_tag})")
349 }
350 ChunkingType::Isolated {
351 _ty,
352 merge_tag: None,
353 } => {
354 write!(f, "Isolated")
355 }
356 ChunkingType::Shared {
357 inherit_async,
358 merge_tag: Some(merge_tag),
359 } => {
360 write!(
361 f,
362 "Shared(inherit_async: {inherit_async}, merge_tag: {merge_tag})"
363 )
364 }
365 ChunkingType::Shared {
366 inherit_async,
367 merge_tag: None,
368 } => {
369 write!(f, "Shared(inherit_async: {inherit_async})")
370 }
371 ChunkingType::Traced => write!(f, "Traced"),
372 }
373 }
374}
375
376impl ChunkingType {
377 pub fn is_inherit_async(&self) -> bool {
378 matches!(
379 self,
380 ChunkingType::Parallel {
381 inherit_async: true,
382 ..
383 } | ChunkingType::Shared {
384 inherit_async: true,
385 ..
386 }
387 )
388 }
389
390 pub fn is_parallel(&self) -> bool {
391 matches!(self, ChunkingType::Parallel { .. })
392 }
393
394 pub fn is_merged(&self) -> bool {
395 matches!(
396 self,
397 ChunkingType::Isolated {
398 merge_tag: Some(_),
399 ..
400 } | ChunkingType::Shared {
401 merge_tag: Some(_),
402 ..
403 }
404 )
405 }
406
407 pub fn without_inherit_async(&self) -> Self {
408 match self {
409 ChunkingType::Parallel { hoisted, .. } => ChunkingType::Parallel {
410 hoisted: *hoisted,
411 inherit_async: false,
412 },
413 ChunkingType::Async => ChunkingType::Async,
414 ChunkingType::Isolated { _ty, merge_tag } => ChunkingType::Isolated {
415 _ty: *_ty,
416 merge_tag: merge_tag.clone(),
417 },
418 ChunkingType::Shared {
419 inherit_async: _,
420 merge_tag,
421 } => ChunkingType::Shared {
422 inherit_async: false,
423 merge_tag: merge_tag.clone(),
424 },
425 ChunkingType::Traced => ChunkingType::Traced,
426 }
427 }
428}
429
430#[turbo_tasks::value(cell = "new")]
431pub struct ChunkGroupContentInner {
432 pub chunkable_items: Vec<ChunkableModuleOrBatch>,
433 pub batch_groups: Vec<ResolvedVc<ModuleBatchGroup>>,
434 #[bincode(with = "turbo_bincode::indexset")]
435 pub async_modules: FxIndexSet<ResolvedVc<Box<dyn ChunkableModule>>>,
436 #[bincode(with = "turbo_bincode::indexset")]
437 pub traced_modules: FxIndexSet<ResolvedVc<Box<dyn Module>>>,
438 pub available_modules: ResolvedVc<AvailableModulesSet>,
439}
440
441pub struct ChunkGroupContent {
442 pub inner: ReadRef<ChunkGroupContentInner>,
443 pub availability_info: AvailabilityInfo,
444}
445
446#[turbo_tasks::value_trait]
447pub trait ChunkItem: OutputAssetsReference {
448 #[turbo_tasks::function]
452 fn asset_ident(self: Vc<Self>) -> Vc<AssetIdent>;
453
454 #[turbo_tasks::function]
459 fn content_ident(self: Vc<Self>) -> Vc<AssetIdent> {
460 self.asset_ident()
461 }
462
463 fn ty(&self) -> Vc<Box<dyn ChunkType>>;
465
466 #[turbo_tasks::function]
469 fn module(self: Vc<Self>) -> Vc<Box<dyn Module>>;
470
471 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>>;
472}
473
474#[turbo_tasks::value_trait]
475pub trait ChunkType: ValueToString {
476 #[turbo_tasks::function]
478 fn is_style(self: Vc<Self>) -> Vc<bool>;
479
480 #[turbo_tasks::function]
482 fn chunk(
483 &self,
484 chunking_context: Vc<Box<dyn ChunkingContext>>,
485 chunk_items: Vec<ChunkItemOrBatchWithAsyncModuleInfo>,
486 batch_groups: Vec<ResolvedVc<ChunkItemBatchGroup>>,
487 ) -> Vc<Box<dyn Chunk>>;
488
489 #[turbo_tasks::function]
490 fn chunk_item_size(
491 &self,
492 chunking_context: Vc<Box<dyn ChunkingContext>>,
493 chunk_item: Vc<Box<dyn ChunkItem>>,
494 async_module_info: Option<Vc<AsyncModuleInfo>>,
495 ) -> Vc<usize>;
496}
497
498pub fn round_chunk_item_size(size: usize) -> usize {
499 let a = size.next_power_of_two();
500 size & (a | (a >> 1) | (a >> 2))
501}
502
503#[turbo_tasks::value(transparent)]
504pub struct ChunkItems(pub Vec<ResolvedVc<Box<dyn ChunkItem>>>);
505
506#[turbo_tasks::value]
507pub struct AsyncModuleInfo {
508 pub referenced_async_modules: AutoSet<ResolvedVc<Box<dyn Module>>>,
509}
510
511#[turbo_tasks::value_impl]
512impl AsyncModuleInfo {
513 #[turbo_tasks::function]
514 pub fn new(referenced_async_modules: Vec<ResolvedVc<Box<dyn Module>>>) -> Result<Vc<Self>> {
515 Ok(Self {
516 referenced_async_modules: referenced_async_modules.into_iter().collect(),
517 }
518 .cell())
519 }
520}
521
522#[derive(
523 Debug, Clone, Copy, PartialEq, Eq, Hash, TraceRawVcs, TaskInput, NonLocalValue, Encode, Decode,
524)]
525pub struct ChunkItemWithAsyncModuleInfo {
526 pub chunk_item: ResolvedVc<Box<dyn ChunkItem>>,
527 pub chunk_type: ResolvedVc<Box<dyn ChunkType>>,
528 pub module: Option<ResolvedVc<Box<dyn ChunkableModule>>>,
529 pub async_info: Option<ResolvedVc<AsyncModuleInfo>>,
530}
531
532pub trait ChunkItemExt {
533 fn id(self: Vc<Self>) -> impl Future<Output = Result<ModuleId>> + Send;
535}
536
537impl<T> ChunkItemExt for T
538where
539 T: Upcast<Box<dyn ChunkItem>> + Send,
540{
541 async fn id(self: Vc<Self>) -> Result<ModuleId> {
543 let chunk_item = Vc::upcast_non_strict(self);
544 chunk_item
545 .into_trait_ref()
546 .await?
547 .chunking_context()
548 .chunk_item_id_strategy()
549 .await?
550 .get_id(chunk_item)
551 .await
552 }
553}
554
555pub trait ModuleChunkItemIdExt {
556 fn chunk_item_id(
558 self: Vc<Self>,
559 chunking_context: Vc<Box<dyn ChunkingContext>>,
560 ) -> impl Future<Output = Result<ModuleId>> + Send;
561}
562impl<T> ModuleChunkItemIdExt for T
563where
564 T: Upcast<Box<dyn Module>> + Send,
565{
566 async fn chunk_item_id(
567 self: Vc<Self>,
568 chunking_context: Vc<Box<dyn ChunkingContext>>,
569 ) -> Result<ModuleId> {
570 chunking_context
571 .chunk_item_id_strategy()
572 .await?
573 .get_id_from_module(Vc::upcast_non_strict(self))
574 .await
575 }
576}
577
578#[cfg(test)]
579mod tests {
580 use super::*;
581
582 #[test]
583 fn test_round_chunk_item_size() {
584 assert_eq!(round_chunk_item_size(0), 0);
585 assert_eq!(round_chunk_item_size(1), 1);
586 assert_eq!(round_chunk_item_size(2), 2);
587 assert_eq!(round_chunk_item_size(3), 3);
588 assert_eq!(round_chunk_item_size(4), 4);
589 assert_eq!(round_chunk_item_size(5), 4);
590 assert_eq!(round_chunk_item_size(6), 6);
591 assert_eq!(round_chunk_item_size(7), 6);
592 assert_eq!(round_chunk_item_size(8), 8);
593 assert_eq!(round_chunk_item_size(49000), 32_768);
594 assert_eq!(round_chunk_item_size(50000), 49_152);
595
596 assert_eq!(changes_in_range(0..1000), 19);
597 assert_eq!(changes_in_range(1000..2000), 2);
598 assert_eq!(changes_in_range(2000..3000), 1);
599
600 assert_eq!(changes_in_range(3000..10000), 4);
601
602 fn changes_in_range(range: std::ops::Range<usize>) -> usize {
603 let len = range.len();
604 let mut count = 0;
605 for i in range {
606 let a = round_chunk_item_size(i);
607 assert!(a >= i * 2 / 3);
608 assert!(a <= i);
609 let b = round_chunk_item_size(i + 1);
610
611 if a == b {
612 count += 1;
613 }
614 }
615 len - count
616 }
617 }
618}