use crate::{ElementNode, ElementType, ExpressionNode, PropNode, RuntimeHelper, TemplateChildNode};
use super::super::{
children::push_interpolation_value,
context::CodegenContext,
expression::generate_expression,
helpers::{escape_js_string, to_valid_asset_identifier},
node::generate_node,
patch_flag::{calculate_element_patch_info, patch_flag_name},
props::is_supported_directive,
};
use vize_carton::ToCompactString;
pub fn generate_v_once_element(ctx: &mut CodegenContext, el: &ElementNode<'_>) {
let cache_index = ctx.next_cache_index();
ctx.use_helper(RuntimeHelper::SetBlockTracking);
ctx.push("_cache[");
ctx.push(&cache_index.to_compact_string());
ctx.push("] || (");
ctx.indent();
ctx.newline();
ctx.push(ctx.helper(RuntimeHelper::SetBlockTracking));
ctx.push("(-1, true),");
ctx.newline();
ctx.push("(_cache[");
ctx.push(&cache_index.to_compact_string());
ctx.push("] = ");
if el.tag_type == ElementType::Component {
ctx.use_helper(RuntimeHelper::CreateVNode);
ctx.use_helper(RuntimeHelper::ResolveComponent);
ctx.push(ctx.helper(RuntimeHelper::CreateVNode));
ctx.push("(");
ctx.push(&to_valid_asset_identifier("component", &el.tag));
ctx.push(")");
} else {
ctx.use_helper(RuntimeHelper::CreateElementVNode);
ctx.push(ctx.helper(RuntimeHelper::CreateElementVNode));
ctx.push("(\"");
ctx.push(&el.tag);
ctx.push("\"");
let has_props = el.props.iter().any(|p| match p {
PropNode::Directive(dir) => dir.name != "once" && is_supported_directive(dir),
PropNode::Attribute(_) => true,
});
if has_props {
ctx.push(", ");
generate_v_once_props(ctx, el);
} else if !el.children.is_empty() {
ctx.push(", null");
}
if !el.children.is_empty() {
ctx.push(", [");
ctx.indent();
for (i, child) in el.children.iter().enumerate() {
if i > 0 {
ctx.push(",");
}
ctx.newline();
generate_v_once_child(ctx, child);
}
ctx.deindent();
ctx.newline();
ctx.push("]");
}
let (patch_flag, _) = calculate_element_patch_info(
el,
ctx.options.binding_metadata.as_ref(),
ctx.cache_handlers_in_current_scope(),
);
if let Some(flag) = patch_flag {
let filtered_flag = flag & (2 | 4); if filtered_flag > 0 {
if el.children.is_empty() && !has_props {
ctx.push(", null");
}
ctx.push(", ");
ctx.push(&filtered_flag.to_compact_string());
ctx.push(" /* ");
let flag_name = patch_flag_name(filtered_flag);
ctx.push(&flag_name);
ctx.push(" */");
}
}
ctx.push(")");
}
ctx.push(").cacheIndex = ");
ctx.push(&cache_index.to_compact_string());
ctx.push(",");
ctx.newline();
ctx.push(ctx.helper(RuntimeHelper::SetBlockTracking));
ctx.push("(1),");
ctx.newline();
ctx.push("_cache[");
ctx.push(&cache_index.to_compact_string());
ctx.push("]");
ctx.deindent();
ctx.newline();
ctx.push(")");
}
pub fn generate_v_once_props(ctx: &mut CodegenContext, el: &ElementNode<'_>) {
ctx.push("{");
ctx.indent();
let mut first = true;
for prop in &el.props {
match prop {
PropNode::Directive(dir) if dir.name == "once" => continue,
PropNode::Directive(dir) if dir.name == "bind" => {
if let Some(ExpressionNode::Simple(arg)) = &dir.arg {
if !first {
ctx.push(",");
}
first = false;
ctx.newline();
if arg.content == "class" {
ctx.use_helper(RuntimeHelper::NormalizeClass);
ctx.push("class: ");
ctx.push(ctx.helper(RuntimeHelper::NormalizeClass));
ctx.push("(");
if let Some(exp) = &dir.exp {
generate_expression(ctx, exp);
}
ctx.push(")");
} else if arg.content == "style" {
ctx.use_helper(RuntimeHelper::NormalizeStyle);
ctx.push("style: ");
ctx.push(ctx.helper(RuntimeHelper::NormalizeStyle));
ctx.push("(");
if let Some(exp) = &dir.exp {
generate_expression(ctx, exp);
}
ctx.push(")");
} else {
ctx.push(&arg.content);
ctx.push(": ");
if let Some(exp) = &dir.exp {
generate_expression(ctx, exp);
}
}
}
}
PropNode::Attribute(attr) => {
if !first {
ctx.push(",");
}
first = false;
ctx.newline();
ctx.push(&attr.name);
ctx.push(": ");
if let Some(value) = &attr.value {
ctx.push("\"");
ctx.push(&escape_js_string(&value.content));
ctx.push("\"");
} else {
ctx.push("\"\"");
}
}
_ => {}
}
}
ctx.deindent();
ctx.newline();
ctx.push("}");
}
pub fn generate_v_once_child(ctx: &mut CodegenContext, node: &TemplateChildNode<'_>) {
match node {
TemplateChildNode::Text(text) => {
ctx.use_helper(RuntimeHelper::CreateText);
ctx.push(ctx.helper(RuntimeHelper::CreateText));
ctx.push("(\"");
ctx.push(&escape_js_string(&text.content));
ctx.push("\")");
}
TemplateChildNode::Interpolation(interp) => {
ctx.use_helper(RuntimeHelper::CreateText);
ctx.push(ctx.helper(RuntimeHelper::CreateText));
ctx.push("(");
push_interpolation_value(ctx, interp);
ctx.push(", 1 /* TEXT */)");
}
_ => generate_node(ctx, node),
}
}