1use omena_cascade::{
2 CascadeValue, CustomPropertyLeastFixedPointSummaryV0, StaticSupportsAssumptionV0,
3 summarize_custom_property_least_fixed_point,
4};
5use omena_parser::StyleDialect;
6use omena_scss_eval::{
7 summarize_native_css_static_edit_plan, summarize_native_css_static_edit_plan_from_transform_ir,
8};
9use omena_transform_cst::TransformIrV0;
10
11use crate::domains::{
12 calc::reduce_css_calc_with_lexer,
13 cascade_flatten::{
14 flatten_css_layers_with_ir_transaction_on_ir, flatten_css_scopes_with_ir_transaction_on_ir,
15 },
16 color::compress_css_colors_with_lexer,
17 color_lowering::{
18 lower_css_color_function_with_lexer, lower_css_color_mix_with_lexer,
19 lower_css_light_dark_with_lexer, lower_css_oklab_oklch_with_lexer,
20 lower_relative_color_with_lexer,
21 },
22 css_modules_classes::{
23 local_css_module_composes_resolutions_from_ir,
24 rewrite_css_module_class_names_with_ir_transaction_on_ir,
25 strip_resolved_css_module_composes_with_ir_transaction_on_ir,
26 tree_shake_css_class_rules_with_ir_transaction_on_ir,
27 },
28 css_modules_values::{
29 resolve_static_css_modules_values_with_lexer,
30 tree_shake_css_modules_values_with_ir_transaction_on_ir,
31 },
32 custom_property::{
33 collect_static_root_custom_property_env, parse_static_custom_property_env_value,
34 substitute_static_css_custom_properties_with_lexer,
35 tree_shake_css_custom_properties_with_ir_transaction_on_ir,
36 },
37 design_token::route_design_token_values_with_ir_transaction_on_ir,
38 import_inline::{
39 inline_css_imports_for_static_module_evaluation_with_lexer,
40 inline_css_imports_with_ir_transaction, inline_css_imports_with_ir_transaction_on_ir,
41 restore_less_inline_literal_placeholders as restore_less_inline_literal_placeholders_with_lexer,
42 },
43 keyframes::tree_shake_css_keyframes_with_ir_transaction_on_ir,
44 logical::lower_css_logical_to_physical_with_lexer,
45 nesting::unwrap_css_nesting_with_ir_transaction_on_ir,
46 number::compress_css_numbers_with_lexer,
47 rule_cleanup::{
48 dedupe_exact_css_rules_with_ir_transaction_on_ir,
49 remove_empty_css_rules_with_ir_transaction_on_ir,
50 },
51 rule_merge::{
52 merge_adjacent_same_block_css_selectors_with_ir_transaction_on_ir,
53 merge_adjacent_same_selector_css_rules_with_ir_transaction_on_ir,
54 },
55 selector::compress_css_is_where_selectors_with_lexer,
56 shorthand::combine_css_shorthands_with_lexer,
57 static_eval::{
58 StaticMediaEvaluationOptions, evaluate_static_container_rules_with_ir_transaction_on_ir,
59 evaluate_static_media_rules_with_ir_transaction_on_ir,
60 evaluate_static_supports_rules_with_ir_transaction_on_ir,
61 },
62 text::{
63 normalize_css_font_declarations_with_lexer, normalize_css_string_quotes_with_lexer,
64 strip_css_url_quotes_with_lexer,
65 },
66 trivia::{normalize_css_whitespace_with_lexer, strip_css_comments_with_lexer},
67 unit::normalize_css_units_with_lexer,
68 vendor_prefix::{
69 add_css_vendor_prefixes_with_lexer_and_policy, remove_stale_css_vendor_prefixes_with_lexer,
70 },
71};
72use crate::helpers::ir_transaction::{
73 TransformIrReplacementKindV0, TransformIrSourceReplacementErrorV0,
74 TransformIrSourceReplacementV0, replace_ir_node_spans_in_ir,
75};
76use crate::helpers::rules::collect_top_level_ordinary_rule_slices;
77use crate::model::{
78 TransformClassNameRewriteV0, TransformCssModuleComposesResolutionV0,
79 TransformCssModuleValueResolutionV0, TransformDesignTokenRouteV0, TransformImportInlineV0,
80 TransformLessInlineLiteralPlaceholderV0, TransformSemanticRemovalCandidate,
81 TransformVendorPrefixPolicyV0,
82};
83use crate::runtime::lex_cache::lex_cached as lex;
84
85pub(crate) fn strip_css_comments(source: &str, dialect: StyleDialect) -> (String, usize) {
86 strip_css_comments_with_lexer(source, dialect)
87}
88
89pub(crate) fn compress_css_numbers(source: &str, dialect: StyleDialect) -> (String, usize) {
90 compress_css_numbers_with_lexer(source, dialect)
91}
92
93pub(crate) fn compress_css_colors(source: &str, dialect: StyleDialect) -> (String, usize) {
94 compress_css_colors_with_lexer(source, dialect)
95}
96
97pub(crate) fn normalize_css_units(source: &str, dialect: StyleDialect) -> (String, usize) {
98 normalize_css_units_with_lexer(source, dialect)
99}
100
101pub(crate) fn strip_css_url_quotes(source: &str, dialect: StyleDialect) -> (String, usize) {
102 strip_css_url_quotes_with_lexer(source, dialect)
103}
104
105pub(crate) fn normalize_css_string_quotes(source: &str, dialect: StyleDialect) -> (String, usize) {
106 let (source, font_declaration_mutations) =
107 normalize_css_font_declarations_with_lexer(source, dialect);
108 let (source, token_mutations) = normalize_css_string_quotes_with_lexer(&source, dialect);
109 (source, font_declaration_mutations + token_mutations)
110}
111
112pub(crate) fn compress_css_is_where_selectors(
113 source: &str,
114 dialect: StyleDialect,
115) -> (String, usize) {
116 compress_css_is_where_selectors_with_lexer(source, dialect)
117}
118
119pub(crate) fn remove_empty_css_rules_in_ir(
120 ir: &mut TransformIrV0,
121 dialect: StyleDialect,
122) -> Result<usize, TransformIrSourceReplacementErrorV0> {
123 remove_empty_css_rules_with_ir_transaction_on_ir(ir, dialect)
124}
125
126pub(crate) fn combine_css_shorthands(source: &str, dialect: StyleDialect) -> (String, usize) {
127 combine_css_shorthands_with_lexer(source, dialect)
128}
129
130pub(crate) fn dedupe_exact_css_rules_in_ir(
131 ir: &mut TransformIrV0,
132 dialect: StyleDialect,
133) -> Result<usize, TransformIrSourceReplacementErrorV0> {
134 dedupe_exact_css_rules_with_ir_transaction_on_ir(ir, dialect)
135}
136
137pub(crate) fn merge_adjacent_same_selector_css_rules_in_ir(
138 ir: &mut TransformIrV0,
139 dialect: StyleDialect,
140) -> Result<usize, TransformIrSourceReplacementErrorV0> {
141 merge_adjacent_same_selector_css_rules_with_ir_transaction_on_ir(ir, dialect)
142}
143
144pub(crate) fn merge_adjacent_same_block_css_selectors_in_ir(
145 ir: &mut TransformIrV0,
146 dialect: StyleDialect,
147) -> Result<usize, TransformIrSourceReplacementErrorV0> {
148 merge_adjacent_same_block_css_selectors_with_ir_transaction_on_ir(ir, dialect)
149}
150
151pub(crate) fn add_css_vendor_prefixes(
152 source: &str,
153 dialect: StyleDialect,
154 policy: TransformVendorPrefixPolicyV0,
155) -> (String, usize) {
156 add_css_vendor_prefixes_with_lexer_and_policy(source, dialect, policy)
157}
158
159pub(crate) fn remove_stale_css_vendor_prefixes(
160 source: &str,
161 dialect: StyleDialect,
162) -> (String, usize) {
163 remove_stale_css_vendor_prefixes_with_lexer(source, dialect)
164}
165
166pub(crate) fn lower_css_light_dark(source: &str, dialect: StyleDialect) -> (String, usize) {
167 lower_css_light_dark_with_lexer(source, dialect)
168}
169
170pub(crate) fn lower_css_color_mix(source: &str, dialect: StyleDialect) -> (String, usize) {
171 lower_css_color_mix_with_lexer(source, dialect)
172}
173
174pub(crate) fn lower_css_oklab_oklch(source: &str, dialect: StyleDialect) -> (String, usize) {
175 lower_css_oklab_oklch_with_lexer(source, dialect)
176}
177
178pub(crate) fn lower_css_color_function(source: &str, dialect: StyleDialect) -> (String, usize) {
179 lower_css_color_function_with_lexer(source, dialect)
180}
181
182pub(crate) fn lower_relative_color(source: &str, dialect: StyleDialect) -> (String, usize) {
183 lower_relative_color_with_lexer(source, dialect)
184}
185
186pub(crate) fn lower_css_logical_to_physical(
187 source: &str,
188 dialect: StyleDialect,
189) -> (String, usize) {
190 lower_css_logical_to_physical_with_lexer(source, dialect)
191}
192
193pub(crate) fn unwrap_css_nesting_in_ir(
194 ir: &mut TransformIrV0,
195 dialect: StyleDialect,
196) -> Result<usize, TransformIrSourceReplacementErrorV0> {
197 unwrap_css_nesting_with_ir_transaction_on_ir(ir, dialect)
198}
199
200pub(crate) fn flatten_css_scopes_in_ir(
201 ir: &mut TransformIrV0,
202 dialect: StyleDialect,
203) -> Result<usize, TransformIrSourceReplacementErrorV0> {
204 flatten_css_scopes_with_ir_transaction_on_ir(ir, dialect)
205}
206
207pub(crate) fn flatten_css_layers_in_ir(
208 ir: &mut TransformIrV0,
209 dialect: StyleDialect,
210 closed_bundle: bool,
211) -> Result<usize, TransformIrSourceReplacementErrorV0> {
212 flatten_css_layers_with_ir_transaction_on_ir(ir, dialect, closed_bundle)
213}
214
215pub(crate) fn evaluate_static_supports_rules_in_ir(
216 ir: &mut TransformIrV0,
217 dialect: StyleDialect,
218 assumption: StaticSupportsAssumptionV0,
219) -> Result<usize, TransformIrSourceReplacementErrorV0> {
220 evaluate_static_supports_rules_with_ir_transaction_on_ir(ir, dialect, assumption)
221}
222
223pub(crate) fn evaluate_static_media_rules_in_ir(
224 ir: &mut TransformIrV0,
225 dialect: StyleDialect,
226) -> Result<usize, TransformIrSourceReplacementErrorV0> {
227 evaluate_static_media_rules_with_ir_transaction_on_ir(
228 ir,
229 dialect,
230 StaticMediaEvaluationOptions::default(),
231 )
232}
233
234pub(crate) fn evaluate_static_container_rules_in_ir(
235 ir: &mut TransformIrV0,
236 dialect: StyleDialect,
237) -> Result<usize, TransformIrSourceReplacementErrorV0> {
238 evaluate_static_container_rules_with_ir_transaction_on_ir(ir, dialect)
239}
240
241pub(crate) fn evaluate_native_css_static_values_with_plan(
242 source: &str,
243 dialect: StyleDialect,
244) -> (String, usize) {
245 if dialect != StyleDialect::Css {
246 return (source.to_string(), 0);
247 }
248 let Some(plan) = summarize_native_css_static_edit_plan(source, dialect) else {
249 return (source.to_string(), 0);
250 };
251 let mutation_count = if plan.output_changed {
252 plan.edit_count
253 } else {
254 0
255 };
256 (plan.edited_css, mutation_count)
257}
258
259pub(crate) fn evaluate_native_css_static_values_in_ir(
260 ir: &mut TransformIrV0,
261 dialect: StyleDialect,
262) -> Result<usize, TransformIrSourceReplacementErrorV0> {
263 if dialect != StyleDialect::Css {
264 return Ok(0);
265 }
266 let Some(plan) = summarize_native_css_static_edit_plan_from_transform_ir(ir, dialect) else {
267 return Ok(0);
268 };
269 let replacements = plan
270 .edits
271 .into_iter()
272 .map(|edit| TransformIrSourceReplacementV0 {
273 source_span_start: edit.start,
274 source_span_end: edit.end,
275 replacement: edit.replacement,
276 kind: match edit.edit_kind {
277 "whenRuleBranchFold" | "ifRuleBranchFold" => TransformIrReplacementKindV0::AtRule,
278 _ => TransformIrReplacementKindV0::Declaration,
279 },
280 })
281 .collect::<Vec<_>>();
282 replace_ir_node_spans_in_ir(ir, "native-css-static-eval", replacements.as_slice())
283}
284
285pub(crate) fn evaluate_dead_media_branch_rules_in_ir(
286 ir: &mut TransformIrV0,
287 dialect: StyleDialect,
288 drop_dark_mode_media_queries: bool,
289) -> Result<usize, TransformIrSourceReplacementErrorV0> {
290 evaluate_static_media_rules_with_ir_transaction_on_ir(
291 ir,
292 dialect,
293 StaticMediaEvaluationOptions {
294 drop_dark_mode_media_queries,
295 },
296 )
297}
298
299pub fn inline_css_imports(
301 source: &str,
302 dialect: StyleDialect,
303 inlines: &[TransformImportInlineV0],
304) -> (String, usize) {
305 inline_css_imports_with_ir_transaction(source, dialect, inlines)
306 .unwrap_or_else(|_| (source.to_string(), 0))
307}
308
309pub(crate) fn inline_css_imports_in_ir(
310 ir: &mut TransformIrV0,
311 dialect: StyleDialect,
312 inlines: &[TransformImportInlineV0],
313) -> Result<usize, TransformIrSourceReplacementErrorV0> {
314 inline_css_imports_with_ir_transaction_on_ir(ir, dialect, inlines)
315}
316
317pub fn inline_css_imports_for_static_module_evaluation(
319 source: &str,
320 dialect: StyleDialect,
321 inlines: &[TransformImportInlineV0],
322) -> (String, usize, Vec<TransformLessInlineLiteralPlaceholderV0>) {
323 inline_css_imports_for_static_module_evaluation_with_lexer(source, dialect, inlines)
324}
325
326pub fn restore_less_inline_literal_placeholders(
328 source: &str,
329 placeholders: &[TransformLessInlineLiteralPlaceholderV0],
330) -> String {
331 restore_less_inline_literal_placeholders_with_lexer(source, placeholders)
332}
333
334pub(crate) fn resolve_static_css_modules_values(
335 source: &str,
336 dialect: StyleDialect,
337 resolutions: &[TransformCssModuleValueResolutionV0],
338) -> (String, usize) {
339 resolve_static_css_modules_values_with_lexer(source, dialect, resolutions)
340}
341
342pub(crate) fn resolve_css_module_composes_in_ir(
343 ir: &mut TransformIrV0,
344 dialect: StyleDialect,
345 resolutions: &[TransformCssModuleComposesResolutionV0],
346) -> Result<usize, TransformIrSourceReplacementErrorV0> {
347 strip_resolved_css_module_composes_with_ir_transaction_on_ir(ir, dialect, resolutions)
348}
349
350pub(crate) fn css_module_composes_resolutions_for_ir(
351 ir: &TransformIrV0,
352 _dialect: StyleDialect,
353 resolutions: &[TransformCssModuleComposesResolutionV0],
354) -> Vec<TransformCssModuleComposesResolutionV0> {
355 let mut merged = local_css_module_composes_resolutions_from_ir(ir);
356 for resolution in resolutions {
357 let Some(existing) = merged
358 .iter_mut()
359 .find(|existing| existing.local_class_name == resolution.local_class_name)
360 else {
361 merged.push(resolution.clone());
362 continue;
363 };
364 for exported_class_name in &resolution.exported_class_names {
365 if !existing
366 .exported_class_names
367 .iter()
368 .any(|existing| existing == exported_class_name)
369 {
370 existing
371 .exported_class_names
372 .push(exported_class_name.clone());
373 }
374 }
375 }
376 merged.sort_by(|left, right| left.local_class_name.cmp(&right.local_class_name));
377 merged
378}
379
380pub(crate) fn route_design_token_values_in_ir(
381 ir: &mut TransformIrV0,
382 dialect: StyleDialect,
383 routes: &[TransformDesignTokenRouteV0],
384) -> Result<usize, TransformIrSourceReplacementErrorV0> {
385 route_design_token_values_with_ir_transaction_on_ir(ir, dialect, routes)
386}
387
388pub(crate) fn tree_shake_css_class_rules_in_ir(
389 ir: &mut TransformIrV0,
390 dialect: StyleDialect,
391 reachable_class_names: &[String],
392) -> Result<Vec<TransformSemanticRemovalCandidate>, TransformIrSourceReplacementErrorV0> {
393 tree_shake_css_class_rules_with_ir_transaction_on_ir(ir, dialect, reachable_class_names)
394}
395
396pub(crate) fn tree_shake_css_keyframes_in_ir(
397 ir: &mut TransformIrV0,
398 dialect: StyleDialect,
399 reachable_keyframe_names: &[String],
400 reachable_class_names: &[String],
401) -> Result<Vec<TransformSemanticRemovalCandidate>, TransformIrSourceReplacementErrorV0> {
402 tree_shake_css_keyframes_with_ir_transaction_on_ir(
403 ir,
404 dialect,
405 reachable_keyframe_names,
406 reachable_class_names,
407 )
408}
409
410pub(crate) fn tree_shake_css_modules_values_in_ir(
411 ir: &mut TransformIrV0,
412 dialect: StyleDialect,
413 reachable_value_names: &[String],
414 reachable_keyframe_names: &[String],
415 reachable_class_names: &[String],
416) -> Result<Vec<TransformSemanticRemovalCandidate>, TransformIrSourceReplacementErrorV0> {
417 tree_shake_css_modules_values_with_ir_transaction_on_ir(
418 ir,
419 dialect,
420 reachable_value_names,
421 reachable_keyframe_names,
422 reachable_class_names,
423 )
424}
425
426pub(crate) fn tree_shake_css_custom_properties_in_ir(
427 ir: &mut TransformIrV0,
428 dialect: StyleDialect,
429 reachable_custom_property_names: &[String],
430 reachable_keyframe_names: &[String],
431 reachable_class_names: &[String],
432) -> Result<Vec<TransformSemanticRemovalCandidate>, TransformIrSourceReplacementErrorV0> {
433 tree_shake_css_custom_properties_with_ir_transaction_on_ir(
434 ir,
435 dialect,
436 reachable_custom_property_names,
437 reachable_keyframe_names,
438 reachable_class_names,
439 )
440}
441
442pub(crate) fn rewrite_css_module_class_names_in_ir(
443 ir: &mut TransformIrV0,
444 dialect: StyleDialect,
445 rewrites: &[TransformClassNameRewriteV0],
446) -> Result<usize, TransformIrSourceReplacementErrorV0> {
447 rewrite_css_module_class_names_with_ir_transaction_on_ir(ir, dialect, rewrites)
448}
449
450pub(crate) fn substitute_static_css_custom_properties(
451 source: &str,
452 dialect: StyleDialect,
453) -> (String, usize) {
454 substitute_static_css_custom_properties_with_lexer(source, dialect)
455}
456
457pub fn summarize_static_css_custom_property_fixed_point_from_source(
459 source: &str,
460 dialect: StyleDialect,
461) -> CustomPropertyLeastFixedPointSummaryV0 {
462 let lexed = lex(source, dialect);
463 let tokens = lexed.tokens();
464 let env_rules = collect_top_level_ordinary_rule_slices(source, tokens);
465 let env = collect_static_root_custom_property_env(tokens, &env_rules);
466 summarize_custom_property_least_fixed_point(&env)
467}
468
469pub fn parse_static_css_cascade_value(value: &str) -> Option<CascadeValue> {
471 match value.trim() {
472 "initial" => Some(CascadeValue::Initial),
473 "inherit" => Some(CascadeValue::Inherit),
474 "unset" => Some(CascadeValue::Unset),
475 value => parse_static_custom_property_env_value(value),
476 }
477}
478
479pub(crate) fn reduce_css_calc(source: &str, dialect: StyleDialect) -> (String, usize) {
480 reduce_css_calc_with_lexer(source, dialect)
481}
482
483pub(crate) fn normalize_css_whitespace(source: &str, dialect: StyleDialect) -> (String, usize) {
484 normalize_css_whitespace_with_lexer(source, dialect)
485}