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