Skip to main content

turbopack_core/module_graph/
style_groups.rs

1//! Algorithm-neutral output types for the style-chunking pipeline.
2//!
3//! [`StyleGroups`] is the cell type both algorithms — the default ("loose") one in
4//! [`super::style_groups_loose`] and the graph-based one in [`super::style_groups_graph`] —
5//! produce. Living here means neither algorithm has to import from the other.
6
7use bincode::{Decode, Encode};
8use turbo_tasks::{FxIndexMap, OperationValue, ResolvedVc, Vc, trace::TraceRawVcs};
9
10use crate::chunk::{ChunkItemBatchWithAsyncModuleInfo, ChunkItemWithAsyncModuleInfo};
11
12/// Wrapper around an `f32` that implements [`TaskInput`] (and the other derives the
13/// [`StyleGroupsAlgorithm`] enum needs) by going through the IEEE-754 bit pattern. Use
14/// [`F32TaskInput::get`] / [`F32TaskInput::from`] at the boundary; do not match on the inner
15/// `u32` directly.
16#[turbo_tasks::task_input]
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, OperationValue, TraceRawVcs, Encode, Decode)]
18pub struct F32TaskInput(u32);
19
20impl F32TaskInput {
21    pub const fn from(value: f32) -> Self {
22        Self(value.to_bits())
23    }
24    pub const fn get(self) -> f32 {
25        f32::from_bits(self.0)
26    }
27}
28
29/// Selects the algorithm used to compute [`StyleGroups`].
30#[turbo_tasks::value(shared, operation, task_input)]
31#[derive(Clone, Debug, Default, Hash)]
32pub enum StyleGroupsAlgorithm {
33    /// Default ("loose") algorithm, see
34    /// [`crate::module_graph::style_groups_loose::compute_style_groups`].
35    #[default]
36    Default,
37    /// Graph-analysis based algorithm, see
38    /// [`crate::module_graph::style_groups_graph::compute_style_groups_graph`].
39    Graph {
40        /// See `experimental.cssChunking.requestCost` in Next.js.
41        request_cost: F32TaskInput,
42        /// See `experimental.cssChunking.weightDistribution` in Next.js.
43        weight_distribution: F32TaskInput,
44    },
45}
46
47impl StyleGroupsAlgorithm {
48    /// Build a [`StyleGroupsAlgorithm::Graph`] variant from real `f32` cost parameters.
49    pub fn graph(request_cost: f32, weight_distribution: f32) -> Self {
50        Self::Graph {
51            request_cost: F32TaskInput::from(request_cost),
52            weight_distribution: F32TaskInput::from(weight_distribution),
53        }
54    }
55}
56
57#[turbo_tasks::task_input]
58#[derive(Debug, Clone, PartialEq, Eq, Hash, TraceRawVcs, Encode, Decode)]
59pub struct StyleGroupsConfig {
60    pub max_chunk_size: usize,
61    pub algorithm: StyleGroupsAlgorithm,
62}
63
64/// Per-item metadata produced by the style chunking algorithms.
65#[turbo_tasks::task_input]
66#[derive(Debug, Clone, PartialEq, Eq, Hash, TraceRawVcs, Encode, Decode)]
67pub struct StyleItemInfo {
68    /// Stable sort key applied by the production-chunking pass when ordering chunks within a chunk
69    /// group. The loose algorithm produces all `None` orders and relies on input order; the graph
70    /// algorithm produces all `Some(_)` orders (including for singletons). Mixing `Some` and
71    /// `None` within a single [`StyleGroups`] result is not produced in practice — the
72    /// production-chunking sort treats `None` as a sort key that is less than any `Some(_)`, but
73    /// this branch is only exercised in the all-`None` (loose) case.
74    pub order: Option<u32>,
75    /// `Some(batch)` when this chunk item shares its emitted chunk with other items. `None` for
76    /// items that end up alone in their own chunk under the graph algorithm.
77    pub batch: Option<ResolvedVc<ChunkItemBatchWithAsyncModuleInfo>>,
78}
79
80/// Styling must not be duplicated in the application. The simplest way to achieve this is to put
81/// every styling chunk item into a separate chunk. That works, but isn't efficient since it would
82/// cause a lot of requests. Instead, multiple chunk items are grouped together and placed in a
83/// single shared chunk. `StyleGroups` specifies how chunk items are grouped together.
84#[turbo_tasks::value(shared)]
85pub struct StyleGroups {
86    /// Per-item info keyed by chunk item.
87    ///
88    /// The loose algorithm only inserts items it actively grouped into shared chunks (everything
89    /// else is implicitly emitted as a singleton chunk preserving input order). The graph
90    /// algorithm inserts every input item — including singletons — so its result fully determines
91    /// the final per-chunk-group ordering through `StyleItemInfo::order`.
92    #[bincode(with = "turbo_bincode::indexmap")]
93    pub shared_chunk_items: FxIndexMap<ChunkItemWithAsyncModuleInfo, StyleItemInfo>,
94}
95
96/// Constructor for [`StyleGroups`] that's accessible from both algorithm modules without
97/// forcing the cell visibility wider.
98pub(super) fn make_style_groups(
99    shared_chunk_items: FxIndexMap<ChunkItemWithAsyncModuleInfo, StyleItemInfo>,
100) -> Vc<StyleGroups> {
101    StyleGroups { shared_chunk_items }.cell()
102}