use crate::{
DirectiveNode, ElementNode, ElementType, ExpressionNode, Namespace, PropNode, TemplateChildNode,
};
use vize_carton::is_builtin_directive;
use super::super::{
context::CodegenContext, node::generate_node, props::is_supported_directive,
v_for::generate_for, v_if::generate_if,
};
pub fn has_v_once(el: &ElementNode<'_>) -> bool {
el.props.iter().any(|prop| {
if let PropNode::Directive(dir) = prop {
dir.name.as_str() == "once"
} else {
false
}
})
}
pub(crate) fn is_whitespace_or_comment(child: &TemplateChildNode<'_>) -> bool {
match child {
TemplateChildNode::Text(t) => t.content.trim().is_empty(),
TemplateChildNode::Comment(_) => true,
_ => false,
}
}
pub fn has_vshow_directive(el: &ElementNode<'_>) -> bool {
el.props.iter().any(|prop| {
if let PropNode::Directive(dir) = prop {
dir.name.as_str() == "show" && dir.exp.is_some()
} else {
false
}
})
}
pub fn has_custom_directives(el: &ElementNode<'_>) -> bool {
el.props.iter().any(|prop| {
if let PropNode::Directive(dir) = prop {
!is_builtin_directive(&dir.name)
} else {
false
}
})
}
pub fn get_custom_directives<'a, 'b>(el: &'b ElementNode<'a>) -> Vec<&'b DirectiveNode<'a>> {
el.props
.iter()
.filter_map(|prop| {
if let PropNode::Directive(dir) = prop
&& !is_builtin_directive(&dir.name)
{
return Some(dir.as_ref());
}
None
})
.collect()
}
pub fn has_vmodel_directive(el: &ElementNode<'_>) -> bool {
if el.tag_type != ElementType::Element {
return false;
}
if !matches!(el.tag.as_str(), "input" | "textarea" | "select") {
return false;
}
el.props.iter().any(|prop| {
if let PropNode::Directive(dir) = prop {
dir.name.as_str() == "model"
} else {
false
}
})
}
pub(crate) fn get_vmodel_directive<'a, 'b>(
el: &'b ElementNode<'a>,
) -> Option<&'b DirectiveNode<'a>> {
el.props.iter().find_map(|prop| {
if let PropNode::Directive(dir) = prop
&& dir.name.as_str() == "model"
{
return Some(dir.as_ref());
}
None
})
}
pub(crate) fn is_is_prop(p: &PropNode<'_>) -> bool {
match p {
PropNode::Attribute(attr) => attr.name == "is",
PropNode::Directive(dir) => {
if dir.name == "bind"
&& let Some(ExpressionNode::Simple(arg)) = &dir.arg
{
return arg.content == "is";
}
false
}
}
}
pub(crate) fn is_dynamic_component_tag(tag: &str) -> bool {
tag == "component"
}
pub(crate) fn is_dynamic_component(el: &ElementNode<'_>) -> bool {
is_dynamic_component_tag(&el.tag) || (el.tag == "Component" && el.props.iter().any(is_is_prop))
}
pub(crate) fn is_renderable_prop(prop: &PropNode<'_>) -> bool {
match prop {
PropNode::Attribute(_) => true,
PropNode::Directive(dir) => is_supported_directive(dir),
}
}
pub fn has_renderable_props(el: &ElementNode<'_>) -> bool {
el.props.iter().any(|prop| is_renderable_prop(prop))
}
pub(crate) fn has_dynamic_key_binding(el: &ElementNode<'_>) -> bool {
el.props.iter().any(|prop| {
matches!(
prop,
PropNode::Directive(dir)
if dir.name == "bind"
&& dir.exp.is_some()
&& matches!(
dir.arg.as_ref(),
Some(ExpressionNode::Simple(arg))
if arg.is_static && arg.content.as_str() == "key"
)
)
})
}
pub(crate) fn crosses_namespace_boundary(ctx: &CodegenContext, el: &ElementNode<'_>) -> bool {
el.tag_type == ElementType::Element
&& (el.ns != ctx.parent_ns || children_cross_namespace_boundary(el.ns, &el.children))
}
pub(crate) fn child_namespace(el: &ElementNode<'_>) -> Namespace {
match el.ns {
Namespace::Svg if matches!(el.tag.as_str(), "foreignObject" | "desc" | "title") => {
Namespace::Html
}
Namespace::MathMl
if matches!(
el.tag.as_str(),
"annotation-xml" | "mi" | "mo" | "mn" | "ms" | "mtext"
) =>
{
Namespace::Html
}
_ => el.ns,
}
}
fn children_cross_namespace_boundary(ns: Namespace, children: &[TemplateChildNode<'_>]) -> bool {
children
.iter()
.any(|child| child_crosses_namespace_boundary(ns, child))
}
fn child_crosses_namespace_boundary(ns: Namespace, child: &TemplateChildNode<'_>) -> bool {
match child {
TemplateChildNode::Element(el) => match el.tag_type {
ElementType::Element => el.ns != ns,
ElementType::Template => children_cross_namespace_boundary(ns, &el.children),
_ => false,
},
TemplateChildNode::If(if_node) => if_node
.branches
.iter()
.any(|branch| children_cross_namespace_boundary(ns, &branch.children)),
TemplateChildNode::For(for_node) => {
children_cross_namespace_boundary(ns, &for_node.children)
}
_ => false,
}
}
pub fn generate_root_node(ctx: &mut CodegenContext, node: &TemplateChildNode<'_>) {
match node {
TemplateChildNode::Element(el) => super::block::generate_element_block(ctx, el),
TemplateChildNode::If(if_node) => generate_if(ctx, if_node),
TemplateChildNode::For(for_node) => generate_for(ctx, for_node),
_ => generate_node(ctx, node),
}
}