vize_l1 0.481.0

L1 - the lossless Vue-template surface tree (codename Sinopia, Davinci P2-7)
Documentation
use super::{assert_original, observe, selected};
use crate::embed::{Grammar, Lang, Shape};
use crate::markup::{
    ArgSyntax, DirectiveName, DirectivePrefix, NativeConditionKind, NativeTemplateGrammar,
};
use oxc_ast::ast::{Expression, TSType};
use oxc_span::GetSpan;
use vize_l0::{Allocator, Span};

#[test]
fn unicode_nonzero_static_heads_keep_original_parts_ordinals_and_distinct_stock_roots() {
    let arena = Allocator::default();
    let source = "<!--é--><template><p title=x v-bind:雪='条件' :data-id='ready' v-if='ok'/></template><!--尾-->";
    let first = selected(&arena, source);
    let second = selected(&arena, source);
    let mut roots = alloc::vec::Vec::new();
    for owner in [&first, &second] {
        assert_eq!(owner.component().block().start(), 19);
        assert_eq!(
            owner.component().block().source(),
            "<p title=x v-bind:雪='条件' :data-id='ready' v-if='ok'/>"
        );
        let element = owner.children().next().unwrap().into_element().unwrap();
        assert_eq!(element.attributes().len(), 4);
        let plain = owner
            .observe_attribute_head(element.attributes().next().unwrap())
            .unwrap();
        assert_eq!(plain.directive(), None);
        assert!(plain.static_binding().unwrap().is_none());
        for (ordinal, name, argument, value, raw, parts) in [
            (
                1,
                Span::new(30, 40),
                Span::new(37, 40),
                Span::new(42, 48),
                "条件",
                DirectiveName {
                    prefix: DirectivePrefix::Full,
                    name: Span::new(32, 36),
                    arg: Some(ArgSyntax::Static(Span::new(37, 40))),
                    modifiers: Span::new(40, 40),
                },
            ),
            (
                2,
                Span::new(50, 58),
                Span::new(51, 58),
                Span::new(60, 65),
                "ready",
                DirectiveName {
                    prefix: DirectivePrefix::Bind,
                    name: Span::new(50, 50),
                    arg: Some(ArgSyntax::Static(Span::new(51, 58))),
                    modifiers: Span::new(58, 58),
                },
            ),
        ] {
            let attribute = element.attributes().nth(ordinal).unwrap();
            let before = arena.allocated_bytes();
            let head = owner.observe_attribute_head(attribute.reborrow()).unwrap();
            let binding = head.static_binding().unwrap().unwrap();
            assert_eq!(arena.allocated_bytes(), before);
            assert!(core::ptr::eq(binding.head(), &head));
            assert!(core::ptr::eq(head.selected(), owner));
            assert!(core::ptr::eq(
                head.attribute().surface(),
                attribute.surface()
            ));
            assert_eq!(head.attribute().ordinal(), ordinal);
            assert_eq!(head.name_block().span(), name);
            assert_eq!(head.name_block().source(), name.slice(source));
            assert_eq!(head.directive(), Some(parts));
            assert_eq!(head.condition_kind(), None);
            assert_eq!(binding.argument_span(), argument);
            let operand = binding.observe_expression().unwrap();
            assert_eq!(operand.name_span(), name);
            assert_eq!(operand.argument_span(), argument);
            assert_eq!(operand.value_span(), value);
            assert_eq!(operand.raw_value(), raw);
            let syntax = operand.syntax();
            assert_eq!(syntax.source().span(), value);
            assert_eq!(syntax.source().text(), raw);
            assert!(core::ptr::eq(syntax.source().text(), operand.raw_value()));
            assert!(syntax.source().decode_map().is_none());
            assert_eq!(syntax.parser_prefix(), 2);
            assert_eq!(syntax.hole(), None);
            assert_eq!(syntax.comments().count(), 0);
            assert_eq!(syntax.diagnostics().count(), 0);
            let Expression::Identifier(identifier) = syntax.expression().unwrap() else {
                panic!("original static binding identifier")
            };
            assert_eq!(identifier.name.as_str(), raw);
            let end = if ordinal == 1 { 8 } else { 7 };
            assert_eq!(identifier.span, oxc_span::Span::new(2, end));
            assert_eq!(
                syntax.decoded_span(identifier.span),
                Ok(Span::new(0, end - 2))
            );
            assert_eq!(syntax.authored_span(identifier.span), Ok(value));
            assert_original(&operand, owner, ordinal);
            roots.push(syntax.expression().unwrap());
        }
        let conditional = owner
            .observe_attribute_head(element.attributes().nth(3).unwrap())
            .unwrap();
        assert_eq!(conditional.name_block().span(), Span::new(67, 71));
        assert_eq!(conditional.condition_kind(), Some(NativeConditionKind::If));
        assert!(conditional.static_binding().unwrap().is_none());
    }
    assert_eq!(roots.len(), 4);
    assert!(!core::ptr::eq(roots[0], roots[2]));
    assert!(!core::ptr::eq(roots[1], roots[3]));
}

