use crate::{
ast::*,
transforms::v_memo::{get_memo_exp, has_v_memo},
};
use super::{
super::{
children::{generate_children, is_directive_comment},
context::CodegenContext,
expression::generate_expression,
helpers::{is_builtin_component, to_valid_asset_identifier},
node::generate_node,
patch_flag::{
calculate_element_patch_info, calculate_element_patch_info_skip_is, patch_flag_name,
},
props::generate_props,
slots::{
generate_slot_outlet_name, generate_slot_outlet_props, generate_slots,
has_dynamic_slots_flag, has_slot_children, has_slot_outlet_props,
},
},
directives::{
generate_custom_directives_closing, generate_vmodel_closing, generate_vshow_closing,
},
helpers::{
child_namespace, crosses_namespace_boundary, has_custom_directives,
has_dynamic_key_binding, has_renderable_props, has_vmodel_directive, has_vshow_directive,
is_dynamic_component, is_is_prop, is_renderable_prop, is_whitespace_or_comment,
},
};
use vize_carton::ToCompactString;
pub fn generate_element(ctx: &mut CodegenContext, el: &ElementNode<'_>) {
if crosses_namespace_boundary(ctx, el) {
super::block::generate_element_block(ctx, el);
return;
}
if matches!(el.tag_type, ElementType::Element | ElementType::Component)
&& has_dynamic_key_binding(el)
{
super::block::generate_element_block(ctx, el);
return;
}
if el.tag_type == ElementType::Component
&& (is_dynamic_component(el)
|| matches!(
el.tag.as_str(),
"Teleport" | "teleport" | "Suspense" | "suspense" | "KeepAlive" | "keep-alive"
))
{
super::block::generate_element_block(ctx, el);
return;
}
if super::helpers::has_v_once(el) {
super::v_once::generate_v_once_element(ctx, el);
return;
}
let memo_cache_index = if has_v_memo(el) {
if let Some(memo_exp) = get_memo_exp(el) {
let cache_index = ctx.next_cache_index();
ctx.use_helper(RuntimeHelper::WithMemo);
ctx.push(ctx.helper(RuntimeHelper::WithMemo));
ctx.push("(");
generate_expression(ctx, memo_exp);
ctx.push(", () => ");
Some(cache_index)
} else {
None
}
} else {
None
};
match el.tag_type {
ElementType::Element => {
let has_custom_dirs = has_custom_directives(el);
if has_custom_dirs {
ctx.use_helper(RuntimeHelper::WithDirectives);
ctx.push(ctx.helper(RuntimeHelper::WithDirectives));
ctx.push("(");
}
let has_vmodel = has_vmodel_directive(el);
if has_vmodel && !has_custom_dirs {
ctx.use_helper(RuntimeHelper::WithDirectives);
ctx.push(ctx.helper(RuntimeHelper::WithDirectives));
ctx.push("(");
}
let has_vshow = has_vshow_directive(el) && !has_vmodel && !has_custom_dirs;
if has_vshow {
ctx.use_helper(RuntimeHelper::WithDirectives);
ctx.use_helper(RuntimeHelper::VShow);
ctx.push(ctx.helper(RuntimeHelper::WithDirectives));
ctx.push("(");
}
ctx.push_pure();
let helper = ctx.helper(RuntimeHelper::CreateElementVNode);
ctx.use_helper(RuntimeHelper::CreateElementVNode);
ctx.push(helper);
ctx.push("(\"");
ctx.push(&el.tag);
ctx.push("\"");
let (patch_flag, dynamic_props) = calculate_element_patch_info(
el,
ctx.options.binding_metadata.as_ref(),
ctx.cache_handlers_in_current_scope(),
);
let has_patch_info = patch_flag.is_some() || dynamic_props.is_some();
if let Some(hoisted_index) = el.hoisted_props_index {
ctx.push(", _hoisted_");
ctx.push(&hoisted_index.to_compact_string());
} else if super::helpers::has_renderable_props(el) {
ctx.push(", ");
ctx.props_is_plain_element = true;
generate_props(ctx, &el.props);
ctx.props_is_plain_element = false;
} else if !el.children.is_empty() || has_patch_info {
ctx.push(", null");
}
if !el.children.is_empty() {
ctx.push(", ");
ctx.with_parent_namespace(child_namespace(el), |ctx| {
generate_children(ctx, &el.children);
});
} else if has_patch_info {
ctx.push(", null");
}
if let Some(flag) = patch_flag {
ctx.push(", ");
ctx.push(&flag.to_compact_string());
ctx.push(" /* ");
let flag_name = patch_flag_name(flag);
ctx.push(&flag_name);
ctx.push(" */");
}
if let Some(props) = dynamic_props {
ctx.push(", [");
for (i, prop) in props.iter().enumerate() {
if i > 0 {
ctx.push(", ");
}
ctx.push("\"");
ctx.push(prop);
ctx.push("\"");
}
ctx.push("]");
}
ctx.push(")");
if has_custom_dirs {
generate_custom_directives_closing(ctx, el);
}
if has_vmodel && !has_custom_dirs {
generate_vmodel_closing(ctx, el);
}
if has_vshow {
generate_vshow_closing(ctx, el);
}
}
ElementType::Component => {
let has_custom_dirs = has_custom_directives(el);
if has_custom_dirs {
ctx.use_helper(RuntimeHelper::WithDirectives);
ctx.push(ctx.helper(RuntimeHelper::WithDirectives));
ctx.push("(");
}
let has_vshow = has_vshow_directive(el) && !has_custom_dirs;
if has_vshow {
ctx.use_helper(RuntimeHelper::WithDirectives);
ctx.use_helper(RuntimeHelper::VShow);
ctx.push(ctx.helper(RuntimeHelper::WithDirectives));
ctx.push("(");
}
ctx.push_pure();
let helper = ctx.helper(RuntimeHelper::CreateVNode);
ctx.use_helper(RuntimeHelper::CreateVNode);
ctx.push(helper);
ctx.push("(");
let is_dynamic = is_dynamic_component(el);
let (dynamic_is, static_is) = if is_dynamic {
let dynamic = el.props.iter().find_map(|p| {
if let PropNode::Directive(dir) = p
&& dir.name == "bind"
&& let Some(ExpressionNode::Simple(arg)) = &dir.arg
&& arg.content == "is"
{
return dir.exp.as_ref();
}
None
});
let static_val = el.props.iter().find_map(|p| {
if let PropNode::Attribute(attr) = p
&& attr.name == "is"
{
return attr.value.as_ref().map(|v| v.content.as_str());
}
None
});
(dynamic, static_val)
} else {
(None, None)
};
if let Some(is_exp) = dynamic_is {
ctx.use_helper(RuntimeHelper::ResolveDynamicComponent);
ctx.push(ctx.helper(RuntimeHelper::ResolveDynamicComponent));
ctx.push("(");
generate_expression(ctx, is_exp);
ctx.push(")");
} else if let Some(component_name) = static_is {
ctx.use_helper(RuntimeHelper::ResolveDynamicComponent);
ctx.push(ctx.helper(RuntimeHelper::ResolveDynamicComponent));
ctx.push("(\"");
ctx.push(component_name);
ctx.push("\")");
} else if let Some(builtin) = is_builtin_component(&el.tag) {
ctx.use_helper(builtin);
ctx.push(ctx.helper(builtin));
} else if let Some(binding_name) = ctx.resolve_component_binding_name(&el.tag) {
if !ctx.options.inline {
ctx.push("$setup.");
}
ctx.push(&binding_name);
} else {
ctx.push(&to_valid_asset_identifier("component", &el.tag));
}
let (mut patch_flag, dynamic_props) = if is_dynamic {
calculate_element_patch_info_skip_is(
el,
ctx.options.binding_metadata.as_ref(),
ctx.cache_handlers_in_current_scope(),
)
} else {
calculate_element_patch_info(
el,
ctx.options.binding_metadata.as_ref(),
ctx.cache_handlers_in_current_scope(),
)
};
if has_slot_children(el)
&& let Some(flag) = patch_flag
{
let new_flag = flag & !1;
patch_flag = if new_flag > 0 { Some(new_flag) } else { None };
}
if el.tag == "KeepAlive" || el.tag == "keep-alive" || has_dynamic_slots_flag(el) {
patch_flag = Some(patch_flag.unwrap_or(0) | 1024);
}
let has_patch_info = patch_flag.is_some() || dynamic_props.is_some();
let effective_has_props = if is_dynamic {
el.props
.iter()
.any(|p| !is_is_prop(p) && is_renderable_prop(p))
} else {
has_renderable_props(el)
};
if effective_has_props {
ctx.push(", ");
if is_dynamic {
ctx.skip_is_prop = true;
}
generate_props(ctx, &el.props);
ctx.skip_is_prop = false;
} else if !el.children.is_empty() || has_patch_info {
ctx.push(", null");
}
if has_slot_children(el) {
ctx.push(", ");
generate_slots(ctx, el);
} else if el.children.iter().any(|c| !is_whitespace_or_comment(c)) {
ctx.push(", [");
ctx.indent();
let filtered: Vec<_> = el
.children
.iter()
.filter(|c| !is_directive_comment(c))
.collect();
for (i, child) in filtered.iter().enumerate() {
if i > 0 {
ctx.push(",");
}
ctx.newline();
generate_node(ctx, child);
}
ctx.deindent();
ctx.newline();
ctx.push("]");
} else if has_patch_info {
ctx.push(", null");
}
if let Some(flag) = patch_flag {
ctx.push(", ");
ctx.push(&flag.to_compact_string());
ctx.push(" /* ");
let flag_name = patch_flag_name(flag);
ctx.push(&flag_name);
ctx.push(" */");
}
if let Some(props) = dynamic_props {
ctx.push(", [");
for (i, prop) in props.iter().enumerate() {
if i > 0 {
ctx.push(", ");
}
ctx.push("\"");
ctx.push(prop);
ctx.push("\"");
}
ctx.push("]");
}
ctx.push(")");
if has_custom_dirs {
generate_custom_directives_closing(ctx, el);
}
if has_vshow {
generate_vshow_closing(ctx, el);
}
}
ElementType::Slot => {
let helper = ctx.helper(RuntimeHelper::RenderSlot);
ctx.use_helper(RuntimeHelper::RenderSlot);
ctx.push(helper);
ctx.push("(_ctx.$slots, ");
generate_slot_outlet_name(ctx, el);
let has_slot_props = has_slot_outlet_props(el);
let prev_skip_scope_id = ctx.skip_scope_id;
ctx.skip_scope_id = true;
if !el.children.is_empty() {
if !has_slot_props {
ctx.push(", {}");
} else {
ctx.push(", ");
generate_slot_outlet_props(ctx, el);
}
ctx.push(", () => [");
ctx.skip_scope_id = prev_skip_scope_id;
ctx.indent();
let filtered: Vec<_> = el
.children
.iter()
.filter(|c| !is_directive_comment(c))
.collect();
for (i, child) in filtered.iter().enumerate() {
if i > 0 {
ctx.push(",");
}
ctx.newline();
generate_node(ctx, child);
}
ctx.deindent();
ctx.newline();
ctx.push("])");
} else if has_slot_props {
ctx.push(", ");
generate_slot_outlet_props(ctx, el);
ctx.skip_scope_id = prev_skip_scope_id;
ctx.push(")");
} else {
ctx.skip_scope_id = prev_skip_scope_id;
ctx.push(")");
}
}
ElementType::Template => {
let filtered: Vec<_> = el
.children
.iter()
.filter(|c| !is_directive_comment(c))
.collect();
if filtered.len() == 1 {
generate_node(ctx, filtered[0]);
} else {
ctx.push("[");
for (i, child) in filtered.iter().enumerate() {
if i > 0 {
ctx.push(", ");
}
generate_node(ctx, child);
}
ctx.push("]");
}
}
}
if let Some(cache_index) = memo_cache_index {
ctx.push(", _cache, ");
ctx.push(&cache_index.to_compact_string());
ctx.push(")");
}
}