Skip to main content

turbopack_core/chunk/
chunking_context.rs

1use anyhow::Result;
2use bincode::{Decode, Encode};
3use rustc_hash::{FxHashMap, FxHashSet};
4use serde::{Deserialize, Serialize};
5use turbo_rcstr::RcStr;
6use turbo_tasks::{ResolvedVc, Upcast, Vc, trace::TraceRawVcs, turbobail};
7use turbo_tasks_fs::FileSystemPath;
8use turbo_tasks_hash::DeterministicHash;
9
10use crate::{
11    asset::{Asset, AssetContent},
12    chunk::{
13        ChunkItem, ChunkType, ChunkableModule, availability_info::AvailabilityInfo,
14        chunk_id_strategy::ModuleIdStrategy,
15    },
16    environment::{ChunkLoading, Environment},
17    ident::AssetIdent,
18    module::Module,
19    module_graph::{
20        ModuleGraph, binding_usage_info::ModuleExportUsage, chunk_group_info::ChunkGroup,
21        module_batches::BatchingConfig, style_groups::StyleGroupsAlgorithm,
22    },
23    output::{
24        ExpandOutputAssetsInput, OutputAsset, OutputAssets, OutputAssetsReferences,
25        OutputAssetsWithReferenced, expand_output_assets,
26    },
27    reference::ModuleReference,
28};
29
30#[turbo_tasks::task_input]
31#[derive(
32    Debug,
33    Clone,
34    Copy,
35    PartialEq,
36    Eq,
37    Hash,
38    Deserialize,
39    TraceRawVcs,
40    DeterministicHash,
41    Encode,
42    Decode,
43)]
44#[serde(rename_all = "kebab-case")]
45pub enum MangleType {
46    OptimalSize,
47    Deterministic,
48}
49
50#[turbo_tasks::value(shared, task_input)]
51#[derive(Debug, Clone, Copy, Hash, DeterministicHash, Deserialize)]
52pub enum MinifyType {
53    // TODO instead of adding a new property here,
54    // refactor that to Minify(MinifyOptions) to allow defaults on MinifyOptions
55    Minify { mangle: Option<MangleType> },
56    NoMinify,
57}
58
59impl Default for MinifyType {
60    fn default() -> Self {
61        Self::Minify {
62            mangle: Some(MangleType::OptimalSize),
63        }
64    }
65}
66
67#[turbo_tasks::value(shared, task_input)]
68#[derive(Debug, Default, Clone, Copy, Hash, DeterministicHash)]
69pub enum SourceMapsType {
70    /// Extracts source maps from input files and writes source maps for output files.
71    #[default]
72    Full,
73    /// Ignores existing input source maps, but writes source maps for output files.
74    Partial,
75    /// Ignores the existence of source maps and does not write source maps for output files.
76    None,
77}
78
79/// Suffix to append to asset URLs.
80#[turbo_tasks::value(shared)]
81#[derive(Debug, Clone)]
82pub enum AssetSuffix {
83    /// No suffix.
84    None,
85    /// A constant suffix to append to URLs.
86    Constant(RcStr),
87    /// Infer the suffix at runtime from the script src attribute.
88    /// Only valid in browser runtime for chunk loading, not for static asset URL generation.
89    Inferred,
90    /// Read the suffix from a global variable at runtime.
91    /// Used for server-side rendering where the suffix is set via `globalThis.{global_name}`.
92    FromGlobal(RcStr),
93}
94
95/// URL behavior configuration for static assets.
96#[turbo_tasks::value(shared)]
97#[derive(Debug, Clone)]
98pub struct UrlBehavior {
99    pub suffix: AssetSuffix,
100    /// Static suffix for contexts that cannot use dynamic JS expressions (e.g., CSS `url()`
101    /// references). Must be a constant string known at build time (e.g., `?dpl=<deployment_id>`).
102    pub static_suffix: ResolvedVc<Option<RcStr>>,
103}
104
105#[turbo_tasks::task_input]
106#[derive(
107    Debug,
108    Clone,
109    Copy,
110    PartialEq,
111    Eq,
112    Hash,
113    Serialize,
114    Deserialize,
115    TraceRawVcs,
116    DeterministicHash,
117    Encode,
118    Decode,
119)]
120pub enum ChunkGroupType {
121    Entry,
122    Evaluated,
123}
124
125#[turbo_tasks::value(shared)]
126#[derive(Clone)]
127pub struct ChunkGroupResult {
128    pub assets: ResolvedVc<OutputAssets>,
129    pub referenced_assets: ResolvedVc<OutputAssets>,
130    pub references: ResolvedVc<OutputAssetsReferences>,
131    pub availability_info: AvailabilityInfo,
132    pub chunk_group_bootstrap_params: Option<RcStr>,
133}
134
135impl ChunkGroupResult {
136    pub fn empty() -> Vc<Self> {
137        ChunkGroupResult {
138            assets: ResolvedVc::cell(vec![]),
139            referenced_assets: ResolvedVc::cell(vec![]),
140            references: ResolvedVc::cell(vec![]),
141            availability_info: AvailabilityInfo::root(),
142            chunk_group_bootstrap_params: None,
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            chunk_group_bootstrap_params: None,
154        }
155        .resolved_cell()
156    }
157}
158
159#[turbo_tasks::value_impl]
160impl ChunkGroupResult {
161    #[turbo_tasks::function]
162    pub fn output_assets_with_referenced(&self) -> Vc<OutputAssetsWithReferenced> {
163        OutputAssetsWithReferenced {
164            assets: self.assets,
165            referenced_assets: self.referenced_assets,
166            references: self.references,
167        }
168        .cell()
169    }
170
171    #[turbo_tasks::function]
172    pub async fn concatenate(&self, next: Vc<Self>) -> Result<Vc<Self>> {
173        let next = next.await?;
174        Ok(ChunkGroupResult {
175            assets: self.assets.concatenate(*next.assets).to_resolved().await?,
176            referenced_assets: self
177                .referenced_assets
178                .concatenate(*next.referenced_assets)
179                .to_resolved()
180                .await?,
181            references: self
182                .references
183                .concatenate(*next.references)
184                .to_resolved()
185                .await?,
186            availability_info: next.availability_info,
187            chunk_group_bootstrap_params: next.chunk_group_bootstrap_params.clone(),
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?)
200                    .map(ExpandOutputAssetsInput::Asset)
201                    .chain(
202                        self.references
203                            .await?
204                            .into_iter()
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                    .map(ExpandOutputAssetsInput::Asset)
228                    .chain(
229                        self.references
230                            .await?
231                            .into_iter()
232                            .map(ExpandOutputAssetsInput::Reference),
233                    ),
234                false,
235            )
236            .await?,
237        ))
238    }
239}
240
241#[turbo_tasks::value(shared)]
242pub struct EntryChunkGroupResult {
243    pub asset: ResolvedVc<Box<dyn OutputAsset>>,
244    pub availability_info: AvailabilityInfo,
245}
246
247#[turbo_tasks::task_input]
248#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, TraceRawVcs, Encode, Decode)]
249pub struct ChunkingConfig {
250    /// Try to avoid creating more than 1 chunk smaller than this size.
251    /// It merges multiple small chunks into bigger ones to avoid that.
252    pub min_chunk_size: usize,
253
254    /// Try to avoid creating more than this number of chunks per group.
255    /// It merges multiple chunks into bigger ones to avoid that.
256    pub max_chunk_count_per_group: usize,
257
258    /// Never merges chunks bigger than this size with other chunks.
259    /// This makes sure that code in big chunks is not duplicated in multiple chunks.
260    pub max_merge_chunk_size: usize,
261
262    /// When enabled, a merged chunk also emits its constituent component chunks (referenced,
263    /// loaded on demand) so the runtime can fetch an individual component chunk instead of the
264    /// whole merged chunk when it is already cached.
265    pub generate_component_chunks: bool,
266
267    /// Minimum size for a component chunk to be emitted on its own when
268    /// `generate_component_chunks` is enabled. Component chunks smaller than this are folded
269    /// into a single remainder component chunk.
270    pub min_component_chunk_size: usize,
271
272    /// Selects the algorithm used to compute
273    /// [`crate::module_graph::style_groups::StyleGroups`]. Only consulted for the CSS chunk
274    /// type.
275    pub style_groups_algorithm: StyleGroupsAlgorithm,
276
277    /// First-page-load priority as an integer percentage (`0..=100`), or `None` to use the
278    /// default. Used by the production chunker's merge heuristics.
279    pub first_page_load_priority: Option<u32>,
280
281    /// Priority boost as an integer percentage (e.g. `150` for a 1.5x boost), or `None` to use the
282    /// default. Used by the production chunker's merge heuristics.
283    pub priority_boost_percent: Option<u32>,
284
285    /// Estimated request cost in bytes, or `None` to use the default. Used by the production
286    /// chunker's merge heuristics.
287    pub request_cost: Option<u64>,
288
289    #[allow(dead_code)]
290    pub placeholder_for_future_extensions: (),
291}
292
293#[turbo_tasks::value(transparent)]
294pub struct ChunkingConfigs(FxHashMap<ResolvedVc<Box<dyn ChunkType>>, ChunkingConfig>);
295
296#[turbo_tasks::value(shared)]
297#[derive(Debug, Clone, Copy, Hash, Default, Deserialize)]
298pub enum SourceMapSourceType {
299    AbsoluteFileUri,
300    RelativeUri,
301    #[default]
302    TurbopackUri,
303}
304
305#[turbo_tasks::value(transparent, cell = "keyed")]
306pub struct UnusedReferences(FxHashSet<ResolvedVc<Box<dyn ModuleReference>>>);
307
308#[turbo_tasks::value(shared)]
309#[derive(Debug, Clone, Default)]
310pub struct WorkerConfigurationOptions {
311    /// The worker base-path override. When `Some`, takes precedence over
312    /// `chunk_base_path` for the worker entrypoint URL and the module chunks
313    /// loaded inside the worker.
314    pub asset_prefix: Option<RcStr>,
315    /// The list of global variable names to forward to workers. These globals
316    /// are read from `globalThis` at worker creation time and passed to the
317    /// worker via URL params.
318    pub forwarded_globals: Vec<RcStr>,
319}
320
321/// A context for the chunking that influences the way chunks are created
322#[turbo_tasks::value_trait]
323pub trait ChunkingContext {
324    #[turbo_tasks::function]
325    fn name(self: Vc<Self>) -> Vc<RcStr>;
326    #[turbo_tasks::function]
327    fn source_map_source_type(self: Vc<Self>) -> Vc<SourceMapSourceType>;
328    /// The root path of the project
329    #[turbo_tasks::function]
330    fn root_path(self: Vc<Self>) -> Vc<FileSystemPath>;
331    /// The output root path in the output filesystem
332    #[turbo_tasks::function]
333    fn output_root(self: Vc<Self>) -> Vc<FileSystemPath>;
334    /// A relative path how to reach the root path from the output root. This is used to compute
335    /// original paths at runtime relative to the output files. e. g. import.meta.url needs that.
336    #[turbo_tasks::function]
337    fn output_root_to_root_path(self: Vc<Self>) -> Vc<RcStr>;
338
339    // TODO remove this, a chunking context should not be bound to a specific
340    // environment since this can change due to transitions in the module graph
341    #[turbo_tasks::function]
342    fn environment(self: Vc<Self>) -> Vc<Environment>;
343
344    /// The path to the folder where all chunks are placed. This can be used to compute relative
345    /// paths.
346    #[turbo_tasks::function]
347    fn chunk_root_path(self: Vc<Self>) -> Vc<FileSystemPath>;
348
349    #[turbo_tasks::function]
350    fn chunk_loading(self: Vc<Self>) -> Vc<ChunkLoading> {
351        self.environment().chunk_loading()
352    }
353
354    // TODO(alexkirsz) Remove this from the chunking context. This should be at the
355    // discretion of chunking context implementors. However, we currently use this
356    // in a couple of places in `turbopack-css`, so we need to remove that
357    // dependency first.
358    #[turbo_tasks::function]
359    fn chunk_path(
360        self: Vc<Self>,
361        asset: Option<Vc<Box<dyn Asset>>>,
362        ident: Vc<AssetIdent>,
363        content_hashing_prefix: Option<RcStr>,
364        extension: RcStr,
365    ) -> Vc<FileSystemPath>;
366
367    /// Reference Source Map Assets for chunks
368    #[turbo_tasks::function]
369    fn reference_chunk_source_maps(self: Vc<Self>, chunk: Vc<Box<dyn OutputAsset>>) -> Vc<bool>;
370
371    /// Include Source Maps for modules
372    #[turbo_tasks::function]
373    fn reference_module_source_maps(self: Vc<Self>, module: Vc<Box<dyn Module>>) -> Vc<bool>;
374
375    /// Returns a URL (relative or absolute, depending on the asset prefix) to
376    /// the static asset based on its `ident`.
377    /// The `tag` is an arbitrary string that can be used to distinguish
378    /// different usages of the same asset (e.g. different base paths).
379    #[turbo_tasks::function]
380    fn asset_url(self: Vc<Self>, ident: FileSystemPath, tag: Option<RcStr>) -> Result<Vc<RcStr>>;
381
382    #[turbo_tasks::function]
383    fn service_worker_scope_base_path(self: Vc<Self>) -> Vc<RcStr> {
384        Vc::cell(RcStr::default())
385    }
386
387    #[turbo_tasks::function]
388    fn asset_path(
389        self: Vc<Self>,
390        content: Vc<AssetContent>,
391        original_asset_ident: Vc<AssetIdent>,
392        tag: Option<RcStr>,
393    ) -> Vc<FileSystemPath>;
394
395    /// Returns the URL behavior for a given tag.
396    /// This determines how asset URLs are suffixed (e.g., for deployment IDs).
397    #[turbo_tasks::function]
398    fn url_behavior(self: Vc<Self>, _tag: Option<RcStr>) -> Vc<UrlBehavior> {
399        UrlBehavior {
400            suffix: AssetSuffix::Inferred,
401            static_suffix: ResolvedVc::cell(None),
402        }
403        .cell()
404    }
405
406    #[turbo_tasks::function]
407    fn chunking_configs(self: Vc<Self>) -> Vc<ChunkingConfigs> {
408        Vc::cell(Default::default())
409    }
410
411    #[turbo_tasks::function]
412    fn batching_config(self: Vc<Self>) -> Vc<BatchingConfig> {
413        BatchingConfig::new(BatchingConfig {
414            ..Default::default()
415        })
416    }
417
418    /// Whether async modules should create an new availability boundary and therefore nested async
419    /// modules include less modules. Enabling this will lead to better optimized async chunks,
420    /// but it will require to compute all possible paths in the application, which might lead to
421    /// many combinations.
422    #[turbo_tasks::function]
423    fn is_nested_async_availability_enabled(self: Vc<Self>) -> Vc<bool> {
424        Vc::cell(false)
425    }
426
427    /// Whether to use `MergeableModule` to merge modules if possible.
428    #[turbo_tasks::function]
429    fn is_module_merging_enabled(self: Vc<Self>) -> Vc<bool> {
430        Vc::cell(false)
431    }
432
433    /// Whether to include information about the content of the chunk into the runtime, to allow
434    /// more incremental loading of individual chunk items.
435    #[turbo_tasks::function]
436    fn is_dynamic_chunk_content_loading_enabled(self: Vc<Self>) -> Vc<bool> {
437        Vc::cell(false)
438    }
439
440    #[turbo_tasks::function]
441    fn minify_type(self: Vc<Self>) -> Vc<MinifyType> {
442        MinifyType::NoMinify.cell()
443    }
444
445    #[turbo_tasks::function]
446    fn should_use_absolute_url_references(self: Vc<Self>) -> Vc<bool> {
447        Vc::cell(false)
448    }
449
450    #[turbo_tasks::function]
451    fn async_loader_chunk_item(
452        &self,
453        module: Vc<Box<dyn ChunkableModule>>,
454        module_graph: Vc<ModuleGraph>,
455        availability_info: AvailabilityInfo,
456    ) -> Vc<Box<dyn ChunkItem>>;
457    #[turbo_tasks::function]
458    fn async_loader_chunk_item_ident(&self, module: Vc<Box<dyn ChunkableModule>>)
459    -> Vc<AssetIdent>;
460
461    #[turbo_tasks::function]
462    fn chunk_group(
463        self: Vc<Self>,
464        ident: Vc<AssetIdent>,
465        chunk_group: ChunkGroup,
466        module_graph: Vc<ModuleGraph>,
467        availability_info: AvailabilityInfo,
468    ) -> Vc<ChunkGroupResult>;
469
470    /// Like [`Self::chunk_group`], but additionally produces an evaluate chunk
471    /// (and, in dev, a chunk-list register chunk) that bootstraps and runs
472    /// `chunk_group`'s entries.
473    ///
474    /// `extra_chunks` are not part of this chunk group's module graph, but they
475    /// are loaded alongside the entries (and tracked in the chunk-list register
476    /// chunk for HMR) — used to extend the entry's HMR-tracked chunks with
477    /// chunks computed elsewhere (e.g. app-router client references).
478    #[turbo_tasks::function]
479    fn evaluated_chunk_group(
480        self: Vc<Self>,
481        ident: Vc<AssetIdent>,
482        chunk_group: ChunkGroup,
483        module_graph: Vc<ModuleGraph>,
484        extra_chunks: Vc<OutputAssets>,
485        availability_info: AvailabilityInfo,
486    ) -> Vc<ChunkGroupResult>;
487
488    /// In development, produces a standalone HMR chunk-list register chunk
489    /// that tracks `chunks` for hot-module-replacement without producing an
490    /// evaluate chunk. Returns `None` (empty vec) outside dev or when HMR is
491    /// disabled. Used to register a page-specific chunk list that covers
492    /// client-reference chunks built outside the shared module graph.
493    #[turbo_tasks::function]
494    fn hmr_chunk_list(
495        self: Vc<Self>,
496        _ident: Vc<AssetIdent>,
497        _chunks: Vc<OutputAssets>,
498    ) -> Vc<OutputAssets> {
499        OutputAssets::empty()
500    }
501
502    /// Generates an output chunk that:
503    /// * loads the given extra_chunks in addition to the generated chunks; and
504    /// * evaluates the given assets; and
505    /// * exports the result of evaluating the last module as a CommonJS default export.
506    #[turbo_tasks::function]
507    fn entry_chunk_group(
508        self: Vc<Self>,
509        path: FileSystemPath,
510        chunk_group: ChunkGroup,
511        module_graph: Vc<ModuleGraph>,
512        extra_chunks: Vc<OutputAssets>,
513        extra_referenced_assets: Vc<OutputAssets>,
514        availability_info: AvailabilityInfo,
515    ) -> Result<Vc<EntryChunkGroupResult>>;
516
517    #[turbo_tasks::function]
518    async fn chunk_item_id_strategy(self: Vc<Self>) -> Result<Vc<ModuleIdStrategy>>;
519
520    #[turbo_tasks::function]
521    async fn module_export_usage(
522        self: Vc<Self>,
523        module: Vc<Box<dyn Module>>,
524    ) -> Result<Vc<ModuleExportUsage>>;
525
526    #[turbo_tasks::function]
527    async fn unused_references(self: Vc<Self>) -> Result<Vc<UnusedReferences>>;
528
529    /// Returns whether debug IDs are enabled for this chunking context.
530    #[turbo_tasks::function]
531    fn debug_ids_enabled(self: Vc<Self>) -> Vc<bool>;
532
533    /// Returns the worker-related configuration: the base-path override and the
534    /// list of globals to forward to workers.
535    #[turbo_tasks::function]
536    fn worker_configuration_options(self: Vc<Self>) -> Vc<WorkerConfigurationOptions> {
537        WorkerConfigurationOptions::default().cell()
538    }
539
540    /// Returns the worker entrypoint for this chunking context.
541    #[turbo_tasks::function]
542    async fn worker_entrypoint(self: Vc<Self>) -> Result<Vc<Box<dyn OutputAsset>>> {
543        turbobail!("Worker entrypoint is not supported by {}", self.name());
544    }
545}
546pub trait ChunkingContextExt {
547    fn root_chunk_group(
548        self: Vc<Self>,
549        ident: Vc<AssetIdent>,
550        chunk_group: ChunkGroup,
551        module_graph: Vc<ModuleGraph>,
552    ) -> Vc<ChunkGroupResult>
553    where
554        Self: Send;
555
556    fn root_chunk_group_assets(
557        self: Vc<Self>,
558        ident: Vc<AssetIdent>,
559        chunk_group: ChunkGroup,
560        module_graph: Vc<ModuleGraph>,
561    ) -> Vc<OutputAssetsWithReferenced>
562    where
563        Self: Send;
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        extra_chunks: Vc<OutputAssets>,
571        availability_info: AvailabilityInfo,
572    ) -> Vc<OutputAssetsWithReferenced>
573    where
574        Self: Send;
575
576    fn entry_chunk_group_asset(
577        self: Vc<Self>,
578        path: FileSystemPath,
579        chunk_group: ChunkGroup,
580        module_graph: Vc<ModuleGraph>,
581        extra_chunks: Vc<OutputAssets>,
582        extra_referenced_assets: Vc<OutputAssets>,
583        availability_info: AvailabilityInfo,
584    ) -> Vc<Box<dyn OutputAsset>>
585    where
586        Self: Send;
587
588    fn root_entry_chunk_group(
589        self: Vc<Self>,
590        path: FileSystemPath,
591        chunk_group: ChunkGroup,
592        module_graph: Vc<ModuleGraph>,
593        extra_chunks: Vc<OutputAssets>,
594        extra_referenced_assets: Vc<OutputAssets>,
595    ) -> Vc<EntryChunkGroupResult>
596    where
597        Self: Send;
598
599    fn root_entry_chunk_group_asset(
600        self: Vc<Self>,
601        path: FileSystemPath,
602        chunk_group: ChunkGroup,
603        module_graph: Vc<ModuleGraph>,
604        extra_chunks: Vc<OutputAssets>,
605        extra_referenced_assets: Vc<OutputAssets>,
606    ) -> Vc<Box<dyn OutputAsset>>
607    where
608        Self: Send;
609
610    fn chunk_group_assets(
611        self: Vc<Self>,
612        ident: Vc<AssetIdent>,
613        chunk_group: ChunkGroup,
614        module_graph: Vc<ModuleGraph>,
615        availability_info: AvailabilityInfo,
616    ) -> Vc<OutputAssetsWithReferenced>
617    where
618        Self: Send;
619
620    /// Computes the relative path from the chunk output root to the project root.
621    ///
622    /// This is used to compute relative paths for source maps in certain configurations.
623    fn relative_path_from_chunk_root_to_project_root(self: Vc<Self>) -> Vc<RcStr>
624    where
625        Self: Send;
626}
627
628impl<T: ChunkingContext + Send + Upcast<Box<dyn ChunkingContext>>> ChunkingContextExt for T {
629    fn root_chunk_group(
630        self: Vc<Self>,
631        ident: Vc<AssetIdent>,
632        chunk_group: ChunkGroup,
633        module_graph: Vc<ModuleGraph>,
634    ) -> Vc<ChunkGroupResult> {
635        self.chunk_group(ident, chunk_group, module_graph, AvailabilityInfo::root())
636    }
637
638    fn root_chunk_group_assets(
639        self: Vc<Self>,
640        ident: Vc<AssetIdent>,
641        chunk_group: ChunkGroup,
642        module_graph: Vc<ModuleGraph>,
643    ) -> Vc<OutputAssetsWithReferenced> {
644        root_chunk_group_assets(
645            Vc::upcast_non_strict(self),
646            ident,
647            chunk_group,
648            module_graph,
649        )
650    }
651
652    fn evaluated_chunk_group_assets(
653        self: Vc<Self>,
654        ident: Vc<AssetIdent>,
655        chunk_group: ChunkGroup,
656        module_graph: Vc<ModuleGraph>,
657        extra_chunks: Vc<OutputAssets>,
658        availability_info: AvailabilityInfo,
659    ) -> Vc<OutputAssetsWithReferenced> {
660        evaluated_chunk_group_assets(
661            Vc::upcast_non_strict(self),
662            ident,
663            chunk_group,
664            module_graph,
665            extra_chunks,
666            availability_info,
667        )
668    }
669
670    fn entry_chunk_group_asset(
671        self: Vc<Self>,
672        path: FileSystemPath,
673        chunk_group: ChunkGroup,
674        module_graph: Vc<ModuleGraph>,
675        extra_chunks: Vc<OutputAssets>,
676        extra_referenced_assets: Vc<OutputAssets>,
677        availability_info: AvailabilityInfo,
678    ) -> Vc<Box<dyn OutputAsset>> {
679        entry_chunk_group_asset(
680            Vc::upcast_non_strict(self),
681            path,
682            chunk_group,
683            module_graph,
684            extra_chunks,
685            extra_referenced_assets,
686            availability_info,
687        )
688    }
689
690    fn root_entry_chunk_group(
691        self: Vc<Self>,
692        path: FileSystemPath,
693        chunk_group: ChunkGroup,
694        module_graph: Vc<ModuleGraph>,
695        extra_chunks: Vc<OutputAssets>,
696        extra_referenced_assets: Vc<OutputAssets>,
697    ) -> Vc<EntryChunkGroupResult> {
698        self.entry_chunk_group(
699            path,
700            chunk_group,
701            module_graph,
702            extra_chunks,
703            extra_referenced_assets,
704            AvailabilityInfo::root(),
705        )
706    }
707
708    fn root_entry_chunk_group_asset(
709        self: Vc<Self>,
710        path: FileSystemPath,
711        chunk_group: ChunkGroup,
712        module_graph: Vc<ModuleGraph>,
713        extra_chunks: Vc<OutputAssets>,
714        extra_referenced_assets: Vc<OutputAssets>,
715    ) -> Vc<Box<dyn OutputAsset>> {
716        entry_chunk_group_asset(
717            Vc::upcast_non_strict(self),
718            path,
719            chunk_group,
720            module_graph,
721            extra_chunks,
722            extra_referenced_assets,
723            AvailabilityInfo::root(),
724        )
725    }
726
727    fn chunk_group_assets(
728        self: Vc<Self>,
729        ident: Vc<AssetIdent>,
730        chunk_group: ChunkGroup,
731        module_graph: Vc<ModuleGraph>,
732        availability_info: AvailabilityInfo,
733    ) -> Vc<OutputAssetsWithReferenced> {
734        chunk_group_assets(
735            Vc::upcast_non_strict(self),
736            ident,
737            chunk_group,
738            module_graph,
739            availability_info,
740        )
741    }
742
743    fn relative_path_from_chunk_root_to_project_root(self: Vc<Self>) -> Vc<RcStr> {
744        relative_path_from_chunk_root_to_project_root(Vc::upcast_non_strict(self))
745    }
746}
747
748#[turbo_tasks::function]
749async fn relative_path_from_chunk_root_to_project_root(
750    chunking_context: Vc<Box<dyn ChunkingContext>>,
751) -> Result<Vc<RcStr>> {
752    // Example,
753    //   project root: /project/root
754    //   output root: /project/root/dist
755    //   chunk root path: /project/root/dist/ssr/chunks
756    //   output_root_to_chunk_root: ../
757    //
758    // Example2,
759    //   project root: /project/root
760    //   output root: /project/out
761    //   chunk root path: /project/out/ssr/chunks
762    //   output_root_to_chunk_root: ../root
763    //
764    // From that we want to return  ../../../root to get from a path in `chunks` to a path in the
765    // project root.
766
767    let chunk_root_path = chunking_context.chunk_root_path().await?;
768    let output_root = chunking_context.output_root().await?;
769    let chunk_to_output_root = chunk_root_path.get_relative_path_to(&output_root);
770    let Some(chunk_to_output_root) = chunk_to_output_root else {
771        turbobail!(
772            "expected chunk_root_path: {} to be inside of output_root: {}",
773            chunking_context.chunk_root_path(),
774            chunking_context.output_root()
775        );
776    };
777    let output_root_to_chunk_root_path = chunking_context.output_root_to_root_path().await?;
778
779    // Note we cannot use `normalize_path` here since it rejects paths that start with `../`
780    Ok(Vc::cell(
781        format!(
782            "{}/{}",
783            chunk_to_output_root, output_root_to_chunk_root_path
784        )
785        .into(),
786    ))
787}
788
789#[turbo_tasks::function]
790fn root_chunk_group_assets(
791    chunking_context: Vc<Box<dyn ChunkingContext>>,
792    ident: Vc<AssetIdent>,
793    chunk_group: ChunkGroup,
794    module_graph: Vc<ModuleGraph>,
795) -> Vc<OutputAssetsWithReferenced> {
796    chunking_context
797        .root_chunk_group(ident, chunk_group, module_graph)
798        .output_assets_with_referenced()
799}
800
801#[turbo_tasks::function]
802fn evaluated_chunk_group_assets(
803    chunking_context: Vc<Box<dyn ChunkingContext>>,
804    ident: Vc<AssetIdent>,
805    chunk_group: ChunkGroup,
806    module_graph: Vc<ModuleGraph>,
807    extra_chunks: Vc<OutputAssets>,
808    availability_info: AvailabilityInfo,
809) -> Vc<OutputAssetsWithReferenced> {
810    chunking_context
811        .evaluated_chunk_group(
812            ident,
813            chunk_group,
814            module_graph,
815            extra_chunks,
816            availability_info,
817        )
818        .output_assets_with_referenced()
819}
820
821#[turbo_tasks::function]
822async fn entry_chunk_group_asset(
823    chunking_context: Vc<Box<dyn ChunkingContext>>,
824    path: FileSystemPath,
825    chunk_group: ChunkGroup,
826    module_graph: Vc<ModuleGraph>,
827    extra_chunks: Vc<OutputAssets>,
828    extra_referenced_assets: Vc<OutputAssets>,
829    availability_info: AvailabilityInfo,
830) -> Result<Vc<Box<dyn OutputAsset>>> {
831    Ok(*chunking_context
832        .entry_chunk_group(
833            path,
834            chunk_group,
835            module_graph,
836            extra_chunks,
837            extra_referenced_assets,
838            availability_info,
839        )
840        .await?
841        .asset)
842}
843
844#[turbo_tasks::function]
845fn chunk_group_assets(
846    chunking_context: Vc<Box<dyn ChunkingContext>>,
847    ident: Vc<AssetIdent>,
848    chunk_group: ChunkGroup,
849    module_graph: Vc<ModuleGraph>,
850    availability_info: AvailabilityInfo,
851) -> Vc<OutputAssetsWithReferenced> {
852    chunking_context
853        .chunk_group(ident, chunk_group, module_graph, availability_info)
854        .output_assets_with_referenced()
855}