1use anyhow::{Result, bail};
2use bincode::{Decode, Encode};
3use rustc_hash::FxHashMap;
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, ModuleId,
14 availability_info::AvailabilityInfo,
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#[derive(
81 Debug,
82 TaskInput,
83 Clone,
84 Copy,
85 PartialEq,
86 Eq,
87 Hash,
88 Serialize,
89 Deserialize,
90 TraceRawVcs,
91 DeterministicHash,
92 NonLocalValue,
93 Encode,
94 Decode,
95)]
96pub enum ChunkGroupType {
97 Entry,
98 Evaluated,
99}
100
101#[turbo_tasks::value(shared)]
102#[derive(Clone)]
103pub struct ChunkGroupResult {
104 pub assets: ResolvedVc<OutputAssets>,
105 pub referenced_assets: ResolvedVc<OutputAssets>,
106 pub references: ResolvedVc<OutputAssetsReferences>,
107 pub availability_info: AvailabilityInfo,
108}
109
110impl ChunkGroupResult {
111 pub fn empty() -> Vc<Self> {
112 ChunkGroupResult {
113 assets: ResolvedVc::cell(vec![]),
114 referenced_assets: ResolvedVc::cell(vec![]),
115 references: ResolvedVc::cell(vec![]),
116 availability_info: AvailabilityInfo::root(),
117 }
118 .cell()
119 }
120
121 pub fn empty_resolved() -> ResolvedVc<Self> {
122 ChunkGroupResult {
123 assets: ResolvedVc::cell(vec![]),
124 referenced_assets: ResolvedVc::cell(vec![]),
125 references: ResolvedVc::cell(vec![]),
126 availability_info: AvailabilityInfo::root(),
127 }
128 .resolved_cell()
129 }
130}
131
132#[turbo_tasks::value_impl]
133impl ChunkGroupResult {
134 #[turbo_tasks::function]
135 pub async fn output_assets_with_referenced(&self) -> Result<Vc<OutputAssetsWithReferenced>> {
136 Ok(OutputAssetsWithReferenced {
137 assets: self.assets,
138 referenced_assets: self.referenced_assets,
139 references: self.references,
140 }
141 .cell())
142 }
143
144 #[turbo_tasks::function]
145 pub async fn concatenate(&self, next: Vc<Self>) -> Result<Vc<Self>> {
146 let next = next.await?;
147 Ok(ChunkGroupResult {
148 assets: self.assets.concatenate(*next.assets).to_resolved().await?,
149 referenced_assets: self
150 .referenced_assets
151 .concatenate(*next.referenced_assets)
152 .to_resolved()
153 .await?,
154 references: self
155 .references
156 .concatenate(*next.references)
157 .to_resolved()
158 .await?,
159 availability_info: next.availability_info,
160 }
161 .cell())
162 }
163
164 #[turbo_tasks::function]
165 pub async fn all_assets(&self) -> Result<Vc<OutputAssets>> {
166 Ok(Vc::cell(
167 expand_output_assets(
168 self.assets
169 .await?
170 .into_iter()
171 .chain(self.referenced_assets.await?.into_iter())
172 .copied()
173 .map(ExpandOutputAssetsInput::Asset)
174 .chain(
175 self.references
176 .await?
177 .into_iter()
178 .copied()
179 .map(ExpandOutputAssetsInput::Reference),
180 ),
181 false,
182 )
183 .await?,
184 ))
185 }
186
187 #[turbo_tasks::function]
190 pub fn primary_assets(&self) -> Vc<OutputAssets> {
191 *self.assets
192 }
193
194 #[turbo_tasks::function]
195 pub async fn referenced_assets(&self) -> Result<Vc<OutputAssets>> {
196 Ok(Vc::cell(
197 expand_output_assets(
198 self.referenced_assets
199 .await?
200 .into_iter()
201 .copied()
202 .map(ExpandOutputAssetsInput::Asset)
203 .chain(
204 self.references
205 .await?
206 .into_iter()
207 .copied()
208 .map(ExpandOutputAssetsInput::Reference),
209 ),
210 false,
211 )
212 .await?,
213 ))
214 }
215}
216
217#[turbo_tasks::value(shared)]
218pub struct EntryChunkGroupResult {
219 pub asset: ResolvedVc<Box<dyn OutputAsset>>,
220 pub availability_info: AvailabilityInfo,
221}
222
223#[derive(
224 Default,
225 Debug,
226 Clone,
227 PartialEq,
228 Eq,
229 Hash,
230 TraceRawVcs,
231 NonLocalValue,
232 TaskInput,
233 Encode,
234 Decode,
235)]
236pub struct ChunkingConfig {
237 pub min_chunk_size: usize,
240
241 pub max_chunk_count_per_group: usize,
244
245 pub max_merge_chunk_size: usize,
248
249 #[allow(dead_code)]
250 pub placeholder_for_future_extensions: (),
251}
252
253#[turbo_tasks::value(transparent)]
254pub struct ChunkingConfigs(FxHashMap<ResolvedVc<Box<dyn ChunkType>>, ChunkingConfig>);
255
256#[turbo_tasks::value(shared)]
257#[derive(Debug, Clone, Copy, Hash, TaskInput, Default, Deserialize)]
258pub enum SourceMapSourceType {
259 AbsoluteFileUri,
260 RelativeUri,
261 #[default]
262 TurbopackUri,
263}
264
265#[turbo_tasks::value_trait]
267pub trait ChunkingContext {
268 #[turbo_tasks::function]
269 fn name(self: Vc<Self>) -> Vc<RcStr>;
270 #[turbo_tasks::function]
271 fn source_map_source_type(self: Vc<Self>) -> Vc<SourceMapSourceType>;
272 #[turbo_tasks::function]
274 fn root_path(self: Vc<Self>) -> Vc<FileSystemPath>;
275 #[turbo_tasks::function]
277 fn output_root(self: Vc<Self>) -> Vc<FileSystemPath>;
278 #[turbo_tasks::function]
281 fn output_root_to_root_path(self: Vc<Self>) -> Vc<RcStr>;
282
283 #[turbo_tasks::function]
286 fn environment(self: Vc<Self>) -> Vc<Environment>;
287
288 #[turbo_tasks::function]
291 fn chunk_root_path(self: Vc<Self>) -> Vc<FileSystemPath>;
292
293 #[turbo_tasks::function]
298 fn chunk_path(
299 self: Vc<Self>,
300 asset: Option<Vc<Box<dyn Asset>>>,
301 ident: Vc<AssetIdent>,
302 content_hashing_prefix: Option<RcStr>,
303 extension: RcStr,
304 ) -> Vc<FileSystemPath>;
305
306 #[turbo_tasks::function]
308 fn reference_chunk_source_maps(self: Vc<Self>, chunk: Vc<Box<dyn OutputAsset>>) -> Vc<bool>;
309
310 #[turbo_tasks::function]
312 fn reference_module_source_maps(self: Vc<Self>, module: Vc<Box<dyn Module>>) -> Vc<bool>;
313
314 #[turbo_tasks::function]
319 fn asset_url(self: Vc<Self>, ident: FileSystemPath, tag: Option<RcStr>) -> Result<Vc<RcStr>>;
320
321 #[turbo_tasks::function]
322 fn asset_path(
323 self: Vc<Self>,
324 content_hash: RcStr,
325 original_asset_ident: Vc<AssetIdent>,
326 tag: Option<RcStr>,
327 ) -> Vc<FileSystemPath>;
328
329 #[turbo_tasks::function]
330 fn is_hot_module_replacement_enabled(self: Vc<Self>) -> Vc<bool> {
331 Vc::cell(false)
332 }
333
334 #[turbo_tasks::function]
335 fn chunking_configs(self: Vc<Self>) -> Vc<ChunkingConfigs> {
336 Vc::cell(Default::default())
337 }
338
339 #[turbo_tasks::function]
340 fn batching_config(self: Vc<Self>) -> Vc<BatchingConfig> {
341 BatchingConfig::new(BatchingConfig {
342 ..Default::default()
343 })
344 }
345
346 #[turbo_tasks::function]
349 fn is_tracing_enabled(self: Vc<Self>) -> Vc<bool> {
350 Vc::cell(false)
351 }
352
353 #[turbo_tasks::function]
358 fn is_nested_async_availability_enabled(self: Vc<Self>) -> Vc<bool> {
359 Vc::cell(false)
360 }
361
362 #[turbo_tasks::function]
364 fn is_module_merging_enabled(self: Vc<Self>) -> Vc<bool> {
365 Vc::cell(false)
366 }
367
368 #[turbo_tasks::function]
371 fn is_dynamic_chunk_content_loading_enabled(self: Vc<Self>) -> Vc<bool> {
372 Vc::cell(false)
373 }
374
375 #[turbo_tasks::function]
376 fn minify_type(self: Vc<Self>) -> Vc<MinifyType> {
377 MinifyType::NoMinify.cell()
378 }
379
380 #[turbo_tasks::function]
381 fn should_use_absolute_url_references(self: Vc<Self>) -> Vc<bool> {
382 Vc::cell(false)
383 }
384
385 #[turbo_tasks::function]
386 fn async_loader_chunk_item(
387 &self,
388 module: Vc<Box<dyn ChunkableModule>>,
389 module_graph: Vc<ModuleGraph>,
390 availability_info: AvailabilityInfo,
391 ) -> Vc<Box<dyn ChunkItem>>;
392 #[turbo_tasks::function]
393 fn async_loader_chunk_item_id(&self, module: Vc<Box<dyn ChunkableModule>>) -> Vc<ModuleId>;
394
395 #[turbo_tasks::function]
396 fn chunk_group(
397 self: Vc<Self>,
398 ident: Vc<AssetIdent>,
399 chunk_group: ChunkGroup,
400 module_graph: Vc<ModuleGraph>,
401 availability_info: AvailabilityInfo,
402 ) -> Vc<ChunkGroupResult>;
403
404 #[turbo_tasks::function]
405 fn evaluated_chunk_group(
406 self: Vc<Self>,
407 ident: Vc<AssetIdent>,
408 chunk_group: ChunkGroup,
409 module_graph: Vc<ModuleGraph>,
410 availability_info: AvailabilityInfo,
411 ) -> Vc<ChunkGroupResult>;
412
413 #[turbo_tasks::function]
418 fn entry_chunk_group(
419 self: Vc<Self>,
420 path: FileSystemPath,
421 evaluatable_assets: Vc<EvaluatableAssets>,
422 module_graph: Vc<ModuleGraph>,
423 extra_chunks: Vc<OutputAssets>,
424 extra_referenced_assets: Vc<OutputAssets>,
425 availability_info: AvailabilityInfo,
426 ) -> Result<Vc<EntryChunkGroupResult>>;
427
428 #[turbo_tasks::function]
429 async fn chunk_item_id_from_ident(
430 self: Vc<Self>,
431 ident: Vc<AssetIdent>,
432 ) -> Result<Vc<ModuleId>>;
433
434 #[turbo_tasks::function]
435 fn chunk_item_id(self: Vc<Self>, module: Vc<Box<dyn ChunkItem>>) -> Vc<ModuleId> {
436 self.chunk_item_id_from_ident(module.asset_ident())
437 }
438 #[turbo_tasks::function]
439 fn chunk_item_id_from_module(self: Vc<Self>, module: Vc<Box<dyn Module>>) -> Vc<ModuleId> {
440 self.chunk_item_id_from_ident(module.ident())
441 }
442
443 #[turbo_tasks::function]
444 async fn module_export_usage(
445 self: Vc<Self>,
446 module: Vc<Box<dyn Module>>,
447 ) -> Result<Vc<ModuleExportUsage>>;
448
449 #[turbo_tasks::function]
450 async fn is_reference_unused(
451 self: Vc<Self>,
452 reference: Vc<Box<dyn ModuleReference>>,
453 ) -> Result<Vc<bool>>;
454
455 #[turbo_tasks::function]
457 fn debug_ids_enabled(self: Vc<Self>) -> Vc<bool>;
458}
459
460pub trait ChunkingContextExt {
461 fn root_chunk_group(
462 self: Vc<Self>,
463 ident: Vc<AssetIdent>,
464 chunk_group: ChunkGroup,
465 module_graph: Vc<ModuleGraph>,
466 ) -> Vc<ChunkGroupResult>
467 where
468 Self: Send;
469
470 fn root_chunk_group_assets(
471 self: Vc<Self>,
472 ident: Vc<AssetIdent>,
473 chunk_group: ChunkGroup,
474 module_graph: Vc<ModuleGraph>,
475 ) -> Vc<OutputAssetsWithReferenced>
476 where
477 Self: Send;
478
479 fn evaluated_chunk_group_assets(
480 self: Vc<Self>,
481 ident: Vc<AssetIdent>,
482 chunk_group: ChunkGroup,
483 module_graph: Vc<ModuleGraph>,
484 availability_info: AvailabilityInfo,
485 ) -> Vc<OutputAssetsWithReferenced>
486 where
487 Self: Send;
488
489 fn entry_chunk_group_asset(
490 self: Vc<Self>,
491 path: FileSystemPath,
492 evaluatable_assets: Vc<EvaluatableAssets>,
493 module_graph: Vc<ModuleGraph>,
494 extra_chunks: Vc<OutputAssets>,
495 extra_referenced_assets: Vc<OutputAssets>,
496 availability_info: AvailabilityInfo,
497 ) -> Vc<Box<dyn OutputAsset>>
498 where
499 Self: Send;
500
501 fn root_entry_chunk_group(
502 self: Vc<Self>,
503 path: FileSystemPath,
504 evaluatable_assets: Vc<EvaluatableAssets>,
505 module_graph: Vc<ModuleGraph>,
506 extra_chunks: Vc<OutputAssets>,
507 extra_referenced_assets: Vc<OutputAssets>,
508 ) -> Vc<EntryChunkGroupResult>
509 where
510 Self: Send;
511
512 fn root_entry_chunk_group_asset(
513 self: Vc<Self>,
514 path: FileSystemPath,
515 evaluatable_assets: Vc<EvaluatableAssets>,
516 module_graph: Vc<ModuleGraph>,
517 extra_chunks: Vc<OutputAssets>,
518 extra_referenced_assets: Vc<OutputAssets>,
519 ) -> Vc<Box<dyn OutputAsset>>
520 where
521 Self: Send;
522
523 fn chunk_group_assets(
524 self: Vc<Self>,
525 ident: Vc<AssetIdent>,
526 chunk_group: ChunkGroup,
527 module_graph: Vc<ModuleGraph>,
528 availability_info: AvailabilityInfo,
529 ) -> Vc<OutputAssetsWithReferenced>
530 where
531 Self: Send;
532
533 fn relative_path_from_chunk_root_to_project_root(self: Vc<Self>) -> Vc<RcStr>
537 where
538 Self: Send;
539}
540
541impl<T: ChunkingContext + Send + Upcast<Box<dyn ChunkingContext>>> ChunkingContextExt for T {
542 fn root_chunk_group(
543 self: Vc<Self>,
544 ident: Vc<AssetIdent>,
545 chunk_group: ChunkGroup,
546 module_graph: Vc<ModuleGraph>,
547 ) -> Vc<ChunkGroupResult> {
548 self.chunk_group(ident, chunk_group, module_graph, AvailabilityInfo::root())
549 }
550
551 fn root_chunk_group_assets(
552 self: Vc<Self>,
553 ident: Vc<AssetIdent>,
554 chunk_group: ChunkGroup,
555 module_graph: Vc<ModuleGraph>,
556 ) -> Vc<OutputAssetsWithReferenced> {
557 root_chunk_group_assets(
558 Vc::upcast_non_strict(self),
559 ident,
560 chunk_group,
561 module_graph,
562 )
563 }
564
565 fn evaluated_chunk_group_assets(
566 self: Vc<Self>,
567 ident: Vc<AssetIdent>,
568 chunk_group: ChunkGroup,
569 module_graph: Vc<ModuleGraph>,
570 availability_info: AvailabilityInfo,
571 ) -> Vc<OutputAssetsWithReferenced> {
572 evaluated_chunk_group_assets(
573 Vc::upcast_non_strict(self),
574 ident,
575 chunk_group,
576 module_graph,
577 availability_info,
578 )
579 }
580
581 fn entry_chunk_group_asset(
582 self: Vc<Self>,
583 path: FileSystemPath,
584 evaluatable_assets: Vc<EvaluatableAssets>,
585 module_graph: Vc<ModuleGraph>,
586 extra_chunks: Vc<OutputAssets>,
587 extra_referenced_assets: Vc<OutputAssets>,
588 availability_info: AvailabilityInfo,
589 ) -> Vc<Box<dyn OutputAsset>> {
590 entry_chunk_group_asset(
591 Vc::upcast_non_strict(self),
592 path,
593 evaluatable_assets,
594 module_graph,
595 extra_chunks,
596 extra_referenced_assets,
597 availability_info,
598 )
599 }
600
601 fn root_entry_chunk_group(
602 self: Vc<Self>,
603 path: FileSystemPath,
604 evaluatable_assets: Vc<EvaluatableAssets>,
605 module_graph: Vc<ModuleGraph>,
606 extra_chunks: Vc<OutputAssets>,
607 extra_referenced_assets: Vc<OutputAssets>,
608 ) -> Vc<EntryChunkGroupResult> {
609 self.entry_chunk_group(
610 path,
611 evaluatable_assets,
612 module_graph,
613 extra_chunks,
614 extra_referenced_assets,
615 AvailabilityInfo::root(),
616 )
617 }
618
619 fn root_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 ) -> Vc<Box<dyn OutputAsset>> {
627 entry_chunk_group_asset(
628 Vc::upcast_non_strict(self),
629 path,
630 evaluatable_assets,
631 module_graph,
632 extra_chunks,
633 extra_referenced_assets,
634 AvailabilityInfo::root(),
635 )
636 }
637
638 fn chunk_group_assets(
639 self: Vc<Self>,
640 ident: Vc<AssetIdent>,
641 chunk_group: ChunkGroup,
642 module_graph: Vc<ModuleGraph>,
643 availability_info: AvailabilityInfo,
644 ) -> Vc<OutputAssetsWithReferenced> {
645 chunk_group_assets(
646 Vc::upcast_non_strict(self),
647 ident,
648 chunk_group,
649 module_graph,
650 availability_info,
651 )
652 }
653
654 fn relative_path_from_chunk_root_to_project_root(self: Vc<Self>) -> Vc<RcStr> {
655 relative_path_from_chunk_root_to_project_root(Vc::upcast_non_strict(self))
656 }
657}
658
659#[turbo_tasks::function]
660async fn relative_path_from_chunk_root_to_project_root(
661 chunking_context: Vc<Box<dyn ChunkingContext>>,
662) -> Result<Vc<RcStr>> {
663 let chunk_root_path = chunking_context.chunk_root_path().await?;
679 let output_root = chunking_context.output_root().await?;
680 let chunk_to_output_root = chunk_root_path.get_relative_path_to(&output_root);
681 let Some(chunk_to_output_root) = chunk_to_output_root else {
682 bail!(
683 "expected chunk_root_path: {chunk_root_path} to be inside of output_root: \
684 {output_root}",
685 chunk_root_path = chunk_root_path.value_to_string().await?,
686 output_root = output_root.value_to_string().await?
687 );
688 };
689 let output_root_to_chunk_root_path = chunking_context.output_root_to_root_path().await?;
690
691 Ok(Vc::cell(
693 format!(
694 "{}/{}",
695 chunk_to_output_root, output_root_to_chunk_root_path
696 )
697 .into(),
698 ))
699}
700
701#[turbo_tasks::function]
702fn root_chunk_group_assets(
703 chunking_context: Vc<Box<dyn ChunkingContext>>,
704 ident: Vc<AssetIdent>,
705 chunk_group: ChunkGroup,
706 module_graph: Vc<ModuleGraph>,
707) -> Vc<OutputAssetsWithReferenced> {
708 chunking_context
709 .root_chunk_group(ident, chunk_group, module_graph)
710 .output_assets_with_referenced()
711}
712
713#[turbo_tasks::function]
714fn evaluated_chunk_group_assets(
715 chunking_context: Vc<Box<dyn ChunkingContext>>,
716 ident: Vc<AssetIdent>,
717 chunk_group: ChunkGroup,
718 module_graph: Vc<ModuleGraph>,
719 availability_info: AvailabilityInfo,
720) -> Vc<OutputAssetsWithReferenced> {
721 chunking_context
722 .evaluated_chunk_group(ident, chunk_group, module_graph, availability_info)
723 .output_assets_with_referenced()
724}
725
726#[turbo_tasks::function]
727async fn entry_chunk_group_asset(
728 chunking_context: Vc<Box<dyn ChunkingContext>>,
729 path: FileSystemPath,
730 evaluatable_assets: Vc<EvaluatableAssets>,
731 module_graph: Vc<ModuleGraph>,
732 extra_chunks: Vc<OutputAssets>,
733 extra_referenced_assets: Vc<OutputAssets>,
734 availability_info: AvailabilityInfo,
735) -> Result<Vc<Box<dyn OutputAsset>>> {
736 Ok(*chunking_context
737 .entry_chunk_group(
738 path,
739 evaluatable_assets,
740 module_graph,
741 extra_chunks,
742 extra_referenced_assets,
743 availability_info,
744 )
745 .await?
746 .asset)
747}
748
749#[turbo_tasks::function]
750fn chunk_group_assets(
751 chunking_context: Vc<Box<dyn ChunkingContext>>,
752 ident: Vc<AssetIdent>,
753 chunk_group: ChunkGroup,
754 module_graph: Vc<ModuleGraph>,
755 availability_info: AvailabilityInfo,
756) -> Vc<OutputAssetsWithReferenced> {
757 chunking_context
758 .chunk_group(ident, chunk_group, module_graph, availability_info)
759 .output_assets_with_referenced()
760}