diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 43f5e8a36..0852dc153 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -94,7 +94,7 @@ impl<'a> Transformer<'a> { es2015_shorthand_properties: ShorthandProperties::new(Rc::clone(&ast), &options), es2015_template_literals: TemplateLiterals::new(Rc::clone(&ast), &options), es3_property_literal: PropertyLiteral::new(Rc::clone(&ast ), &options), - react_jsx: options.react_jsx.map(|options| ReactJsx::new(Rc::clone(&ast), ctx.clone(), options)), + react_jsx: ReactJsx::new(Rc::clone(&ast), ctx.clone(), options) } } diff --git a/crates/oxc_transformer/src/options.rs b/crates/oxc_transformer/src/options.rs index b08f5f567..cd7b28b74 100644 --- a/crates/oxc_transformer/src/options.rs +++ b/crates/oxc_transformer/src/options.rs @@ -24,6 +24,7 @@ pub struct TransformOptions { pub sticky_regex: bool, pub template_literals: bool, pub property_literals: bool, + pub babel_8_breaking: Option, } /// See diff --git a/crates/oxc_transformer/src/react_jsx/mod.rs b/crates/oxc_transformer/src/react_jsx/mod.rs index 23ba5422b..45178c4be 100644 --- a/crates/oxc_transformer/src/react_jsx/mod.rs +++ b/crates/oxc_transformer/src/react_jsx/mod.rs @@ -15,7 +15,7 @@ use oxc_syntax::{ }; pub use self::options::{ReactJsxOptions, ReactJsxRuntime}; -use crate::context::TransformerCtx; +use crate::{context::TransformerCtx, TransformOptions}; #[derive(Debug, Error, Diagnostic)] #[error("pragma and pragmaFrag cannot be set when runtime is automatic.")] @@ -59,6 +59,7 @@ pub struct ReactJsx<'a> { import_create_element: bool, require_jsx_runtime: bool, jsx_runtime_importer: Atom, + pub babel_8_breaking: Option, } enum JSXElementOrFragment<'a, 'b> { @@ -100,21 +101,35 @@ impl<'a, 'b> JSXElementOrFragment<'a, 'b> { } impl<'a> ReactJsx<'a> { - pub fn new(ast: Rc>, ctx: TransformerCtx<'a>, options: ReactJsxOptions) -> Self { + pub fn new( + ast: Rc>, + mut ctx: TransformerCtx<'a>, + options: TransformOptions, + ) -> Option { let imports = ast.new_vec(); - let options = options.with_comments(&ctx.semantic()); + let jsx_options = options.react_jsx?.with_comments(&ctx.semantic()); + if options.babel_8_breaking == Some(true) { + if jsx_options.use_built_ins.is_some() { + ctx.error(miette::Error::msg("@babel/plugin-transform-react-jsx: Since \"useBuiltIns\" is removed in Babel 8, you can remove it from the config.")); + return None; + } + + if jsx_options.use_spread.is_some() { + ctx.error(miette::Error::msg("@babel/plugin-transform-react-jsx: Since Babel 8, an inline object with spread elements is always used, and the \"useSpread\" option is no longer available. Please remove it from your config.")); + return None; + } + } let jsx_runtime_importer = - if options.import_source == "react" || options.runtime.is_classic() { + if jsx_options.import_source == "react" || jsx_options.runtime.is_classic() { Atom::new_inline("react/jsx-runtime") } else { - Atom::from(format!("{}/jsx-runtime", options.import_source)) + Atom::from(format!("{}/jsx-runtime", jsx_options.import_source)) }; - - Self { + Some(Self { ast, ctx, - options, + options: jsx_options, imports, jsx_runtime_importer, require_jsx_runtime: false, @@ -122,7 +137,8 @@ impl<'a> ReactJsx<'a> { import_jsxs: false, import_fragment: false, import_create_element: false, - } + babel_8_breaking: options.babel_8_breaking, + }) } pub fn transform_expression(&mut self, expr: &mut Expression<'a>) { diff --git a/crates/oxc_transformer/src/react_jsx/options.rs b/crates/oxc_transformer/src/react_jsx/options.rs index 20262d456..a2983ace0 100644 --- a/crates/oxc_transformer/src/react_jsx/options.rs +++ b/crates/oxc_transformer/src/react_jsx/options.rs @@ -7,6 +7,7 @@ use serde::Deserialize; #[serde(rename_all = "camelCase")] pub struct ReactJsxOptions { /// Decides which runtime to use. + #[serde(default)] pub runtime: ReactJsxRuntime, /// Toggles whether or not to throw an error if an XML namespaced tag name is used. e.g. `` /// Though the JSX spec allows this, it is disabled by default since React's JSX does not currently have support for it. @@ -25,6 +26,15 @@ pub struct ReactJsxOptions { /// Replace the component used when compiling JSX fragments. It should be a valid JSX tag name. default to `React.Fragment` #[serde(default = "default_pragma_frag")] pub pragma_frag: Cow<'static, str>, + + /// When spreading props, use Object.assign directly instead of Babel's extend helper. + /// Use `Some` instead of `bool` because we want to know if user set this field explicitly, + /// which used for creating warning, https://github.com/oxc-project/oxc/blob/c3e2098c04d8916cb812bdd16d2026bb430ac25f/crates/oxc_transformer/src/react_jsx/mod.rs#L111-L114 + pub use_built_ins: Option, + /// When spreading props, use inline object with spread elements directly instead of Babel's extend helper or Object.assign. + /// Use `Some` instead of `bool` because we want to know if user set this field explicitly, + /// which used for creating warning, https://github.com/oxc-project/oxc/blob/c3e2098c04d8916cb812bdd16d2026bb430ac25f/crates/oxc_transformer/src/react_jsx/mod.rs#L111-L114 + pub use_spread: Option, } fn default_throw_if_namespace() -> bool { @@ -51,6 +61,8 @@ impl Default for ReactJsxOptions { import_source: default_import_source(), pragma: default_pragma(), pragma_frag: default_pragma_frag(), + use_built_ins: None, + use_spread: None, } } } diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index bac4c71b6..a8794e880 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,4 +1,4 @@ -Passed: 265/1081 +Passed: 269/1081 # All Passed: * babel-plugin-transform-numeric-separator @@ -844,7 +844,7 @@ Passed: 265/1081 * regression/11061/input.mjs * variable-declaration/non-null-in-optional-chain/input.ts -# babel-plugin-transform-react-jsx (139/156) +# babel-plugin-transform-react-jsx (143/156) * autoImport/after-polyfills-compiled-to-cjs/input.mjs * autoImport/complicated-scope-module/input.js * react/arrow-functions/input.js @@ -855,10 +855,6 @@ Passed: 265/1081 * react-automatic/should-throw-when-filter-is-specified/input.js * regression/issue-12478-automatic/input.js * regression/issue-12478-classic/input.js -* removed-options/invalid-use-builtins-false/input.js -* removed-options/invalid-use-builtins-true/input.js -* removed-options/invalid-use-spread-false/input.js -* removed-options/invalid-use-spread-true/input.js * runtime/invalid-runtime/input.js * spread-transform/transform-to-babel-extend/input.js * spread-transform/transform-to-object-assign/input.js diff --git a/tasks/transform_conformance/src/test_case.rs b/tasks/transform_conformance/src/test_case.rs index 56c2e3f06..ca0e3cb1d 100644 --- a/tasks/transform_conformance/src/test_case.rs +++ b/tasks/transform_conformance/src/test_case.rs @@ -88,6 +88,7 @@ pub trait TestCase { let options = self.options(); TransformOptions { target: TransformTarget::ESNext, + babel_8_breaking: options.babel_8_breaking, react_jsx: options .get_plugin("transform-react-jsx") .map(get_options::),