Skip to main content

turbopack_core/chunk/
chunking_context.rs

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    // TODO instead of adding a new property here,
55    // refactor that to Minify(MinifyOptions) to allow defaults on MinifyOptions
56    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    /// Extracts source maps from input files and writes source maps for output files.
72    #[default]
73    Full,
74    /// Ignores existing input source maps, but writes source maps for output files.
75    Partial,
76    /// Ignores the existence of source maps and does not write source maps for output files.
77    None,
78}
79
80/// Suffix to append to asset URLs.
81#[turbo_tasks::value(shared)]
82#[derive(Debug, Clone)]
83pub enum AssetSuffix {
84    /// No suffix.
85    None,
86    /// A constant suffix to append to URLs.
87    Constant(RcStr),
88    /// Infer the suffix at runtime from the script src attribute.
89    /// Only valid in browser runtime for chunk loading, not for static asset URL generation.
90    Inferred,
91    /// Read the suffix from a global variable at runtime.
92    /// Used for server-side rendering where the suffix is set via `globalThis.{global_name}`.
93    FromGlobal(RcStr),
94}
95
96/// URL behavior configuration for static assets.
97#[turbo_tasks::value(shared)]
98#[derive(Debug, Clone)]
99pub struct UrlBehavior {
100    pub suffix: AssetSuffix,
101    /// Static suffix for contexts that cannot use dynamic JS expressions (e.g., CSS `url()`
102    /// references). Must be a constant string known at build time (e.g., `?dpl=<deployment_id>`).
103    pub static_suffix: ResolvedVc<Option<RcStr>>,
104}
105
106#[derive(
107    Debug,
108    TaskInput,
109    Clone,
110    Copy,
111    PartialEq,
112    Eq,
113    Hash,
114    Serialize,
115    Deserialize,
116    TraceRawVcs,
117    DeterministicHash,
118    NonLocalValue,
119    Encode,
120    Decode,
121)]
122pub enum ChunkGroupType {
123    Entry,
124    Evaluated,
125}
126
127#[turbo_tasks::value(shared)]
128#[derive(Clone)]
129pub struct ChunkGroupResult {
130    pub assets: ResolvedVc<OutputAssets>,
131    pub referenced_assets: ResolvedVc<OutputAssets>,
132    pub references: ResolvedVc<OutputAssetsReferences>,
133    pub availability_info: AvailabilityInfo,
134}
135
136impl ChunkGroupResult {
137    pub fn empty() -> Vc<Self> {
138        ChunkGroupResult {
139            assets: ResolvedVc::cell(vec![]),
140            referenced_assets: ResolvedVc::cell(vec![]),
141            references: ResolvedVc::cell(vec![]),
142            availability_info: AvailabilityInfo::root(),
143        }
144        .cell()
145    }
146
147    pub fn empty_resolved() -> ResolvedVc<Self> {
148        ChunkGroupResult {
149            assets: ResolvedVc::cell(vec![]),
150            referenced_assets: ResolvedVc::cell(vec![]),
151            references: ResolvedVc::cell(vec![]),
152            availability_info: AvailabilityInfo::root(),
153        }
154        .resolved_cell()
155    }
156}
157
158#[turbo_tasks::value_impl]
159impl ChunkGroupResult {
160    #[turbo_tasks::function]
161    pub async fn output_assets_with_referenced(&self) -> Result<Vc<OutputAssetsWithReferenced>> {
162        Ok(OutputAssetsWithReferenced {
163            assets: self.assets,
164            referenced_assets: self.referenced_assets,
165            references: self.references,
166        }
167        .cell())
168    }
169
170    #[turbo_tasks::function]
171    pub async fn concatenate(&self, next: Vc<Self>) -> Result<Vc<Self>> {
172        let next = next.await?;
173        Ok(ChunkGroupResult {
174            assets: self.assets.concatenate(*next.assets).to_resolved().await?,
175            referenced_assets: self
176                .referenced_assets
177                .concatenate(*next.referenced_assets)
178                .to_resolved()
179                .await?,
180            references: self
181                .references
182                .concatenate(*next.references)
183                .to_resolved()
184                .await?,
185            availability_info: next.availability_info,
186        }
187        .cell())
188    }
189
190    #[turbo_tasks::function]
191    pub async fn all_assets(&self) -> Result<Vc<OutputAssets>> {
192        Ok(Vc::cell(
193            expand_output_assets(
194                self.assets
195                    .await?
196                    .into_iter()
197                    .chain(self.referenced_assets.await?.into_iter())
198                    .copied()
199                    .map(ExpandOutputAssetsInput::Asset)
200                    .chain(
201                        self.references
202                            .await?
203                            .into_iter()
204                            .copied()
205                            .map(ExpandOutputAssetsInput::Reference),
206                    ),
207                false,
208            )
209            .await?,
210        ))
211    }
212
213    /// Returns only primary asset entries. Doesn't expand OutputAssets. Doesn't return referenced
214    /// assets.
215    #[turbo_tasks::function]
216    pub fn primary_assets(&self) -> Vc<OutputAssets> {
217        *self.assets
218    }
219
220    #[turbo_tasks::function]
221    pub async fn referenced_assets(&self) -> Result<Vc<OutputAssets>> {
222        Ok(Vc::cell(
223            expand_output_assets(
224                self.referenced_assets
225                    .await?
226                    .into_iter()
227                    .copied()
228                    .map(ExpandOutputAssetsInput::Asset)
229                    .chain(
230                        self.references
231                            .await?
232                            .into_iter()
233                            .copied()
234                            .map(ExpandOutputAssetsInput::Reference),
235                    ),
236                false,
237            )
238            .await?,
239        ))
240    }
241}
242
243#[turbo_tasks::value(shared)]
244pub struct EntryChunkGroupResult {
245    pub asset: ResolvedVc<Box<dyn OutputAsset>>,
246    pub availability_info: AvailabilityInfo,
247}
248
249#[derive(
250    Default,
251    Debug,
252    Clone,
253    PartialEq,
254    Eq,
255    Hash,
256    TraceRawVcs,
257    NonLocalValue,
258    TaskInput,
259    Encode,
260    Decode,
261)]
262pub struct ChunkingConfig {
263    /// Try to avoid creating more than 1 chunk smaller than this size.
264    /// It merges multiple small chunks into bigger ones to avoid that.
265    pub min_chunk_size: usize,
266
267    /// Try to avoid creating more than this number of chunks per group.
268    /// It merges multiple chunks into bigger ones to avoid that.
269    pub max_chunk_count_per_group: usize,
270
271    /// Never merges chunks bigger than this size with other chunks.
272    /// This makes sure that code in big chunks is not duplicated in multiple chunks.
273    pub max_merge_chunk_size: usize,
274
275    #[allow(dead_code)]
276    pub placeholder_for_future_extensions: (),
277}
278
279#[turbo_tasks::value(transparent)]
280pub struct ChunkingConfigs(FxHashMap<ResolvedVc<Box<dyn ChunkType>>, ChunkingConfig>);
281
282#[turbo_tasks::value(shared)]
283#[derive(Debug, Clone, Copy, Hash, TaskInput, Default, Deserialize)]
284pub enum SourceMapSourceType {
285    AbsoluteFileUri,
286    RelativeUri,
287    #[default]
288    TurbopackUri,
289}
290
291#[turbo_tasks::value(transparent, cell = "keyed")]
292pub struct UnusedReferences(FxHashSet<ResolvedVc<Box<dyn ModuleReference>>>);
293
294/// A context for the chunking that influences the way chunks are created
295#[turbo_tasks::value_trait]
296pub trait ChunkingContext {
297    #[turbo_tasks::function]
298    fn name(self: Vc<Self>) -> Vc<RcStr>;
299    #[turbo_tasks::function]
300    fn source_map_source_type(self: Vc<Self>) -> Vc<SourceMapSourceType>;
301    /// The root path of the project
302    #[turbo_tasks::function]
303    fn root_path(self: Vc<Self>) -> Vc<FileSystemPath>;
304    /// The output root path in the output filesystem
305    #[turbo_tasks::function]
306    fn output_root(self: Vc<Self>) -> Vc<FileSystemPath>;
307    /// A relative path how to reach the root path from the output root. This is used to compute
308    /// original paths at runtime relative to the output files. e. g. import.meta.url needs that.
309    #[turbo_tasks::function]
310    fn output_root_to_root_path(self: Vc<Self>) -> Vc<RcStr>;
311
312    // TODO remove this, a chunking context should not be bound to a specific
313    // environment since this can change due to transitions in the module graph
314    #[turbo_tasks::function]
315    fn environment(self: Vc<Self>) -> Vc<Environment>;
316
317    /// The path to the folder where all chunks are placed. This can be used to compute relative
318    /// paths.
319    #[turbo_tasks::function]
320    fn chunk_root_path(self: Vc<Self>) -> Vc<FileSystemPath>;
321
322    // TODO(alexkirsz) Remove this from the chunking context. This should be at the
323    // discretion of chunking context implementors. However, we currently use this
324    // in a couple of places in `turbopack-css`, so we need to remove that
325    // dependency first.
326    #[turbo_tasks::function]
327    fn chunk_path(
328        self: Vc<Self>,
329        asset: Option<Vc<Box<dyn Asset>>>,
330        ident: Vc<AssetIdent>,
331        content_hashing_prefix: Option<RcStr>,
332        extension: RcStr,
333    ) -> Vc<FileSystemPath>;
334
335    /// Reference Source Map Assets for chunks
336    #[turbo_tasks::function]
337    fn reference_chunk_source_maps(self: Vc<Self>, chunk: Vc<Box<dyn OutputAsset>>) -> Vc<bool>;
338
339    /// Include Source Maps for modules
340    #[turbo_tasks::function]
341    fn reference_module_source_maps(self: Vc<Self>, module: Vc<Box<dyn Module>>) -> Vc<bool>;
342
343    /// Returns a URL (relative or absolute, depending on the asset prefix) to
344    /// the static asset based on its `ident`.
345    /// The `tag` is an arbitrary string that can be used to distinguish
346    /// different usages of the same asset (e.g. different base paths).
347    #[turbo_tasks::function]
348    fn asset_url(self: Vc<Self>, ident: FileSystemPath, tag: Option<RcStr>) -> Result<Vc<RcStr>>;
349
350    #[turbo_tasks::function]
351    fn asset_path(
352        self: Vc<Self>,
353        content_hash: RcStr,
354        original_asset_ident: Vc<AssetIdent>,
355        tag: Option<RcStr>,
356    ) -> Vc<FileSystemPath>;
357
358    /// Returns the URL behavior for a given tag.
359    /// This determines how asset URLs are suffixed (e.g., for deployment IDs).
360    #[turbo_tasks::function]
361    fn url_behavior(self: Vc<Self>, _tag: Option<RcStr>) -> Vc<UrlBehavior> {
362        UrlBehavior {
363            suffix: AssetSuffix::Inferred,
364            static_suffix: ResolvedVc::cell(None),
365        }
366        .cell()
367    }
368
369    #[turbo_tasks::function]
370    fn is_hot_module_replacement_enabled(self: Vc<Self>) -> Vc<bool> {
371        Vc::cell(false)
372    }
373
374    #[turbo_tasks::function]
375    fn chunking_configs(self: Vc<Self>) -> Vc<ChunkingConfigs> {
376        Vc::cell(Default::default())
377    }
378
379    #[turbo_tasks::function]
380    fn batching_config(self: Vc<Self>) -> Vc<BatchingConfig> {
381        BatchingConfig::new(BatchingConfig {
382            ..Default::default()
383        })
384    }
385
386    /// Whether `ChunkingType::Traced` are used to create corresponding output assets for each
387    /// traced module.
388    #[turbo_tasks::function]
389    fn is_tracing_enabled(self: Vc<Self>) -> Vc<bool> {
390        Vc::cell(false)
391    }
392
393    /// Whether async modules should create an new availability boundary and therefore nested async
394    /// modules include less modules. Enabling this will lead to better optimized async chunks,
395    /// but it will require to compute all possible paths in the application, which might lead to
396    /// many combinations.
397    #[turbo_tasks::function]
398    fn is_nested_async_availability_enabled(self: Vc<Self>) -> Vc<bool> {
399        Vc::cell(false)
400    }
401
402    /// Whether to use `MergeableModule` to merge modules if possible.
403    #[turbo_tasks::function]
404    fn is_module_merging_enabled(self: Vc<Self>) -> Vc<bool> {
405        Vc::cell(false)
406    }
407
408    /// Whether to include information about the content of the chunk into the runtime, to allow
409    /// more incremental loading of individual chunk items.
410    #[turbo_tasks::function]
411    fn is_dynamic_chunk_content_loading_enabled(self: Vc<Self>) -> Vc<bool> {
412        Vc::cell(false)
413    }
414
415    #[turbo_tasks::function]
416    fn minify_type(self: Vc<Self>) -> Vc<MinifyType> {
417        MinifyType::NoMinify.cell()
418    }
419
420    #[turbo_tasks::function]
421    fn should_use_absolute_url_references(self: Vc<Self>) -> Vc<bool> {
422        Vc::cell(false)
423    }
424
425    #[turbo_tasks::function]
426    fn async_loader_chunk_item(
427        &self,
428        module: Vc<Box<dyn ChunkableModule>>,
429        module_graph: Vc<ModuleGraph>,
430        availability_info: AvailabilityInfo,
431    ) -> Vc<Box<dyn ChunkItem>>;
432    #[turbo_tasks::function]
433    fn async_loader_chunk_item_ident(&self, module: Vc<Box<dyn ChunkableModule>>)
434    -> Vc<AssetIdent>;
435
436    #[turbo_tasks::function]
437    fn chunk_group(
438        self: Vc<Self>,
439        ident: Vc<AssetIdent>,
440        chunk_group: ChunkGroup,
441        module_graph: Vc<ModuleGraph>,
442        availability_info: AvailabilityInfo,
443    ) -> Vc<ChunkGroupResult>;
444
445    #[turbo_tasks::function]
446    fn evaluated_chunk_group(
447        self: Vc<Self>,
448        ident: Vc<AssetIdent>,
449        chunk_group: ChunkGroup,
450        module_graph: Vc<ModuleGraph>,
451        availability_info: AvailabilityInfo,
452    ) -> Vc<ChunkGroupResult>;
453
454    /// Generates an output chunk that:
455    /// * loads the given extra_chunks in addition to the generated chunks; and
456    /// * evaluates the given assets; and
457    /// * exports the result of evaluating the last module as a CommonJS default export.
458    #[turbo_tasks::function]
459    fn entry_chunk_group(
460        self: Vc<Self>,
461        path: FileSystemPath,
462        evaluatable_assets: Vc<EvaluatableAssets>,
463        module_graph: Vc<ModuleGraph>,
464        extra_chunks: Vc<OutputAssets>,
465        extra_referenced_assets: Vc<OutputAssets>,
466        availability_info: AvailabilityInfo,
467    ) -> Result<Vc<EntryChunkGroupResult>>;
468
469    #[turbo_tasks::function]
470    async fn chunk_item_id_strategy(self: Vc<Self>) -> Result<Vc<ModuleIdStrategy>>;
471
472    #[turbo_tasks::function]
473    async fn module_export_usage(
474        self: Vc<Self>,
475        module: Vc<Box<dyn Module>>,
476    ) -> Result<Vc<ModuleExportUsage>>;
477
478    #[turbo_tasks::function]
479    async fn unused_references(self: Vc<Self>) -> Result<Vc<UnusedReferences>>;
480
481    /// Returns whether debug IDs are enabled for this chunking context.
482    #[turbo_tasks::function]
483    fn debug_ids_enabled(self: Vc<Self>) -> Vc<bool>;
484
485    /// Returns the list of global variable names to forward to workers.
486    /// These globals are read from globalThis at worker creation time and passed
487    /// to the worker via URL params.
488    #[turbo_tasks::function]
489    fn worker_forwarded_globals(self: Vc<Self>) -> Vc<Vec<RcStr>> {
490        Vc::cell(vec![])
491    }
492
493    /// Returns the worker entrypoint for this chunking context.
494    #[turbo_tasks::function]
495    async fn worker_entrypoint(self: Vc<Self>) -> Result<Vc<Box<dyn OutputAsset>>> {
496        bail!(
497            "Worker entrypoint is not supported by {name}",
498            name = self.name().await?
499        );
500    }
501}
502pub trait ChunkingContextExt {
503    fn root_chunk_group(
504        self: Vc<Self>,
505        ident: Vc<AssetIdent>,
506        chunk_group: ChunkGroup,
507        module_graph: Vc<ModuleGraph>,
508    ) -> Vc<ChunkGroupResult>
509    where
510        Self: Send;
511
512    fn root_chunk_group_assets(
513        self: Vc<Self>,
514        ident: Vc<AssetIdent>,
515        chunk_group: ChunkGroup,
516        module_graph: Vc<ModuleGraph>,
517    ) -> Vc<OutputAssetsWithReferenced>
518    where
519        Self: Send;
520
521    fn evaluated_chunk_group_assets(
522        self: Vc<Self>,
523        ident: Vc<AssetIdent>,
524        chunk_group: ChunkGroup,
525        module_graph: Vc<ModuleGraph>,
526        availability_info: AvailabilityInfo,
527    ) -> Vc<OutputAssetsWithReferenced>
528    where
529        Self: Send;
530
531    fn entry_chunk_group_asset(
532        self: Vc<Self>,
533        path: FileSystemPath,
534        evaluatable_assets: Vc<EvaluatableAssets>,
535        module_graph: Vc<ModuleGraph>,
536        extra_chunks: Vc<OutputAssets>,
537        extra_referenced_assets: Vc<OutputAssets>,
538        availability_info: AvailabilityInfo,
539    ) -> Vc<Box<dyn OutputAsset>>
540    where
541        Self: Send;
542
543    fn root_entry_chunk_group(
544        self: Vc<Self>,
545        path: FileSystemPath,
546        evaluatable_assets: Vc<EvaluatableAssets>,
547        module_graph: Vc<ModuleGraph>,
548        extra_chunks: Vc<OutputAssets>,
549        extra_referenced_assets: Vc<OutputAssets>,
550    ) -> Vc<EntryChunkGroupResult>
551    where
552        Self: Send;
553
554    fn root_entry_chunk_group_asset(
555        self: Vc<Self>,
556        path: FileSystemPath,
557        evaluatable_assets: Vc<EvaluatableAssets>,
558        module_graph: Vc<ModuleGraph>,
559        extra_chunks: Vc<OutputAssets>,
560        extra_referenced_assets: Vc<OutputAssets>,
561    ) -> Vc<Box<dyn OutputAsset>>
562    where
563        Self: Send;
564
565    fn 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    where
573        Self: Send;
574
575    /// Computes the relative path from the chunk output root to the project root.
576    ///
577    /// This is used to compute relative paths for source maps in certain configurations.
578    fn relative_path_from_chunk_root_to_project_root(self: Vc<Self>) -> Vc<RcStr>
579    where
580        Self: Send;
581}
582
583impl<T: ChunkingContext + Send + Upcast<Box<dyn ChunkingContext>>> ChunkingContextExt for T {
584    fn root_chunk_group(
585        self: Vc<Self>,
586        ident: Vc<AssetIdent>,
587        chunk_group: ChunkGroup,
588        module_graph: Vc<ModuleGraph>,
589    ) -> Vc<ChunkGroupResult> {
590        self.chunk_group(ident, chunk_group, module_graph, AvailabilityInfo::root())
591    }
592
593    fn root_chunk_group_assets(
594        self: Vc<Self>,
595        ident: Vc<AssetIdent>,
596        chunk_group: ChunkGroup,
597        module_graph: Vc<ModuleGraph>,
598    ) -> Vc<OutputAssetsWithReferenced> {
599        root_chunk_group_assets(
600            Vc::upcast_non_strict(self),
601            ident,
602            chunk_group,
603            module_graph,
604        )
605    }
606
607    fn evaluated_chunk_group_assets(
608        self: Vc<Self>,
609        ident: Vc<AssetIdent>,
610        chunk_group: ChunkGroup,
611        module_graph: Vc<ModuleGraph>,
612        availability_info: AvailabilityInfo,
613    ) -> Vc<OutputAssetsWithReferenced> {
614        evaluated_chunk_group_assets(
615            Vc::upcast_non_strict(self),
616            ident,
617            chunk_group,
618            module_graph,
619            availability_info,
620        )
621    }
622
623    fn entry_chunk_group_asset(
624        self: Vc<Self>,
625        path: FileSystemPath,
626        evaluatable_assets: Vc<EvaluatableAssets>,
627        module_graph: Vc<ModuleGraph>,
628        extra_chunks: Vc<OutputAssets>,
629        extra_referenced_assets: Vc<OutputAssets>,
630        availability_info: AvailabilityInfo,
631    ) -> Vc<Box<dyn OutputAsset>> {
632        entry_chunk_group_asset(
633            Vc::upcast_non_strict(self),
634            path,
635            evaluatable_assets,
636            module_graph,
637            extra_chunks,
638            extra_referenced_assets,
639            availability_info,
640        )
641    }
642
643    fn root_entry_chunk_group(
644        self: Vc<Self>,
645        path: FileSystemPath,
646        evaluatable_assets: Vc<EvaluatableAssets>,
647        module_graph: Vc<ModuleGraph>,
648        extra_chunks: Vc<OutputAssets>,
649        extra_referenced_assets: Vc<OutputAssets>,
650    ) -> Vc<EntryChunkGroupResult> {
651        self.entry_chunk_group(
652            path,
653            evaluatable_assets,
654            module_graph,
655            extra_chunks,
656            extra_referenced_assets,
657            AvailabilityInfo::root(),
658        )
659    }
660
661    fn root_entry_chunk_group_asset(
662        self: Vc<Self>,
663        path: FileSystemPath,
664        evaluatable_assets: Vc<EvaluatableAssets>,
665        module_graph: Vc<ModuleGraph>,
666        extra_chunks: Vc<OutputAssets>,
667        extra_referenced_assets: Vc<OutputAssets>,
668    ) -> Vc<Box<dyn OutputAsset>> {
669        entry_chunk_group_asset(
670            Vc::upcast_non_strict(self),
671            path,
672            evaluatable_assets,
673            module_graph,
674            extra_chunks,
675            extra_referenced_assets,
676            AvailabilityInfo::root(),
677        )
678    }
679
680    fn chunk_group_assets(
681        self: Vc<Self>,
682        ident: Vc<AssetIdent>,
683        chunk_group: ChunkGroup,
684        module_graph: Vc<ModuleGraph>,
685        availability_info: AvailabilityInfo,
686    ) -> Vc<OutputAssetsWithReferenced> {
687        chunk_group_assets(
688            Vc::upcast_non_strict(self),
689            ident,
690            chunk_group,
691            module_graph,
692            availability_info,
693        )
694    }
695
696    fn relative_path_from_chunk_root_to_project_root(self: Vc<Self>) -> Vc<RcStr> {
697        relative_path_from_chunk_root_to_project_root(Vc::upcast_non_strict(self))
698    }
699}
700
701#[turbo_tasks::function]
702async fn relative_path_from_chunk_root_to_project_root(
703    chunking_context: Vc<Box<dyn ChunkingContext>>,
704) -> Result<Vc<RcStr>> {
705    // Example,
706    //   project root: /project/root
707    //   output root: /project/root/dist
708    //   chunk root path: /project/root/dist/ssr/chunks
709    //   output_root_to_chunk_root: ../
710    //
711    // Example2,
712    //   project root: /project/root
713    //   output root: /project/out
714    //   chunk root path: /project/out/ssr/chunks
715    //   output_root_to_chunk_root: ../root
716    //
717    // From that we want to return  ../../../root to get from a path in `chunks` to a path in the
718    // project root.
719
720    let chunk_root_path = chunking_context.chunk_root_path().await?;
721    let output_root = chunking_context.output_root().await?;
722    let chunk_to_output_root = chunk_root_path.get_relative_path_to(&output_root);
723    let Some(chunk_to_output_root) = chunk_to_output_root else {
724        bail!(
725            "expected chunk_root_path: {chunk_root_path} to be inside of output_root: \
726             {output_root}",
727            chunk_root_path = chunk_root_path.value_to_string().await?,
728            output_root = output_root.value_to_string().await?
729        );
730    };
731    let output_root_to_chunk_root_path = chunking_context.output_root_to_root_path().await?;
732
733    // Note we cannot use `normalize_path` here since it rejects paths that start with `../`
734    Ok(Vc::cell(
735        format!(
736            "{}/{}",
737            chunk_to_output_root, output_root_to_chunk_root_path
738        )
739        .into(),
740    ))
741}
742
743#[turbo_tasks::function]
744fn root_chunk_group_assets(
745    chunking_context: Vc<Box<dyn ChunkingContext>>,
746    ident: Vc<AssetIdent>,
747    chunk_group: ChunkGroup,
748    module_graph: Vc<ModuleGraph>,
749) -> Vc<OutputAssetsWithReferenced> {
750    chunking_context
751        .root_chunk_group(ident, chunk_group, module_graph)
752        .output_assets_with_referenced()
753}
754
755#[turbo_tasks::function]
756fn evaluated_chunk_group_assets(
757    chunking_context: Vc<Box<dyn ChunkingContext>>,
758    ident: Vc<AssetIdent>,
759    chunk_group: ChunkGroup,
760    module_graph: Vc<ModuleGraph>,
761    availability_info: AvailabilityInfo,
762) -> Vc<OutputAssetsWithReferenced> {
763    chunking_context
764        .evaluated_chunk_group(ident, chunk_group, module_graph, availability_info)
765        .output_assets_with_referenced()
766}
767
768#[turbo_tasks::function]
769async fn entry_chunk_group_asset(
770    chunking_context: Vc<Box<dyn ChunkingContext>>,
771    path: FileSystemPath,
772    evaluatable_assets: Vc<EvaluatableAssets>,
773    module_graph: Vc<ModuleGraph>,
774    extra_chunks: Vc<OutputAssets>,
775    extra_referenced_assets: Vc<OutputAssets>,
776    availability_info: AvailabilityInfo,
777) -> Result<Vc<Box<dyn OutputAsset>>> {
778    Ok(*chunking_context
779        .entry_chunk_group(
780            path,
781            evaluatable_assets,
782            module_graph,
783            extra_chunks,
784            extra_referenced_assets,
785            availability_info,
786        )
787        .await?
788        .asset)
789}
790
791#[turbo_tasks::function]
792fn chunk_group_assets(
793    chunking_context: Vc<Box<dyn ChunkingContext>>,
794    ident: Vc<AssetIdent>,
795    chunk_group: ChunkGroup,
796    module_graph: Vc<ModuleGraph>,
797    availability_info: AvailabilityInfo,
798) -> Vc<OutputAssetsWithReferenced> {
799    chunking_context
800        .chunk_group(ident, chunk_group, module_graph, availability_info)
801        .output_assets_with_referenced()
802}