Skip to main content

vize_relief/ast/
elements.rs

1//! Element-related AST node types.
2//!
3//! Contains element, attribute, directive, text, comment,
4//! and interpolation node definitions.
5
6use vize_carton::{Box, Bump, String, Vec, directive::DirectiveKind};
7
8use super::{
9    codegen::{CacheExpression, VNodeCall},
10    control_flow::ForParseResult,
11    core::{ElementType, Namespace, NodeType, SourceLocation},
12    expressions::{ExpressionNode, SimpleExpressionNode},
13};
14
15/// Element node
16#[derive(Debug)]
17pub struct ElementNode<'a> {
18    pub ns: Namespace,
19    pub tag: String,
20    pub tag_type: ElementType,
21    pub props: Vec<'a, PropNode<'a>>,
22    pub children: Vec<'a, super::TemplateChildNode<'a>>,
23    pub is_self_closing: bool,
24    pub loc: SourceLocation,
25    pub inner_loc: Option<SourceLocation>,
26    pub codegen_node: Option<ElementCodegenNode<'a>>,
27    /// If props are hoisted, this is the index into the hoists array (1-based for _hoisted_N)
28    pub hoisted_props_index: Option<usize>,
29}
30
31impl<'a> ElementNode<'a> {
32    pub fn new(allocator: &'a Bump, tag: impl Into<String>, loc: SourceLocation) -> Self {
33        Self {
34            ns: Namespace::Html,
35            tag: tag.into(),
36            tag_type: ElementType::Element,
37            props: Vec::new_in(allocator),
38            children: Vec::new_in(allocator),
39            is_self_closing: false,
40            loc,
41            inner_loc: None,
42            codegen_node: None,
43            hoisted_props_index: None,
44        }
45    }
46
47    pub fn node_type(&self) -> NodeType {
48        NodeType::Element
49    }
50}
51
52/// Element codegen node (VNodeCall, SimpleExpression, CacheExpression, etc.)
53#[derive(Debug)]
54pub enum ElementCodegenNode<'a> {
55    VNodeCall(Box<'a, VNodeCall<'a>>),
56    SimpleExpression(Box<'a, SimpleExpressionNode<'a>>),
57    CacheExpression(Box<'a, CacheExpression<'a>>),
58}
59
60/// Prop node (attribute or directive)
61#[derive(Debug)]
62pub enum PropNode<'a> {
63    Attribute(Box<'a, AttributeNode>),
64    Directive(Box<'a, DirectiveNode<'a>>),
65}
66
67impl<'a> PropNode<'a> {
68    pub fn loc(&self) -> &SourceLocation {
69        match self {
70            Self::Attribute(n) => &n.loc,
71            Self::Directive(n) => &n.loc,
72        }
73    }
74}
75
76/// Attribute node
77#[derive(Debug)]
78pub struct AttributeNode {
79    pub name: String,
80    pub name_loc: SourceLocation,
81    pub value: Option<TextNode>,
82    pub loc: SourceLocation,
83}
84
85impl AttributeNode {
86    pub fn new(name: impl Into<String>, loc: SourceLocation) -> Self {
87        Self {
88            name: name.into(),
89            name_loc: loc.clone(),
90            value: None,
91            loc,
92        }
93    }
94
95    pub fn node_type(&self) -> NodeType {
96        NodeType::Attribute
97    }
98}
99
100/// Directive node (v-if, v-for, v-bind, etc.)
101#[derive(Debug)]
102pub struct DirectiveNode<'a> {
103    /// Normalized directive name without prefix (e.g., "if", "for", "bind")
104    pub name: String,
105    /// Raw attribute name including shorthand (e.g., "@click", ":class")
106    pub raw_name: Option<String>,
107    /// Directive expression
108    pub exp: Option<ExpressionNode<'a>>,
109    /// Directive argument (e.g., "click" in @click)
110    pub arg: Option<ExpressionNode<'a>>,
111    /// Directive modifiers (e.g., ["stop", "prevent"] in @click.stop.prevent)
112    pub modifiers: Vec<'a, SimpleExpressionNode<'a>>,
113    /// Parsed result for v-for
114    pub for_parse_result: Option<ForParseResult<'a>>,
115    /// Whether this is a Vue 3.4+ same-name shorthand (`:foo` without value)
116    pub shorthand: bool,
117    pub loc: SourceLocation,
118}
119
120impl<'a> DirectiveNode<'a> {
121    pub fn new(allocator: &'a Bump, name: impl Into<String>, loc: SourceLocation) -> Self {
122        Self {
123            name: name.into(),
124            raw_name: None,
125            exp: None,
126            arg: None,
127            modifiers: Vec::new_in(allocator),
128            for_parse_result: None,
129            shorthand: false,
130            loc,
131        }
132    }
133
134    pub fn node_type(&self) -> NodeType {
135        NodeType::Directive
136    }
137}
138
139/// Text node
140#[derive(Debug)]
141pub struct TextNode {
142    pub content: String,
143    pub loc: SourceLocation,
144}
145
146impl TextNode {
147    pub fn new(content: impl Into<String>, loc: SourceLocation) -> Self {
148        Self {
149            content: content.into(),
150            loc,
151        }
152    }
153
154    pub fn node_type(&self) -> NodeType {
155        NodeType::Text
156    }
157}
158
159/// Comment node
160#[derive(Debug)]
161pub struct CommentNode {
162    pub content: String,
163    pub loc: SourceLocation,
164    /// Parsed `@vize:` directive, if this comment contains one.
165    pub directive: Option<DirectiveKind>,
166}
167
168impl CommentNode {
169    pub fn new(content: impl Into<String>, loc: SourceLocation) -> Self {
170        Self {
171            content: content.into(),
172            loc,
173            directive: None,
174        }
175    }
176
177    pub fn node_type(&self) -> NodeType {
178        NodeType::Comment
179    }
180}
181
182/// Interpolation node ({{ expr }})
183#[derive(Debug)]
184pub struct InterpolationNode<'a> {
185    pub content: ExpressionNode<'a>,
186    pub loc: SourceLocation,
187}
188
189impl<'a> InterpolationNode<'a> {
190    pub fn node_type(&self) -> NodeType {
191        NodeType::Interpolation
192    }
193}