diff --git a/crates/oxc_formatter/src/print/jsx/element.rs b/crates/oxc_formatter/src/print/jsx/element.rs index 1f7295b4e3524..dcb6f2a8a7789 100644 --- a/crates/oxc_formatter/src/print/jsx/element.rs +++ b/crates/oxc_formatter/src/print/jsx/element.rs @@ -229,15 +229,18 @@ impl<'a> Format<'a> for AnyJsxTagWithChildren<'a, '_> { /// This is a very special situation where we're returning a JsxElement /// from an arrow function that's passed as an argument to a function, -/// which is itself inside a JSX expression child. +/// which is itself inside a JSX expression container. /// -/// If you're wondering why this is the only other case, it's because -/// Prettier defines it to be that way. +/// This matches Prettier's `shouldBreakJsxElement` behavior. /// /// ```jsx -/// let bar =
-/// {foo(() =>
the quick brown fox jumps over the lazy dog
)} -///
; +/// // As JSX child: +/// let bar =
+/// {foo(() =>
the quick brown fox jumps over the lazy dog
)} +///
; +/// +/// // As JSX attribute: +/// ({name}))} />; /// ``` pub fn should_expand(mut parent: &AstNodes<'_>) -> bool { if let AstNodes::ExpressionStatement(stmt) = parent { @@ -245,7 +248,7 @@ pub fn should_expand(mut parent: &AstNodes<'_>) -> bool { // to determine if it should expand. parent = stmt.grand_parent(); } - let maybe_jsx_expression_child = match parent { + let maybe_jsx_expression_container = match parent { AstNodes::ArrowFunctionExpression(arrow) if arrow.expression => match arrow.parent() { AstNodes::CallExpression(call) => call.parent(), _ => return false, @@ -253,9 +256,8 @@ pub fn should_expand(mut parent: &AstNodes<'_>) -> bool { _ => return false, }; matches!( - maybe_jsx_expression_child.without_chain_expression(), - AstNodes::JSXExpressionContainer(container) - if matches!(container.parent(), AstNodes::JSXElement(_) | AstNodes::JSXFragment(_)) + maybe_jsx_expression_container.without_chain_expression(), + AstNodes::JSXExpressionContainer(_) ) } diff --git a/crates/oxc_formatter/tests/fixtures/js/jsx/arrow-callback.jsx b/crates/oxc_formatter/tests/fixtures/js/jsx/arrow-callback.jsx new file mode 100644 index 0000000000000..fda4a1fbd4e00 --- /dev/null +++ b/crates/oxc_formatter/tests/fixtures/js/jsx/arrow-callback.jsx @@ -0,0 +1,53 @@ +// Arrow functions returning JSX inside JSX expression containers +// The multiline structure should be preserved when the arrow body is JSX + +// https://github.com/oxc-project/oxc/issues/18738 +// Arrow returning JSX in attribute callback +[ + { + baz: () => { + return ( + {name} + ))} + > + Foo + + } + } +]; + +// Simpler attribute case + ( + {item.name} + ))} +/>; + +// JSX as child +
+ {items.map(item => ( + {item.name} + ))} +
; + +// Multiple attributes with callback returning JSX + ( + {col.label} + ))} + rows={rows.map(row => ( + {row.data} + ))} +/>; + +// Nested JSX in attribute + ( + ( + {sub.name} + ))} + /> + ))} +/>; diff --git a/crates/oxc_formatter/tests/fixtures/js/jsx/arrow-callback.jsx.snap b/crates/oxc_formatter/tests/fixtures/js/jsx/arrow-callback.jsx.snap new file mode 100644 index 0000000000000..36552d9320d26 --- /dev/null +++ b/crates/oxc_formatter/tests/fixtures/js/jsx/arrow-callback.jsx.snap @@ -0,0 +1,178 @@ +--- +source: crates/oxc_formatter/tests/fixtures/mod.rs +--- +==================== Input ==================== +// Arrow functions returning JSX inside JSX expression containers +// The multiline structure should be preserved when the arrow body is JSX + +// https://github.com/oxc-project/oxc/issues/18738 +// Arrow returning JSX in attribute callback +[ + { + baz: () => { + return ( + {name} + ))} + > + Foo + + } + } +]; + +// Simpler attribute case + ( + {item.name} + ))} +/>; + +// JSX as child +
+ {items.map(item => ( + {item.name} + ))} +
; + +// Multiple attributes with callback returning JSX + ( + {col.label} + ))} + rows={rows.map(row => ( + {row.data} + ))} +/>; + +// Nested JSX in attribute + ( + ( + {sub.name} + ))} + /> + ))} +/>; + +==================== Output ==================== +------------------ +{ printWidth: 80 } +------------------ +// Arrow functions returning JSX inside JSX expression containers +// The multiline structure should be preserved when the arrow body is JSX + +// https://github.com/oxc-project/oxc/issues/18738 +// Arrow returning JSX in attribute callback +[ + { + baz: () => { + return ( + ( + {name} + ))} + > + Foo + + ); + }, + }, +]; + +// Simpler attribute case + ( + {item.name} + ))} +/>; + +// JSX as child +
+ {items.map((item) => ( + {item.name} + ))} +
; + +// Multiple attributes with callback returning JSX + ( + {col.label} + ))} + rows={rows.map((row) => ( + {row.data} + ))} +/>; + +// Nested JSX in attribute + ( + ( + {sub.name} + ))} + /> + ))} +/>; + +------------------- +{ printWidth: 100 } +------------------- +// Arrow functions returning JSX inside JSX expression containers +// The multiline structure should be preserved when the arrow body is JSX + +// https://github.com/oxc-project/oxc/issues/18738 +// Arrow returning JSX in attribute callback +[ + { + baz: () => { + return ( + ( + {name} + ))} + > + Foo + + ); + }, + }, +]; + +// Simpler attribute case + ( + {item.name} + ))} +/>; + +// JSX as child +
+ {items.map((item) => ( + {item.name} + ))} +
; + +// Multiple attributes with callback returning JSX + ( + {col.label} + ))} + rows={rows.map((row) => ( + {row.data} + ))} +/>; + +// Nested JSX in attribute + ( + ( + {sub.name} + ))} + /> + ))} +/>; + +===================== End =====================