turbopack_core/chunk/
chunking_context.rs

1use anyhow::Result;
2use rustc_hash::FxHashMap;
3use serde::{Deserialize, Serialize};
4use turbo_rcstr::RcStr;
5use turbo_tasks::{NonLocalValue, ResolvedVc, TaskInput, Upcast, Vc, trace::TraceRawVcs};
6use turbo_tasks_fs::FileSystemPath;
7use turbo_tasks_hash::DeterministicHash;
8
9use super::{ChunkableModule, EvaluatableAssets, availability_info::AvailabilityInfo};
10use crate::{
11    asset::Asset,
12    chunk::{ChunkItem, ChunkType, ModuleId},
13    environment::Environment,
14    ident::AssetIdent,
15    module::Module,
16    module_graph::{
17        ModuleGraph, chunk_group_info::ChunkGroup, export_usage::ModuleExportUsage,
18        module_batches::BatchingConfig,
19    },
20    output::{OutputAsset, OutputAssets},
21};
22
23#[derive(
24    Debug,
25    TaskInput,
26    Clone,
27    Copy,
28    PartialEq,
29    Eq,
30    Hash,
31    Serialize,
32    Deserialize,
33    TraceRawVcs,
34    DeterministicHash,
35    NonLocalValue,
36)]
37#[serde(rename_all = "kebab-case")]
38pub enum MangleType {
39    OptimalSize,
40    Deterministic,
41}
42
43#[turbo_tasks::value(shared)]
44#[derive(Debug, TaskInput, Clone, Copy, Hash, DeterministicHash)]
45pub enum MinifyType {
46    // TODO instead of adding a new property here,
47    // refactor that to Minify(MinifyOptions) to allow defaults on MinifyOptions
48    Minify { mangle: Option<MangleType> },
49    NoMinify,
50}
51
52impl Default for MinifyType {
53    fn default() -> Self {
54        Self::Minify {
55            mangle: Some(MangleType::OptimalSize),
56        }
57    }
58}
59
60#[turbo_tasks::value(shared)]
61#[derive(Debug, Default, TaskInput, Clone, Copy, Hash, DeterministicHash)]
62pub enum SourceMapsType {
63    /// Extracts source maps from input files and writes source maps for output files.
64    #[default]
65    Full,
66    /// Ignores the existence of source maps and does not write source maps for output files.
67    None,
68}
69
70#[derive(
71    Debug,
72    TaskInput,
73    Clone,
74    Copy,
75    PartialEq,
76    Eq,
77    Hash,
78    Serialize,
79    Deserialize,
80    TraceRawVcs,
81    DeterministicHash,
82    NonLocalValue,
83)]
84pub enum ChunkGroupType {
85    Entry,
86    Evaluated,
87}
88
89#[turbo_tasks::value(shared)]
90pub struct ChunkGroupResult {
91    pub assets: ResolvedVc<OutputAssets>,
92    pub availability_info: AvailabilityInfo,
93}
94
95#[turbo_tasks::value(shared)]
96pub struct EntryChunkGroupResult {
97    pub asset: ResolvedVc<Box<dyn OutputAsset>>,
98    pub availability_info: AvailabilityInfo,
99}
100
101#[derive(
102    Default,
103    Debug,
104    Clone,
105    PartialEq,
106    Eq,
107    Hash,
108    Serialize,
109    Deserialize,
110    TraceRawVcs,
111    NonLocalValue,
112    TaskInput,
113)]
114pub struct ChunkingConfig {
115    /// Try to avoid creating more than 1 chunk smaller than this size.
116    /// It merges multiple small chunks into bigger ones to avoid that.
117    pub min_chunk_size: usize,
118
119    /// Try to avoid creating more than this number of chunks per group.
120    /// It merges multiple chunks into bigger ones to avoid that.
121    pub max_chunk_count_per_group: usize,
122
123    /// Never merges chunks bigger than this size with other chunks.
124    /// This makes sure that code in big chunks is not duplicated in multiple chunks.
125    pub max_merge_chunk_size: usize,
126
127    #[allow(dead_code)]
128    pub placeholder_for_future_extensions: (),
129}
130
131#[turbo_tasks::value(transparent)]
132pub struct ChunkingConfigs(FxHashMap<ResolvedVc<Box<dyn ChunkType>>, ChunkingConfig>);
133
134/// A context for the chunking that influences the way chunks are created
135#[turbo_tasks::value_trait]
136pub trait ChunkingContext {
137    #[turbo_tasks::function]
138    fn name(self: Vc<Self>) -> Vc<RcStr>;
139    #[turbo_tasks::function]
140    fn should_use_file_source_map_uris(self: Vc<Self>) -> Vc<bool>;
141    /// The root path of the project
142    #[turbo_tasks::function]
143    fn root_path(self: Vc<Self>) -> Vc<FileSystemPath>;
144    /// The output root path in the output filesystem
145    #[turbo_tasks::function]
146    fn output_root(self: Vc<Self>) -> Vc<FileSystemPath>;
147    /// A relative path how to reach the root path from the output root. This is used to compute
148    /// original paths at runtime relative to the output files. e. g. import.meta.url needs that.
149    #[turbo_tasks::function]
150    fn output_root_to_root_path(self: Vc<Self>) -> Vc<RcStr>;
151
152    // TODO remove this, a chunking context should not be bound to a specific
153    // environment since this can change due to transitions in the module graph
154    #[turbo_tasks::function]
155    fn environment(self: Vc<Self>) -> Vc<Environment>;
156
157    /// The path to the folder where all chunks are placed. This can be used to compute relative
158    /// paths.
159    #[turbo_tasks::function]
160    fn chunk_root_path(self: Vc<Self>) -> Vc<FileSystemPath>;
161
162    // TODO(alexkirsz) Remove this from the chunking context. This should be at the
163    // discretion of chunking context implementors. However, we currently use this
164    // in a couple of places in `turbopack-css`, so we need to remove that
165    // dependency first.
166    #[turbo_tasks::function]
167    fn chunk_path(
168        self: Vc<Self>,
169        asset: Option<Vc<Box<dyn Asset>>>,
170        ident: Vc<AssetIdent>,
171        content_hashing_prefix: Option<RcStr>,
172        extension: RcStr,
173    ) -> Vc<FileSystemPath>;
174
175    /// Reference Source Map Assets for chunks
176    #[turbo_tasks::function]
177    fn reference_chunk_source_maps(self: Vc<Self>, chunk: Vc<Box<dyn OutputAsset>>) -> Vc<bool>;
178
179    /// Include Source Maps for modules
180    #[turbo_tasks::function]
181    fn reference_module_source_maps(self: Vc<Self>, module: Vc<Box<dyn Module>>) -> Vc<bool>;
182
183    /// Returns a URL (relative or absolute, depending on the asset prefix) to
184    /// the static asset based on its `ident`.
185    #[turbo_tasks::function]
186    fn asset_url(self: Vc<Self>, ident: FileSystemPath) -> Result<Vc<RcStr>>;
187
188    #[turbo_tasks::function]
189    fn asset_path(
190        self: Vc<Self>,
191        content_hash: RcStr,
192        original_asset_ident: Vc<AssetIdent>,
193    ) -> Vc<FileSystemPath>;
194
195    #[turbo_tasks::function]
196    fn is_hot_module_replacement_enabled(self: Vc<Self>) -> Vc<bool> {
197        Vc::cell(false)
198    }
199
200    #[turbo_tasks::function]
201    fn chunking_configs(self: Vc<Self>) -> Vc<ChunkingConfigs> {
202        Vc::cell(Default::default())
203    }
204
205    #[turbo_tasks::function]
206    fn batching_config(self: Vc<Self>) -> Vc<BatchingConfig> {
207        BatchingConfig::new(BatchingConfig {
208            ..Default::default()
209        })
210    }
211
212    /// Whether `ChunkingType::Traced` are used to create corresponding output assets for each
213    /// traced module.
214    #[turbo_tasks::function]
215    fn is_tracing_enabled(self: Vc<Self>) -> Vc<bool> {
216        Vc::cell(false)
217    }
218
219    /// Whether to use `MergeableModule` to merge modules if possible.
220    #[turbo_tasks::function]
221    fn is_module_merging_enabled(self: Vc<Self>) -> Vc<bool> {
222        Vc::cell(false)
223    }
224
225    /// Whether to include information about the content of the chunk into the runtime, to allow
226    /// more incremental loading of individual chunk items.
227    #[turbo_tasks::function]
228    fn is_dynamic_chunk_content_loading_enabled(self: Vc<Self>) -> Vc<bool> {
229        Vc::cell(false)
230    }
231
232    #[turbo_tasks::function]
233    fn minify_type(self: Vc<Self>) -> Vc<MinifyType> {
234        MinifyType::NoMinify.cell()
235    }
236
237    #[turbo_tasks::function]
238    fn async_loader_chunk_item(
239        &self,
240        module: Vc<Box<dyn ChunkableModule>>,
241        module_graph: Vc<ModuleGraph>,
242        availability_info: AvailabilityInfo,
243    ) -> Vc<Box<dyn ChunkItem>>;
244    #[turbo_tasks::function]
245    fn async_loader_chunk_item_id(&self, module: Vc<Box<dyn ChunkableModule>>) -> Vc<ModuleId>;
246
247    #[turbo_tasks::function]
248    fn chunk_group(
249        self: Vc<Self>,
250        ident: Vc<AssetIdent>,
251        chunk_group: ChunkGroup,
252        module_graph: Vc<ModuleGraph>,
253        availability_info: AvailabilityInfo,
254    ) -> Vc<ChunkGroupResult>;
255
256    #[turbo_tasks::function]
257    fn evaluated_chunk_group(
258        self: Vc<Self>,
259        ident: Vc<AssetIdent>,
260        chunk_group: ChunkGroup,
261        module_graph: Vc<ModuleGraph>,
262        availability_info: AvailabilityInfo,
263    ) -> Vc<ChunkGroupResult>;
264
265    /// Generates an output chunk that:
266    /// * loads the given extra_chunks in addition to the generated chunks; and
267    /// * evaluates the given assets; and
268    /// * exports the result of evaluating the last module as a CommonJS default export.
269    #[turbo_tasks::function]
270    fn entry_chunk_group(
271        self: Vc<Self>,
272        path: FileSystemPath,
273        evaluatable_assets: Vc<EvaluatableAssets>,
274        module_graph: Vc<ModuleGraph>,
275        extra_chunks: Vc<OutputAssets>,
276        availability_info: AvailabilityInfo,
277    ) -> Result<Vc<EntryChunkGroupResult>>;
278
279    #[turbo_tasks::function]
280    async fn chunk_item_id_from_ident(
281        self: Vc<Self>,
282        ident: Vc<AssetIdent>,
283    ) -> Result<Vc<ModuleId>>;
284
285    #[turbo_tasks::function]
286    fn chunk_item_id(self: Vc<Self>, module: Vc<Box<dyn ChunkItem>>) -> Vc<ModuleId> {
287        self.chunk_item_id_from_ident(module.asset_ident())
288    }
289    #[turbo_tasks::function]
290    fn chunk_item_id_from_module(self: Vc<Self>, module: Vc<Box<dyn Module>>) -> Vc<ModuleId> {
291        self.chunk_item_id_from_ident(module.ident())
292    }
293
294    #[turbo_tasks::function]
295    async fn module_export_usage(
296        self: Vc<Self>,
297        module: Vc<Box<dyn Module>>,
298    ) -> Result<Vc<ModuleExportUsage>>;
299}
300
301pub trait ChunkingContextExt {
302    fn root_chunk_group(
303        self: Vc<Self>,
304        ident: Vc<AssetIdent>,
305        chunk_group: ChunkGroup,
306        module_graph: Vc<ModuleGraph>,
307    ) -> Vc<ChunkGroupResult>
308    where
309        Self: Send;
310
311    fn root_chunk_group_assets(
312        self: Vc<Self>,
313        ident: Vc<AssetIdent>,
314        chunk_group: ChunkGroup,
315        module_graph: Vc<ModuleGraph>,
316    ) -> Vc<OutputAssets>
317    where
318        Self: Send;
319
320    fn evaluated_chunk_group_assets(
321        self: Vc<Self>,
322        ident: Vc<AssetIdent>,
323        chunk_group: ChunkGroup,
324        module_graph: Vc<ModuleGraph>,
325        availability_info: AvailabilityInfo,
326    ) -> Vc<OutputAssets>
327    where
328        Self: Send;
329
330    fn entry_chunk_group_asset(
331        self: Vc<Self>,
332        path: FileSystemPath,
333        evaluatable_assets: Vc<EvaluatableAssets>,
334        module_graph: Vc<ModuleGraph>,
335        extra_chunks: Vc<OutputAssets>,
336        availability_info: AvailabilityInfo,
337    ) -> Vc<Box<dyn OutputAsset>>
338    where
339        Self: Send;
340
341    fn root_entry_chunk_group(
342        self: Vc<Self>,
343        path: FileSystemPath,
344        evaluatable_assets: Vc<EvaluatableAssets>,
345        module_graph: Vc<ModuleGraph>,
346        extra_chunks: Vc<OutputAssets>,
347    ) -> Vc<EntryChunkGroupResult>
348    where
349        Self: Send;
350
351    fn root_entry_chunk_group_asset(
352        self: Vc<Self>,
353        path: FileSystemPath,
354        evaluatable_assets: Vc<EvaluatableAssets>,
355        module_graph: Vc<ModuleGraph>,
356        extra_chunks: Vc<OutputAssets>,
357    ) -> Vc<Box<dyn OutputAsset>>
358    where
359        Self: Send;
360
361    fn chunk_group_assets(
362        self: Vc<Self>,
363        ident: Vc<AssetIdent>,
364        chunk_group: ChunkGroup,
365        module_graph: Vc<ModuleGraph>,
366        availability_info: AvailabilityInfo,
367    ) -> Vc<OutputAssets>
368    where
369        Self: Send;
370}
371
372impl<T: ChunkingContext + Send + Upcast<Box<dyn ChunkingContext>>> ChunkingContextExt for T {
373    fn root_chunk_group(
374        self: Vc<Self>,
375        ident: Vc<AssetIdent>,
376        chunk_group: ChunkGroup,
377        module_graph: Vc<ModuleGraph>,
378    ) -> Vc<ChunkGroupResult> {
379        self.chunk_group(ident, chunk_group, module_graph, AvailabilityInfo::Root)
380    }
381
382    fn root_chunk_group_assets(
383        self: Vc<Self>,
384        ident: Vc<AssetIdent>,
385        chunk_group: ChunkGroup,
386        module_graph: Vc<ModuleGraph>,
387    ) -> Vc<OutputAssets> {
388        root_chunk_group_assets(Vc::upcast(self), ident, chunk_group, module_graph)
389    }
390
391    fn evaluated_chunk_group_assets(
392        self: Vc<Self>,
393        ident: Vc<AssetIdent>,
394        chunk_group: ChunkGroup,
395        module_graph: Vc<ModuleGraph>,
396        availability_info: AvailabilityInfo,
397    ) -> Vc<OutputAssets> {
398        evaluated_chunk_group_assets(
399            Vc::upcast(self),
400            ident,
401            chunk_group,
402            module_graph,
403            availability_info,
404        )
405    }
406
407    fn entry_chunk_group_asset(
408        self: Vc<Self>,
409        path: FileSystemPath,
410        evaluatable_assets: Vc<EvaluatableAssets>,
411        module_graph: Vc<ModuleGraph>,
412        extra_chunks: Vc<OutputAssets>,
413        availability_info: AvailabilityInfo,
414    ) -> Vc<Box<dyn OutputAsset>> {
415        entry_chunk_group_asset(
416            Vc::upcast(self),
417            path,
418            evaluatable_assets,
419            module_graph,
420            extra_chunks,
421            availability_info,
422        )
423    }
424
425    fn root_entry_chunk_group(
426        self: Vc<Self>,
427        path: FileSystemPath,
428        evaluatable_assets: Vc<EvaluatableAssets>,
429        module_graph: Vc<ModuleGraph>,
430        extra_chunks: Vc<OutputAssets>,
431    ) -> Vc<EntryChunkGroupResult> {
432        self.entry_chunk_group(
433            path,
434            evaluatable_assets,
435            module_graph,
436            extra_chunks,
437            AvailabilityInfo::Root,
438        )
439    }
440
441    fn root_entry_chunk_group_asset(
442        self: Vc<Self>,
443        path: FileSystemPath,
444        evaluatable_assets: Vc<EvaluatableAssets>,
445        module_graph: Vc<ModuleGraph>,
446        extra_chunks: Vc<OutputAssets>,
447    ) -> Vc<Box<dyn OutputAsset>> {
448        entry_chunk_group_asset(
449            Vc::upcast(self),
450            path,
451            evaluatable_assets,
452            module_graph,
453            extra_chunks,
454            AvailabilityInfo::Root,
455        )
456    }
457
458    fn chunk_group_assets(
459        self: Vc<Self>,
460        ident: Vc<AssetIdent>,
461        chunk_group: ChunkGroup,
462        module_graph: Vc<ModuleGraph>,
463        availability_info: AvailabilityInfo,
464    ) -> Vc<OutputAssets> {
465        chunk_group_assets(
466            Vc::upcast(self),
467            ident,
468            chunk_group,
469            module_graph,
470            availability_info,
471        )
472    }
473}
474
475#[turbo_tasks::function]
476async fn root_chunk_group_assets(
477    chunking_context: Vc<Box<dyn ChunkingContext>>,
478    ident: Vc<AssetIdent>,
479    chunk_group: ChunkGroup,
480    module_graph: Vc<ModuleGraph>,
481) -> Result<Vc<OutputAssets>> {
482    Ok(*chunking_context
483        .root_chunk_group(ident, chunk_group, module_graph)
484        .await?
485        .assets)
486}
487
488#[turbo_tasks::function]
489async fn evaluated_chunk_group_assets(
490    chunking_context: Vc<Box<dyn ChunkingContext>>,
491    ident: Vc<AssetIdent>,
492    chunk_group: ChunkGroup,
493    module_graph: Vc<ModuleGraph>,
494    availability_info: AvailabilityInfo,
495) -> Result<Vc<OutputAssets>> {
496    Ok(*chunking_context
497        .evaluated_chunk_group(ident, chunk_group, module_graph, availability_info)
498        .await?
499        .assets)
500}
501
502#[turbo_tasks::function]
503async fn entry_chunk_group_asset(
504    chunking_context: Vc<Box<dyn ChunkingContext>>,
505    path: FileSystemPath,
506    evaluatable_assets: Vc<EvaluatableAssets>,
507    module_graph: Vc<ModuleGraph>,
508    extra_chunks: Vc<OutputAssets>,
509    availability_info: AvailabilityInfo,
510) -> Result<Vc<Box<dyn OutputAsset>>> {
511    Ok(*chunking_context
512        .entry_chunk_group(
513            path,
514            evaluatable_assets,
515            module_graph,
516            extra_chunks,
517            availability_info,
518        )
519        .await?
520        .asset)
521}
522
523#[turbo_tasks::function]
524async fn chunk_group_assets(
525    chunking_context: Vc<Box<dyn ChunkingContext>>,
526    ident: Vc<AssetIdent>,
527    chunk_group: ChunkGroup,
528    module_graph: Vc<ModuleGraph>,
529    availability_info: AvailabilityInfo,
530) -> Result<Vc<OutputAssets>> {
531    Ok(*chunking_context
532        .chunk_group(ident, chunk_group, module_graph, availability_info)
533        .await?
534        .assets)
535}