use serde::{Deserialize, Serialize};
use vize_carton::directive::DirectiveKind;
use vize_carton::PatchFlags;
use vize_carton::{Box, Bump, String, Vec};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u8)]
pub enum NodeType {
Root = 0,
Element = 1,
Text = 2,
Comment = 3,
SimpleExpression = 4,
Interpolation = 5,
Attribute = 6,
Directive = 7,
CompoundExpression = 8,
If = 9,
IfBranch = 10,
For = 11,
TextCall = 12,
VNodeCall = 13,
JsCallExpression = 14,
JsObjectExpression = 15,
JsProperty = 16,
JsArrayExpression = 17,
JsFunctionExpression = 18,
JsConditionalExpression = 19,
JsCacheExpression = 20,
JsBlockStatement = 21,
JsTemplateLiteral = 22,
JsIfStatement = 23,
JsAssignmentExpression = 24,
JsSequenceExpression = 25,
JsReturnStatement = 26,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[repr(u8)]
pub enum ElementType {
#[default]
Element = 0,
Component = 1,
Slot = 2,
Template = 3,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[repr(u8)]
pub enum Namespace {
#[default]
Html = 0,
Svg = 1,
MathMl = 2,
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Default,
)]
#[repr(u8)]
pub enum ConstantType {
#[default]
NotConstant = 0,
CanSkipPatch = 1,
CanCache = 2,
CanStringify = 3,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
pub struct Position {
pub offset: u32,
pub line: u32,
pub column: u32,
}
impl Position {
pub const fn new(offset: u32, line: u32, column: u32) -> Self {
Self {
offset,
line,
column,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SourceLocation {
pub start: Position,
pub end: Position,
pub source: String,
}
impl Default for SourceLocation {
fn default() -> Self {
Self::STUB
}
}
static STUB_LOCATION: SourceLocation = SourceLocation {
start: Position {
offset: 0,
line: 1,
column: 1,
},
end: Position {
offset: 0,
line: 1,
column: 1,
},
source: String::const_new(""),
};
impl SourceLocation {
pub const STUB: Self = Self {
start: Position {
offset: 0,
line: 1,
column: 1,
},
end: Position {
offset: 0,
line: 1,
column: 1,
},
source: String::const_new(""),
};
pub fn new(start: Position, end: Position, source: impl Into<String>) -> Self {
Self {
start,
end,
source: source.into(),
}
}
}
#[derive(Debug)]
pub struct RootNode<'a> {
pub children: Vec<'a, TemplateChildNode<'a>>,
pub helpers: Vec<'a, RuntimeHelper>,
pub components: Vec<'a, String>,
pub directives: Vec<'a, String>,
pub hoists: Vec<'a, Option<JsChildNode<'a>>>,
pub imports: Vec<'a, ImportItem<'a>>,
pub cached: Vec<'a, Option<Box<'a, CacheExpression<'a>>>>,
pub temps: u32,
pub source: String,
pub loc: SourceLocation,
pub codegen_node: Option<CodegenNode<'a>>,
pub transformed: bool,
}
impl<'a> RootNode<'a> {
pub fn new(allocator: &'a Bump, source: impl Into<String>) -> Self {
Self {
children: Vec::new_in(allocator),
helpers: Vec::new_in(allocator),
components: Vec::new_in(allocator),
directives: Vec::new_in(allocator),
hoists: Vec::new_in(allocator),
imports: Vec::new_in(allocator),
cached: Vec::new_in(allocator),
temps: 0,
source: source.into(),
loc: SourceLocation::STUB,
codegen_node: None,
transformed: false,
}
}
pub fn node_type(&self) -> NodeType {
NodeType::Root
}
}
#[derive(Debug)]
pub struct ImportItem<'a> {
pub exp: Box<'a, SimpleExpressionNode<'a>>,
pub path: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[repr(u8)]
pub enum RuntimeHelper {
Fragment,
Teleport,
Suspense,
KeepAlive,
BaseTransition,
Transition,
TransitionGroup,
OpenBlock,
CreateBlock,
CreateElementBlock,
CreateVNode,
CreateElementVNode,
CreateComment,
CreateText,
CreateStatic,
ResolveComponent,
ResolveDynamicComponent,
ResolveDirective,
ResolveFilter,
WithDirectives,
RenderList,
RenderSlot,
CreateSlots,
ToDisplayString,
MergeProps,
NormalizeClass,
NormalizeStyle,
NormalizeProps,
GuardReactiveProps,
ToHandlers,
Camelize,
Capitalize,
ToHandlerKey,
SetBlockTracking,
PushScopeId,
PopScopeId,
WithCtx,
Unref,
IsRef,
WithMemo,
IsMemoSame,
VShow,
VModelText,
VModelCheckbox,
VModelRadio,
VModelSelect,
VModelDynamic,
WithModifiers,
WithKeys,
SsrInterpolate,
SsrRenderVNode,
SsrRenderComponent,
SsrRenderSlot,
SsrRenderSlotInner,
SsrRenderAttrs,
SsrRenderAttr,
SsrRenderDynamicAttr,
SsrIncludeBooleanAttr,
SsrRenderClass,
SsrRenderStyle,
SsrRenderDynamicModel,
SsrGetDynamicModelProps,
SsrRenderList,
SsrLooseEqual,
SsrLooseContain,
SsrGetDirectiveProps,
SsrRenderTeleport,
SsrRenderSuspense,
}
impl RuntimeHelper {
pub fn name(&self) -> &'static str {
match self {
Self::Fragment => "Fragment",
Self::Teleport => "Teleport",
Self::Suspense => "Suspense",
Self::KeepAlive => "KeepAlive",
Self::BaseTransition => "BaseTransition",
Self::Transition => "Transition",
Self::TransitionGroup => "TransitionGroup",
Self::OpenBlock => "openBlock",
Self::CreateBlock => "createBlock",
Self::CreateElementBlock => "createElementBlock",
Self::CreateVNode => "createVNode",
Self::CreateElementVNode => "createElementVNode",
Self::CreateComment => "createCommentVNode",
Self::CreateText => "createTextVNode",
Self::CreateStatic => "createStaticVNode",
Self::ResolveComponent => "resolveComponent",
Self::ResolveDynamicComponent => "resolveDynamicComponent",
Self::ResolveDirective => "resolveDirective",
Self::ResolveFilter => "resolveFilter",
Self::WithDirectives => "withDirectives",
Self::RenderList => "renderList",
Self::RenderSlot => "renderSlot",
Self::CreateSlots => "createSlots",
Self::ToDisplayString => "toDisplayString",
Self::MergeProps => "mergeProps",
Self::NormalizeClass => "normalizeClass",
Self::NormalizeStyle => "normalizeStyle",
Self::NormalizeProps => "normalizeProps",
Self::GuardReactiveProps => "guardReactiveProps",
Self::ToHandlers => "toHandlers",
Self::Camelize => "camelize",
Self::Capitalize => "capitalize",
Self::ToHandlerKey => "toHandlerKey",
Self::SetBlockTracking => "setBlockTracking",
Self::PushScopeId => "pushScopeId",
Self::PopScopeId => "popScopeId",
Self::WithCtx => "withCtx",
Self::Unref => "unref",
Self::IsRef => "isRef",
Self::WithMemo => "withMemo",
Self::IsMemoSame => "isMemoSame",
Self::VShow => "vShow",
Self::VModelText => "vModelText",
Self::VModelCheckbox => "vModelCheckbox",
Self::VModelRadio => "vModelRadio",
Self::VModelSelect => "vModelSelect",
Self::VModelDynamic => "vModelDynamic",
Self::WithModifiers => "withModifiers",
Self::WithKeys => "withKeys",
Self::SsrInterpolate => "ssrInterpolate",
Self::SsrRenderVNode => "ssrRenderVNode",
Self::SsrRenderComponent => "ssrRenderComponent",
Self::SsrRenderSlot => "ssrRenderSlot",
Self::SsrRenderSlotInner => "ssrRenderSlotInner",
Self::SsrRenderAttrs => "ssrRenderAttrs",
Self::SsrRenderAttr => "ssrRenderAttr",
Self::SsrRenderDynamicAttr => "ssrRenderDynamicAttr",
Self::SsrIncludeBooleanAttr => "ssrIncludeBooleanAttr",
Self::SsrRenderClass => "ssrRenderClass",
Self::SsrRenderStyle => "ssrRenderStyle",
Self::SsrRenderDynamicModel => "ssrRenderDynamicModel",
Self::SsrGetDynamicModelProps => "ssrGetDynamicModelProps",
Self::SsrRenderList => "ssrRenderList",
Self::SsrLooseEqual => "ssrLooseEqual",
Self::SsrLooseContain => "ssrLooseContain",
Self::SsrGetDirectiveProps => "ssrGetDirectiveProps",
Self::SsrRenderTeleport => "ssrRenderTeleport",
Self::SsrRenderSuspense => "ssrRenderSuspense",
}
}
pub fn is_ssr(&self) -> bool {
matches!(
self,
Self::SsrInterpolate
| Self::SsrRenderVNode
| Self::SsrRenderComponent
| Self::SsrRenderSlot
| Self::SsrRenderSlotInner
| Self::SsrRenderAttrs
| Self::SsrRenderAttr
| Self::SsrRenderDynamicAttr
| Self::SsrIncludeBooleanAttr
| Self::SsrRenderClass
| Self::SsrRenderStyle
| Self::SsrRenderDynamicModel
| Self::SsrGetDynamicModelProps
| Self::SsrRenderList
| Self::SsrLooseEqual
| Self::SsrLooseContain
| Self::SsrGetDirectiveProps
| Self::SsrRenderTeleport
| Self::SsrRenderSuspense
)
}
}
#[derive(Debug)]
pub enum TemplateChildNode<'a> {
Element(Box<'a, ElementNode<'a>>),
Text(Box<'a, TextNode>),
Comment(Box<'a, CommentNode>),
Interpolation(Box<'a, InterpolationNode<'a>>),
If(Box<'a, IfNode<'a>>),
IfBranch(Box<'a, IfBranchNode<'a>>),
For(Box<'a, ForNode<'a>>),
TextCall(Box<'a, TextCallNode<'a>>),
CompoundExpression(Box<'a, CompoundExpressionNode<'a>>),
Hoisted(usize),
}
impl<'a> TemplateChildNode<'a> {
pub fn node_type(&self) -> NodeType {
match self {
Self::Element(_) => NodeType::Element,
Self::Text(_) => NodeType::Text,
Self::Comment(_) => NodeType::Comment,
Self::Interpolation(_) => NodeType::Interpolation,
Self::If(_) => NodeType::If,
Self::IfBranch(_) => NodeType::IfBranch,
Self::For(_) => NodeType::For,
Self::TextCall(_) => NodeType::TextCall,
Self::CompoundExpression(_) => NodeType::CompoundExpression,
Self::Hoisted(_) => NodeType::SimpleExpression, }
}
pub fn loc(&self) -> &SourceLocation {
match self {
Self::Element(n) => &n.loc,
Self::Text(n) => &n.loc,
Self::Comment(n) => &n.loc,
Self::Interpolation(n) => &n.loc,
Self::If(n) => &n.loc,
Self::IfBranch(n) => &n.loc,
Self::For(n) => &n.loc,
Self::TextCall(n) => &n.loc,
Self::CompoundExpression(n) => &n.loc,
Self::Hoisted(_) => &STUB_LOCATION, }
}
}
#[derive(Debug)]
pub struct ElementNode<'a> {
pub ns: Namespace,
pub tag: String,
pub tag_type: ElementType,
pub props: Vec<'a, PropNode<'a>>,
pub children: Vec<'a, TemplateChildNode<'a>>,
pub is_self_closing: bool,
pub loc: SourceLocation,
pub inner_loc: Option<SourceLocation>,
pub codegen_node: Option<ElementCodegenNode<'a>>,
pub hoisted_props_index: Option<usize>,
}
impl<'a> ElementNode<'a> {
pub fn new(allocator: &'a Bump, tag: impl Into<String>, loc: SourceLocation) -> Self {
Self {
ns: Namespace::Html,
tag: tag.into(),
tag_type: ElementType::Element,
props: Vec::new_in(allocator),
children: Vec::new_in(allocator),
is_self_closing: false,
loc,
inner_loc: None,
codegen_node: None,
hoisted_props_index: None,
}
}
pub fn node_type(&self) -> NodeType {
NodeType::Element
}
}
#[derive(Debug)]
pub enum ElementCodegenNode<'a> {
VNodeCall(Box<'a, VNodeCall<'a>>),
SimpleExpression(Box<'a, SimpleExpressionNode<'a>>),
CacheExpression(Box<'a, CacheExpression<'a>>),
}
#[derive(Debug)]
pub enum PropNode<'a> {
Attribute(Box<'a, AttributeNode>),
Directive(Box<'a, DirectiveNode<'a>>),
}
impl<'a> PropNode<'a> {
pub fn loc(&self) -> &SourceLocation {
match self {
Self::Attribute(n) => &n.loc,
Self::Directive(n) => &n.loc,
}
}
}
#[derive(Debug)]
pub struct AttributeNode {
pub name: String,
pub name_loc: SourceLocation,
pub value: Option<TextNode>,
pub loc: SourceLocation,
}
impl AttributeNode {
pub fn new(name: impl Into<String>, loc: SourceLocation) -> Self {
Self {
name: name.into(),
name_loc: loc.clone(),
value: None,
loc,
}
}
pub fn node_type(&self) -> NodeType {
NodeType::Attribute
}
}
#[derive(Debug)]
pub struct DirectiveNode<'a> {
pub name: String,
pub raw_name: Option<String>,
pub exp: Option<ExpressionNode<'a>>,
pub arg: Option<ExpressionNode<'a>>,
pub modifiers: Vec<'a, SimpleExpressionNode<'a>>,
pub for_parse_result: Option<ForParseResult<'a>>,
pub loc: SourceLocation,
}
impl<'a> DirectiveNode<'a> {
pub fn new(allocator: &'a Bump, name: impl Into<String>, loc: SourceLocation) -> Self {
Self {
name: name.into(),
raw_name: None,
exp: None,
arg: None,
modifiers: Vec::new_in(allocator),
for_parse_result: None,
loc,
}
}
pub fn node_type(&self) -> NodeType {
NodeType::Directive
}
}
#[derive(Debug)]
pub struct TextNode {
pub content: String,
pub loc: SourceLocation,
}
impl TextNode {
pub fn new(content: impl Into<String>, loc: SourceLocation) -> Self {
Self {
content: content.into(),
loc,
}
}
pub fn node_type(&self) -> NodeType {
NodeType::Text
}
}
#[derive(Debug)]
pub struct CommentNode {
pub content: String,
pub loc: SourceLocation,
pub directive: Option<DirectiveKind>,
}
impl CommentNode {
pub fn new(content: impl Into<String>, loc: SourceLocation) -> Self {
Self {
content: content.into(),
loc,
directive: None,
}
}
pub fn node_type(&self) -> NodeType {
NodeType::Comment
}
}
#[derive(Debug)]
pub struct InterpolationNode<'a> {
pub content: ExpressionNode<'a>,
pub loc: SourceLocation,
}
impl<'a> InterpolationNode<'a> {
pub fn node_type(&self) -> NodeType {
NodeType::Interpolation
}
}
#[derive(Debug)]
pub enum ExpressionNode<'a> {
Simple(Box<'a, SimpleExpressionNode<'a>>),
Compound(Box<'a, CompoundExpressionNode<'a>>),
}
impl<'a> ExpressionNode<'a> {
pub fn loc(&self) -> &SourceLocation {
match self {
Self::Simple(n) => &n.loc,
Self::Compound(n) => &n.loc,
}
}
}
#[derive(Debug)]
pub struct SimpleExpressionNode<'a> {
pub content: String,
pub is_static: bool,
pub const_type: ConstantType,
pub loc: SourceLocation,
pub js_ast: Option<JsExpression<'a>>,
pub hoisted: Option<Box<'a, JsChildNode<'a>>>,
pub identifiers: Option<Vec<'a, String>>,
pub is_handler_key: bool,
pub is_ref_transformed: bool,
}
impl<'a> SimpleExpressionNode<'a> {
pub fn new(content: impl Into<String>, is_static: bool, loc: SourceLocation) -> Self {
Self {
content: content.into(),
is_static,
const_type: if is_static {
ConstantType::CanStringify
} else {
ConstantType::NotConstant
},
loc,
js_ast: None,
hoisted: None,
identifiers: None,
is_handler_key: false,
is_ref_transformed: false,
}
}
pub fn node_type(&self) -> NodeType {
NodeType::SimpleExpression
}
}
#[derive(Debug)]
pub struct JsExpression<'a> {
pub raw: String,
_marker: std::marker::PhantomData<&'a ()>,
}
#[derive(Debug)]
pub struct CompoundExpressionNode<'a> {
pub children: Vec<'a, CompoundExpressionChild<'a>>,
pub loc: SourceLocation,
pub identifiers: Option<Vec<'a, String>>,
pub is_handler_key: bool,
}
impl<'a> CompoundExpressionNode<'a> {
pub fn new(allocator: &'a Bump, loc: SourceLocation) -> Self {
Self {
children: Vec::new_in(allocator),
loc,
identifiers: None,
is_handler_key: false,
}
}
pub fn node_type(&self) -> NodeType {
NodeType::CompoundExpression
}
}
#[derive(Debug)]
pub enum CompoundExpressionChild<'a> {
Simple(Box<'a, SimpleExpressionNode<'a>>),
Compound(Box<'a, CompoundExpressionNode<'a>>),
Interpolation(Box<'a, InterpolationNode<'a>>),
Text(Box<'a, TextNode>),
String(String),
Symbol(RuntimeHelper),
}
#[derive(Debug)]
pub struct IfNode<'a> {
pub branches: Vec<'a, IfBranchNode<'a>>,
pub loc: SourceLocation,
pub codegen_node: Option<IfCodegenNode<'a>>,
}
impl<'a> IfNode<'a> {
pub fn new(allocator: &'a Bump, loc: SourceLocation) -> Self {
Self {
branches: Vec::new_in(allocator),
loc,
codegen_node: None,
}
}
pub fn node_type(&self) -> NodeType {
NodeType::If
}
}
#[derive(Debug)]
pub enum IfCodegenNode<'a> {
Conditional(Box<'a, ConditionalExpression<'a>>),
Cache(Box<'a, CacheExpression<'a>>),
}
#[derive(Debug)]
pub struct IfBranchNode<'a> {
pub condition: Option<ExpressionNode<'a>>,
pub children: Vec<'a, TemplateChildNode<'a>>,
pub user_key: Option<PropNode<'a>>,
pub is_template_if: bool,
pub loc: SourceLocation,
}
impl<'a> IfBranchNode<'a> {
pub fn new(
allocator: &'a Bump,
condition: Option<ExpressionNode<'a>>,
loc: SourceLocation,
) -> Self {
Self {
condition,
children: Vec::new_in(allocator),
user_key: None,
is_template_if: false,
loc,
}
}
pub fn node_type(&self) -> NodeType {
NodeType::IfBranch
}
}
#[derive(Debug)]
pub struct ForNode<'a> {
pub source: ExpressionNode<'a>,
pub value_alias: Option<ExpressionNode<'a>>,
pub key_alias: Option<ExpressionNode<'a>>,
pub object_index_alias: Option<ExpressionNode<'a>>,
pub parse_result: ForParseResult<'a>,
pub children: Vec<'a, TemplateChildNode<'a>>,
pub loc: SourceLocation,
pub codegen_node: Option<Box<'a, VNodeCall<'a>>>,
}
impl<'a> ForNode<'a> {
pub fn node_type(&self) -> NodeType {
NodeType::For
}
}
#[derive(Debug)]
pub struct ForParseResult<'a> {
pub source: ExpressionNode<'a>,
pub value: Option<ExpressionNode<'a>>,
pub key: Option<ExpressionNode<'a>>,
pub index: Option<ExpressionNode<'a>>,
pub finalized: bool,
}
#[derive(Debug)]
pub struct TextCallNode<'a> {
pub content: TextCallContent<'a>,
pub loc: SourceLocation,
pub codegen_node: Option<TextCallCodegenNode<'a>>,
}
impl<'a> TextCallNode<'a> {
pub fn node_type(&self) -> NodeType {
NodeType::TextCall
}
}
#[derive(Debug)]
pub enum TextCallContent<'a> {
Text(Box<'a, TextNode>),
Interpolation(Box<'a, InterpolationNode<'a>>),
Compound(Box<'a, CompoundExpressionNode<'a>>),
}
#[derive(Debug)]
pub enum TextCallCodegenNode<'a> {
Call(Box<'a, CallExpression<'a>>),
Simple(Box<'a, SimpleExpressionNode<'a>>),
}
#[derive(Debug)]
pub struct VNodeCall<'a> {
pub tag: VNodeTag<'a>,
pub props: Option<PropsExpression<'a>>,
pub children: Option<VNodeChildren<'a>>,
pub patch_flag: Option<PatchFlags>,
pub dynamic_props: Option<DynamicProps<'a>>,
pub directives: Option<DirectiveArguments<'a>>,
pub is_block: bool,
pub disable_tracking: bool,
pub is_component: bool,
pub loc: SourceLocation,
}
impl<'a> VNodeCall<'a> {
pub fn node_type(&self) -> NodeType {
NodeType::VNodeCall
}
}
#[derive(Debug)]
pub enum VNodeTag<'a> {
String(String),
Symbol(RuntimeHelper),
Call(Box<'a, CallExpression<'a>>),
}
#[derive(Debug)]
pub enum VNodeChildren<'a> {
Multiple(Vec<'a, TemplateChildNode<'a>>),
Single(TemplateTextChildNode<'a>),
Slots(Box<'a, SlotsExpression<'a>>),
ForRenderList(Box<'a, CallExpression<'a>>),
Simple(Box<'a, SimpleExpressionNode<'a>>),
Cache(Box<'a, CacheExpression<'a>>),
}
#[derive(Debug)]
pub enum TemplateTextChildNode<'a> {
Text(Box<'a, TextNode>),
Interpolation(Box<'a, InterpolationNode<'a>>),
Compound(Box<'a, CompoundExpressionNode<'a>>),
}
#[derive(Debug)]
pub enum PropsExpression<'a> {
Object(Box<'a, ObjectExpression<'a>>),
Call(Box<'a, CallExpression<'a>>),
Simple(Box<'a, SimpleExpressionNode<'a>>),
}
#[derive(Debug)]
pub enum DynamicProps<'a> {
String(String),
Simple(Box<'a, SimpleExpressionNode<'a>>),
}
#[derive(Debug)]
pub struct DirectiveArguments<'a> {
pub elements: Vec<'a, DirectiveArgumentNode<'a>>,
pub loc: SourceLocation,
}
#[derive(Debug)]
pub struct DirectiveArgumentNode<'a> {
pub directive: String,
pub exp: Option<ExpressionNode<'a>>,
pub arg: Option<ExpressionNode<'a>>,
pub modifiers: Option<Box<'a, ObjectExpression<'a>>>,
}
#[derive(Debug)]
pub enum SlotsExpression<'a> {
Object(Box<'a, ObjectExpression<'a>>),
Dynamic(Box<'a, CallExpression<'a>>),
}
#[derive(Debug)]
pub enum JsChildNode<'a> {
VNodeCall(Box<'a, VNodeCall<'a>>),
Call(Box<'a, CallExpression<'a>>),
Object(Box<'a, ObjectExpression<'a>>),
Array(Box<'a, ArrayExpression<'a>>),
Function(Box<'a, FunctionExpression<'a>>),
Conditional(Box<'a, ConditionalExpression<'a>>),
Cache(Box<'a, CacheExpression<'a>>),
Assignment(Box<'a, AssignmentExpression<'a>>),
Sequence(Box<'a, SequenceExpression<'a>>),
SimpleExpression(Box<'a, SimpleExpressionNode<'a>>),
CompoundExpression(Box<'a, CompoundExpressionNode<'a>>),
}
#[derive(Debug)]
pub enum CodegenNode<'a> {
TemplateChild(TemplateChildNode<'a>),
JsChild(JsChildNode<'a>),
BlockStatement(Box<'a, BlockStatement<'a>>),
}
#[derive(Debug)]
pub struct CallExpression<'a> {
pub callee: Callee,
pub arguments: Vec<'a, CallArgument<'a>>,
pub loc: SourceLocation,
}
impl<'a> CallExpression<'a> {
pub fn new(allocator: &'a Bump, callee: Callee, loc: SourceLocation) -> Self {
Self {
callee,
arguments: Vec::new_in(allocator),
loc,
}
}
pub fn node_type(&self) -> NodeType {
NodeType::JsCallExpression
}
}
#[derive(Debug)]
pub enum Callee {
String(String),
Symbol(RuntimeHelper),
}
#[derive(Debug)]
pub enum CallArgument<'a> {
String(String),
Symbol(RuntimeHelper),
JsChild(JsChildNode<'a>),
TemplateChild(TemplateChildNode<'a>),
TemplateChildren(Vec<'a, TemplateChildNode<'a>>),
}
#[derive(Debug)]
pub struct ObjectExpression<'a> {
pub properties: Vec<'a, Property<'a>>,
pub loc: SourceLocation,
}
impl<'a> ObjectExpression<'a> {
pub fn new(allocator: &'a Bump, loc: SourceLocation) -> Self {
Self {
properties: Vec::new_in(allocator),
loc,
}
}
pub fn node_type(&self) -> NodeType {
NodeType::JsObjectExpression
}
}
#[derive(Debug)]
pub struct Property<'a> {
pub key: ExpressionNode<'a>,
pub value: JsChildNode<'a>,
pub loc: SourceLocation,
}
impl<'a> Property<'a> {
pub fn node_type(&self) -> NodeType {
NodeType::JsProperty
}
}
#[derive(Debug)]
pub struct ArrayExpression<'a> {
pub elements: Vec<'a, ArrayElement<'a>>,
pub loc: SourceLocation,
}
impl<'a> ArrayExpression<'a> {
pub fn new(allocator: &'a Bump, loc: SourceLocation) -> Self {
Self {
elements: Vec::new_in(allocator),
loc,
}
}
pub fn node_type(&self) -> NodeType {
NodeType::JsArrayExpression
}
}
#[derive(Debug)]
pub enum ArrayElement<'a> {
String(String),
Node(JsChildNode<'a>),
}
#[derive(Debug)]
pub struct FunctionExpression<'a> {
pub params: Option<FunctionParams<'a>>,
pub returns: Option<FunctionReturns<'a>>,
pub body: Option<FunctionBody<'a>>,
pub newline: bool,
pub is_slot: bool,
pub is_non_scoped_slot: bool,
pub loc: SourceLocation,
}
impl<'a> FunctionExpression<'a> {
pub fn node_type(&self) -> NodeType {
NodeType::JsFunctionExpression
}
}
#[derive(Debug)]
pub enum FunctionParams<'a> {
Single(ExpressionNode<'a>),
String(String),
Multiple(Vec<'a, FunctionParam<'a>>),
}
#[derive(Debug)]
pub enum FunctionParam<'a> {
Expression(ExpressionNode<'a>),
String(String),
}
#[derive(Debug)]
pub enum FunctionReturns<'a> {
Single(TemplateChildNode<'a>),
Multiple(Vec<'a, TemplateChildNode<'a>>),
JsChild(JsChildNode<'a>),
}
#[derive(Debug)]
pub enum FunctionBody<'a> {
Block(Box<'a, BlockStatement<'a>>),
If(Box<'a, IfStatement<'a>>),
}
#[derive(Debug)]
pub struct ConditionalExpression<'a> {
pub test: JsChildNode<'a>,
pub consequent: JsChildNode<'a>,
pub alternate: JsChildNode<'a>,
pub newline: bool,
pub loc: SourceLocation,
}
impl<'a> ConditionalExpression<'a> {
pub fn node_type(&self) -> NodeType {
NodeType::JsConditionalExpression
}
}
#[derive(Debug)]
pub struct CacheExpression<'a> {
pub index: u32,
pub value: JsChildNode<'a>,
pub need_pause_tracking: bool,
pub in_v_once: bool,
pub need_array_spread: bool,
pub loc: SourceLocation,
}
impl<'a> CacheExpression<'a> {
pub fn node_type(&self) -> NodeType {
NodeType::JsCacheExpression
}
}
#[derive(Debug)]
pub struct BlockStatement<'a> {
pub body: Vec<'a, BlockStatementBody<'a>>,
pub loc: SourceLocation,
}
impl<'a> BlockStatement<'a> {
pub fn new(allocator: &'a Bump, loc: SourceLocation) -> Self {
Self {
body: Vec::new_in(allocator),
loc,
}
}
pub fn node_type(&self) -> NodeType {
NodeType::JsBlockStatement
}
}
#[derive(Debug)]
pub enum BlockStatementBody<'a> {
JsChild(JsChildNode<'a>),
If(Box<'a, IfStatement<'a>>),
}
#[derive(Debug)]
pub struct TemplateLiteral<'a> {
pub elements: Vec<'a, TemplateLiteralElement<'a>>,
pub loc: SourceLocation,
}
impl<'a> TemplateLiteral<'a> {
pub fn node_type(&self) -> NodeType {
NodeType::JsTemplateLiteral
}
}
#[derive(Debug)]
pub enum TemplateLiteralElement<'a> {
String(String),
JsChild(JsChildNode<'a>),
}
#[derive(Debug)]
pub struct IfStatement<'a> {
pub test: ExpressionNode<'a>,
pub consequent: Box<'a, BlockStatement<'a>>,
pub alternate: Option<IfStatementAlternate<'a>>,
pub loc: SourceLocation,
}
impl<'a> IfStatement<'a> {
pub fn node_type(&self) -> NodeType {
NodeType::JsIfStatement
}
}
#[derive(Debug)]
pub enum IfStatementAlternate<'a> {
If(Box<'a, IfStatement<'a>>),
Block(Box<'a, BlockStatement<'a>>),
Return(Box<'a, ReturnStatement<'a>>),
}
#[derive(Debug)]
pub struct AssignmentExpression<'a> {
pub left: Box<'a, SimpleExpressionNode<'a>>,
pub right: JsChildNode<'a>,
pub loc: SourceLocation,
}
impl<'a> AssignmentExpression<'a> {
pub fn node_type(&self) -> NodeType {
NodeType::JsAssignmentExpression
}
}
#[derive(Debug)]
pub struct SequenceExpression<'a> {
pub expressions: Vec<'a, JsChildNode<'a>>,
pub loc: SourceLocation,
}
impl<'a> SequenceExpression<'a> {
pub fn node_type(&self) -> NodeType {
NodeType::JsSequenceExpression
}
}
#[derive(Debug)]
pub struct ReturnStatement<'a> {
pub returns: ReturnValue<'a>,
pub loc: SourceLocation,
}
impl<'a> ReturnStatement<'a> {
pub fn node_type(&self) -> NodeType {
NodeType::JsReturnStatement
}
}
#[derive(Debug)]
pub enum ReturnValue<'a> {
Single(TemplateChildNode<'a>),
Multiple(Vec<'a, TemplateChildNode<'a>>),
JsChild(JsChildNode<'a>),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn node_type_discriminants() {
assert_eq!(NodeType::Root as u8, 0);
assert_eq!(NodeType::Element as u8, 1);
assert_eq!(NodeType::Text as u8, 2);
assert_eq!(NodeType::Comment as u8, 3);
assert_eq!(NodeType::SimpleExpression as u8, 4);
assert_eq!(NodeType::Interpolation as u8, 5);
assert_eq!(NodeType::Attribute as u8, 6);
assert_eq!(NodeType::Directive as u8, 7);
assert_eq!(NodeType::CompoundExpression as u8, 8);
assert_eq!(NodeType::If as u8, 9);
assert_eq!(NodeType::IfBranch as u8, 10);
assert_eq!(NodeType::For as u8, 11);
assert_eq!(NodeType::TextCall as u8, 12);
assert_eq!(NodeType::VNodeCall as u8, 13);
assert_eq!(NodeType::JsCallExpression as u8, 14);
assert_eq!(NodeType::JsObjectExpression as u8, 15);
assert_eq!(NodeType::JsProperty as u8, 16);
assert_eq!(NodeType::JsArrayExpression as u8, 17);
assert_eq!(NodeType::JsFunctionExpression as u8, 18);
assert_eq!(NodeType::JsConditionalExpression as u8, 19);
assert_eq!(NodeType::JsCacheExpression as u8, 20);
assert_eq!(NodeType::JsBlockStatement as u8, 21);
assert_eq!(NodeType::JsTemplateLiteral as u8, 22);
assert_eq!(NodeType::JsIfStatement as u8, 23);
assert_eq!(NodeType::JsAssignmentExpression as u8, 24);
assert_eq!(NodeType::JsSequenceExpression as u8, 25);
assert_eq!(NodeType::JsReturnStatement as u8, 26);
}
#[test]
fn element_type_discriminants() {
assert_eq!(ElementType::Element as u8, 0);
assert_eq!(ElementType::Component as u8, 1);
assert_eq!(ElementType::Slot as u8, 2);
assert_eq!(ElementType::Template as u8, 3);
}
#[test]
fn namespace_discriminants() {
assert_eq!(Namespace::Html as u8, 0);
assert_eq!(Namespace::Svg as u8, 1);
assert_eq!(Namespace::MathMl as u8, 2);
}
#[test]
fn constant_type_discriminants() {
assert_eq!(ConstantType::NotConstant as u8, 0);
assert_eq!(ConstantType::CanSkipPatch as u8, 1);
assert_eq!(ConstantType::CanCache as u8, 2);
assert_eq!(ConstantType::CanStringify as u8, 3);
}
#[test]
fn constant_type_ordering() {
assert!(ConstantType::NotConstant < ConstantType::CanSkipPatch);
assert!(ConstantType::CanSkipPatch < ConstantType::CanCache);
assert!(ConstantType::CanCache < ConstantType::CanStringify);
}
#[test]
fn element_type_default() {
assert_eq!(ElementType::default(), ElementType::Element);
}
#[test]
fn namespace_default() {
assert_eq!(Namespace::default(), Namespace::Html);
}
#[test]
fn constant_type_default() {
assert_eq!(ConstantType::default(), ConstantType::NotConstant);
}
#[test]
fn runtime_helper_core_names() {
assert_eq!(RuntimeHelper::Fragment.name(), "Fragment");
assert_eq!(RuntimeHelper::CreateVNode.name(), "createVNode");
assert_eq!(
RuntimeHelper::CreateElementVNode.name(),
"createElementVNode"
);
assert_eq!(
RuntimeHelper::CreateElementBlock.name(),
"createElementBlock"
);
assert_eq!(RuntimeHelper::OpenBlock.name(), "openBlock");
assert_eq!(RuntimeHelper::ToDisplayString.name(), "toDisplayString");
assert_eq!(RuntimeHelper::ResolveComponent.name(), "resolveComponent");
assert_eq!(RuntimeHelper::WithDirectives.name(), "withDirectives");
assert_eq!(RuntimeHelper::RenderList.name(), "renderList");
assert_eq!(RuntimeHelper::RenderSlot.name(), "renderSlot");
assert_eq!(RuntimeHelper::VShow.name(), "vShow");
assert_eq!(RuntimeHelper::WithCtx.name(), "withCtx");
}
#[test]
fn runtime_helper_ssr_names() {
assert_eq!(RuntimeHelper::SsrInterpolate.name(), "ssrInterpolate");
assert_eq!(
RuntimeHelper::SsrRenderComponent.name(),
"ssrRenderComponent"
);
assert_eq!(RuntimeHelper::SsrRenderList.name(), "ssrRenderList");
assert_eq!(RuntimeHelper::SsrRenderAttrs.name(), "ssrRenderAttrs");
assert_eq!(RuntimeHelper::SsrRenderAttr.name(), "ssrRenderAttr");
assert_eq!(RuntimeHelper::SsrRenderClass.name(), "ssrRenderClass");
assert_eq!(RuntimeHelper::SsrRenderStyle.name(), "ssrRenderStyle");
assert_eq!(RuntimeHelper::SsrRenderSlot.name(), "ssrRenderSlot");
}
#[test]
fn runtime_helper_is_ssr() {
let ssr_helpers = [
RuntimeHelper::SsrInterpolate,
RuntimeHelper::SsrRenderVNode,
RuntimeHelper::SsrRenderComponent,
RuntimeHelper::SsrRenderSlot,
RuntimeHelper::SsrRenderSlotInner,
RuntimeHelper::SsrRenderAttrs,
RuntimeHelper::SsrRenderAttr,
RuntimeHelper::SsrRenderDynamicAttr,
RuntimeHelper::SsrIncludeBooleanAttr,
RuntimeHelper::SsrRenderClass,
RuntimeHelper::SsrRenderStyle,
RuntimeHelper::SsrRenderDynamicModel,
RuntimeHelper::SsrGetDynamicModelProps,
RuntimeHelper::SsrRenderList,
RuntimeHelper::SsrLooseEqual,
RuntimeHelper::SsrLooseContain,
RuntimeHelper::SsrGetDirectiveProps,
RuntimeHelper::SsrRenderTeleport,
RuntimeHelper::SsrRenderSuspense,
];
for helper in &ssr_helpers {
assert!(helper.is_ssr(), "{:?} should be SSR", helper);
}
}
#[test]
fn runtime_helper_core_not_ssr() {
let core_helpers = [
RuntimeHelper::Fragment,
RuntimeHelper::CreateVNode,
RuntimeHelper::CreateElementVNode,
RuntimeHelper::OpenBlock,
RuntimeHelper::CreateBlock,
RuntimeHelper::ToDisplayString,
RuntimeHelper::ResolveComponent,
RuntimeHelper::WithDirectives,
RuntimeHelper::VShow,
RuntimeHelper::WithCtx,
];
for helper in &core_helpers {
assert!(!helper.is_ssr(), "{:?} should not be SSR", helper);
}
}
#[test]
fn root_node_new() {
let allocator = Bump::new();
let root = RootNode::new(&allocator, "test");
assert_eq!(root.source.as_str(), "test");
assert!(root.children.is_empty());
assert!(root.helpers.is_empty());
assert!(root.components.is_empty());
assert_eq!(root.temps, 0);
assert!(!root.transformed);
assert!(root.codegen_node.is_none());
assert_eq!(root.node_type(), NodeType::Root);
}
#[test]
fn element_node_new() {
let allocator = Bump::new();
let el = ElementNode::new(&allocator, "div", SourceLocation::STUB);
assert_eq!(el.tag.as_str(), "div");
assert_eq!(el.ns, Namespace::Html);
assert_eq!(el.tag_type, ElementType::Element);
assert!(el.props.is_empty());
assert!(el.children.is_empty());
assert!(!el.is_self_closing);
assert!(el.codegen_node.is_none());
assert_eq!(el.node_type(), NodeType::Element);
}
#[test]
fn text_node_new() {
let node = TextNode::new("hello", SourceLocation::STUB);
assert_eq!(node.content.as_str(), "hello");
assert_eq!(node.node_type(), NodeType::Text);
}
#[test]
fn comment_node_new() {
let node = CommentNode::new("a comment", SourceLocation::STUB);
assert_eq!(node.content.as_str(), "a comment");
assert_eq!(node.node_type(), NodeType::Comment);
}
#[test]
fn directive_node_new() {
let allocator = Bump::new();
let dir = DirectiveNode::new(&allocator, "if", SourceLocation::STUB);
assert_eq!(dir.name.as_str(), "if");
assert!(dir.raw_name.is_none());
assert!(dir.exp.is_none());
assert!(dir.arg.is_none());
assert!(dir.modifiers.is_empty());
assert!(dir.for_parse_result.is_none());
assert_eq!(dir.node_type(), NodeType::Directive);
}
#[test]
fn attribute_node_new() {
let attr = AttributeNode::new("id", SourceLocation::STUB);
assert_eq!(attr.name.as_str(), "id");
assert!(attr.value.is_none());
assert_eq!(attr.node_type(), NodeType::Attribute);
}
#[test]
fn simple_expression_static() {
let expr = SimpleExpressionNode::new("hello", true, SourceLocation::STUB);
assert_eq!(expr.content.as_str(), "hello");
assert!(expr.is_static);
assert_eq!(expr.const_type, ConstantType::CanStringify);
assert!(expr.js_ast.is_none());
assert!(!expr.is_handler_key);
assert!(!expr.is_ref_transformed);
assert_eq!(expr.node_type(), NodeType::SimpleExpression);
}
#[test]
fn simple_expression_dynamic() {
let expr = SimpleExpressionNode::new("foo", false, SourceLocation::STUB);
assert!(!expr.is_static);
assert_eq!(expr.const_type, ConstantType::NotConstant);
}
#[test]
fn compound_expression_new() {
let allocator = Bump::new();
let compound = CompoundExpressionNode::new(&allocator, SourceLocation::STUB);
assert!(compound.children.is_empty());
assert!(compound.identifiers.is_none());
assert!(!compound.is_handler_key);
assert_eq!(compound.node_type(), NodeType::CompoundExpression);
}
#[test]
fn if_node_new() {
let allocator = Bump::new();
let if_node = IfNode::new(&allocator, SourceLocation::STUB);
assert!(if_node.branches.is_empty());
assert!(if_node.codegen_node.is_none());
assert_eq!(if_node.node_type(), NodeType::If);
}
#[test]
fn if_branch_node_new() {
let allocator = Bump::new();
let branch = IfBranchNode::new(&allocator, None, SourceLocation::STUB);
assert!(branch.condition.is_none());
assert!(branch.children.is_empty());
assert!(branch.user_key.is_none());
assert!(!branch.is_template_if);
assert_eq!(branch.node_type(), NodeType::IfBranch);
}
#[test]
fn call_expression_new() {
let allocator = Bump::new();
let call = CallExpression::new(
&allocator,
Callee::Symbol(RuntimeHelper::CreateVNode),
SourceLocation::STUB,
);
assert!(call.arguments.is_empty());
assert_eq!(call.node_type(), NodeType::JsCallExpression);
}
#[test]
fn object_expression_new() {
let allocator = Bump::new();
let obj = ObjectExpression::new(&allocator, SourceLocation::STUB);
assert!(obj.properties.is_empty());
assert_eq!(obj.node_type(), NodeType::JsObjectExpression);
}
#[test]
fn array_expression_new() {
let allocator = Bump::new();
let arr = ArrayExpression::new(&allocator, SourceLocation::STUB);
assert!(arr.elements.is_empty());
assert_eq!(arr.node_type(), NodeType::JsArrayExpression);
}
#[test]
fn block_statement_new() {
let allocator = Bump::new();
let block = BlockStatement::new(&allocator, SourceLocation::STUB);
assert!(block.body.is_empty());
assert_eq!(block.node_type(), NodeType::JsBlockStatement);
}
#[test]
fn template_child_text_loc() {
let allocator = Bump::new();
let loc = SourceLocation::new(Position::new(5, 1, 6), Position::new(10, 1, 11), "hello");
let text = TextNode::new("hello", loc.clone());
let child = TemplateChildNode::Text(Box::new_in(text, &allocator));
assert_eq!(*child.loc(), loc);
assert_eq!(child.node_type(), NodeType::Text);
}
#[test]
fn template_child_hoisted_loc() {
let child = TemplateChildNode::Hoisted(0);
assert_eq!(child.loc().start.offset, 0);
assert_eq!(child.loc().start.line, 1);
assert_eq!(child.loc().start.column, 1);
assert_eq!(child.node_type(), NodeType::SimpleExpression);
}
#[test]
fn source_location_stub() {
let stub = SourceLocation::STUB;
assert_eq!(stub.start.offset, 0);
assert_eq!(stub.start.line, 1);
assert_eq!(stub.start.column, 1);
assert_eq!(stub.end.offset, 0);
assert_eq!(stub.source.as_str(), "");
}
#[test]
fn source_location_default_is_stub() {
let default_loc = SourceLocation::default();
assert_eq!(default_loc, SourceLocation::STUB);
}
#[test]
fn source_location_new() {
let loc = SourceLocation::new(Position::new(0, 1, 1), Position::new(5, 1, 6), "hello");
assert_eq!(loc.start.offset, 0);
assert_eq!(loc.end.offset, 5);
assert_eq!(loc.source.as_str(), "hello");
}
#[test]
fn position_new() {
let pos = Position::new(42, 3, 10);
assert_eq!(pos.offset, 42);
assert_eq!(pos.line, 3);
assert_eq!(pos.column, 10);
}
#[test]
fn position_default() {
let pos = Position::default();
assert_eq!(pos.offset, 0);
assert_eq!(pos.line, 0);
assert_eq!(pos.column, 0);
}
}