omena-transform-passes 0.3.0

Transform pass registry and DAG planner for Omena CSS
Documentation
use super::execute_transform_passes_on_source;
use crate::{
    TransformCascadeEnvironmentV0, TransformExecutionContextV0, TransformExecutionPolicyV0,
    TransformStrictPolicyReasonV0,
    execute_transform_passes_on_source_with_dialect_context_and_policy,
};
use omena_parser::StyleDialect;
use omena_transform_cst::TransformPassKind;

#[test]
fn execution_runtime_rewrites_declaration_values_inside_group_rules() {
    let source = r#"@media (min-width: 1px) { .a { width: calc(1px + 1px); margin: 1px 1px 1px 1px; color: blue; } } @supports (display: grid) { .b { color: blue; } }"#;
    let execution = execute_transform_passes_on_source(
        source,
        &[
            TransformPassKind::ShorthandCombining,
            TransformPassKind::CalcReduction,
            TransformPassKind::ColorCompression,
            TransformPassKind::PrintCss,
        ],
    );

    assert_eq!(execution.mutation_count, 4);
    assert_eq!(
        execution.output_css,
        r#"@media (min-width: 1px) { .a { width: 2px; margin: 1px; color: #00f; } } @supports (display: grid) { .b { color: #00f; } }"#
    );
    assert_eq!(
        execution.executed_pass_ids,
        vec![
            "shorthand-combining",
            "calc-reduction",
            "color-compression",
            "print-css"
        ]
    );
}

#[test]
fn execution_runtime_removes_cascade_safe_duplicate_rules() {
    let source = r#".a { color: red; } .b { color: red; } .a { color: blue; } .a { color: red; } @media (min-width: 1px) { .m { color: red; } .x { color: blue; } .m { color: red; } } @media (max-width: 1px) { .m { color: red; } }"#;
    let execution = execute_transform_passes_on_source(
        source,
        &[
            TransformPassKind::RuleDeduplication,
            TransformPassKind::PrintCss,
        ],
    );

    assert_eq!(execution.mutation_count, 2);
    assert_eq!(
        execution.output_css,
        r#" .b { color: red; } .a { color: blue; } .a { color: red; } @media (min-width: 1px) {  .x { color: blue; } .m { color: red; } } @media (max-width: 1px) { .m { color: red; } }"#
    );
    assert_eq!(
        execution.executed_pass_ids,
        vec!["rule-deduplication", "print-css"]
    );
}

#[test]
fn execution_runtime_removes_overridden_same_property_declarations() {
    let source = r#".a { color: red; color: blue; --tone: red; --tone: blue; display: -webkit-box; display: flex; color: green !important; color: black !important; composes: base; composes: utility; } :export { token: red; token: blue; }"#;
    let execution = execute_transform_passes_on_source(
        source,
        &[
            TransformPassKind::RuleDeduplication,
            TransformPassKind::PrintCss,
        ],
    );

    assert_eq!(execution.mutation_count, 3);
    assert_eq!(
        execution.output_css,
        r#".a {  color: blue;  --tone: blue; display: -webkit-box; display: flex;  color: black !important; composes: base; composes: utility; } :export { token: red; token: blue; }"#
    );
    assert_eq!(
        execution.executed_pass_ids,
        vec!["rule-deduplication", "print-css"]
    );
}

#[test]
fn execution_runtime_merges_adjacent_same_selector_rules_only() {
    let source = r#".a { color: red; } .a { background: blue; } .a { outline: 0; } .b { color: red; } .a { border: 0; } @media (min-width: 1px) { .m { color: red; } .m { background: blue; } }"#;
    let execution = execute_transform_passes_on_source(
        source,
        &[TransformPassKind::RuleMerging, TransformPassKind::PrintCss],
    );

    assert_eq!(execution.mutation_count, 2);
    assert_eq!(
        execution.output_css,
        r#".a { color: red; background: blue; outline: 0; } .b { color: red; } .a { border: 0; } @media (min-width: 1px) { .m { color: red; background: blue; } }"#
    );
    assert_eq!(
        execution.executed_pass_ids,
        vec!["rule-merging", "print-css"]
    );
}

#[test]
fn strict_verification_refuses_winner_sensitive_pass_without_cascade_environment() {
    let source = ".a { color: red; } .a { background: blue; }";
    let policy = TransformExecutionPolicyV0::for_profile("strict-verification").unwrap_or_default();
    let execution = execute_transform_passes_on_source_with_dialect_context_and_policy(
        source,
        StyleDialect::Css,
        &[TransformPassKind::RuleMerging],
        &TransformExecutionContextV0::default(),
        &policy,
    );

    assert_eq!(execution.output_css, source);
    assert_eq!(execution.strict_policy.refused_count, 1);
    assert_eq!(execution.strict_policy.rolled_back_count, 0);
    assert_eq!(
        execution.strict_policy.refusal_reasons[0].pass_id,
        "rule-merging"
    );
}

