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, 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#[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
430pub struct ChunkGroupContent {
431 pub chunkable_items: Vec<ChunkableModuleOrBatch>,
432 pub batch_groups: Vec<ResolvedVc<ModuleBatchGroup>>,
433 pub async_modules: FxIndexSet<ResolvedVc<Box<dyn ChunkableModule>>>,
434 pub traced_modules: FxIndexSet<ResolvedVc<Box<dyn Module>>>,
435 pub availability_info: AvailabilityInfo,
436}
437
438#[turbo_tasks::value_trait]
439pub trait ChunkItem: OutputAssetsReference {
440 #[turbo_tasks::function]
444 fn asset_ident(self: Vc<Self>) -> Vc<AssetIdent>;
445
446 #[turbo_tasks::function]
451 fn content_ident(self: Vc<Self>) -> Vc<AssetIdent> {
452 self.asset_ident()
453 }
454
455 fn ty(&self) -> Vc<Box<dyn ChunkType>>;
457
458 #[turbo_tasks::function]
461 fn module(self: Vc<Self>) -> Vc<Box<dyn Module>>;
462
463 fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>>;
464}
465
466#[turbo_tasks::value_trait]
467pub trait ChunkType: ValueToString {
468 #[turbo_tasks::function]
470 fn is_style(self: Vc<Self>) -> Vc<bool>;
471
472 #[turbo_tasks::function]
474 fn chunk(
475 &self,
476 chunking_context: Vc<Box<dyn ChunkingContext>>,
477 chunk_items: Vec<ChunkItemOrBatchWithAsyncModuleInfo>,
478 batch_groups: Vec<ResolvedVc<ChunkItemBatchGroup>>,
479 ) -> Vc<Box<dyn Chunk>>;
480
481 #[turbo_tasks::function]
482 fn chunk_item_size(
483 &self,
484 chunking_context: Vc<Box<dyn ChunkingContext>>,
485 chunk_item: Vc<Box<dyn ChunkItem>>,
486 async_module_info: Option<Vc<AsyncModuleInfo>>,
487 ) -> Vc<usize>;
488}
489
490pub fn round_chunk_item_size(size: usize) -> usize {
491 let a = size.next_power_of_two();
492 size & (a | (a >> 1) | (a >> 2))
493}
494
495#[turbo_tasks::value(transparent)]
496pub struct ChunkItems(pub Vec<ResolvedVc<Box<dyn ChunkItem>>>);
497
498#[turbo_tasks::value]
499pub struct AsyncModuleInfo {
500 pub referenced_async_modules: AutoSet<ResolvedVc<Box<dyn Module>>>,
501}
502
503#[turbo_tasks::value_impl]
504impl AsyncModuleInfo {
505 #[turbo_tasks::function]
506 pub fn new(referenced_async_modules: Vec<ResolvedVc<Box<dyn Module>>>) -> Result<Vc<Self>> {
507 Ok(Self {
508 referenced_async_modules: referenced_async_modules.into_iter().collect(),
509 }
510 .cell())
511 }
512}
513
514#[derive(
515 Debug, Clone, Copy, PartialEq, Eq, Hash, TraceRawVcs, TaskInput, NonLocalValue, Encode, Decode,
516)]
517pub struct ChunkItemWithAsyncModuleInfo {
518 pub chunk_item: ResolvedVc<Box<dyn ChunkItem>>,
519 pub chunk_type: ResolvedVc<Box<dyn ChunkType>>,
520 pub module: Option<ResolvedVc<Box<dyn ChunkableModule>>>,
521 pub async_info: Option<ResolvedVc<AsyncModuleInfo>>,
522}
523
524pub trait ChunkItemExt {
525 fn id(self: Vc<Self>) -> impl Future<Output = Result<ModuleId>> + Send;
527}
528
529impl<T> ChunkItemExt for T
530where
531 T: Upcast<Box<dyn ChunkItem>> + Send,
532{
533 async fn id(self: Vc<Self>) -> Result<ModuleId> {
535 let chunk_item = Vc::upcast_non_strict(self);
536 chunk_item
537 .into_trait_ref()
538 .await?
539 .chunking_context()
540 .chunk_item_id_strategy()
541 .await?
542 .get_id(chunk_item)
543 .await
544 }
545}
546
547pub trait ModuleChunkItemIdExt {
548 fn chunk_item_id(
550 self: Vc<Self>,
551 chunking_context: Vc<Box<dyn ChunkingContext>>,
552 ) -> impl Future<Output = Result<ModuleId>> + Send;
553}
554impl<T> ModuleChunkItemIdExt for T
555where
556 T: Upcast<Box<dyn Module>> + Send,
557{
558 async fn chunk_item_id(
559 self: Vc<Self>,
560 chunking_context: Vc<Box<dyn ChunkingContext>>,
561 ) -> Result<ModuleId> {
562 chunking_context
563 .chunk_item_id_strategy()
564 .await?
565 .get_id_from_module(Vc::upcast_non_strict(self))
566 .await
567 }
568}
569
570#[cfg(test)]
571mod tests {
572 use super::*;
573
574 #[test]
575 fn test_round_chunk_item_size() {
576 assert_eq!(round_chunk_item_size(0), 0);
577 assert_eq!(round_chunk_item_size(1), 1);
578 assert_eq!(round_chunk_item_size(2), 2);
579 assert_eq!(round_chunk_item_size(3), 3);
580 assert_eq!(round_chunk_item_size(4), 4);
581 assert_eq!(round_chunk_item_size(5), 4);
582 assert_eq!(round_chunk_item_size(6), 6);
583 assert_eq!(round_chunk_item_size(7), 6);
584 assert_eq!(round_chunk_item_size(8), 8);
585 assert_eq!(round_chunk_item_size(49000), 32_768);
586 assert_eq!(round_chunk_item_size(50000), 49_152);
587
588 assert_eq!(changes_in_range(0..1000), 19);
589 assert_eq!(changes_in_range(1000..2000), 2);
590 assert_eq!(changes_in_range(2000..3000), 1);
591
592 assert_eq!(changes_in_range(3000..10000), 4);
593
594 fn changes_in_range(range: std::ops::Range<usize>) -> usize {
595 let len = range.len();
596 let mut count = 0;
597 for i in range {
598 let a = round_chunk_item_size(i);
599 assert!(a >= i * 2 / 3);
600 assert!(a <= i);
601 let b = round_chunk_item_size(i + 1);
602
603 if a == b {
604 count += 1;
605 }
606 }
607 len - count
608 }
609 }
610}