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