vize_l1 0.545.0

L1 - the lossless Vue-template surface tree (codename Sinopia, Davinci P2-7)
Documentation
use super::{StyleIssueCode, StyleSyntax};
use crate::{container::Vue, container::vue::DescriptorOptions, markup::SurfaceParseOptions};
use cssparser::Token;
use vize_l0::{
    Allocator, Span,
    config::{VueDialect, VueVersion},
};

fn options() -> DescriptorOptions {
    DescriptorOptions {
        version: VueVersion::V3,
        dialect: VueDialect::Vue,
        template: SurfaceParseOptions::default(),
    }
}

#[test]
fn original_css_parser_tokens_declarations_and_absolute_spans_survive_once() {
    let source = "<!-- 雪🌸 -->\r\n<template><p class='foo'>雪</p></template>\r\n<style scoped lang=css>/* hi */\r\n.\\66 oo { color: red; content: '雪🌸'; --size: 2px; }</style>";
    let arena = Allocator::default();
    let observation = Vue.observe_descriptor(&arena, source, options());
    let original = observation.admitted().unwrap().styles().next().unwrap();
    let syntax = StyleSyntax::observe(original);
    assert!(
        syntax.parser_error().is_none(),
        "{:?}",
        syntax.parser_error()
    );
    assert!(core::ptr::eq(
        syntax.source().source(),
        original.block().source()
    ));
    assert!(core::ptr::eq(syntax.source().root_source(), source));
    assert_eq!(syntax.container_index(), 1);
    let rule = syntax.rule().unwrap();
    assert_eq!(rule.declarations().len(), 3);
    assert_eq!(rule.declarations()[0].name(), "color");
    assert_eq!(rule.declarations()[2].name(), "--size");
    let name = rule
        .prelude()
        .iter()
        .find(|token| matches!(token.token(), Token::Ident(_)))
        .unwrap();
    assert!(matches!(name.token(), Token::Ident(value) if value.as_ref() == "foo"));
    assert_eq!(
        &source[name.span().start as usize..name.span().end as usize],
        "\\66 oo"
    );
    let receipt = syntax.simple_class().unwrap();
    assert!(core::ptr::eq(receipt.syntax(), &syntax));
    assert_eq!(receipt.insertion(), name.span().end);
    for token in rule.prelude().iter().chain(
        rule.declarations()
            .iter()
            .flat_map(|declaration| declaration.value()),
    ) {
        assert!(syntax.source().contains_block_span(token.span()));
        assert!(!source[token.span().start as usize..token.span().end as usize].is_empty());
    }
}

#[test]
fn selector_admission_reads_actual_tokens_instead_of_hiding_whitespace_or_shape() {
    for css in [
        ".a{}",
        ".雪 {}",
        ".a/**/{}",
        "./**/a{}",
        ".a {color:red!important}",
    ] {
        let source = alloc::format!("<template><p/></template><style scoped>{css}</style>");
        let arena = Allocator::default();
        let observation = Vue.observe_descriptor(&arena, &source, options());
        let syntax = StyleSyntax::observe(observation.admitted().unwrap().styles().next().unwrap());
        assert!(syntax.simple_class().is_ok(), "{css}: {:?}", syntax.issue());
    }
    for css in [
        ". a{}",
        ".a .b{}",
        ".a,.b{}",
        "p{}",
        ".a:hover{}",
        ":deep(.a){}",
        ".a[data-x]{}",
    ] {
        let source = alloc::format!("<template><p/></template><style scoped>{css}</style>");
        let arena = Allocator::default();
        let observation = Vue.observe_descriptor(&arena, &source, options());
        let syntax = StyleSyntax::observe(observation.admitted().unwrap().styles().next().unwrap());
        assert!(syntax.simple_class().is_err(), "{css}");
        assert!(syntax.rule().is_some());
        assert!(core::ptr::eq(
            syntax.source().root_source(),
            source.as_str()
        ));
    }
}

