turbopack_core/chunk/
chunking_context.rs

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