Skip to content

Commit

Permalink
feat(transformer): implement jsx spread child
Browse files Browse the repository at this point in the history
closes #8690
  • Loading branch information
Boshen committed Jan 28, 2025
1 parent 003c190 commit 88e510d
Show file tree
Hide file tree
Showing 10 changed files with 76 additions and 18 deletions.
4 changes: 0 additions & 4 deletions crates/oxc_transformer/src/jsx/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,3 @@ pub fn valueless_key(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Please provide an explicit key value. Using \"key\" as a shorthand for \"key={true}\" is not allowed.")
.with_label(span)
}

pub fn spread_children_are_not_supported(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Spread children are not supported in React.").with_label(span)
}
46 changes: 37 additions & 9 deletions crates/oxc_transformer/src/jsx/jsx_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ impl<'a> JsxImpl<'a, '_> {
// Append children to object properties in automatic mode
if is_automatic {
let mut children = ctx.ast.vec_from_iter(
children.iter().filter_map(|child| self.transform_jsx_child(child, ctx)),
children.iter().filter_map(|child| self.transform_jsx_child_automatic(child, ctx)),
);
children_len = children.len();
if children_len != 0 {
Expand Down Expand Up @@ -750,10 +750,7 @@ impl<'a> JsxImpl<'a, '_> {
// React.createElement(type, arguments, ...children)
// ^^^^^^^^^^^
arguments.extend(
children
.iter()
.filter_map(|child| self.transform_jsx_child(child, ctx))
.map(Argument::from),
children.iter().filter_map(|child| self.transform_jsx_child_classic(child, ctx)),
);
}

Expand Down Expand Up @@ -883,6 +880,40 @@ impl<'a> JsxImpl<'a, '_> {
}
}

fn transform_jsx_child_automatic(
&mut self,
child: &JSXChild<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> {
// Align spread child behavior with esbuild.
// Instead of Babel throwing `Spread children are not supported in React.`
// `<>{...foo}</>` -> `jsxs(Fragment, { children: [ ...foo ] })`
if let JSXChild::Spread(e) = child {
// SAFETY: `ast.copy` is unsound! We need to fix.
let argument = unsafe { ctx.ast.copy(&e.expression) };
let spread_element = ctx.ast.array_expression_element_spread_element(e.span, argument);
let elements = ctx.ast.vec1(spread_element);
return Some(ctx.ast.expression_array(e.span, elements, None));
}
self.transform_jsx_child(child, ctx)
}

fn transform_jsx_child_classic(
&mut self,
child: &JSXChild<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<Argument<'a>> {
// Align spread child behavior with esbuild.
// Instead of Babel throwing `Spread children are not supported in React.`
// `<>{...foo}</>` -> `React.createElement(React.Fragment, null, ...foo)`
if let JSXChild::Spread(e) = child {
// SAFETY: `ast.copy` is unsound! We need to fix.
let argument = unsafe { ctx.ast.copy(&e.expression) };
return Some(ctx.ast.argument_spread_element(e.span, argument));
}
self.transform_jsx_child(child, ctx).map(Argument::from)
}

fn transform_jsx_child(
&mut self,
child: &JSXChild<'a>,
Expand All @@ -903,10 +934,7 @@ impl<'a> JsxImpl<'a, '_> {
JSXChild::Fragment(e) => {
Some(self.transform_jsx(&JSXElementOrFragment::Fragment(e), ctx))
}
JSXChild::Spread(e) => {
self.ctx.error(diagnostics::spread_children_are_not_supported(e.span));
None
}
JSXChild::Spread(_) => unreachable!(),
}
}

Expand Down
15 changes: 12 additions & 3 deletions tasks/coverage/snapshots/semantic_typescript.snap
Original file line number Diff line number Diff line change
Expand Up @@ -39578,11 +39578,20 @@ after transform: ScopeId(0): [ScopeId(1), ScopeId(2)]
rebuilt : ScopeId(0): [ScopeId(1)]

tasks/coverage/typescript/tests/cases/conformance/jsx/tsxSpreadChildren.tsx
semantic error: Spread children are not supported in React.
semantic error: Bindings mismatch:
after transform: ScopeId(0): ["JSX", "React", "Todo", "TodoList", "_jsxFileName", "_objectSpread", "_reactJsxRuntime", "x"]
rebuilt : ScopeId(0): ["Todo", "TodoList", "_jsxFileName", "_objectSpread", "_reactJsxRuntime", "x"]
Scope children mismatch:
after transform: ScopeId(0): [ScopeId(1), ScopeId(4), ScopeId(5), ScopeId(6), ScopeId(7)]
rebuilt : ScopeId(0): [ScopeId(1), ScopeId(2)]

tasks/coverage/typescript/tests/cases/conformance/jsx/tsxSpreadChildrenInvalidType.tsx
semantic error: Spread children are not supported in React.
Spread children are not supported in React.
semantic error: Bindings mismatch:
after transform: ScopeId(0): ["JSX", "React", "Todo", "TodoList", "TodoListNoError", "_jsxFileName", "_objectSpread", "_reactJsxRuntime", "x"]
rebuilt : ScopeId(0): ["Todo", "TodoList", "TodoListNoError", "_jsxFileName", "_objectSpread", "_reactJsxRuntime", "x"]
Scope children mismatch:
after transform: ScopeId(0): [ScopeId(1), ScopeId(4), ScopeId(5), ScopeId(6), ScopeId(7), ScopeId(8)]
rebuilt : ScopeId(0): [ScopeId(1), ScopeId(2), ScopeId(3)]

tasks/coverage/typescript/tests/cases/conformance/jsx/tsxStatelessFunctionComponentOverload2.tsx
semantic error: Scope children mismatch:
Expand Down
4 changes: 2 additions & 2 deletions tasks/transform_conformance/snapshots/oxc.snap.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
commit: acbc09a8

Passed: 133/155
Passed: 135/157

# All Passed:
* babel-plugin-transform-class-static-block
Expand Down Expand Up @@ -308,7 +308,7 @@ rebuilt : SymbolId(2): []
x Output mismatch


# babel-plugin-transform-react-jsx (35/38)
# babel-plugin-transform-react-jsx (37/40)
* refresh/does-not-transform-it-because-it-is-not-used-in-the-AST/input.jsx
x Output mismatch

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<>{...foo}</>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"plugins": [
[
"transform-react-jsx",
{
"runtime": "automatic"
}
]
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var _reactJsxRuntime = require("react/jsx-runtime");
_reactJsxRuntime.jsx(_reactJsxRuntime.Fragment, { children: [...foo] });
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<>{...foo}</>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"plugins": [
[
"transform-react-jsx",
{
"runtime": "classic"
}
]
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
React.createElement(React.Fragment, null, ...foo);

0 comments on commit 88e510d

Please sign in to comment.