1use anyhow::{Result, bail};
2use bincode::{Decode, Encode};
3use rustc_hash::{FxHashMap, FxHashSet};
4use serde::{Deserialize, Serialize};
5use turbo_rcstr::RcStr;
6use turbo_tasks::{NonLocalValue, ResolvedVc, TaskInput, Upcast, Vc, trace::TraceRawVcs};
7use turbo_tasks_fs::FileSystemPath;
8use turbo_tasks_hash::DeterministicHash;
9
10use crate::{
11 asset::Asset,
12 chunk::{
13 ChunkItem, ChunkType, ChunkableModule, EvaluatableAssets,
14 availability_info::AvailabilityInfo, chunk_id_strategy::ModuleIdStrategy,
15 },
16 context::AssetContext,
17 environment::Environment,
18 ident::AssetIdent,
19 module::Module,
20 module_graph::{
21 ModuleGraph, binding_usage_info::ModuleExportUsage, chunk_group_info::ChunkGroup,
22 module_batches::BatchingConfig,
23 },
24 output::{
25 ExpandOutputAssetsInput, OutputAsset, OutputAssets, OutputAssetsReferences,
26 OutputAssetsWithReferenced, expand_output_assets,
27 },
28 reference::ModuleReference,
29};
30
31#[derive(
32 Debug,
33 TaskInput,
34 Clone,
35 Copy,
36 PartialEq,
37 Eq,
38 Hash,
39 Deserialize,
40 TraceRawVcs,
41 DeterministicHash,
42 NonLocalValue,
43 Encode,
44 Decode,
45)]
46#[serde(rename_all = "kebab-case")]
47pub enum MangleType {
48 OptimalSize,
49 Deterministic,
50}
51
52#[turbo_tasks::value(shared)]
53#[derive(Debug, TaskInput, Clone, Copy, Hash, DeterministicHash, Deserialize)]
54pub enum MinifyType {
55 Minify { mangle: Option<MangleType> },
58 NoMinify,
59}
60
61impl Default for MinifyType {
62 fn default() -> Self {
63 Self::Minify {
64 mangle: Some(MangleType::OptimalSize),
65 }
66 }
67}
68
69#[turbo_tasks::value(shared)]
70#[derive(Debug, Default, TaskInput, Clone, Copy, Hash, DeterministicHash)]
71pub enum SourceMapsType {
72 #[default]
74 Full,
75 Partial,
77 None,
79}
80
81#[derive(
82 Debug,
83 TaskInput,
84 Clone,
85 Copy,
86 PartialEq,
87 Eq,
88 Hash,
89 Serialize,
90 Deserialize,
91 TraceRawVcs,
92 DeterministicHash,
93 NonLocalValue,
94 Encode,
95 Decode,
96)]
97pub enum ChunkGroupType {
98 Entry,
99 Evaluated,
100}
101
102#[turbo_tasks::value(shared)]
103#[derive(Clone)]
104pub struct ChunkGroupResult {
105 pub assets: ResolvedVc<OutputAssets>,
106 pub referenced_assets: ResolvedVc<OutputAssets>,
107 pub references: ResolvedVc<OutputAssetsReferences>,
108 pub availability_info: AvailabilityInfo,
109}
110
111impl ChunkGroupResult {
112 pub fn empty() -> Vc<Self> {
113 ChunkGroupResult {
114 assets: ResolvedVc::cell(vec![]),
115 referenced_assets: ResolvedVc::cell(vec![]),
116 references: ResolvedVc::cell(vec![]),
117 availability_info: AvailabilityInfo::root(),
118 }
119 .cell()
120 }
121
122 pub fn empty_resolved() -> ResolvedVc<Self> {
123 ChunkGroupResult {
124 assets: ResolvedVc::cell(vec![]),
125 referenced_assets: ResolvedVc::cell(vec![]),
126 references: ResolvedVc::cell(vec![]),
127 availability_info: AvailabilityInfo::root(),
128 }
129 .resolved_cell()
130 }
131}
132
133#[turbo_tasks::value_impl]
134impl ChunkGroupResult {
135 #[turbo_tasks::function]
136 pub async fn output_assets_with_referenced(&self) -> Result<Vc<OutputAssetsWithReferenced>> {
137 Ok(OutputAssetsWithReferenced {
138 assets: self.assets,
139 referenced_assets: self.referenced_assets,
140 references: self.references,
141 }
142 .cell())
143 }
144
145 #[turbo_tasks::function]
146 pub async fn concatenate(&self, next: Vc<Self>) -> Result<Vc<Self>> {
147 let next = next.await?;
148 Ok(ChunkGroupResult {
149 assets: self.assets.concatenate(*next.assets).to_resolved().await?,
150 referenced_assets: self
151 .referenced_assets
152 .concatenate(*next.referenced_assets)
153 .to_resolved()
154 .await?,
155 references: self
156 .references
157 .concatenate(*next.references)
158 .to_resolved()
159 .await?,
160 availability_info: next.availability_info,
161 }
162 .cell())
163 }
164
165 #[turbo_tasks::function]
166 pub async fn all_assets(&self) -> Result<Vc<OutputAssets>> {
167 Ok(Vc::cell(
168 expand_output_assets(
169 self.assets
170 .await?
171 .into_iter()
172 .chain(self.referenced_assets.await?.into_iter())
173 .copied()
174 .map(ExpandOutputAssetsInput::Asset)
175 .chain(
176 self.references
177 .await?
178 .into_iter()
179 .copied()
180 .map(ExpandOutputAssetsInput::Reference),
181 ),
182 false,
183 )
184 .await?,
185 ))
186 }
187
188 #[turbo_tasks::function]
191 pub fn primary_assets(&self) -> Vc<OutputAssets> {
192 *self.assets
193 }
194
195 #[turbo_tasks::function]
196 pub async fn referenced_assets(&self) -> Result<Vc<OutputAssets>> {
197 Ok(Vc::cell(
198 expand_output_assets(
199 self.referenced_assets
200 .await?
201 .into_iter()
202 .copied()
203 .map(ExpandOutputAssetsInput::Asset)
204 .chain(
205 self.references
206 .await?
207 .into_iter()
208 .copied()
209 .map(ExpandOutputAssetsInput::Reference),
210 ),
211 false,
212 )
213 .await?,
214 ))
215 }
216}
217
218#[turbo_tasks::value(shared)]
219pub struct EntryChunkGroupResult {
220 pub asset: ResolvedVc<Box<dyn OutputAsset>>,
221 pub availability_info: AvailabilityInfo,
222}
223
224#[derive(
225 Default,
226 Debug,
227 Clone,
228 PartialEq,
229 Eq,
230 Hash,
231 TraceRawVcs,
232 NonLocalValue,
233 TaskInput,
234 Encode,
235 Decode,
236)]
237pub struct ChunkingConfig {
238 pub min_chunk_size: usize,
241
242 pub max_chunk_count_per_group: usize,
245
246 pub max_merge_chunk_size: usize,
249
250 #[allow(dead_code)]
251 pub placeholder_for_future_extensions: (),
252}
253
254#[turbo_tasks::value(transparent)]
255pub struct ChunkingConfigs(FxHashMap<ResolvedVc<Box<dyn ChunkType>>, ChunkingConfig>);
256
257#[turbo_tasks::value(shared)]
258#[derive(Debug, Clone, Copy, Hash, TaskInput, Default, Deserialize)]
259pub enum SourceMapSourceType {
260 AbsoluteFileUri,
261 RelativeUri,
262 #[default]
263 TurbopackUri,
264}
265
266#[turbo_tasks::value(transparent, cell = "keyed")]
267pub struct UnusedReferences(FxHashSet<ResolvedVc<Box<dyn ModuleReference>>>);
268
269#[turbo_tasks::value_trait]
271pub trait ChunkingContext {
272 #[turbo_tasks::function]
273 fn name(self: Vc<Self>) -> Vc<RcStr>;
274 #[turbo_tasks::function]
275 fn source_map_source_type(self: Vc<Self>) -> Vc<SourceMapSourceType>;
276 #[turbo_tasks::function]
278 fn root_path(self: Vc<Self>) -> Vc<FileSystemPath>;
279 #[turbo_tasks::function]
281 fn output_root(self: Vc<Self>) -> Vc<FileSystemPath>;
282 #[turbo_tasks::function]
285 fn output_root_to_root_path(self: Vc<Self>) -> Vc<RcStr>;
286
287 #[turbo_tasks::function]
290 fn environment(self: Vc<Self>) -> Vc<Environment>;
291
292 #[turbo_tasks::function]
295 fn chunk_root_path(self: Vc<Self>) -> Vc<FileSystemPath>;
296
297 #[turbo_tasks::function]
302 fn chunk_path(
303 self: Vc<Self>,
304 asset: Option<Vc<Box<dyn Asset>>>,
305 ident: Vc<AssetIdent>,
306 content_hashing_prefix: Option<RcStr>,
307 extension: RcStr,
308 ) -> Vc<FileSystemPath>;
309
310 #[turbo_tasks::function]
312 fn reference_chunk_source_maps(self: Vc<Self>, chunk: Vc<Box<dyn OutputAsset>>) -> Vc<bool>;
313
314 #[turbo_tasks::function]
316 fn reference_module_source_maps(self: Vc<Self>, module: Vc<Box<dyn Module>>) -> Vc<bool>;
317
318 #[turbo_tasks::function]
323 fn asset_url(self: Vc<Self>, ident: FileSystemPath, tag: Option<RcStr>) -> Result<Vc<RcStr>>;
324
325 #[turbo_tasks::function]
326 fn asset_path(
327 self: Vc<Self>,
328 content_hash: RcStr,
329 original_asset_ident: Vc<AssetIdent>,
330 tag: Option<RcStr>,
331 ) -> Vc<FileSystemPath>;
332
333 #[turbo_tasks::function]
334 fn is_hot_module_replacement_enabled(self: Vc<Self>) -> Vc<bool> {
335 Vc::cell(false)
336 }
337
338 #[turbo_tasks::function]
339 fn chunking_configs(self: Vc<Self>) -> Vc<ChunkingConfigs> {
340 Vc::cell(Default::default())
341 }
342
343 #[turbo_tasks::function]
344 fn batching_config(self: Vc<Self>) -> Vc<BatchingConfig> {
345 BatchingConfig::new(BatchingConfig {
346 ..Default::default()
347 })
348 }
349
350 #[turbo_tasks::function]
353 fn is_tracing_enabled(self: Vc<Self>) -> Vc<bool> {
354 Vc::cell(false)
355 }
356
357 #[turbo_tasks::function]
362 fn is_nested_async_availability_enabled(self: Vc<Self>) -> Vc<bool> {
363 Vc::cell(false)
364 }
365
366 #[turbo_tasks::function]
368 fn is_module_merging_enabled(self: Vc<Self>) -> Vc<bool> {
369 Vc::cell(false)
370 }
371
372 #[turbo_tasks::function]
375 fn is_dynamic_chunk_content_loading_enabled(self: Vc<Self>) -> Vc<bool> {
376 Vc::cell(false)
377 }
378
379 #[turbo_tasks::function]
380 fn minify_type(self: Vc<Self>) -> Vc<MinifyType> {
381 MinifyType::NoMinify.cell()
382 }
383
384 #[turbo_tasks::function]
385 fn should_use_absolute_url_references(self: Vc<Self>) -> Vc<bool> {
386 Vc::cell(false)
387 }
388
389 #[turbo_tasks::function]
390 fn async_loader_chunk_item(
391 &self,
392 module: Vc<Box<dyn ChunkableModule>>,
393 module_graph: Vc<ModuleGraph>,
394 availability_info: AvailabilityInfo,
395 ) -> Vc<Box<dyn ChunkItem>>;
396 #[turbo_tasks::function]
397 fn async_loader_chunk_item_ident(&self, module: Vc<Box<dyn ChunkableModule>>)
398 -> Vc<AssetIdent>;
399
400 #[turbo_tasks::function]
401 fn chunk_group(
402 self: Vc<Self>,
403 ident: Vc<AssetIdent>,
404 chunk_group: ChunkGroup,
405 module_graph: Vc<ModuleGraph>,
406 availability_info: AvailabilityInfo,
407 ) -> Vc<ChunkGroupResult>;
408
409 #[turbo_tasks::function]
410 fn evaluated_chunk_group(
411 self: Vc<Self>,
412 ident: Vc<AssetIdent>,
413 chunk_group: ChunkGroup,
414 module_graph: Vc<ModuleGraph>,
415 availability_info: AvailabilityInfo,
416 ) -> Vc<ChunkGroupResult>;
417
418 #[turbo_tasks::function]
423 fn entry_chunk_group(
424 self: Vc<Self>,
425 path: FileSystemPath,
426 evaluatable_assets: Vc<EvaluatableAssets>,
427 module_graph: Vc<ModuleGraph>,
428 extra_chunks: Vc<OutputAssets>,
429 extra_referenced_assets: Vc<OutputAssets>,
430 availability_info: AvailabilityInfo,
431 ) -> Result<Vc<EntryChunkGroupResult>>;
432
433 #[turbo_tasks::function]
434 async fn chunk_item_id_strategy(self: Vc<Self>) -> Result<Vc<ModuleIdStrategy>>;
435
436 #[turbo_tasks::function]
437 async fn module_export_usage(
438 self: Vc<Self>,
439 module: Vc<Box<dyn Module>>,
440 ) -> Result<Vc<ModuleExportUsage>>;
441
442 #[turbo_tasks::function]
443 async fn unused_references(self: Vc<Self>) -> Result<Vc<UnusedReferences>>;
444
445 #[turbo_tasks::function]
447 fn debug_ids_enabled(self: Vc<Self>) -> Vc<bool>;
448
449 #[turbo_tasks::function]
452 async fn worker_entrypoint(
453 self: Vc<Self>,
454 asset_context: Vc<Box<dyn AssetContext>>,
455 ) -> Result<Vc<Box<dyn OutputAsset>>> {
456 let _ = asset_context;
457 bail!(
458 "Worker entrypoint is not supported by {name}",
459 name = self.name().await?
460 );
461 }
462}
463pub trait ChunkingContextExt {
464 fn root_chunk_group(
465 self: Vc<Self>,
466 ident: Vc<AssetIdent>,
467 chunk_group: ChunkGroup,
468 module_graph: Vc<ModuleGraph>,
469 ) -> Vc<ChunkGroupResult>
470 where
471 Self: Send;
472
473 fn root_chunk_group_assets(
474 self: Vc<Self>,
475 ident: Vc<AssetIdent>,
476 chunk_group: ChunkGroup,
477 module_graph: Vc<ModuleGraph>,
478 ) -> Vc<OutputAssetsWithReferenced>
479 where
480 Self: Send;
481
482 fn evaluated_chunk_group_assets(
483 self: Vc<Self>,
484 ident: Vc<AssetIdent>,
485 chunk_group: ChunkGroup,
486 module_graph: Vc<ModuleGraph>,
487 availability_info: AvailabilityInfo,
488 ) -> Vc<OutputAssetsWithReferenced>
489 where
490 Self: Send;
491
492 fn entry_chunk_group_asset(
493 self: Vc<Self>,
494 path: FileSystemPath,
495 evaluatable_assets: Vc<EvaluatableAssets>,
496 module_graph: Vc<ModuleGraph>,
497 extra_chunks: Vc<OutputAssets>,
498 extra_referenced_assets: Vc<OutputAssets>,
499 availability_info: AvailabilityInfo,
500 ) -> Vc<Box<dyn OutputAsset>>
501 where
502 Self: Send;
503
504 fn root_entry_chunk_group(
505 self: Vc<Self>,
506 path: FileSystemPath,
507 evaluatable_assets: Vc<EvaluatableAssets>,
508 module_graph: Vc<ModuleGraph>,
509 extra_chunks: Vc<OutputAssets>,
510 extra_referenced_assets: Vc<OutputAssets>,
511 ) -> Vc<EntryChunkGroupResult>
512 where
513 Self: Send;
514
515 fn root_entry_chunk_group_asset(
516 self: Vc<Self>,
517 path: FileSystemPath,
518 evaluatable_assets: Vc<EvaluatableAssets>,
519 module_graph: Vc<ModuleGraph>,
520 extra_chunks: Vc<OutputAssets>,
521 extra_referenced_assets: Vc<OutputAssets>,
522 ) -> Vc<Box<dyn OutputAsset>>
523 where
524 Self: Send;
525
526 fn chunk_group_assets(
527 self: Vc<Self>,
528 ident: Vc<AssetIdent>,
529 chunk_group: ChunkGroup,
530 module_graph: Vc<ModuleGraph>,
531 availability_info: AvailabilityInfo,
532 ) -> Vc<OutputAssetsWithReferenced>
533 where
534 Self: Send;
535
536 fn relative_path_from_chunk_root_to_project_root(self: Vc<Self>) -> Vc<RcStr>
540 where
541 Self: Send;
542}
543
544impl<T: ChunkingContext + Send + Upcast<Box<dyn ChunkingContext>>> ChunkingContextExt for T {
545 fn root_chunk_group(
546 self: Vc<Self>,
547 ident: Vc<AssetIdent>,
548 chunk_group: ChunkGroup,
549 module_graph: Vc<ModuleGraph>,
550 ) -> Vc<ChunkGroupResult> {
551 self.chunk_group(ident, chunk_group, module_graph, AvailabilityInfo::root())
552 }
553
554 fn root_chunk_group_assets(
555 self: Vc<Self>,
556 ident: Vc<AssetIdent>,
557 chunk_group: ChunkGroup,
558 module_graph: Vc<ModuleGraph>,
559 ) -> Vc<OutputAssetsWithReferenced> {
560 root_chunk_group_assets(
561 Vc::upcast_non_strict(self),
562 ident,
563 chunk_group,
564 module_graph,
565 )
566 }
567
568 fn evaluated_chunk_group_assets(
569 self: Vc<Self>,
570 ident: Vc<AssetIdent>,
571 chunk_group: ChunkGroup,
572 module_graph: Vc<ModuleGraph>,
573 availability_info: AvailabilityInfo,
574 ) -> Vc<OutputAssetsWithReferenced> {
575 evaluated_chunk_group_assets(
576 Vc::upcast_non_strict(self),
577 ident,
578 chunk_group,
579 module_graph,
580 availability_info,
581 )
582 }
583
584 fn entry_chunk_group_asset(
585 self: Vc<Self>,
586 path: FileSystemPath,
587 evaluatable_assets: Vc<EvaluatableAssets>,
588 module_graph: Vc<ModuleGraph>,
589 extra_chunks: Vc<OutputAssets>,
590 extra_referenced_assets: Vc<OutputAssets>,
591 availability_info: AvailabilityInfo,
592 ) -> Vc<Box<dyn OutputAsset>> {
593 entry_chunk_group_asset(
594 Vc::upcast_non_strict(self),
595 path,
596 evaluatable_assets,
597 module_graph,
598 extra_chunks,
599 extra_referenced_assets,
600 availability_info,
601 )
602 }
603
604 fn root_entry_chunk_group(
605 self: Vc<Self>,
606 path: FileSystemPath,
607 evaluatable_assets: Vc<EvaluatableAssets>,
608 module_graph: Vc<ModuleGraph>,
609 extra_chunks: Vc<OutputAssets>,
610 extra_referenced_assets: Vc<OutputAssets>,
611 ) -> Vc<EntryChunkGroupResult> {
612 self.entry_chunk_group(
613 path,
614 evaluatable_assets,
615 module_graph,
616 extra_chunks,
617 extra_referenced_assets,
618 AvailabilityInfo::root(),
619 )
620 }
621
622 fn root_entry_chunk_group_asset(
623 self: Vc<Self>,
624 path: FileSystemPath,
625 evaluatable_assets: Vc<EvaluatableAssets>,
626 module_graph: Vc<ModuleGraph>,
627 extra_chunks: Vc<OutputAssets>,
628 extra_referenced_assets: Vc<OutputAssets>,
629 ) -> Vc<Box<dyn OutputAsset>> {
630 entry_chunk_group_asset(
631 Vc::upcast_non_strict(self),
632 path,
633 evaluatable_assets,
634 module_graph,
635 extra_chunks,
636 extra_referenced_assets,
637 AvailabilityInfo::root(),
638 )
639 }
640
641 fn chunk_group_assets(
642 self: Vc<Self>,
643 ident: Vc<AssetIdent>,
644 chunk_group: ChunkGroup,
645 module_graph: Vc<ModuleGraph>,
646 availability_info: AvailabilityInfo,
647 ) -> Vc<OutputAssetsWithReferenced> {
648 chunk_group_assets(
649 Vc::upcast_non_strict(self),
650 ident,
651 chunk_group,
652 module_graph,
653 availability_info,
654 )
655 }
656
657 fn relative_path_from_chunk_root_to_project_root(self: Vc<Self>) -> Vc<RcStr> {
658 relative_path_from_chunk_root_to_project_root(Vc::upcast_non_strict(self))
659 }
660}
661
662#[turbo_tasks::function]
663async fn relative_path_from_chunk_root_to_project_root(
664 chunking_context: Vc<Box<dyn ChunkingContext>>,
665) -> Result<Vc<RcStr>> {
666 let chunk_root_path = chunking_context.chunk_root_path().await?;
682 let output_root = chunking_context.output_root().await?;
683 let chunk_to_output_root = chunk_root_path.get_relative_path_to(&output_root);
684 let Some(chunk_to_output_root) = chunk_to_output_root else {
685 bail!(
686 "expected chunk_root_path: {chunk_root_path} to be inside of output_root: \
687 {output_root}",
688 chunk_root_path = chunk_root_path.value_to_string().await?,
689 output_root = output_root.value_to_string().await?
690 );
691 };
692 let output_root_to_chunk_root_path = chunking_context.output_root_to_root_path().await?;
693
694 Ok(Vc::cell(
696 format!(
697 "{}/{}",
698 chunk_to_output_root, output_root_to_chunk_root_path
699 )
700 .into(),
701 ))
702}
703
704#[turbo_tasks::function]
705fn root_chunk_group_assets(
706 chunking_context: Vc<Box<dyn ChunkingContext>>,
707 ident: Vc<AssetIdent>,
708 chunk_group: ChunkGroup,
709 module_graph: Vc<ModuleGraph>,
710) -> Vc<OutputAssetsWithReferenced> {
711 chunking_context
712 .root_chunk_group(ident, chunk_group, module_graph)
713 .output_assets_with_referenced()
714}
715
716#[turbo_tasks::function]
717fn evaluated_chunk_group_assets(
718 chunking_context: Vc<Box<dyn ChunkingContext>>,
719 ident: Vc<AssetIdent>,
720 chunk_group: ChunkGroup,
721 module_graph: Vc<ModuleGraph>,
722 availability_info: AvailabilityInfo,
723) -> Vc<OutputAssetsWithReferenced> {
724 chunking_context
725 .evaluated_chunk_group(ident, chunk_group, module_graph, availability_info)
726 .output_assets_with_referenced()
727}
728
729#[turbo_tasks::function]
730async fn entry_chunk_group_asset(
731 chunking_context: Vc<Box<dyn ChunkingContext>>,
732 path: FileSystemPath,
733 evaluatable_assets: Vc<EvaluatableAssets>,
734 module_graph: Vc<ModuleGraph>,
735 extra_chunks: Vc<OutputAssets>,
736 extra_referenced_assets: Vc<OutputAssets>,
737 availability_info: AvailabilityInfo,
738) -> Result<Vc<Box<dyn OutputAsset>>> {
739 Ok(*chunking_context
740 .entry_chunk_group(
741 path,
742 evaluatable_assets,
743 module_graph,
744 extra_chunks,
745 extra_referenced_assets,
746 availability_info,
747 )
748 .await?
749 .asset)
750}
751
752#[turbo_tasks::function]
753fn chunk_group_assets(
754 chunking_context: Vc<Box<dyn ChunkingContext>>,
755 ident: Vc<AssetIdent>,
756 chunk_group: ChunkGroup,
757 module_graph: Vc<ModuleGraph>,
758 availability_info: AvailabilityInfo,
759) -> Vc<OutputAssetsWithReferenced> {
760 chunking_context
761 .chunk_group(ident, chunk_group, module_graph, availability_info)
762 .output_assets_with_referenced()
763}