diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 369e958fc7..d5bc196438 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -17,6 +17,9 @@ Semantic Versioning. Masani][].) * Detection of multiple `export default` statements ([E0715][]) now also applies to `export {... as default};` statements. +* JSX elements and fragments are now allowed in JSX attributes without + surrounding them in `{` and `}` (e.g. + `>{items}`). * TypeScript support (still experimental): * `export as namespace` statements are now parsed. * Const generic parameters (``) are now parsed. diff --git a/src/quick-lint-js/fe/parse-expression.cpp b/src/quick-lint-js/fe/parse-expression.cpp index 27ab731f37..96eb5ef2f3 100644 --- a/src/quick-lint-js/fe/parse-expression.cpp +++ b/src/quick-lint-js/fe/parse-expression.cpp @@ -3861,6 +3861,11 @@ Expression* Parser::parse_jsx_element_or_fragment(Parse_Visitor_Base& v, break; } + // > + case Token_Type::less: + children.emplace_back(this->parse_jsx_element_or_fragment(v)); + break; + default: QLJS_PARSER_UNIMPLEMENTED(); break; diff --git a/test/test-parse-expression-jsx.cpp b/test/test-parse-expression-jsx.cpp index 0607d13104..aaaad775d3 100644 --- a/test/test-parse-expression-jsx.cpp +++ b/test/test-parse-expression-jsx.cpp @@ -280,6 +280,24 @@ TEST_F(Test_Parse_Expression_JSX, tag_with_attributes) { ASSERT_EQ(summarize(ast), "jsxelement(input)"); } + { + Test_Parser p(u8"
/>"_sv, jsx_options); + Expression* ast = p.parse_expression(); + ASSERT_EQ(summarize(ast), "jsxelement(div, jsxelement(span))"); + } + + { + Test_Parser p(u8"
{child} />"_sv, jsx_options); + Expression* ast = p.parse_expression(); + ASSERT_EQ(summarize(ast), "jsxelement(div, jsxelement(span, var child))"); + } + + { + Test_Parser p(u8"
{child} />"_sv, jsx_options); + Expression* ast = p.parse_expression(); + ASSERT_EQ(summarize(ast), "jsxelement(div, jsxfragment(var child))"); + } + { Test_Parser p(u8""_sv, jsx_options); Expression* ast = p.parse_expression(); @@ -401,6 +419,15 @@ TEST_F(Test_Parse_Expression_JSX, EXPECT_EQ(summarize(ast), "jsxelement(const)"); } } + +TEST_F(Test_Parse_Expression_JSX, greater_greater_token_is_split) { + { + Test_Parser p(u8"> "_sv, jsx_options); + // ^^ Token should be split into two '>'s. + Expression* ast = p.parse_expression(); + ASSERT_EQ(summarize(ast), "jsxelement(A, jsxelement(B))"); + } +} } }