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