omena-transform-passes 0.2.0

Transform pass registry and DAG planner for Omena CSS
Documentation
use crate::{
    TransformExecutionContextV0, execute_transform_passes_on_source,
    execute_transform_passes_on_source_with_dialect_and_context,
};
use omena_parser::StyleDialect;
use omena_transform_cst::TransformPassKind;

#[test]
fn execution_runtime_keeps_keyframe_tree_shaking_planned_without_closed_world_context() {
    let source = r#"@keyframes unused { to { opacity: 1; } } .btn { color: red; }"#;
    let execution = execute_transform_passes_on_source(
        source,
        &[
            TransformPassKind::TreeShakeKeyframes,
            TransformPassKind::PrintCss,
        ],
    );

    assert_eq!(execution.output_css, source);
    assert_eq!(execution.mutation_count, 0);
    assert_eq!(execution.executed_pass_ids, vec!["print-css"]);
    assert_eq!(
        execution.planned_only_pass_ids,
        vec!["tree-shake-keyframes"]
    );
}

#[test]
fn execution_runtime_tree_shakes_keyframes_with_closed_world_context() {
    let source = r#"@-webkit-keyframes fade { to { opacity: 1; } } @keyframes fade { to { opacity: 1; } } @-webkit-keyframes spin { to { transform: rotate(1turn); } } @keyframes spin { to { transform: rotate(1turn); } } @-webkit-keyframes dead { to { opacity: 0; } } @keyframes dead { to { opacity: 0; } } @keyframes ghost { to { opacity: .5; } } .btn { animation: 1s ease fade; } .dead-ref { animation: ghost 1s ease; }"#;
    let context = TransformExecutionContextV0 {
        closed_style_world: true,
        reachable_class_names: vec!["btn".to_string()],
        reachable_keyframe_names: vec!["spin".to_string()],
        ..TransformExecutionContextV0::default()
    };
    let execution = execute_transform_passes_on_source_with_dialect_and_context(
        source,
        StyleDialect::Css,
        &[
            TransformPassKind::TreeShakeKeyframes,
            TransformPassKind::PrintCss,
        ],
        &context,
    );

    assert_eq!(execution.mutation_count, 3);
    assert_eq!(
        execution.output_css,
        r#"@-webkit-keyframes fade { to { opacity: 1; } } @keyframes fade { to { opacity: 1; } } @-webkit-keyframes spin { to { transform: rotate(1turn); } } @keyframes spin { to { transform: rotate(1turn); } }    .btn { animation: 1s ease fade; } .dead-ref { animation: ghost 1s ease; }"#
    );
    assert_eq!(
        execution.executed_pass_ids,
        vec!["tree-shake-keyframes", "print-css"]
    );
    assert_eq!(execution.semantic_removals.len(), 3);
    assert!(
        execution
            .semantic_removals
            .iter()
            .all(|removal| removal.symbol_kind == "keyframes")
    );
    assert!(
        execution
            .semantic_removals
            .iter()
            .any(|removal| removal.name == "dead" && removal.pass_id == "tree-shake-keyframes")
    );
    assert!(
        execution.semantic_removals.iter().any(|removal| {
            removal.name == "ghost" && removal.pass_id == "tree-shake-keyframes"
        })
    );
}

#[test]
fn execution_runtime_tree_shakes_nested_keyframes_with_closed_world_context() {
    let source = r#"@media screen { @keyframes spin { to { opacity: 1; } } @keyframes ghost { to { opacity: 0; } } } @supports (display: grid) { @-webkit-keyframes dead { to { opacity: .5; } } } .btn { animation: spin 1s; }"#;
    let context = TransformExecutionContextV0 {
        closed_style_world: true,
        reachable_class_names: vec!["btn".to_string()],
        ..TransformExecutionContextV0::default()
    };
    let execution = execute_transform_passes_on_source_with_dialect_and_context(
        source,
        StyleDialect::Css,
        &[
            TransformPassKind::TreeShakeKeyframes,
            TransformPassKind::PrintCss,
        ],
        &context,
    );

    assert_eq!(execution.mutation_count, 2);
    assert_eq!(
        execution.output_css,
        r#"@media screen { @keyframes spin { to { opacity: 1; } }  } @supports (display: grid) {  } .btn { animation: spin 1s; }"#
    );
    assert!(
        execution
            .semantic_removals
            .iter()
            .any(|removal| removal.name == "ghost")
    );
    assert!(
        execution
            .semantic_removals
            .iter()
            .any(|removal| removal.name == "dead")
    );
}

#[test]
fn execution_runtime_tree_shakes_quoted_keyframes_with_closed_world_context() {
    let source = r#"@keyframes "slide" { to { opacity: 1; } } @keyframes "fade in" { to { opacity: 1; } } @keyframes "ghost" { to { opacity: 0; } } .btn { animation-name: "slide"; } .alt { animation: "slide" 1s ease; } .space { animation: "fade in" 1s ease; }"#;
    let context = TransformExecutionContextV0 {
        closed_style_world: true,
        ..TransformExecutionContextV0::default()
    };
    let execution = execute_transform_passes_on_source_with_dialect_and_context(
        source,
        StyleDialect::Css,
        &[
            TransformPassKind::TreeShakeKeyframes,
            TransformPassKind::PrintCss,
        ],
        &context,
    );

    assert_eq!(execution.mutation_count, 1);
    assert_eq!(
        execution.output_css,
        r#"@keyframes "slide" { to { opacity: 1; } } @keyframes "fade in" { to { opacity: 1; } }  .btn { animation-name: "slide"; } .alt { animation: "slide" 1s ease; } .space { animation: "fade in" 1s ease; }"#
    );
    assert_eq!(
        execution.executed_pass_ids,
        vec!["tree-shake-keyframes", "print-css"]
    );
}

#[test]
fn execution_runtime_tree_shakes_escaped_keyframes_with_closed_world_context() {
    let source = r#"@keyframes spin\:fast { to { opacity: 1; } } @keyframes hex\3A fast { to { opacity: .5; } } @keyframes dead { to { opacity: 0; } } .btn { animation: spin\:fast 1s ease; } .dead-ref { animation: dead 1s ease; }"#;
    let context = TransformExecutionContextV0 {
        closed_style_world: true,
        reachable_class_names: vec!["btn".to_string()],
        reachable_keyframe_names: vec!["hex:fast".to_string()],
        ..TransformExecutionContextV0::default()
    };
    let execution = execute_transform_passes_on_source_with_dialect_and_context(
        source,
        StyleDialect::Css,
        &[
            TransformPassKind::TreeShakeKeyframes,
            TransformPassKind::PrintCss,
        ],
        &context,
    );

    assert!(execution.output_css.contains("@keyframes spin\\:fast"));
    assert!(execution.output_css.contains("@keyframes hex\\3A fast"));
    assert!(!execution.output_css.contains("@keyframes dead"));
    assert!(
        execution
            .output_css
            .contains(".btn { animation: spin\\:fast 1s ease; }")
    );
    assert_eq!(execution.mutation_count, 1);
    assert_eq!(
        execution.executed_pass_ids,
        vec!["tree-shake-keyframes", "print-css"]
    );
    assert!(
        execution
            .semantic_removals
            .iter()
            .any(|removal| { removal.name == "dead" && removal.pass_id == "tree-shake-keyframes" })
    );
}