#[test]
fn real_parser_refusals_retain_partial_rule_tokens_and_original_complete_source() {
    for css in [
        "",
        ".a{} .b{}",
        "@media screen{.a{}}",
        "@charset 'utf-8';.a{}",
        ".a{color}",
        ".a{color:red",
        ".a{color:v-bind(color)}",
        ".a{color:rgb(1,2,3)}",
        ".a{& .b{color:red}}",
        ".a{content:'}",
        ".a{content:'x\\' }",
    ] {
        let source =
            alloc::format!("<!-- 雪 -->\r\n<template><p/></template><style scoped>{css}</style>");
        let arena = Allocator::default();
        let observation = Vue.observe_descriptor(&arena, &source, options());
        let syntax = StyleSyntax::observe(observation.admitted().unwrap().styles().next().unwrap());
        assert!(syntax.simple_class().is_err(), "{css}");
        assert!(syntax.parser_error().is_some(), "{css}");
        assert!(
            syntax
                .source()
                .contains_block_span(syntax.issue().unwrap().span)
        );
        assert!(core::ptr::eq(
            syntax.source().root_source(),
            source.as_str()
        ));
    }
    let source = "<template><p/></template><style scoped>.a{color:v\\2d bind(color)}</style>";
    let arena = Allocator::default();
    let observation = Vue.observe_descriptor(&arena, source, options());
    let syntax = StyleSyntax::observe(observation.admitted().unwrap().styles().next().unwrap());
    let token = syntax.rule().unwrap().declarations()[0]
        .value()
        .iter()
        .find(|token| matches!(token.token(), Token::Function(_)))
        .unwrap();
    assert!(matches!(token.token(), Token::Function(value) if value.as_ref() == "v-bind"));
    assert_eq!(
        syntax.issue().unwrap().code,
        StyleIssueCode::UnsupportedValue
    );
    assert_eq!(syntax.issue().unwrap().span, token.span());
    assert_eq!(
        &source[token.span().start as usize..token.span().end as usize],
        "v\\2d bind("
    );
}

#[test]
fn non_css_style_profile_is_retained_without_parsing_the_wrong_grammar() {
    let source = "<style scoped lang=scss>$x:red;.a{color:$x}</style><template><p/></template>";
    let arena = Allocator::default();
    let observation = Vue.observe_descriptor(&arena, source, options());
    let style = observation.admitted().unwrap().styles().next().unwrap();
    let syntax = StyleSyntax::observe(style);
    assert_eq!(
        syntax.issue().unwrap().code,
        StyleIssueCode::UnsupportedLanguage
    );
    assert_eq!(syntax.issue().unwrap().span, style.block().span());
    assert!(syntax.rule().is_none());
    assert!(syntax.parser_error().is_none());
    assert!(core::ptr::eq(syntax.source().root_source(), source));
    assert_eq!(
        Span::new(style.block().start(), style.block().end()),
        syntax.source().span()
    );
}

#[test]
fn quoted_binding_spellings_and_comment_markers_refuse_from_actual_retained_tokens() {
    for value in [
        "'v-bind(color)'",
        "\"v-bind (color)\"",
        "'v/**/-bind(color)'",
        "'v-/* x */bind(color)'",
        "'v-bind/* x */(color)'",
        "'v-bind'",
    ] {
        let source = alloc::format!(
            "<!-- 雪 -->\r\n<template><p/></template><style scoped>.a{{content:{value}}}</style>"
        );
        let arena = Allocator::default();
        let descriptor = Vue.observe_descriptor(&arena, &source, options());
        let syntax = StyleSyntax::observe(descriptor.admitted().unwrap().styles().next().unwrap());
        assert!(syntax.simple_class().is_err());
        let token = syntax.rule().unwrap().declarations()[0]
            .value()
            .iter()
            .find(|token| matches!(token.token(), Token::QuotedString(_)))
            .unwrap();
        let issue = syntax.issue().unwrap();
        assert_eq!(issue.code, StyleIssueCode::UnsupportedValue);
        assert_eq!(issue.span, token.span());
        assert_eq!(
            &source[token.span().start as usize..token.span().end as usize],
            value
        );
        assert!(matches!(
            syntax.parser_error().unwrap().kind,
            cssparser::ParseErrorKind::Custom(actual) if actual == issue
        ));
        assert!(core::ptr::eq(
            syntax.source().root_source(),
            source.as_str()
        ));
    }
}