#[test]
fn intrinsic_js_and_setup_ts_profiles_preserve_original_parentheses_cast_and_child_spans() {
    let arena = Allocator::default();
    for (source, raw, name, argument, value, lang, grammar) in [
        (
            "前\n<template><p :id='(value)'/></template>",
            "(value)",
            Span::new(17, 20),
            Span::new(18, 20),
            Span::new(22, 29),
            Lang::Js,
            NativeTemplateGrammar::JavaScriptModule,
        ),
        (
            "前\n<template><p v-bind:id='value as boolean'/></template><script setup lang=ts>const value=true</script>",
            "value as boolean",
            Span::new(17, 26),
            Span::new(24, 26),
            Span::new(28, 44),
            Lang::Ts,
            NativeTemplateGrammar::TypeScriptModule,
        ),
    ] {
        let owner = selected(&arena, source);
        assert_eq!(owner.grammar(), grammar);
        let operand = observe(&owner, 0);
        assert_eq!(operand.name_span(), name);
        assert_eq!(operand.argument_span(), argument);
        assert_eq!(operand.value_span(), value);
        assert_eq!(operand.raw_value(), raw);
        let syntax = operand.syntax();
        assert_eq!(syntax.source().text(), raw);
        assert_eq!(syntax.source().span(), value);
        assert_eq!(
            syntax.grammar(),
            Grammar {
                shape: Shape::Expr,
                lang
            }
        );
        assert!(syntax.source_type().is_module());
        assert_eq!(syntax.source_type().is_typescript(), lang == Lang::Ts);
        assert_eq!(syntax.parser_prefix(), 2);
        assert_eq!(syntax.hole(), None);
        assert_eq!(syntax.comments().count(), 0);
        assert_eq!(syntax.diagnostics().count(), 0);
        let root = syntax.expression().unwrap();
        assert_eq!(syntax.authored_span(root.span()), Ok(value));
        if lang == Lang::Js {
            let Expression::ParenthesizedExpression(parentheses) = root else {
                panic!("original JS parentheses")
            };
            assert_eq!(parentheses.span, oxc_span::Span::new(2, 9));
            let Expression::Identifier(identifier) = &parentheses.expression else {
                panic!("original JS child")
            };
            assert_eq!(identifier.name.as_str(), "value");
            assert_eq!(identifier.span, oxc_span::Span::new(3, 8));
            assert_eq!(syntax.authored_span(identifier.span), Ok(Span::new(23, 28)));
        } else {
            let Expression::TSAsExpression(cast) = root else {
                panic!("intrinsic TS cast")
            };
            assert_eq!(cast.span, oxc_span::Span::new(2, 18));
            let Expression::Identifier(identifier) = &cast.expression else {
                panic!("original TS value")
            };
            assert_eq!(identifier.name.as_str(), "value");
            assert_eq!(identifier.span, oxc_span::Span::new(2, 7));
            assert_eq!(syntax.authored_span(identifier.span), Ok(Span::new(28, 33)));
            let TSType::TSBooleanKeyword(keyword) = &cast.type_annotation else {
                panic!("original TS type")
            };
            assert_eq!(keyword.span, oxc_span::Span::new(11, 18));
            assert_eq!(syntax.authored_span(keyword.span), Ok(Span::new(37, 44)));
        }
        assert_original(&operand, &owner, 0);
    }
}

#[test]
fn complete_unquoted_values_and_non_identifier_static_names_remain_honest_l1_receipts() {
    let arena = Allocator::default();
    let source = "<template><p :data-id=a+b v-bind:xlink:href='ready'/></template>";
    let owner = selected(&arena, source);
    for (ordinal, name, argument, value, raw, argument_text) in [
        (
            0,
            Span::new(13, 21),
            Span::new(14, 21),
            Span::new(22, 25),
            "a+b",
            "data-id",
        ),
        (
            1,
            Span::new(26, 43),
            Span::new(33, 43),
            Span::new(45, 50),
            "ready",
            "xlink:href",
        ),
    ] {
        let operand = observe(&owner, ordinal);
        assert_eq!(operand.name_span(), name);
        assert_eq!(operand.argument_span(), argument);
        assert_eq!(argument.slice(source), argument_text);
        assert_eq!(operand.value_span(), value);
        assert_eq!(operand.raw_value(), raw);
        assert_eq!(operand.syntax().source().text(), raw);
        assert_eq!(operand.syntax().hole(), None);
        assert_eq!(operand.syntax().diagnostics().count(), 0);
        assert_eq!(operand.syntax().comments().count(), 0);
        if ordinal == 0 {
            let Expression::BinaryExpression(binary) = operand.syntax().expression().unwrap()
            else {
                panic!("original complete unquoted binary")
            };
            assert_eq!(binary.operator.as_str(), "+");
            assert_eq!(binary.span, oxc_span::Span::new(2, 5));
            for (child, name, stock, authored) in [
                (
                    &binary.left,
                    "a",
                    oxc_span::Span::new(2, 3),
                    Span::new(22, 23),
                ),
                (
                    &binary.right,
                    "b",
                    oxc_span::Span::new(4, 5),
                    Span::new(24, 25),
                ),
            ] {
                let Expression::Identifier(identifier) = child else {
                    panic!("original unquoted child")
                };
                assert_eq!(identifier.name.as_str(), name);
                assert_eq!(identifier.span, stock);
                assert_eq!(
                    operand.syntax().authored_span(identifier.span),
                    Ok(authored)
                );
            }
        } else {
            let Expression::Identifier(identifier) = operand.syntax().expression().unwrap() else {
                panic!("original quoted ready identifier")
            };
            assert_eq!(identifier.name.as_str(), "ready");
            assert_eq!(identifier.span, oxc_span::Span::new(2, 7));
        }
        assert_eq!(
            operand
                .syntax()
                .authored_span(operand.syntax().expression().unwrap().span()),
            Ok(value)
        );
        assert_original(&operand, &owner, ordinal);
    }
    let element = owner.children().next().unwrap().into_element().unwrap();
    let unquoted = element.attributes().next().unwrap();
    let frame = unquoted.surface().value.as_ref().unwrap();
    assert!(frame.open_quote.is_none());
    assert!(frame.close_quote.is_none());
    assert_eq!(frame.content.text, "a+b");
}