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