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