#[test]
fn strict_verification_admits_winner_sensitive_pass_with_cascade_environment() {
    let source = ".a { color: red; } .a { background: blue; }";
    let policy = TransformExecutionPolicyV0::for_profile("strict-verification").unwrap_or_default();
    let context = TransformExecutionContextV0 {
        cascade_environment: Some(TransformCascadeEnvironmentV0::default()),
        ..TransformExecutionContextV0::default()
    };
    let execution = execute_transform_passes_on_source_with_dialect_context_and_policy(
        source,
        StyleDialect::Css,
        &[TransformPassKind::RuleMerging],
        &context,
        &policy,
    );

    assert_eq!(execution.strict_policy.refused_count, 0);
    assert_eq!(execution.strict_policy.rolled_back_count, 0);
    assert_eq!(execution.executed_pass_ids, vec!["rule-merging"]);
    assert_ne!(execution.output_css, source);
}

#[test]
fn strict_verification_rolls_back_when_required_observation_is_unavailable() {
    let source = ".a{& .b{color:red}}";
    let policy = TransformExecutionPolicyV0::for_profile("strict-verification").unwrap_or_default();
    let context = TransformExecutionContextV0 {
        cascade_environment: Some(TransformCascadeEnvironmentV0::default()),
        ..TransformExecutionContextV0::default()
    };
    let execution = execute_transform_passes_on_source_with_dialect_context_and_policy(
        source,
        StyleDialect::Css,
        &[TransformPassKind::NestingUnwrap],
        &context,
        &policy,
    );

    assert_eq!(execution.output_css, source);
    assert_eq!(execution.strict_policy.refused_count, 0);
    assert_eq!(execution.strict_policy.rolled_back_count, 1);
    assert_eq!(
        execution.strict_policy.rollback_reasons[0].pass_id,
        "nesting-unwrap"
    );
    assert!(matches!(
        execution.strict_policy.rollback_reasons[0]
            .reasons
            .as_slice(),
        [TransformStrictPolicyReasonV0::ObservationUnavailable { .. }]
    ));

    let descriptive =
        execute_transform_passes_on_source(source, &[TransformPassKind::NestingUnwrap]);
    assert_eq!(descriptive.strict_policy.rolled_back_count, 0);
    assert_ne!(descriptive.output_css, source);
}

#[test]
fn descriptive_execution_does_not_enforce_strict_evidence() {
    let source = ".a { color: red; } .a { background: blue; }";
    let execution = execute_transform_passes_on_source(source, &[TransformPassKind::RuleMerging]);

    assert_eq!(execution.strict_policy.refused_count, 0);
    assert_eq!(execution.strict_policy.rolled_back_count, 0);
    assert_eq!(execution.executed_pass_ids, vec!["rule-merging"]);
}

#[test]
fn execution_runtime_preserves_declaration_boundaries_when_merging_semicolonless_rules() {
    let source = r#".b{color:red}.b{background:blue} @media (min-width: 1px) { .m { color: red } .m { background: blue } }"#;
    let execution = execute_transform_passes_on_source(
        source,
        &[TransformPassKind::RuleMerging, TransformPassKind::PrintCss],
    );

    assert_eq!(execution.mutation_count, 2);
    assert_eq!(
        execution.output_css,
        r#".b { color:red; background:blue; } @media (min-width: 1px) { .m { color: red; background: blue; } }"#
    );
    assert_eq!(
        execution.executed_pass_ids,
        vec!["rule-merging", "print-css"]
    );
}

#[test]
fn execution_runtime_merges_adjacent_same_conditional_wrappers() {
    let source = r#"@media (prefers-color-scheme: dark) { .card { color: white; } } @media (prefers-color-scheme: dark) { .card .title { color: #ddd; } } @supports (display: grid) { .grid { display: grid; } } @supports (display: flex) { .flex { display: flex; } } @supports (display: flex) { .flex .child { display: flex; } }"#;
    let execution = execute_transform_passes_on_source(
        source,
        &[TransformPassKind::RuleMerging, TransformPassKind::PrintCss],
    );

    assert_eq!(execution.mutation_count, 2);
    assert_eq!(
        execution.output_css,
        r#"@media (prefers-color-scheme: dark) { .card { color: white; } .card .title { color: #ddd; } } @supports (display: grid) { .grid { display: grid; } } @supports (display: flex) { .flex { display: flex; } .flex .child { display: flex; } }"#
    );
    assert_eq!(
        execution.executed_pass_ids,
        vec!["rule-merging", "print-css"]
    );
}

#[test]
fn execution_runtime_merges_adjacent_same_block_selectors_only() {
    let source = r#".a { color: red; } .b { color: red; } .c { color: red; } .d { color: blue; } .e { color: red; } .x{color:red;}.y{color:red} @media (min-width: 1px) { .m { color: black; } .n { color: black; } }"#;
    let execution = execute_transform_passes_on_source(
        source,
        &[
            TransformPassKind::SelectorMerging,
            TransformPassKind::PrintCss,
        ],
    );

    assert_eq!(execution.mutation_count, 3);
    assert_eq!(
        execution.output_css,
        r#".a, .b, .c { color: red; } .d { color: blue; } .e, .x, .y { color: red; } @media (min-width: 1px) { .m, .n { color: black; } }"#
    );
    assert_eq!(
        execution.executed_pass_ids,
        vec!["selector-merging", "print-css"]
    );
}