use oxc_allocator::Allocator;
use oxc_ast::ast::*;
use oxc_ast::{AstBuilder, NONE};
use oxc_span::{Atom, SPAN};
pub struct JsxBuilder<'a> {
ast: AstBuilder<'a>,
}
impl<'a> JsxBuilder<'a> {
pub fn new(alloc: &'a Allocator) -> Self {
Self {
ast: AstBuilder::new(alloc),
}
}
pub fn ast(&self) -> &AstBuilder<'a> {
&self.ast
}
pub fn element(
&self,
name: impl Into<Atom<'a>>,
attributes: Vec<JSXAttributeItem<'a>>,
children: Vec<JSXChild<'a>>,
self_closing: bool,
) -> JSXElement<'a> {
let name_atom = name.into();
let opening_ident = self.ast.jsx_identifier(SPAN, name_atom);
let opening_name = JSXElementName::Identifier(self.ast.alloc(opening_ident));
let attrs_vec = self.ast.vec_from_iter(attributes);
let opening_element = self
.ast
.jsx_opening_element(SPAN, opening_name, NONE, attrs_vec);
let closing_element: Option<JSXClosingElement> = if self_closing {
None
} else {
let closing_ident = self.ast.jsx_identifier(SPAN, name_atom);
let closing_name = JSXElementName::Identifier(self.ast.alloc(closing_ident));
Some(self.closing_element(closing_name))
};
let children_vec = self.ast.vec_from_iter(children);
self.ast.jsx_element(
SPAN,
opening_element,
children_vec,
closing_element.map(|e| self.ast.alloc(e)),
)
}
fn closing_element(&self, name: JSXElementName<'a>) -> JSXClosingElement<'a> {
JSXClosingElement { span: SPAN, name }
}
pub fn attr(
&self,
name: impl Into<Atom<'a>>,
value: Option<JSXAttributeValue<'a>>,
) -> JSXAttributeItem<'a> {
let attr_name = self.ast.jsx_attribute_name_identifier(SPAN, name);
let attr = self.ast.jsx_attribute(SPAN, attr_name, value);
JSXAttributeItem::Attribute(self.ast.alloc(attr))
}
pub fn string_attr(&self, value: impl Into<Atom<'a>>) -> JSXAttributeValue<'a> {
JSXAttributeValue::StringLiteral(self.ast.alloc(self.ast.string_literal(SPAN, value, None)))
}
pub fn expr_attr(&self, expr: Expression<'a>) -> JSXAttributeValue<'a> {
JSXAttributeValue::ExpressionContainer(self.ast.alloc(JSXExpressionContainer {
span: SPAN,
expression: JSXExpression::from(expr),
}))
}
pub fn text(&self, value: impl Into<Atom<'a>>) -> JSXChild<'a> {
let value_atom = value.into();
let text = self.ast.jsx_text(SPAN, value_atom, Some(value_atom));
JSXChild::Text(self.ast.alloc(text))
}
pub fn child(&self, element: JSXElement<'a>) -> JSXChild<'a> {
JSXChild::Element(self.ast.alloc(element))
}
pub fn expr_child(&self, expr: Expression<'a>) -> JSXChild<'a> {
JSXChild::ExpressionContainer(self.ast.alloc(JSXExpressionContainer {
span: SPAN,
expression: JSXExpression::from(expr),
}))
}
pub fn fragment(&self, children: Vec<JSXChild<'a>>) -> JSXFragment<'a> {
JSXFragment {
span: SPAN,
opening_fragment: JSXOpeningFragment { span: SPAN },
closing_fragment: JSXClosingFragment { span: SPAN },
children: self.ast.vec_from_iter(children),
}
}
pub fn jsx_expr(&self, element: JSXElement<'a>) -> Expression<'a> {
Expression::JSXElement(self.ast.alloc(element))
}
pub fn jsx_fragment_expr(&self, fragment: JSXFragment<'a>) -> Expression<'a> {
Expression::JSXFragment(self.ast.alloc(fragment))
}
}