vize_atelier_dom 0.258.0

Atelier DOM - The DOM compiler workshop for Vize
Documentation
use vize_atelier_core::options::{BindingMetadata, BindingType, CodegenMode};
use vize_atelier_dom::{DomCompilerOptions, compile_template_with_options};
use vize_carton::{Bump, FxHashMap, String};

fn full_output(preamble: &str, code: &str) -> String {
    let mut full = String::with_capacity(preamble.len() + code.len() + 1);
    full.push_str(preamble);
    full.push('\n');
    full.push_str(code);
    full
}

#[test]
fn inline_setup_ref_component_tag_uses_unref() {
    let allocator = Bump::new();
    let mut bindings = FxHashMap::default();
    bindings.insert("Menu".into(), BindingType::SetupRef);
    bindings.insert("show".into(), BindingType::SetupRef);
    bindings.insert("items".into(), BindingType::SetupRef);

    let options = DomCompilerOptions {
        mode: CodegenMode::Module,
        prefix_identifiers: true,
        inline: true,
        binding_metadata: Some(BindingMetadata {
            bindings,
            props_aliases: FxHashMap::default(),
            is_script_setup: true,
        }),
        ..Default::default()
    };

    let (_, errors, result) = compile_template_with_options(
        &allocator,
        r#"<div><Menu>hello</Menu><Menu v-if="show" /><Menu v-for="item in items" :key="item" /><Menu v-once /><Menu.Item /></div>"#,
        options,
    );

    assert!(errors.is_empty(), "Errors: {:?}", errors);
    let full = full_output(&result.preamble, &result.code);
    assert_eq!(
        component_call_targets(&full),
        vec![
            "_unref(Menu)",
            "_unref(Menu)",
            "_unref(Menu).Item",
            "_unref(Menu)",
            "_unref(Menu)",
        ]
    );
}

fn component_call_targets(source: &str) -> Vec<String> {
    let mut targets = Vec::new();
    for marker in ["_createVNode(", "_createBlock("] {
        let mut offset = 0;
        while let Some(index) = source[offset..].find(marker) {
            let start = offset + index + marker.len();
            let Some(target) = first_call_arg(&source[start..]) else {
                break;
            };
            if !target.starts_with('"') {
                targets.push(target);
            }
            offset = start;
        }
    }
    targets
}

fn first_call_arg(source: &str) -> Option<String> {
    let mut depth = 0i32;
    for (index, ch) in source.char_indices() {
        match ch {
            '(' => depth += 1,
            ')' if depth == 0 => return Some(source[..index].trim().into()),
            ')' => depth -= 1,
            ',' if depth == 0 => return Some(source[..index].trim().into()),
            _ => {}
        }
    }
    None
}

fn binding_matrix() -> BindingMetadata {
    let mut bindings = FxHashMap::default();
    bindings.insert("RefMenu".into(), BindingType::SetupRef);
    bindings.insert("MaybeMenu".into(), BindingType::SetupMaybeRef);
    bindings.insert("LetMenu".into(), BindingType::SetupLet);
    bindings.insert("ImportedMenu".into(), BindingType::SetupConst);
    bindings.insert("ShallowMenu".into(), BindingType::SetupRef);
    bindings.insert("lowercaseWidget".into(), BindingType::SetupConst);
    BindingMetadata {
        bindings,
        props_aliases: FxHashMap::default(),
        is_script_setup: true,
    }
}

#[test]
fn setup_component_tag_binding_matrix_matches_dom_modes() {
    let allocator = Bump::new();
    let source = r#"<RefMenu /><MaybeMenu /><LetMenu /><ImportedMenu /><ShallowMenu /><lowercase-widget /><RefMenu.Item />"#;

    let inline_options = DomCompilerOptions {
        mode: CodegenMode::Module,
        prefix_identifiers: true,
        inline: true,
        binding_metadata: Some(binding_matrix()),
        ..Default::default()
    };
    let (_, inline_errors, inline_result) =
        compile_template_with_options(&allocator, source, inline_options);
    assert_eq!(inline_errors.len(), 0);
    let inline = full_output(&inline_result.preamble, &inline_result.code);
    assert_eq!(
        component_call_targets(&inline),
        vec![
            "_unref(RefMenu)",
            "_unref(MaybeMenu)",
            "_unref(LetMenu)",
            "ImportedMenu",
            "_unref(ShallowMenu)",
            "lowercaseWidget",
            "_unref(RefMenu).Item",
        ]
    );

    let function_options = DomCompilerOptions {
        mode: CodegenMode::Function,
        prefix_identifiers: true,
        inline: false,
        binding_metadata: Some(binding_matrix()),
        ..Default::default()
    };
    let (_, function_errors, function_result) =
        compile_template_with_options(&allocator, source, function_options);
    assert_eq!(function_errors.len(), 0);
    let function = full_output(&function_result.preamble, &function_result.code);
    assert_eq!(
        component_call_targets(&function),
        vec![
            "$setup.RefMenu",
            "$setup.MaybeMenu",
            "$setup.LetMenu",
            "$setup.ImportedMenu",
            "$setup.ShallowMenu",
            "$setup.lowercaseWidget",
            "$setup.RefMenu.Item",
        ]
    );
}