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