diff --git a/crates/oxc_transformer/src/react_jsx/mod.rs b/crates/oxc_transformer/src/react_jsx/mod.rs index 205577014..1697ff1f5 100644 --- a/crates/oxc_transformer/src/react_jsx/mod.rs +++ b/crates/oxc_transformer/src/react_jsx/mod.rs @@ -19,6 +19,7 @@ pub struct ReactJsx<'a> { imports: Vec<'a, Statement<'a>>, import_jsx: bool, + import_jsxs: bool, import_fragment: bool, import_create_element: bool, } @@ -69,6 +70,7 @@ impl<'a> ReactJsx<'a> { options, imports, import_jsx: false, + import_jsxs: false, import_fragment: false, import_create_element: false, } @@ -98,6 +100,7 @@ impl<'a> ReactJsx<'a> { &mut self, e: &JSXElementOrFragment<'a, 'b>, has_key_after_props_spread: bool, + need_jsxs: bool, ) { if self.options.runtime.is_classic() { return; @@ -106,6 +109,7 @@ impl<'a> ReactJsx<'a> { JSXElementOrFragment::Element(_) if has_key_after_props_spread => { self.add_import_create_element(); } + JSXElementOrFragment::Element(_) if need_jsxs => self.add_import_jsxs(), JSXElementOrFragment::Element(_) => self.add_import_jsx(), JSXElementOrFragment::Fragment(_) => self.add_import_fragment(), } @@ -118,6 +122,13 @@ impl<'a> ReactJsx<'a> { } } + fn add_import_jsxs(&mut self) { + if !self.import_jsxs { + self.import_jsxs = true; + self.add_import_statement("jsxs", "_jsxs", "react/jsx-runtime"); + } + } + fn add_import_fragment(&mut self) { if !self.import_fragment { self.import_fragment = true; @@ -158,7 +169,6 @@ impl<'a> ReactJsx<'a> { let is_classic = self.options.runtime.is_classic(); let is_automatic = self.options.runtime.is_automatic(); let has_key_after_props_spread = e.has_key_after_props_spread(); - let callee = self.get_create_element(has_key_after_props_spread); let children = e.children(); // TODO: compute the correct capacity for both runtimes @@ -212,6 +222,7 @@ impl<'a> ReactJsx<'a> { arguments.push(Argument::Expression(null_expr)); } + let mut need_jsxs = false; if is_automatic && !children.is_empty() { let key = self.ast.property_key_identifier(IdentifierName::new(SPAN, "children".into())); @@ -224,6 +235,7 @@ impl<'a> ReactJsx<'a> { elements.push(ArrayExpressionElement::Expression(e)); } } + need_jsxs = true; Some(self.ast.array_expression(SPAN, elements, None)) }; if let Some(value) = value { @@ -259,7 +271,8 @@ impl<'a> ReactJsx<'a> { ); } - self.add_import(e, has_key_after_props_spread); + let callee = self.get_create_element(has_key_after_props_spread, need_jsxs); + self.add_import(e, has_key_after_props_spread, need_jsxs); self.ast.call_expression(SPAN, callee, arguments, false, None) } @@ -269,7 +282,11 @@ impl<'a> ReactJsx<'a> { self.ast.identifier_reference_expression(ident) } - fn get_create_element(&mut self, has_key_after_props_spread: bool) -> Expression<'a> { + fn get_create_element( + &mut self, + has_key_after_props_spread: bool, + jsxs: bool, + ) -> Expression<'a> { match self.options.runtime { ReactJsxRuntime::Classic => { let object = self.get_react_references(); @@ -277,7 +294,13 @@ impl<'a> ReactJsx<'a> { self.ast.static_member_expression(SPAN, object, property, false) } ReactJsxRuntime::Automatic => { - let name = if has_key_after_props_spread { "_createElement" } else { "_jsx" }; + let name = if has_key_after_props_spread { + "_createElement" + } else if jsxs { + "_jsxs" + } else { + "_jsx" + }; let ident = IdentifierReference::new(SPAN, name.into()); self.ast.identifier_reference_expression(ident) } diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index 384693520..f7e756a80 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,4 +1,4 @@ -Passed: 214/1083 +Passed: 219/1083 # All Passed: * babel-plugin-transform-numeric-separator @@ -804,7 +804,7 @@ Passed: 214/1083 * regression/11061/input.mjs * variable-declaration/non-null-in-optional-chain/input.ts -# babel-plugin-transform-react-jsx (65/172) +# babel-plugin-transform-react-jsx (70/172) * autoImport/after-polyfills/input.mjs * autoImport/after-polyfills-2/input.mjs * autoImport/after-polyfills-compiled-to-cjs/input.mjs @@ -869,13 +869,10 @@ Passed: 214/1083 * react/wraps-props-in-react-spread-for-middle-spread-attributes-babel-7/input.js * react-automatic/.should-properly-handle-comments-adjacent-to-children/input.js * react-automatic/arrow-functions/input.js -* react-automatic/concatenates-adjacent-string-literals/input.js * react-automatic/does-not-add-source-self-automatic/input.mjs -* react-automatic/dont-coerce-expression-containers/input.js * react-automatic/handle-fragments-with-key/input.js * react-automatic/handle-nonstatic-children/input.js * react-automatic/handle-spread-with-proto/input.js -* react-automatic/handle-static-children/input.js * react-automatic/optimisation.react.constant-elements/input.js * react-automatic/pragma-works-with-no-space-at-the-end/input.js * react-automatic/should-add-quotes-es3/input.js @@ -889,11 +886,9 @@ Passed: 214/1083 * react-automatic/should-escape-xhtml-jsxtext/input.js * react-automatic/should-escape-xhtml-jsxtext-babel-7/input.js * react-automatic/should-handle-attributed-elements/input.js -* react-automatic/should-have-correct-comma-in-nested-children/input.js * react-automatic/should-not-strip-nbsp-even-coupled-with-other-whitespace/input.js * react-automatic/should-not-strip-tags-with-a-single-child-of-nbsp/input.js * react-automatic/should-properly-handle-comments-between-props/input.js -* react-automatic/should-properly-handle-keys/input.js * react-automatic/should-throw-error-namespaces-if-not-flag/input.js * react-automatic/should-throw-when-filter-is-specified/input.js * react-automatic/should-warn-when-pragma-or-pragmaFrag-is-set/input.js