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")]
306#[allow(clippy::type_complexity)]
307/// For each reference, the targets it resolves to that were dropped as unused. One reference can
308/// resolve to several targets, which are dropped independently.
309pub struct UnusedReferences(
310    FxHashMap<ResolvedVc<Box<dyn ModuleReference>>, FxHashSet<ResolvedVc<Box<dyn Module>>>>,
311);
312
313#[turbo_tasks::value(shared)]
314#[derive(Debug, Clone, Default)]
315pub struct WorkerConfigurationOptions {
316    /// The worker base-path override. When `Some`, takes precedence over
317    /// `chunk_base_path` for the worker entrypoint URL and the module chunks
318    /// loaded inside the worker.
319    pub asset_prefix: Option<RcStr>,
320    /// The list of global variable names to forward to workers. These globals
321    /// are read from `globalThis` at worker creation time and passed to the
322    /// worker via URL params.
323    pub forwarded_globals: Vec<RcStr>,
324}
325
326/// A context for the chunking that influences the way chunks are created
327#[turbo_tasks::value_trait]
328pub trait ChunkingContext {
329    #[turbo_tasks::function]
330    fn name(self: Vc<Self>) -> Vc<RcStr>;
331    #[turbo_tasks::function]
332    fn source_map_source_type(self: Vc<Self>) -> Vc<SourceMapSourceType>;
333    /// The root path of the project
334    #[turbo_tasks::function]
335    fn root_path(self: Vc<Self>) -> Vc<FileSystemPath>;
336    /// The output root path in the output filesystem
337    #[turbo_tasks::function]
338    fn output_root(self: Vc<Self>) -> Vc<FileSystemPath>;
339    /// A relative path how to reach the root path from the output root. This is used to compute
340    /// original paths at runtime relative to the output files. e. g. import.meta.url needs that.
341    #[turbo_tasks::function]
342    fn output_root_to_root_path(self: Vc<Self>) -> Vc<RcStr>;
343
344    // TODO remove this, a chunking context should not be bound to a specific
345    // environment since this can change due to transitions in the module graph
346    #[turbo_tasks::function]
347    fn environment(self: Vc<Self>) -> Vc<Environment>;
348
349    /// The path to the folder where all chunks are placed. This can be used to compute relative
350    /// paths.
351    #[turbo_tasks::function]
352    fn chunk_root_path(self: Vc<Self>) -> Vc<FileSystemPath>;
353
354    #[turbo_tasks::function]
355    fn chunk_loading(self: Vc<Self>) -> Vc<ChunkLoading> {
356        self.environment().chunk_loading()
357    }
358
359    // TODO(alexkirsz) Remove this from the chunking context. This should be at the
360    // discretion of chunking context implementors. However, we currently use this
361    // in a couple of places in `turbopack-css`, so we need to remove that
362    // dependency first.
363    #[turbo_tasks::function]
364    fn chunk_path(
365        self: Vc<Self>,
366        asset: Option<Vc<Box<dyn Asset>>>,
367        ident: Vc<AssetIdent>,
368        content_hashing_prefix: Option<RcStr>,
369        extension: RcStr,
370    ) -> Vc<FileSystemPath>;
371
372    /// Reference Source Map Assets for chunks
373    #[turbo_tasks::function]
374    fn reference_chunk_source_maps(self: Vc<Self>, chunk: Vc<Box<dyn OutputAsset>>) -> Vc<bool>;
375
376    /// Include Source Maps for modules
377    #[turbo_tasks::function]
378    fn reference_module_source_maps(self: Vc<Self>, module: Vc<Box<dyn Module>>) -> Vc<bool>;
379
380    /// Returns a URL (relative or absolute, depending on the asset prefix) to
381    /// the static asset based on its `ident`.
382    /// The `tag` is an arbitrary string that can be used to distinguish
383    /// different usages of the same asset (e.g. different base paths).
384    #[turbo_tasks::function]
385    fn asset_url(self: Vc<Self>, ident: FileSystemPath, tag: Option<RcStr>) -> Result<Vc<RcStr>>;
386
387    #[turbo_tasks::function]
388    fn service_worker_scope_base_path(self: Vc<Self>) -> Vc<RcStr> {
389        Vc::cell(RcStr::default())
390    }
391
392    #[turbo_tasks::function]
393    fn asset_path(
394        self: Vc<Self>,
395        content: Vc<AssetContent>,
396        original_asset_ident: Vc<AssetIdent>,
397        tag: Option<RcStr>,
398    ) -> Vc<FileSystemPath>;
399
400    /// Returns the URL behavior for a given tag.
401    /// This determines how asset URLs are suffixed (e.g., for deployment IDs).
402    #[turbo_tasks::function]
403    fn url_behavior(self: Vc<Self>, _tag: Option<RcStr>) -> Vc<UrlBehavior> {
404        UrlBehavior {
405            suffix: AssetSuffix::Inferred,
406            static_suffix: ResolvedVc::cell(None),
407        }
408        .cell()
409    }
410
411    #[turbo_tasks::function]
412    fn chunking_configs(self: Vc<Self>) -> Vc<ChunkingConfigs> {
413        Vc::cell(Default::default())
414    }
415
416    #[turbo_tasks::function]
417    fn batching_config(self: Vc<Self>) -> Vc<BatchingConfig> {
418        BatchingConfig::new(BatchingConfig {
419            ..Default::default()
420        })
421    }
422
423    /// Whether async modules should create an new availability boundary and therefore nested async
424    /// modules include less modules. Enabling this will lead to better optimized async chunks,
425    /// but it will require to compute all possible paths in the application, which might lead to
426    /// many combinations.
427    #[turbo_tasks::function]
428    fn is_nested_async_availability_enabled(self: Vc<Self>) -> Vc<bool> {
429        Vc::cell(false)
430    }
431
432    /// Whether to use `MergeableModule` to merge modules if possible.
433    #[turbo_tasks::function]
434    fn is_module_merging_enabled(self: Vc<Self>) -> Vc<bool> {
435        Vc::cell(false)
436    }
437
438    /// Whether to include information about the content of the chunk into the runtime, to allow
439    /// more incremental loading of individual chunk items.
440    #[turbo_tasks::function]
441    fn is_dynamic_chunk_content_loading_enabled(self: Vc<Self>) -> Vc<bool> {
442        Vc::cell(false)
443    }
444
445    #[turbo_tasks::function]
446    fn minify_type(self: Vc<Self>) -> Vc<MinifyType> {
447        MinifyType::NoMinify.cell()
448    }
449
450    #[turbo_tasks::function]
451    fn should_use_absolute_url_references(self: Vc<Self>) -> Vc<bool> {
452        Vc::cell(false)
453    }
454
455    #[turbo_tasks::function]
456    fn async_loader_chunk_item(
457        &self,
458        module: Vc<Box<dyn ChunkableModule>>,
459        module_graph: Vc<ModuleGraph>,
460        availability_info: AvailabilityInfo,
461    ) -> Vc<Box<dyn ChunkItem>>;
462    #[turbo_tasks::function]
463    fn async_loader_chunk_item_ident(&self, module: Vc<Box<dyn ChunkableModule>>)
464    -> Vc<AssetIdent>;
465
466    #[turbo_tasks::function]
467    fn chunk_group(
468        self: Vc<Self>,
469        ident: Vc<AssetIdent>,
470        chunk_group: ChunkGroup,
471        module_graph: Vc<ModuleGraph>,
472        availability_info: AvailabilityInfo,
473    ) -> Vc<ChunkGroupResult>;
474
475    /// Like [`Self::chunk_group`], but additionally produces an evaluate chunk
476    /// (and, in dev, a chunk-list register chunk) that bootstraps and runs
477    /// `chunk_group`'s entries.
478    ///
479    /// `extra_chunks` are not part of this chunk group's module graph, but they
480    /// are loaded alongside the entries (and tracked in the chunk-list register
481    /// chunk for HMR) — used to extend the entry's HMR-tracked chunks with
482    /// chunks computed elsewhere (e.g. app-router client references).
483    #[turbo_tasks::function]
484    fn evaluated_chunk_group(
485        self: Vc<Self>,
486        ident: Vc<AssetIdent>,
487        chunk_group: ChunkGroup,
488        module_graph: Vc<ModuleGraph>,
489        extra_chunks: Vc<OutputAssets>,
490        availability_info: AvailabilityInfo,
491    ) -> Vc<ChunkGroupResult>;
492
493    /// In development, produces a standalone HMR chunk-list register chunk
494    /// that tracks `chunks` for hot-module-replacement without producing an
495    /// evaluate chunk. Returns `None` (empty vec) outside dev or when HMR is
496    /// disabled. Used to register a page-specific chunk list that covers
497    /// client-reference chunks built outside the shared module graph.
498    #[turbo_tasks::function]
499    fn hmr_chunk_list(
500        self: Vc<Self>,
501        _ident: Vc<AssetIdent>,
502        _chunks: Vc<OutputAssets>,
503    ) -> Vc<OutputAssets> {
504        OutputAssets::empty()
505    }
506
507    /// Generates an output chunk that:
508    /// * loads the given extra_chunks in addition to the generated chunks; and
509    /// * evaluates the given assets; and
510    /// * exports the result of evaluating the last module as a CommonJS default export.
511    #[turbo_tasks::function]
512    fn entry_chunk_group(
513        self: Vc<Self>,
514        path: FileSystemPath,
515        chunk_group: ChunkGroup,
516        module_graph: Vc<ModuleGraph>,
517        extra_chunks: Vc<OutputAssets>,
518        extra_referenced_assets: Vc<OutputAssets>,
519        availability_info: AvailabilityInfo,
520    ) -> Result<Vc<EntryChunkGroupResult>>;
521
522    #[turbo_tasks::function]
523    async fn chunk_item_id_strategy(self: Vc<Self>) -> Result<Vc<ModuleIdStrategy>>;
524
525    #[turbo_tasks::function]
526    async fn module_export_usage(
527        self: Vc<Self>,
528        module: Vc<Box<dyn Module>>,
529    ) -> Result<Vc<ModuleExportUsage>>;
530
531    #[turbo_tasks::function]
532    async fn unused_references(self: Vc<Self>) -> Result<Vc<UnusedReferences>>;
533
534    /// Returns whether debug IDs are enabled for this chunking context.
535    #[turbo_tasks::function]
536    fn debug_ids_enabled(self: Vc<Self>) -> Vc<bool>;
537
538    /// Returns the worker-related configuration: the base-path override and the
539    /// list of globals to forward to workers.
540    #[turbo_tasks::function]
541    fn worker_configuration_options(self: Vc<Self>) -> Vc<WorkerConfigurationOptions> {
542        WorkerConfigurationOptions::default().cell()
543    }
544
545    /// Returns the worker entrypoint for this chunking context.
546    #[turbo_tasks::function]
547    async fn worker_entrypoint(self: Vc<Self>) -> Result<Vc<Box<dyn OutputAsset>>> {
548        turbobail!("Worker entrypoint is not supported by {}", self.name());
549    }
550}
551pub trait ChunkingContextExt {
552    fn root_chunk_group(
553        self: Vc<Self>,
554        ident: Vc<AssetIdent>,
555        chunk_group: ChunkGroup,
556        module_graph: Vc<ModuleGraph>,
557    ) -> Vc<ChunkGroupResult>
558    where
559        Self: Send;
560
561    fn root_chunk_group_assets(
562        self: Vc<Self>,
563        ident: Vc<AssetIdent>,
564        chunk_group: ChunkGroup,
565        module_graph: Vc<ModuleGraph>,
566    ) -> Vc<OutputAssetsWithReferenced>
567    where
568        Self: Send;
569
570    fn evaluated_chunk_group_assets(
571        self: Vc<Self>,
572        ident: Vc<AssetIdent>,
573        chunk_group: ChunkGroup,
574        module_graph: Vc<ModuleGraph>,
575        extra_chunks: Vc<OutputAssets>,
576        availability_info: AvailabilityInfo,
577    ) -> Vc<OutputAssetsWithReferenced>
578    where
579        Self: Send;
580
581    fn entry_chunk_group_asset(
582        self: Vc<Self>,
583        path: FileSystemPath,
584        chunk_group: ChunkGroup,
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    where
591        Self: Send;
592
593    fn root_entry_chunk_group(
594        self: Vc<Self>,
595        path: FileSystemPath,
596        chunk_group: ChunkGroup,
597        module_graph: Vc<ModuleGraph>,
598        extra_chunks: Vc<OutputAssets>,
599        extra_referenced_assets: Vc<OutputAssets>,
600    ) -> Vc<EntryChunkGroupResult>
601    where
602        Self: Send;
603
604    fn root_entry_chunk_group_asset(
605        self: Vc<Self>,
606        path: FileSystemPath,
607        chunk_group: ChunkGroup,
608        module_graph: Vc<ModuleGraph>,
609        extra_chunks: Vc<OutputAssets>,
610        extra_referenced_assets: Vc<OutputAssets>,
611    ) -> Vc<Box<dyn OutputAsset>>
612    where
613        Self: Send;
614
615    fn chunk_group_assets(
616        self: Vc<Self>,
617        ident: Vc<AssetIdent>,
618        chunk_group: ChunkGroup,
619        module_graph: Vc<ModuleGraph>,
620        availability_info: AvailabilityInfo,
621    ) -> Vc<OutputAssetsWithReferenced>
622    where
623        Self: Send;
624
625    /// Computes the relative path from the chunk output root to the project root.
626    ///
627    /// This is used to compute relative paths for source maps in certain configurations.
628    fn relative_path_from_chunk_root_to_project_root(self: Vc<Self>) -> Vc<RcStr>
629    where
630        Self: Send;
631}
632
633impl<T: ChunkingContext + Send + Upcast<Box<dyn ChunkingContext>>> ChunkingContextExt for T {
634    fn root_chunk_group(
635        self: Vc<Self>,
636        ident: Vc<AssetIdent>,
637        chunk_group: ChunkGroup,
638        module_graph: Vc<ModuleGraph>,
639    ) -> Vc<ChunkGroupResult> {
640        self.chunk_group(ident, chunk_group, module_graph, AvailabilityInfo::root())
641    }
642
643    fn root_chunk_group_assets(
644        self: Vc<Self>,
645        ident: Vc<AssetIdent>,
646        chunk_group: ChunkGroup,
647        module_graph: Vc<ModuleGraph>,
648    ) -> Vc<OutputAssetsWithReferenced> {
649        root_chunk_group_assets(
650            Vc::upcast_non_strict(self),
651            ident,
652            chunk_group,
653            module_graph,
654        )
655    }
656
657    fn evaluated_chunk_group_assets(
658        self: Vc<Self>,
659        ident: Vc<AssetIdent>,
660        chunk_group: ChunkGroup,
661        module_graph: Vc<ModuleGraph>,
662        extra_chunks: Vc<OutputAssets>,
663        availability_info: AvailabilityInfo,
664    ) -> Vc<OutputAssetsWithReferenced> {
665        evaluated_chunk_group_assets(
666            Vc::upcast_non_strict(self),
667            ident,
668            chunk_group,
669            module_graph,
670            extra_chunks,
671            availability_info,
672        )
673    }
674
675    fn entry_chunk_group_asset(
676        self: Vc<Self>,
677        path: FileSystemPath,
678        chunk_group: ChunkGroup,
679        module_graph: Vc<ModuleGraph>,
680        extra_chunks: Vc<OutputAssets>,
681        extra_referenced_assets: Vc<OutputAssets>,
682        availability_info: AvailabilityInfo,
683    ) -> Vc<Box<dyn OutputAsset>> {
684        entry_chunk_group_asset(
685            Vc::upcast_non_strict(self),
686            path,
687            chunk_group,
688            module_graph,
689            extra_chunks,
690            extra_referenced_assets,
691            availability_info,
692        )
693    }
694
695    fn root_entry_chunk_group(
696        self: Vc<Self>,
697        path: FileSystemPath,
698        chunk_group: ChunkGroup,
699        module_graph: Vc<ModuleGraph>,
700        extra_chunks: Vc<OutputAssets>,
701        extra_referenced_assets: Vc<OutputAssets>,
702    ) -> Vc<EntryChunkGroupResult> {
703        self.entry_chunk_group(
704            path,
705            chunk_group,
706            module_graph,
707            extra_chunks,
708            extra_referenced_assets,
709            AvailabilityInfo::root(),
710        )
711    }
712
713    fn root_entry_chunk_group_asset(
714        self: Vc<Self>,
715        path: FileSystemPath,
716        chunk_group: ChunkGroup,
717        module_graph: Vc<ModuleGraph>,
718        extra_chunks: Vc<OutputAssets>,
719        extra_referenced_assets: Vc<OutputAssets>,
720    ) -> Vc<Box<dyn OutputAsset>> {
721        entry_chunk_group_asset(
722            Vc::upcast_non_strict(self),
723            path,
724            chunk_group,
725            module_graph,
726            extra_chunks,
727            extra_referenced_assets,
728            AvailabilityInfo::root(),
729        )
730    }
731
732    fn chunk_group_assets(
733        self: Vc<Self>,
734        ident: Vc<AssetIdent>,
735        chunk_group: ChunkGroup,
736        module_graph: Vc<ModuleGraph>,
737        availability_info: AvailabilityInfo,
738    ) -> Vc<OutputAssetsWithReferenced> {
739        chunk_group_assets(
740            Vc::upcast_non_strict(self),
741            ident,
742            chunk_group,
743            module_graph,
744            availability_info,
745        )
746    }
747
748    fn relative_path_from_chunk_root_to_project_root(self: Vc<Self>) -> Vc<RcStr> {
749        relative_path_from_chunk_root_to_project_root(Vc::upcast_non_strict(self))
750    }
751}
752
753#[turbo_tasks::function]
754async fn relative_path_from_chunk_root_to_project_root(
755    chunking_context: Vc<Box<dyn ChunkingContext>>,
756) -> Result<Vc<RcStr>> {
757    // Example,
758    //   project root: /project/root
759    //   output root: /project/root/dist
760    //   chunk root path: /project/root/dist/ssr/chunks
761    //   output_root_to_chunk_root: ../
762    //
763    // Example2,
764    //   project root: /project/root
765    //   output root: /project/out
766    //   chunk root path: /project/out/ssr/chunks
767    //   output_root_to_chunk_root: ../root
768    //
769    // From that we want to return  ../../../root to get from a path in `chunks` to a path in the
770    // project root.
771
772    let chunk_root_path = chunking_context.chunk_root_path().await?;
773    let output_root = chunking_context.output_root().await?;
774    let chunk_to_output_root = chunk_root_path.get_relative_path_to(&output_root);
775    let Some(chunk_to_output_root) = chunk_to_output_root else {
776        turbobail!(
777            "expected chunk_root_path: {} to be inside of output_root: {}",
778            chunking_context.chunk_root_path(),
779            chunking_context.output_root()
780        );
781    };
782    let output_root_to_chunk_root_path = chunking_context.output_root_to_root_path().await?;
783
784    // Note we cannot use `normalize_path` here since it rejects paths that start with `../`
785    Ok(Vc::cell(
786        format!(
787            "{}/{}",
788            chunk_to_output_root, output_root_to_chunk_root_path
789        )
790        .into(),
791    ))
792}
793
794#[turbo_tasks::function]
795fn root_chunk_group_assets(
796    chunking_context: Vc<Box<dyn ChunkingContext>>,
797    ident: Vc<AssetIdent>,
798    chunk_group: ChunkGroup,
799    module_graph: Vc<ModuleGraph>,
800) -> Vc<OutputAssetsWithReferenced> {
801    chunking_context
802        .root_chunk_group(ident, chunk_group, module_graph)
803        .output_assets_with_referenced()
804}
805
806#[turbo_tasks::function]
807fn evaluated_chunk_group_assets(
808    chunking_context: Vc<Box<dyn ChunkingContext>>,
809    ident: Vc<AssetIdent>,
810    chunk_group: ChunkGroup,
811    module_graph: Vc<ModuleGraph>,
812    extra_chunks: Vc<OutputAssets>,
813    availability_info: AvailabilityInfo,
814) -> Vc<OutputAssetsWithReferenced> {
815    chunking_context
816        .evaluated_chunk_group(
817            ident,
818            chunk_group,
819            module_graph,
820            extra_chunks,
821            availability_info,
822        )
823        .output_assets_with_referenced()
824}
825
826#[turbo_tasks::function]
827async fn entry_chunk_group_asset(
828    chunking_context: Vc<Box<dyn ChunkingContext>>,
829    path: FileSystemPath,
830    chunk_group: ChunkGroup,
831    module_graph: Vc<ModuleGraph>,
832    extra_chunks: Vc<OutputAssets>,
833    extra_referenced_assets: Vc<OutputAssets>,
834    availability_info: AvailabilityInfo,
835) -> Result<Vc<Box<dyn OutputAsset>>> {
836    Ok(*chunking_context
837        .entry_chunk_group(
838            path,
839            chunk_group,
840            module_graph,
841            extra_chunks,
842            extra_referenced_assets,
843            availability_info,
844        )
845        .await?
846        .asset)
847}
848
849#[turbo_tasks::function]
850fn chunk_group_assets(
851    chunking_context: Vc<Box<dyn ChunkingContext>>,
852    ident: Vc<AssetIdent>,
853    chunk_group: ChunkGroup,
854    module_graph: Vc<ModuleGraph>,
855    availability_info: AvailabilityInfo,
856) -> Vc<OutputAssetsWithReferenced> {
857    chunking_context
858        .chunk_group(ident, chunk_group, module_graph, availability_info)
859        .output_assets_with_referenced()
860}