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, Value, 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, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, TraceRawVcs, NonLocalValue,
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    fn name(self: Vc<Self>) -> Vc<RcStr>;
138    fn should_use_file_source_map_uris(self: Vc<Self>) -> Vc<bool>;
139    /// The root path of the project
140    fn root_path(self: Vc<Self>) -> Vc<FileSystemPath>;
141    /// The output root path in the output filesystem
142    fn output_root(self: Vc<Self>) -> Vc<FileSystemPath>;
143    /// A relative path how to reach the root path from the output root. This is used to compute
144    /// original paths at runtime relative to the output files. e. g. import.meta.url needs that.
145    fn output_root_to_root_path(self: Vc<Self>) -> Vc<RcStr>;
146
147    // TODO remove this, a chunking context should not be bound to a specific
148    // environment since this can change due to transitions in the module graph
149    fn environment(self: Vc<Self>) -> Vc<Environment>;
150
151    /// The path to the folder where all chunks are placed. This can be used to compute relative
152    /// paths.
153    fn chunk_root_path(self: Vc<Self>) -> Vc<FileSystemPath>;
154
155    // TODO(alexkirsz) Remove this from the chunking context. This should be at the
156    // discretion of chunking context implementors. However, we currently use this
157    // in a couple of places in `turbopack-css`, so we need to remove that
158    // dependency first.
159    fn chunk_path(
160        self: Vc<Self>,
161        asset: Option<Vc<Box<dyn Asset>>>,
162        ident: Vc<AssetIdent>,
163        extension: RcStr,
164    ) -> Vc<FileSystemPath>;
165
166    /// Reference Source Map Assets for chunks
167    fn reference_chunk_source_maps(self: Vc<Self>, chunk: Vc<Box<dyn OutputAsset>>) -> Vc<bool>;
168
169    /// Include Source Maps for modules
170    fn reference_module_source_maps(self: Vc<Self>, module: Vc<Box<dyn Module>>) -> Vc<bool>;
171
172    /// Returns a URL (relative or absolute, depending on the asset prefix) to
173    /// the static asset based on its `ident`.
174    fn asset_url(self: Vc<Self>, ident: Vc<FileSystemPath>) -> Result<Vc<RcStr>>;
175
176    fn asset_path(
177        self: Vc<Self>,
178        content_hash: RcStr,
179        original_asset_ident: Vc<AssetIdent>,
180    ) -> Vc<FileSystemPath>;
181
182    fn is_hot_module_replacement_enabled(self: Vc<Self>) -> Vc<bool> {
183        Vc::cell(false)
184    }
185
186    fn chunking_configs(self: Vc<Self>) -> Vc<ChunkingConfigs> {
187        Vc::cell(Default::default())
188    }
189
190    fn batching_config(self: Vc<Self>) -> Vc<BatchingConfig> {
191        BatchingConfig::new(BatchingConfig {
192            ..Default::default()
193        })
194    }
195
196    fn is_tracing_enabled(self: Vc<Self>) -> Vc<bool> {
197        Vc::cell(false)
198    }
199
200    fn minify_type(self: Vc<Self>) -> Vc<MinifyType> {
201        MinifyType::NoMinify.cell()
202    }
203
204    fn async_loader_chunk_item(
205        &self,
206        module: Vc<Box<dyn ChunkableModule>>,
207        module_graph: Vc<ModuleGraph>,
208        availability_info: Value<AvailabilityInfo>,
209    ) -> Vc<Box<dyn ChunkItem>>;
210    fn async_loader_chunk_item_id(&self, module: Vc<Box<dyn ChunkableModule>>) -> Vc<ModuleId>;
211
212    fn chunk_group(
213        self: Vc<Self>,
214        ident: Vc<AssetIdent>,
215        chunk_group: ChunkGroup,
216        module_graph: Vc<ModuleGraph>,
217        availability_info: Value<AvailabilityInfo>,
218    ) -> Vc<ChunkGroupResult>;
219
220    fn evaluated_chunk_group(
221        self: Vc<Self>,
222        ident: Vc<AssetIdent>,
223        chunk_group: ChunkGroup,
224        module_graph: Vc<ModuleGraph>,
225        availability_info: Value<AvailabilityInfo>,
226    ) -> Vc<ChunkGroupResult>;
227
228    /// Generates an output chunk that:
229    /// * loads the given extra_chunks in addition to the generated chunks; and
230    /// * evaluates the given assets; and
231    /// * exports the result of evaluating the last module as a CommonJS default export.
232    fn entry_chunk_group(
233        self: Vc<Self>,
234        path: Vc<FileSystemPath>,
235        evaluatable_assets: Vc<EvaluatableAssets>,
236        module_graph: Vc<ModuleGraph>,
237        extra_chunks: Vc<OutputAssets>,
238        availability_info: Value<AvailabilityInfo>,
239    ) -> Result<Vc<EntryChunkGroupResult>>;
240
241    async fn chunk_item_id_from_ident(
242        self: Vc<Self>,
243        ident: Vc<AssetIdent>,
244    ) -> Result<Vc<ModuleId>>;
245
246    fn chunk_item_id(self: Vc<Self>, module: Vc<Box<dyn ChunkItem>>) -> Vc<ModuleId> {
247        self.chunk_item_id_from_ident(module.asset_ident())
248    }
249    fn chunk_item_id_from_module(self: Vc<Self>, module: Vc<Box<dyn Module>>) -> Vc<ModuleId> {
250        self.chunk_item_id_from_ident(module.ident())
251    }
252}
253
254pub trait ChunkingContextExt {
255    fn root_chunk_group(
256        self: Vc<Self>,
257        ident: Vc<AssetIdent>,
258        chunk_group: ChunkGroup,
259        module_graph: Vc<ModuleGraph>,
260    ) -> Vc<ChunkGroupResult>
261    where
262        Self: Send;
263
264    fn root_chunk_group_assets(
265        self: Vc<Self>,
266        ident: Vc<AssetIdent>,
267        chunk_group: ChunkGroup,
268        module_graph: Vc<ModuleGraph>,
269    ) -> Vc<OutputAssets>
270    where
271        Self: Send;
272
273    fn evaluated_chunk_group_assets(
274        self: Vc<Self>,
275        ident: Vc<AssetIdent>,
276        chunk_group: ChunkGroup,
277        module_graph: Vc<ModuleGraph>,
278        availability_info: Value<AvailabilityInfo>,
279    ) -> Vc<OutputAssets>
280    where
281        Self: Send;
282
283    fn entry_chunk_group_asset(
284        self: Vc<Self>,
285        path: Vc<FileSystemPath>,
286        evaluatable_assets: Vc<EvaluatableAssets>,
287        module_graph: Vc<ModuleGraph>,
288        extra_chunks: Vc<OutputAssets>,
289        availability_info: Value<AvailabilityInfo>,
290    ) -> Vc<Box<dyn OutputAsset>>
291    where
292        Self: Send;
293
294    fn root_entry_chunk_group(
295        self: Vc<Self>,
296        path: Vc<FileSystemPath>,
297        evaluatable_assets: Vc<EvaluatableAssets>,
298        module_graph: Vc<ModuleGraph>,
299        extra_chunks: Vc<OutputAssets>,
300    ) -> Vc<EntryChunkGroupResult>
301    where
302        Self: Send;
303
304    fn root_entry_chunk_group_asset(
305        self: Vc<Self>,
306        path: Vc<FileSystemPath>,
307        evaluatable_assets: Vc<EvaluatableAssets>,
308        module_graph: Vc<ModuleGraph>,
309        extra_chunks: Vc<OutputAssets>,
310    ) -> Vc<Box<dyn OutputAsset>>
311    where
312        Self: Send;
313
314    fn chunk_group_assets(
315        self: Vc<Self>,
316        ident: Vc<AssetIdent>,
317        chunk_group: ChunkGroup,
318        module_graph: Vc<ModuleGraph>,
319        availability_info: Value<AvailabilityInfo>,
320    ) -> Vc<OutputAssets>
321    where
322        Self: Send;
323}
324
325impl<T: ChunkingContext + Send + Upcast<Box<dyn ChunkingContext>>> ChunkingContextExt for T {
326    fn root_chunk_group(
327        self: Vc<Self>,
328        ident: Vc<AssetIdent>,
329        chunk_group: ChunkGroup,
330        module_graph: Vc<ModuleGraph>,
331    ) -> Vc<ChunkGroupResult> {
332        self.chunk_group(
333            ident,
334            chunk_group,
335            module_graph,
336            Value::new(AvailabilityInfo::Root),
337        )
338    }
339
340    fn root_chunk_group_assets(
341        self: Vc<Self>,
342        ident: Vc<AssetIdent>,
343        chunk_group: ChunkGroup,
344        module_graph: Vc<ModuleGraph>,
345    ) -> Vc<OutputAssets> {
346        root_chunk_group_assets(Vc::upcast(self), ident, chunk_group, module_graph)
347    }
348
349    fn evaluated_chunk_group_assets(
350        self: Vc<Self>,
351        ident: Vc<AssetIdent>,
352        chunk_group: ChunkGroup,
353        module_graph: Vc<ModuleGraph>,
354        availability_info: Value<AvailabilityInfo>,
355    ) -> Vc<OutputAssets> {
356        evaluated_chunk_group_assets(
357            Vc::upcast(self),
358            ident,
359            chunk_group,
360            module_graph,
361            availability_info,
362        )
363    }
364
365    fn entry_chunk_group_asset(
366        self: Vc<Self>,
367        path: Vc<FileSystemPath>,
368        evaluatable_assets: Vc<EvaluatableAssets>,
369        module_graph: Vc<ModuleGraph>,
370        extra_chunks: Vc<OutputAssets>,
371        availability_info: Value<AvailabilityInfo>,
372    ) -> Vc<Box<dyn OutputAsset>> {
373        entry_chunk_group_asset(
374            Vc::upcast(self),
375            path,
376            evaluatable_assets,
377            module_graph,
378            extra_chunks,
379            availability_info,
380        )
381    }
382
383    fn root_entry_chunk_group(
384        self: Vc<Self>,
385        path: Vc<FileSystemPath>,
386        evaluatable_assets: Vc<EvaluatableAssets>,
387        module_graph: Vc<ModuleGraph>,
388        extra_chunks: Vc<OutputAssets>,
389    ) -> Vc<EntryChunkGroupResult> {
390        self.entry_chunk_group(
391            path,
392            evaluatable_assets,
393            module_graph,
394            extra_chunks,
395            Value::new(AvailabilityInfo::Root),
396        )
397    }
398
399    fn root_entry_chunk_group_asset(
400        self: Vc<Self>,
401        path: Vc<FileSystemPath>,
402        evaluatable_assets: Vc<EvaluatableAssets>,
403        module_graph: Vc<ModuleGraph>,
404        extra_chunks: Vc<OutputAssets>,
405    ) -> Vc<Box<dyn OutputAsset>> {
406        entry_chunk_group_asset(
407            Vc::upcast(self),
408            path,
409            evaluatable_assets,
410            module_graph,
411            extra_chunks,
412            Value::new(AvailabilityInfo::Root),
413        )
414    }
415
416    fn chunk_group_assets(
417        self: Vc<Self>,
418        ident: Vc<AssetIdent>,
419        chunk_group: ChunkGroup,
420        module_graph: Vc<ModuleGraph>,
421        availability_info: Value<AvailabilityInfo>,
422    ) -> Vc<OutputAssets> {
423        chunk_group_assets(
424            Vc::upcast(self),
425            ident,
426            chunk_group,
427            module_graph,
428            availability_info,
429        )
430    }
431}
432
433#[turbo_tasks::function]
434async fn root_chunk_group_assets(
435    chunking_context: Vc<Box<dyn ChunkingContext>>,
436    ident: Vc<AssetIdent>,
437    chunk_group: ChunkGroup,
438    module_graph: Vc<ModuleGraph>,
439) -> Result<Vc<OutputAssets>> {
440    Ok(*chunking_context
441        .root_chunk_group(ident, chunk_group, module_graph)
442        .await?
443        .assets)
444}
445
446#[turbo_tasks::function]
447async fn evaluated_chunk_group_assets(
448    chunking_context: Vc<Box<dyn ChunkingContext>>,
449    ident: Vc<AssetIdent>,
450    chunk_group: ChunkGroup,
451    module_graph: Vc<ModuleGraph>,
452    availability_info: Value<AvailabilityInfo>,
453) -> Result<Vc<OutputAssets>> {
454    Ok(*chunking_context
455        .evaluated_chunk_group(ident, chunk_group, module_graph, availability_info)
456        .await?
457        .assets)
458}
459
460#[turbo_tasks::function]
461async fn entry_chunk_group_asset(
462    chunking_context: Vc<Box<dyn ChunkingContext>>,
463    path: Vc<FileSystemPath>,
464    evaluatable_assets: Vc<EvaluatableAssets>,
465    module_graph: Vc<ModuleGraph>,
466    extra_chunks: Vc<OutputAssets>,
467    availability_info: Value<AvailabilityInfo>,
468) -> Result<Vc<Box<dyn OutputAsset>>> {
469    Ok(*chunking_context
470        .entry_chunk_group(
471            path,
472            evaluatable_assets,
473            module_graph,
474            extra_chunks,
475            availability_info,
476        )
477        .await?
478        .asset)
479}
480
481#[turbo_tasks::function]
482async fn chunk_group_assets(
483    chunking_context: Vc<Box<dyn ChunkingContext>>,
484    ident: Vc<AssetIdent>,
485    chunk_group: ChunkGroup,
486    module_graph: Vc<ModuleGraph>,
487    availability_info: Value<AvailabilityInfo>,
488) -> Result<Vc<OutputAssets>> {
489    Ok(*chunking_context
490        .chunk_group(ident, chunk_group, module_graph, availability_info)
491        .await?
492        .assets)
493}