perf(ast): box enum variants (#3058)

Box all enum variants for JSX types (`JSXAttributeName`,
`JSXAttributeValue`, `JSXChild`, `JSXElementName`,
`JSXMemberExpressionObject`). Part of #3047.

I'm not sure how to interpret the benchmark results. As I said on #3047:

> I imagine it may cost a little in performance in the parser due to
extra calls to `alloc`, but in return traversing the AST should be
cheaper, as the data is more compact, so less cache misses.

Sure enough, there is a small impact (1%) on the 2 parser benchmarks for
JSX files. However, the other benchmarks have too much noise in them to
see whether this is repaid in a speed up on transformer etc, especially
as the transformer benchmarks also include parsing.

What do you think @Boshen?
This commit is contained in:
overlookmotel 2024-04-22 02:09:30 +01:00 committed by GitHub
parent 68d082644c
commit 48e20880d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 182 additions and 176 deletions

View file

@ -83,7 +83,7 @@ pub struct JSXClosingFragment {
#[cfg_attr(feature = "serialize", serde(untagged))]
pub enum JSXElementName<'a> {
/// `<Apple />`
Identifier(JSXIdentifier<'a>),
Identifier(Box<'a, JSXIdentifier<'a>>),
/// `<Apple:Orange />`
NamespacedName(Box<'a, JSXNamespacedName<'a>>),
/// `<Apple.Orange />`
@ -131,7 +131,7 @@ impl<'a> JSXMemberExpression<'a> {
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
pub enum JSXMemberExpressionObject<'a> {
Identifier(JSXIdentifier<'a>),
Identifier(Box<'a, JSXIdentifier<'a>>),
MemberExpression(Box<'a, JSXMemberExpression<'a>>),
}
@ -203,7 +203,7 @@ pub struct JSXSpreadAttribute<'a> {
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
pub enum JSXAttributeName<'a> {
Identifier(JSXIdentifier<'a>),
Identifier(Box<'a, JSXIdentifier<'a>>),
NamespacedName(Box<'a, JSXNamespacedName<'a>>),
}
@ -212,8 +212,8 @@ pub enum JSXAttributeName<'a> {
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
pub enum JSXAttributeValue<'a> {
StringLiteral(StringLiteral<'a>),
ExpressionContainer(JSXExpressionContainer<'a>),
StringLiteral(Box<'a, StringLiteral<'a>>),
ExpressionContainer(Box<'a, JSXExpressionContainer<'a>>),
Element(Box<'a, JSXElement<'a>>),
Fragment(Box<'a, JSXFragment<'a>>),
}
@ -240,11 +240,11 @@ impl<'a> JSXIdentifier<'a> {
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))]
pub enum JSXChild<'a> {
Text(JSXText<'a>),
Text(Box<'a, JSXText<'a>>),
Element(Box<'a, JSXElement<'a>>),
Fragment(Box<'a, JSXFragment<'a>>),
ExpressionContainer(JSXExpressionContainer<'a>),
Spread(JSXSpreadChild<'a>),
ExpressionContainer(Box<'a, JSXExpressionContainer<'a>>),
Spread(Box<'a, JSXSpreadChild<'a>>),
}
#[derive(Debug, Hash)]

View file

@ -1302,12 +1302,16 @@ impl<'a> AstBuilder<'a> {
&self,
span: Span,
expression: JSXExpression<'a>,
) -> JSXExpressionContainer<'a> {
JSXExpressionContainer { span, expression }
) -> Box<'a, JSXExpressionContainer<'a>> {
self.alloc(JSXExpressionContainer { span, expression })
}
pub fn jsx_spread_child(&self, span: Span, expression: Expression<'a>) -> JSXSpreadChild<'a> {
JSXSpreadChild { span, expression }
pub fn jsx_spread_child(
&self,
span: Span,
expression: Expression<'a>,
) -> Box<'a, JSXSpreadChild<'a>> {
self.alloc(JSXSpreadChild { span, expression })
}
pub fn jsx_empty_expression(&self, span: Span) -> JSXEmptyExpression {
@ -1335,8 +1339,8 @@ impl<'a> AstBuilder<'a> {
JSXIdentifier { span, name }
}
pub fn jsx_text(&self, span: Span, value: Atom<'a>) -> JSXText<'a> {
JSXText { span, value }
pub fn jsx_text(&self, span: Span, value: Atom<'a>) -> Box<'a, JSXText<'a>> {
self.alloc(JSXText { span, value })
}
/* ---------- TypeScript ---------- */

View file

@ -1,8 +1,5 @@
use oxc_ast::{
ast::{
JSXAttributeItem, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer,
JSXOpeningElement,
},
ast::{JSXAttributeItem, JSXAttributeValue, JSXElement, JSXExpression, JSXOpeningElement},
AstKind,
};
use oxc_diagnostics::{
@ -212,10 +209,13 @@ impl Rule for AltText {
fn is_valid_alt_prop(item: &JSXAttributeItem<'_>) -> bool {
match get_prop_value(item) {
None => false,
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expr),
..
})) => !expr.is_null_or_undefined(),
Some(JSXAttributeValue::ExpressionContainer(container)) => {
if let JSXExpression::Expression(expr) = &container.expression {
!expr.is_null_or_undefined()
} else {
true
}
}
_ => true,
}
}
@ -229,10 +229,13 @@ fn aria_label_has_value<'a>(item: &'a JSXAttributeItem<'a>) -> bool {
match get_prop_value(item) {
None => false,
Some(JSXAttributeValue::StringLiteral(s)) if s.value.is_empty() => false,
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expr),
..
})) => !expr.is_undefined(),
Some(JSXAttributeValue::ExpressionContainer(container)) => {
if let JSXExpression::Expression(expr) = &container.expression {
!expr.is_undefined()
} else {
true
}
}
_ => true,
}
}

View file

@ -1,5 +1,5 @@
use oxc_ast::{
ast::{JSXAttributeValue, JSXExpression, JSXExpressionContainer},
ast::{JSXAttributeValue, JSXExpression},
AstKind,
};
use oxc_diagnostics::{
@ -134,12 +134,11 @@ impl Rule for AriaRole {
};
match get_prop_value(aria_role) {
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expr),
..
})) => {
if expr.is_undefined() || expr.is_null() {
ctx.diagnostic(AriaRoleDiagnostic(attr.span, String::new()));
Some(JSXAttributeValue::ExpressionContainer(container)) => {
if let JSXExpression::Expression(expr) = &container.expression {
if expr.is_undefined() || expr.is_null() {
ctx.diagnostic(AriaRoleDiagnostic(attr.span, String::new()));
}
}
}
Some(JSXAttributeValue::StringLiteral(str)) => {

View file

@ -1,7 +1,5 @@
use oxc_ast::{
ast::{
JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression, JSXExpressionContainer,
},
ast::{JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression},
AstKind,
};
use oxc_diagnostics::{
@ -87,10 +85,13 @@ impl Rule for HtmlHasLang {
fn is_valid_lang_prop(item: &JSXAttributeItem) -> bool {
match get_prop_value(item) {
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expr),
..
})) => !expr.is_undefined(),
Some(JSXAttributeValue::ExpressionContainer(container)) => {
if let JSXExpression::Expression(expr) = &container.expression {
!expr.is_undefined()
} else {
true
}
}
Some(JSXAttributeValue::StringLiteral(str)) => !str.value.as_str().is_empty(),
_ => true,
}

View file

@ -1,5 +1,5 @@
use oxc_ast::{
ast::{Expression, JSXAttributeValue, JSXElementName, JSXExpression, JSXExpressionContainer},
ast::{Expression, JSXAttributeValue, JSXElementName, JSXExpression},
AstKind,
};
use oxc_diagnostics::{
@ -88,28 +88,27 @@ impl Rule for IframeHasTitle {
return;
}
}
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expr),
..
})) => {
if expr.is_string_literal() {
if let Expression::StringLiteral(str) = expr {
if !str.value.as_str().is_empty() {
return;
Some(JSXAttributeValue::ExpressionContainer(container)) => {
if let JSXExpression::Expression(expr) = &container.expression {
if expr.is_string_literal() {
if let Expression::StringLiteral(str) = expr {
if !str.value.as_str().is_empty() {
return;
}
}
if let Expression::TemplateLiteral(tmpl) = expr {
if !tmpl.quasis.is_empty()
& !tmpl.expressions.is_empty()
& tmpl.quasis.iter().any(|q| !q.value.raw.as_str().is_empty())
{
return;
}
}
}
if let Expression::TemplateLiteral(tmpl) = expr {
if !tmpl.quasis.is_empty()
& !tmpl.expressions.is_empty()
& tmpl.quasis.iter().any(|q| !q.value.raw.as_str().is_empty())
{
return;
}
}
}
if expr.is_identifier_reference() & !expr.is_undefined() {
return;
if expr.is_identifier_reference() & !expr.is_undefined() {
return;
}
}
}
_ => {}

View file

@ -1,10 +1,7 @@
use regex::Regex;
use oxc_ast::{
ast::{
Expression, JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXExpression,
JSXExpressionContainer,
},
ast::{Expression, JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXExpression},
AstKind,
};
use oxc_diagnostics::{
@ -149,18 +146,15 @@ impl Rule for ImgRedundantAlt {
ctx.diagnostic(ImgRedundantAltDiagnostic(alt_attribute_name_span));
}
}
JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expression),
..
}) => match expression {
Expression::StringLiteral(lit) => {
JSXAttributeValue::ExpressionContainer(container) => match &container.expression {
JSXExpression::Expression(Expression::StringLiteral(lit)) => {
let alt_text = lit.value.as_str();
if is_redundant_alt_text(alt_text, &self.redundant_words) {
ctx.diagnostic(ImgRedundantAltDiagnostic(alt_attribute_name_span));
}
}
Expression::TemplateLiteral(lit) => {
JSXExpression::Expression(Expression::TemplateLiteral(lit)) => {
for quasi in &lit.quasis {
let alt_text = quasi.value.raw.as_str();

View file

@ -1,8 +1,6 @@
use language_tags::LanguageTag;
use oxc_ast::{
ast::{
JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression, JSXExpressionContainer,
},
ast::{JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression},
AstKind,
};
use oxc_diagnostics::{
@ -94,10 +92,13 @@ impl Rule for Lang {
fn is_valid_lang_prop(item: &JSXAttributeItem) -> bool {
match get_prop_value(item) {
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expr),
..
})) => !expr.is_undefined(),
Some(JSXAttributeValue::ExpressionContainer(container)) => {
if let JSXExpression::Expression(expr) = &container.expression {
!expr.is_undefined()
} else {
true
}
}
Some(JSXAttributeValue::StringLiteral(str)) => {
let language_tag = LanguageTag::parse(str.value.as_str()).unwrap();
language_tag.is_valid()

View file

@ -1,5 +1,5 @@
use oxc_ast::{
ast::{JSXAttributeValue, JSXExpression, JSXExpressionContainer},
ast::{JSXAttributeValue, JSXExpression},
AstKind,
};
use oxc_diagnostics::{
@ -119,15 +119,14 @@ impl Rule for MouseEventsHaveKeyEvents {
}
match has_jsx_prop(jsx_opening_el, "onFocus").and_then(get_prop_value) {
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expr),
..
})) => {
if expr.is_undefined() {
ctx.diagnostic(MouseEventsHaveKeyEventsDiagnostic::MissOnFocus(
jsx_attr.span(),
String::from(handler),
));
Some(JSXAttributeValue::ExpressionContainer(container)) => {
if let JSXExpression::Expression(expr) = &container.expression {
if expr.is_undefined() {
ctx.diagnostic(MouseEventsHaveKeyEventsDiagnostic::MissOnFocus(
jsx_attr.span(),
String::from(handler),
));
}
}
}
None => {
@ -150,15 +149,14 @@ impl Rule for MouseEventsHaveKeyEvents {
}
match has_jsx_prop(jsx_opening_el, "onBlur").and_then(get_prop_value) {
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expr),
..
})) => {
if expr.is_undefined() {
ctx.diagnostic(MouseEventsHaveKeyEventsDiagnostic::MissOnBlur(
jsx_attr.span(),
String::from(handler),
));
Some(JSXAttributeValue::ExpressionContainer(container)) => {
if let JSXExpression::Expression(expr) = &container.expression {
if expr.is_undefined() {
ctx.diagnostic(MouseEventsHaveKeyEventsDiagnostic::MissOnBlur(
jsx_attr.span(),
String::from(handler),
));
}
}
}
None => {

View file

@ -1,5 +1,5 @@
use oxc_ast::{
ast::{JSXAttributeItem, JSXAttributeValue, JSXExpression, JSXExpressionContainer},
ast::{JSXAttributeItem, JSXAttributeValue, JSXExpression},
AstKind,
};
use oxc_diagnostics::{
@ -48,14 +48,13 @@ impl Rule for NoAccessKey {
Some(JSXAttributeValue::StringLiteral(_)) => {
ctx.diagnostic(NoAccessKeyDiagnostic(attr.span));
}
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expr),
..
})) => {
if expr.is_identifier_reference() & expr.is_undefined() {
return;
Some(JSXAttributeValue::ExpressionContainer(container)) => {
if let JSXExpression::Expression(expr) = &container.expression {
if expr.is_identifier_reference() & expr.is_undefined() {
return;
}
ctx.diagnostic(NoAccessKeyDiagnostic(attr.span));
}
ctx.diagnostic(NoAccessKeyDiagnostic(attr.span));
}
_ => {}
}

View file

@ -1,5 +1,5 @@
use oxc_ast::{
ast::{JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXElementName, JSXIdentifier},
ast::{JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXElementName},
AstKind,
};
use oxc_diagnostics::{
@ -48,8 +48,9 @@ impl Rule for NoBeforeInteractiveScriptOutsideDocument {
if is_in_app_dir(file_path) {
return;
}
let JSXElementName::Identifier(JSXIdentifier { name: tag_name, .. }) = &jsx_el.name
else {
let tag_name = if let JSXElementName::Identifier(ident) = &jsx_el.name {
&ident.name
} else {
return;
};
if jsx_el.attributes.len() == 0 {

View file

@ -1,5 +1,5 @@
use oxc_ast::{
ast::{JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXElementName, JSXIdentifier},
ast::{JSXAttributeItem, JSXAttributeName, JSXAttributeValue, JSXElementName},
AstKind,
};
use oxc_diagnostics::{
@ -111,8 +111,9 @@ const NEXT_POLYFILLED_FEATURES: Set<&'static str> = phf_set! {
impl Rule for NoUnwantedPolyfillio {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if let AstKind::JSXOpeningElement(jsx_el) = node.kind() {
let JSXElementName::Identifier(JSXIdentifier { name: tag_name, .. }) = &jsx_el.name
else {
let tag_name = if let JSXElementName::Identifier(ident) = &jsx_el.name {
&ident.name
} else {
return;
};

View file

@ -1,7 +1,7 @@
use oxc_ast::{
ast::{
Argument, Expression, JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression,
JSXExpressionContainer, ObjectPropertyKind,
ObjectPropertyKind,
},
AstKind,
};
@ -163,10 +163,13 @@ impl Rule for ButtonHasType {
impl ButtonHasType {
fn is_valid_button_type_prop(&self, item: &JSXAttributeItem) -> bool {
match get_prop_value(item) {
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expr),
..
})) => self.is_valid_button_type_prop_expression(expr),
Some(JSXAttributeValue::ExpressionContainer(container)) => {
if let JSXExpression::Expression(expr) = &container.expression {
self.is_valid_button_type_prop_expression(expr)
} else {
false
}
}
Some(JSXAttributeValue::StringLiteral(str)) => {
self.is_valid_button_type_prop_string_literal(str.value.as_str())
}

View file

@ -1,5 +1,5 @@
use oxc_ast::{
ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer},
ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression},
AstKind,
};
use oxc_diagnostics::{
@ -52,12 +52,11 @@ fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) {
for item in &jsx_elem.opening_element.attributes {
match get_prop_value(item) {
None => return,
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expr),
..
})) => {
if let Some(span) = check_expression(expr) {
ctx.diagnostic(JsxNoJsxAsPropDiagnostic(span));
Some(JSXAttributeValue::ExpressionContainer(container)) => {
if let JSXExpression::Expression(expr) = &container.expression {
if let Some(span) = check_expression(expr) {
ctx.diagnostic(JsxNoJsxAsPropDiagnostic(span));
}
}
}
_ => {}

View file

@ -1,5 +1,5 @@
use oxc_ast::{
ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer},
ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression},
AstKind,
};
use oxc_diagnostics::{
@ -58,12 +58,11 @@ fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) {
for item in &jsx_elem.opening_element.attributes {
match get_prop_value(item) {
None => return,
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expr),
..
})) => {
if let Some(span) = check_expression(expr) {
ctx.diagnostic(JsxNoNewArrayAsPropDiagnostic(span));
Some(JSXAttributeValue::ExpressionContainer(container)) => {
if let JSXExpression::Expression(expr) = &container.expression {
if let Some(span) = check_expression(expr) {
ctx.diagnostic(JsxNoNewArrayAsPropDiagnostic(span));
}
}
}
_ => {}

View file

@ -1,8 +1,5 @@
use oxc_ast::{
ast::{
Expression, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer,
MemberExpression,
},
ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression, MemberExpression},
AstKind,
};
use oxc_diagnostics::{
@ -56,12 +53,11 @@ fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) {
for item in &jsx_elem.opening_element.attributes {
match get_prop_value(item) {
None => return,
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expr),
..
})) => {
if let Some(span) = check_expression(expr) {
ctx.diagnostic(JsxNoNewFunctionAsPropDiagnostic(span));
Some(JSXAttributeValue::ExpressionContainer(container)) => {
if let JSXExpression::Expression(expr) = &container.expression {
if let Some(span) = check_expression(expr) {
ctx.diagnostic(JsxNoNewFunctionAsPropDiagnostic(span));
}
}
}
_ => {}

View file

@ -1,5 +1,5 @@
use oxc_ast::{
ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression, JSXExpressionContainer},
ast::{Expression, JSXAttributeValue, JSXElement, JSXExpression},
AstKind,
};
use oxc_diagnostics::{
@ -57,12 +57,11 @@ fn check_jsx_element<'a>(jsx_elem: &JSXElement<'a>, ctx: &LintContext<'a>) {
for item in &jsx_elem.opening_element.attributes {
match get_prop_value(item) {
None => return,
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expr),
..
})) => {
if let Some(span) = check_expression(expr) {
ctx.diagnostic(JsxNoNewObjectAsPropDiagnostic(span));
Some(JSXAttributeValue::ExpressionContainer(container)) => {
if let JSXExpression::Expression(expr) = &container.expression {
if let Some(span) = check_expression(expr) {
ctx.diagnostic(JsxNoNewObjectAsPropDiagnostic(span));
}
}
}
_ => {}

View file

@ -1,8 +1,7 @@
use oxc_ast::{
ast::{
CallExpression, Expression, JSXAttributeItem, JSXAttributeName, JSXAttributeValue,
JSXChild, JSXElement, JSXElementName, JSXExpression, JSXExpressionContainer,
JSXOpeningElement,
JSXChild, JSXElement, JSXElementName, JSXExpression, JSXOpeningElement,
},
AstKind,
};
@ -94,10 +93,13 @@ pub fn is_hidden_from_screen_reader(ctx: &LintContext, node: &JSXOpeningElement)
has_jsx_prop_lowercase(node, "aria-hidden").map_or(false, |v| match get_prop_value(v) {
None => true,
Some(JSXAttributeValue::StringLiteral(s)) if s.value == "true" => true,
Some(JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expr),
..
})) => expr.get_boolean_value().unwrap_or(false),
Some(JSXAttributeValue::ExpressionContainer(container)) => {
if let JSXExpression::Expression(expr) = &container.expression {
expr.get_boolean_value().unwrap_or(false)
} else {
false
}
}
_ => false,
})
}
@ -107,10 +109,13 @@ pub fn object_has_accessible_child(ctx: &LintContext, node: &JSXElement<'_>) ->
node.children.iter().any(|child| match child {
JSXChild::Text(text) => !text.value.is_empty(),
JSXChild::Element(el) => !is_hidden_from_screen_reader(ctx, &el.opening_element),
JSXChild::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expr),
..
}) => !expr.is_undefined() && !expr.is_null(),
JSXChild::ExpressionContainer(container) => {
if let JSXExpression::Expression(expr) = &container.expression {
!expr.is_undefined() && !expr.is_null()
} else {
false
}
}
_ => false,
}) || has_jsx_prop_lowercase(&node.opening_element, "dangerouslySetInnerHTML").is_some()
|| has_jsx_prop_lowercase(&node.opening_element, "children").is_some()
@ -252,15 +257,14 @@ pub fn get_element_type(context: &LintContext, element: &JSXOpeningElement) -> O
pub fn parse_jsx_value(value: &JSXAttributeValue) -> Result<f64, ()> {
match value {
JSXAttributeValue::StringLiteral(str) => str.value.parse().or(Err(())),
JSXAttributeValue::ExpressionContainer(JSXExpressionContainer {
expression: JSXExpression::Expression(expression),
..
}) => match expression {
Expression::StringLiteral(str) => str.value.parse().or(Err(())),
Expression::TemplateLiteral(tmpl) => {
JSXAttributeValue::ExpressionContainer(container) => match &container.expression {
JSXExpression::Expression(Expression::StringLiteral(str)) => {
str.value.parse().or(Err(()))
}
JSXExpression::Expression(Expression::TemplateLiteral(tmpl)) => {
tmpl.quasis.first().unwrap().value.raw.parse().or(Err(()))
}
Expression::NumericLiteral(num) => Ok(num.value),
JSXExpression::Expression(Expression::NumericLiteral(num)) => Ok(num.value),
_ => Err(()),
},
_ => Err(()),

View file

@ -144,7 +144,7 @@ impl<'a> ParserImpl<'a> {
.map(JSXElementName::MemberExpression);
}
Ok(JSXElementName::Identifier(identifier))
Ok(JSXElementName::Identifier(self.ast.alloc(identifier)))
}
/// `JSXMemberExpression` :
@ -156,7 +156,7 @@ impl<'a> ParserImpl<'a> {
object: JSXIdentifier<'a>,
) -> Result<Box<'a, JSXMemberExpression<'a>>> {
let mut span = span;
let mut object = JSXMemberExpressionObject::Identifier(object);
let mut object = JSXMemberExpressionObject::Identifier(self.ast.alloc(object));
let mut property = None;
while self.eat(Kind::Dot) && !self.at(Kind::Eof) {
@ -214,10 +214,11 @@ impl<'a> ParserImpl<'a> {
self.parse_jsx_spread_child().map(JSXChild::Spread).map(Some)
}
// {expr}
Kind::LCurly => self
.parse_jsx_expression_container(/* is_jsx_child */ true)
.map(JSXChild::ExpressionContainer)
.map(Some),
Kind::LCurly => {
self.parse_jsx_expression_container(/* is_jsx_child */ true)
.map(JSXChild::ExpressionContainer)
.map(Some)
}
// text
Kind::JSXText => Ok(Some(JSXChild::Text(self.parse_jsx_text()))),
_ => Err(self.unexpected()),
@ -228,7 +229,7 @@ impl<'a> ParserImpl<'a> {
fn parse_jsx_expression_container(
&mut self,
in_jsx_child: bool,
) -> Result<JSXExpressionContainer<'a>> {
) -> Result<Box<'a, JSXExpressionContainer<'a>>> {
let span = self.start_span();
self.bump_any(); // bump `{`
@ -269,7 +270,7 @@ impl<'a> ParserImpl<'a> {
/// `JSXChildExpression` :
/// { ... `AssignmentExpression` }
fn parse_jsx_spread_child(&mut self) -> Result<JSXSpreadChild<'a>> {
fn parse_jsx_spread_child(&mut self) -> Result<Box<'a, JSXSpreadChild<'a>>> {
let span = self.start_span();
self.bump_any(); // bump `{`
self.expect(Kind::Dot3)?;
@ -336,12 +337,14 @@ impl<'a> ParserImpl<'a> {
)));
}
Ok(JSXAttributeName::Identifier(identifier))
Ok(JSXAttributeName::Identifier(self.ast.alloc(identifier)))
}
fn parse_jsx_attribute_value(&mut self) -> Result<JSXAttributeValue<'a>> {
match self.cur_kind() {
Kind::Str => self.parse_literal_string().map(JSXAttributeValue::StringLiteral),
Kind::Str => self
.parse_literal_string()
.map(|str_lit| JSXAttributeValue::StringLiteral(self.ast.alloc(str_lit))),
Kind::LCurly => {
let expr = self.parse_jsx_expression_container(/* is_jsx_child */ false)?;
Ok(JSXAttributeValue::ExpressionContainer(expr))
@ -374,7 +377,7 @@ impl<'a> ParserImpl<'a> {
Ok(self.ast.jsx_identifier(span, name.into()))
}
fn parse_jsx_text(&mut self) -> JSXText<'a> {
fn parse_jsx_text(&mut self) -> Box<'a, JSXText<'a>> {
let span = self.start_span();
let value = Atom::from(self.cur_string());
self.bump_any();

View file

@ -66,7 +66,8 @@ impl<'a> ReactJsxSelf<'a> {
}
}
let name = JSXAttributeName::Identifier(JSXIdentifier::new(SPAN, SELF.into()));
let name =
JSXAttributeName::Identifier(self.ctx.ast.alloc(JSXIdentifier::new(SPAN, SELF.into())));
let value = {
let jsx_expr = JSXExpression::Expression(self.ctx.ast.this_expression(SPAN));
let container = self.ctx.ast.jsx_expression_container(SPAN, jsx_expr);

View file

@ -80,7 +80,9 @@ impl<'a> ReactJsxSource<'a> {
self.should_add_jsx_file_name_variable = true;
let key = JSXAttributeName::Identifier(self.ctx.ast.jsx_identifier(SPAN, SOURCE.into()));
let key = JSXAttributeName::Identifier(
self.ctx.ast.alloc(self.ctx.ast.jsx_identifier(SPAN, SOURCE.into())),
);
let object = self.get_source_object();
let expr = self.ctx.ast.jsx_expression_container(SPAN, JSXExpression::Expression(object));
let value = JSXAttributeValue::ExpressionContainer(expr);