Skip to main content

omena_transform_passes/
registry.rs

1use omena_cascade::{
2    CascadeValue, CustomPropertyLeastFixedPointSummaryV0,
3    summarize_custom_property_least_fixed_point,
4};
5use omena_parser::{StyleDialect, lex};
6
7use crate::domains::{
8    calc::reduce_css_calc_with_lexer,
9    cascade_flatten::{flatten_css_layers_with_lexer, flatten_css_scopes_with_lexer},
10    color::compress_css_colors_with_lexer,
11    color_lowering::{
12        lower_css_color_function_with_lexer, lower_css_color_mix_with_lexer,
13        lower_css_light_dark_with_lexer, lower_css_oklab_oklch_with_lexer,
14    },
15    css_modules_classes::{
16        local_css_module_composes_resolutions_with_lexer,
17        reachable_class_names_with_local_composes, rewrite_css_module_class_names_with_lexer,
18        strip_resolved_css_module_composes_with_lexer, tree_shake_css_class_rules_with_lexer,
19    },
20    css_modules_values::{
21        resolve_static_css_modules_values_with_lexer, tree_shake_css_modules_values_with_lexer,
22    },
23    custom_property::{
24        collect_static_root_custom_property_env, parse_static_custom_property_env_value,
25        substitute_static_css_custom_properties_with_lexer,
26        tree_shake_css_custom_properties_with_lexer,
27    },
28    design_token::route_design_token_values_with_lexer,
29    import_inline::{
30        inline_css_imports_for_static_module_evaluation_with_lexer, inline_css_imports_with_lexer,
31        restore_less_inline_literal_placeholders as restore_less_inline_literal_placeholders_with_lexer,
32    },
33    keyframes::tree_shake_css_keyframes_with_lexer,
34    logical::lower_css_logical_to_physical_with_lexer,
35    nesting::unwrap_css_nesting_with_lexer,
36    number::compress_css_numbers_with_lexer,
37    reachability::class_name_is_reachable,
38    rule_cleanup::{dedupe_exact_css_rules_with_lexer, remove_empty_css_rules_with_lexer},
39    rule_merge::{
40        merge_adjacent_same_block_css_selectors_with_lexer,
41        merge_adjacent_same_selector_css_rules_with_lexer,
42    },
43    selector::compress_css_is_where_selectors_with_lexer,
44    shorthand::combine_css_shorthands_with_lexer,
45    static_eval::{
46        StaticMediaEvaluationOptions, evaluate_static_media_rules_with_lexer,
47        evaluate_static_supports_rules_with_lexer,
48    },
49    text::{
50        normalize_css_font_declarations_with_lexer, normalize_css_string_quotes_with_lexer,
51        strip_css_url_quotes_with_lexer,
52    },
53    trivia::{normalize_css_whitespace_with_lexer, strip_css_comments_with_lexer},
54    unit::normalize_css_units_with_lexer,
55    vendor_prefix::add_css_vendor_prefixes_with_lexer,
56};
57use crate::helpers::rules::collect_top_level_ordinary_rule_slices;
58use crate::model::{
59    TransformClassNameRewriteV0, TransformCssModuleComposesResolutionV0,
60    TransformCssModuleValueResolutionV0, TransformDesignTokenRouteV0, TransformExecutionContextV0,
61    TransformImportInlineV0, TransformLessInlineLiteralPlaceholderV0,
62    TransformSemanticRemovalCandidate,
63};
64
65pub(crate) fn strip_css_comments(source: &str, dialect: StyleDialect) -> (String, usize) {
66    strip_css_comments_with_lexer(source, dialect)
67}
68
69pub(crate) fn compress_css_numbers(source: &str, dialect: StyleDialect) -> (String, usize) {
70    compress_css_numbers_with_lexer(source, dialect)
71}
72
73pub(crate) fn compress_css_colors(source: &str, dialect: StyleDialect) -> (String, usize) {
74    compress_css_colors_with_lexer(source, dialect)
75}
76
77pub(crate) fn normalize_css_units(source: &str, dialect: StyleDialect) -> (String, usize) {
78    normalize_css_units_with_lexer(source, dialect)
79}
80
81pub(crate) fn strip_css_url_quotes(source: &str, dialect: StyleDialect) -> (String, usize) {
82    strip_css_url_quotes_with_lexer(source, dialect)
83}
84
85pub(crate) fn normalize_css_string_quotes(source: &str, dialect: StyleDialect) -> (String, usize) {
86    let (source, font_declaration_mutations) =
87        normalize_css_font_declarations_with_lexer(source, dialect);
88    let (source, token_mutations) = normalize_css_string_quotes_with_lexer(&source, dialect);
89    (source, font_declaration_mutations + token_mutations)
90}
91
92pub(crate) fn compress_css_is_where_selectors(
93    source: &str,
94    dialect: StyleDialect,
95) -> (String, usize) {
96    compress_css_is_where_selectors_with_lexer(source, dialect)
97}
98
99pub(crate) fn remove_empty_css_rules(source: &str, dialect: StyleDialect) -> (String, usize) {
100    remove_empty_css_rules_with_lexer(source, dialect)
101}
102
103pub(crate) fn combine_css_shorthands(source: &str, dialect: StyleDialect) -> (String, usize) {
104    combine_css_shorthands_with_lexer(source, dialect)
105}
106
107pub(crate) fn dedupe_exact_css_rules(source: &str, dialect: StyleDialect) -> (String, usize) {
108    dedupe_exact_css_rules_with_lexer(source, dialect)
109}
110
111pub(crate) fn merge_adjacent_same_selector_css_rules(
112    source: &str,
113    dialect: StyleDialect,
114) -> (String, usize) {
115    merge_adjacent_same_selector_css_rules_with_lexer(source, dialect)
116}
117
118pub(crate) fn merge_adjacent_same_block_css_selectors(
119    source: &str,
120    dialect: StyleDialect,
121) -> (String, usize) {
122    merge_adjacent_same_block_css_selectors_with_lexer(source, dialect)
123}
124
125pub(crate) fn add_css_vendor_prefixes(source: &str, dialect: StyleDialect) -> (String, usize) {
126    add_css_vendor_prefixes_with_lexer(source, dialect)
127}
128
129pub(crate) fn lower_css_light_dark(source: &str, dialect: StyleDialect) -> (String, usize) {
130    lower_css_light_dark_with_lexer(source, dialect)
131}
132
133pub(crate) fn lower_css_color_mix(source: &str, dialect: StyleDialect) -> (String, usize) {
134    lower_css_color_mix_with_lexer(source, dialect)
135}
136
137pub(crate) fn lower_css_oklab_oklch(source: &str, dialect: StyleDialect) -> (String, usize) {
138    lower_css_oklab_oklch_with_lexer(source, dialect)
139}
140
141pub(crate) fn lower_css_color_function(source: &str, dialect: StyleDialect) -> (String, usize) {
142    lower_css_color_function_with_lexer(source, dialect)
143}
144
145pub(crate) fn lower_css_logical_to_physical(
146    source: &str,
147    dialect: StyleDialect,
148) -> (String, usize) {
149    lower_css_logical_to_physical_with_lexer(source, dialect)
150}
151
152pub(crate) fn unwrap_css_nesting(source: &str, dialect: StyleDialect) -> (String, usize) {
153    unwrap_css_nesting_with_lexer(source, dialect)
154}
155
156pub(crate) fn flatten_css_scopes(source: &str, dialect: StyleDialect) -> (String, usize) {
157    flatten_css_scopes_with_lexer(source, dialect)
158}
159
160pub(crate) fn flatten_css_layers(
161    source: &str,
162    dialect: StyleDialect,
163    closed_bundle: bool,
164) -> (String, usize) {
165    flatten_css_layers_with_lexer(source, dialect, closed_bundle)
166}
167
168pub(crate) fn evaluate_static_supports_rules(
169    source: &str,
170    dialect: StyleDialect,
171) -> (String, usize) {
172    evaluate_static_supports_rules_with_lexer(source, dialect)
173}
174
175pub(crate) fn evaluate_static_media_rules(source: &str, dialect: StyleDialect) -> (String, usize) {
176    evaluate_static_media_rules_with_lexer(source, dialect, StaticMediaEvaluationOptions::default())
177}
178
179pub(crate) fn evaluate_dead_media_branch_rules(
180    source: &str,
181    dialect: StyleDialect,
182    context: &TransformExecutionContextV0,
183) -> (String, usize) {
184    evaluate_static_media_rules_with_lexer(
185        source,
186        dialect,
187        StaticMediaEvaluationOptions {
188            drop_dark_mode_media_queries: context.drop_dark_mode_media_queries,
189        },
190    )
191}
192
193/// Applies resolved CSS `@import` replacements for the import-inline pass.
194pub fn inline_css_imports(
195    source: &str,
196    dialect: StyleDialect,
197    inlines: &[TransformImportInlineV0],
198) -> (String, usize) {
199    inline_css_imports_with_lexer(source, dialect, inlines)
200}
201
202/// Applies import inlining before static Sass/Less module evaluation.
203pub fn inline_css_imports_for_static_module_evaluation(
204    source: &str,
205    dialect: StyleDialect,
206    inlines: &[TransformImportInlineV0],
207) -> (String, usize, Vec<TransformLessInlineLiteralPlaceholderV0>) {
208    inline_css_imports_for_static_module_evaluation_with_lexer(source, dialect, inlines)
209}
210
211/// Restores Less `(inline)` literal import placeholders after static evaluation.
212pub fn restore_less_inline_literal_placeholders(
213    source: &str,
214    placeholders: &[TransformLessInlineLiteralPlaceholderV0],
215) -> String {
216    restore_less_inline_literal_placeholders_with_lexer(source, placeholders)
217}
218
219pub(crate) fn resolve_static_css_modules_values(
220    source: &str,
221    dialect: StyleDialect,
222    resolutions: &[TransformCssModuleValueResolutionV0],
223) -> (String, usize) {
224    resolve_static_css_modules_values_with_lexer(source, dialect, resolutions)
225}
226
227pub(crate) fn resolve_css_module_composes(
228    source: &str,
229    dialect: StyleDialect,
230    resolutions: &[TransformCssModuleComposesResolutionV0],
231) -> (String, usize) {
232    strip_resolved_css_module_composes_with_lexer(source, dialect, resolutions)
233}
234
235pub(crate) fn css_module_composes_resolutions_for_source(
236    source: &str,
237    dialect: StyleDialect,
238    resolutions: &[TransformCssModuleComposesResolutionV0],
239) -> Vec<TransformCssModuleComposesResolutionV0> {
240    let mut merged = local_css_module_composes_resolutions_with_lexer(source, dialect);
241    for resolution in resolutions {
242        let Some(existing) = merged
243            .iter_mut()
244            .find(|existing| existing.local_class_name == resolution.local_class_name)
245        else {
246            merged.push(resolution.clone());
247            continue;
248        };
249        for exported_class_name in &resolution.exported_class_names {
250            if !existing
251                .exported_class_names
252                .iter()
253                .any(|existing| existing == exported_class_name)
254            {
255                existing
256                    .exported_class_names
257                    .push(exported_class_name.clone());
258            }
259        }
260    }
261    merged.sort_by(|left, right| left.local_class_name.cmp(&right.local_class_name));
262    merged
263}
264
265pub(crate) fn route_design_token_values(
266    source: &str,
267    dialect: StyleDialect,
268    routes: &[TransformDesignTokenRouteV0],
269) -> (String, usize) {
270    route_design_token_values_with_lexer(source, dialect, routes)
271}
272
273pub(crate) fn tree_shake_css_class_rules_with_removals(
274    source: &str,
275    dialect: StyleDialect,
276    reachable_class_names: &[String],
277) -> (String, Vec<TransformSemanticRemovalCandidate>) {
278    tree_shake_css_class_rules_with_lexer(source, dialect, reachable_class_names)
279}
280
281pub(crate) fn reachable_class_names_with_composes_exports(
282    source: &str,
283    dialect: StyleDialect,
284    reachable_class_names: &[String],
285    resolutions: &[TransformCssModuleComposesResolutionV0],
286) -> Vec<String> {
287    let mut expanded = reachable_class_names.to_vec();
288    let mut changed = true;
289
290    while changed {
291        changed = false;
292        for resolution in resolutions {
293            if !class_name_is_reachable(&resolution.local_class_name, &expanded) {
294                continue;
295            }
296            for exported_class_name in &resolution.exported_class_names {
297                if !class_name_is_reachable(exported_class_name, &expanded) {
298                    expanded.push(exported_class_name.clone());
299                    changed = true;
300                }
301            }
302        }
303    }
304
305    expanded.sort();
306    expanded.dedup();
307    reachable_class_names_with_local_composes(source, dialect, &expanded)
308}
309
310pub(crate) fn tree_shake_css_keyframes_with_removals(
311    source: &str,
312    dialect: StyleDialect,
313    reachable_keyframe_names: &[String],
314    reachable_class_names: &[String],
315) -> (String, Vec<TransformSemanticRemovalCandidate>) {
316    tree_shake_css_keyframes_with_lexer(
317        source,
318        dialect,
319        reachable_keyframe_names,
320        reachable_class_names,
321    )
322}
323
324pub(crate) fn tree_shake_css_modules_values_with_removals(
325    source: &str,
326    dialect: StyleDialect,
327    reachable_value_names: &[String],
328    reachable_keyframe_names: &[String],
329    reachable_class_names: &[String],
330) -> (String, Vec<TransformSemanticRemovalCandidate>) {
331    tree_shake_css_modules_values_with_lexer(
332        source,
333        dialect,
334        reachable_value_names,
335        reachable_keyframe_names,
336        reachable_class_names,
337    )
338}
339
340pub(crate) fn tree_shake_css_custom_properties_with_removals(
341    source: &str,
342    dialect: StyleDialect,
343    reachable_custom_property_names: &[String],
344    reachable_keyframe_names: &[String],
345    reachable_class_names: &[String],
346) -> (String, Vec<TransformSemanticRemovalCandidate>) {
347    tree_shake_css_custom_properties_with_lexer(
348        source,
349        dialect,
350        reachable_custom_property_names,
351        reachable_keyframe_names,
352        reachable_class_names,
353    )
354}
355
356pub(crate) fn rewrite_css_module_class_names(
357    source: &str,
358    dialect: StyleDialect,
359    rewrites: &[TransformClassNameRewriteV0],
360) -> (String, usize) {
361    rewrite_css_module_class_names_with_lexer(source, dialect, rewrites)
362}
363
364pub(crate) fn substitute_static_css_custom_properties(
365    source: &str,
366    dialect: StyleDialect,
367) -> (String, usize) {
368    substitute_static_css_custom_properties_with_lexer(source, dialect)
369}
370
371/// Summarizes the static custom-property least fixed point for a style source.
372pub fn summarize_static_css_custom_property_fixed_point_from_source(
373    source: &str,
374    dialect: StyleDialect,
375) -> CustomPropertyLeastFixedPointSummaryV0 {
376    let lexed = lex(source, dialect);
377    let tokens = lexed.tokens();
378    let env_rules = collect_top_level_ordinary_rule_slices(source, tokens);
379    let env = collect_static_root_custom_property_env(tokens, &env_rules);
380    summarize_custom_property_least_fixed_point(&env)
381}
382
383/// Parses a static CSS value into the cascade value model used by query consumers.
384pub fn parse_static_css_cascade_value(value: &str) -> Option<CascadeValue> {
385    match value.trim() {
386        "initial" => Some(CascadeValue::Initial),
387        "inherit" => Some(CascadeValue::Inherit),
388        "unset" => Some(CascadeValue::Unset),
389        value => parse_static_custom_property_env_value(value),
390    }
391}
392
393pub(crate) fn reduce_css_calc(source: &str, dialect: StyleDialect) -> (String, usize) {
394    reduce_css_calc_with_lexer(source, dialect)
395}
396
397pub(crate) fn normalize_css_whitespace(source: &str, dialect: StyleDialect) -> (String, usize) {
398    normalize_css_whitespace_with_lexer(source, dialect)
399}