From 4ed0813f9b3d42564f8599dfdf406b50d7e4f9ea Mon Sep 17 00:00:00 2001 From: Dunqing Date: Tue, 7 Nov 2023 18:26:54 +0800 Subject: [PATCH] feat(transformer/react-jsx): support `pragma` option (#1180) --- crates/oxc_transformer/src/react_jsx/mod.rs | 29 +++++++++++++++++-- .../oxc_transformer/src/react_jsx/options.rs | 11 +++++++ tasks/transform_conformance/babel.snap.md | 10 ++----- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/crates/oxc_transformer/src/react_jsx/mod.rs b/crates/oxc_transformer/src/react_jsx/mod.rs index 6b5d39260..69222c937 100644 --- a/crates/oxc_transformer/src/react_jsx/mod.rs +++ b/crates/oxc_transformer/src/react_jsx/mod.rs @@ -315,6 +315,25 @@ impl<'a> ReactJsx<'a> { self.ast.identifier_reference_expression(ident) } + /// Get the callee from `pragma` and `pragmaFrag` + fn get_call_expression_callee(&self, literal_callee: &str) -> Expression<'a> { + let mut callee = literal_callee.split('.'); + let member = callee.next().unwrap(); + let property = callee.next(); + property.map_or_else( + || { + let ident = IdentifierReference::new(SPAN, member.into()); + self.ast.identifier_reference_expression(ident) + }, + |property_name| { + let property = IdentifierName::new(SPAN, property_name.into()); + let ident = IdentifierReference::new(SPAN, member.into()); + let object = self.ast.identifier_reference_expression(ident); + self.ast.static_member_expression(SPAN, object, property, false) + }, + ) + } + fn get_create_element( &mut self, has_key_after_props_spread: bool, @@ -322,9 +341,13 @@ impl<'a> ReactJsx<'a> { ) -> Expression<'a> { match self.options.runtime { ReactJsxRuntime::Classic => { - let object = self.get_react_references(); - let property = IdentifierName::new(SPAN, "createElement".into()); - self.ast.static_member_expression(SPAN, object, property, false) + if self.options.pragma == "React.createElement" { + let object = self.get_react_references(); + let property = IdentifierName::new(SPAN, "createElement".into()); + return self.ast.static_member_expression(SPAN, object, property, false); + } + + self.get_call_expression_callee(self.options.pragma.as_ref()) } ReactJsxRuntime::Automatic => { let name = if has_key_after_props_spread { diff --git a/crates/oxc_transformer/src/react_jsx/options.rs b/crates/oxc_transformer/src/react_jsx/options.rs index c94a12dd4..c2cde462c 100644 --- a/crates/oxc_transformer/src/react_jsx/options.rs +++ b/crates/oxc_transformer/src/react_jsx/options.rs @@ -14,12 +14,23 @@ pub struct ReactJsxOptions { /// Replaces the import source when importing functions. default to `react` #[serde(default = "default_import_source")] pub import_source: Cow<'static, str>, + /// Replace the function used when compiling JSX expressions. + /// It should be a qualified name (e.g. React.createElement) or an identifier (e.g. createElement). + /// default to `React.createElement` + /// + /// Note that the @jsx React.DOM pragma has been deprecated as of React v0.12 + #[serde(default = "default_pragma")] + pub pragma: Cow<'static, str>, } fn default_import_source() -> Cow<'static, str> { Cow::Borrowed("react") } +fn default_pragma() -> Cow<'static, str> { + Cow::Borrowed("React.createElement") +} + #[derive(Debug, Default, Clone, Copy, Deserialize)] #[serde(rename_all = "lowercase")] pub enum ReactJsxRuntime { diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index 6a8b88130..7a6716b9f 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,4 +1,4 @@ -Passed: 258/1113 +Passed: 262/1113 # All Passed: * babel-plugin-transform-numeric-separator @@ -825,7 +825,7 @@ Passed: 258/1113 * regression/11061/input.mjs * variable-declaration/non-null-in-optional-chain/input.ts -# babel-plugin-transform-react-jsx (96/170) +# babel-plugin-transform-react-jsx (100/170) * autoImport/after-polyfills-compiled-to-cjs/input.mjs * autoImport/after-polyfills-script-not-supported/input.js * autoImport/auto-import-react-source-type-module/input.js @@ -836,17 +836,14 @@ Passed: 258/1113 * pure/false-pragma-comment-automatic-runtime/input.js * pure/false-pragma-comment-classic-runtime/input.js * pure/false-pragma-option-automatic-runtime/input.js -* pure/false-pragma-option-classic-runtime/input.js * pure/true-default-pragma-automatic-runtime/input.js * pure/true-pragma-comment-automatic-runtime/input.js * pure/true-pragma-comment-classic-runtime/input.js * pure/true-pragma-option-automatic-runtime/input.js -* pure/true-pragma-option-classic-runtime/input.js * pure/unset-default-pragma-automatic-runtime/input.js * pure/unset-pragma-comment-automatic-runtime/input.js * pure/unset-pragma-comment-classic-runtime/input.js * pure/unset-pragma-option-automatic-runtime/input.js -* pure/unset-pragma-option-classic-runtime/input.js * react/adds-appropriate-newlines-when-using-spread-attribute-babel-7/input.js * react/arrow-functions/input.js * react/assignment-babel-7/input.js @@ -855,7 +852,6 @@ Passed: 258/1113 * react/handle-spread-with-proto-babel-7/input.js * react/honor-custom-jsx-comment/input.js * react/honor-custom-jsx-comment-if-jsx-pragma-option-set/input.js -* react/honor-custom-jsx-pragma-option/input.js * react/optimisation.react.constant-elements/input.js * react/pragma-works-with-no-space-at-the-end/input.js * react/should-add-quotes-es3/input.js @@ -865,7 +861,6 @@ Passed: 258/1113 * react/should-disallow-spread-children/input.js * react/should-disallow-valueless-key/input.js * react/should-disallow-xml-namespacing/input.js -* react/should-support-xml-namespaces-if-flag/input.js * react/should-throw-error-namespaces-if-not-flag/input.js * react/should-warn-when-importSource-is-set/input.js * react/should-warn-when-importSource-pragma-is-set/input.js @@ -896,6 +891,7 @@ Passed: 258/1113 * removed-options/invalid-use-spread-false/input.js * removed-options/invalid-use-spread-true/input.js * runtime/defaults-to-automatic/input.js +* runtime/defaults-to-classis-babel-7/input.js * runtime/invalid-runtime/input.js * runtime/runtime-automatic/input.js * spread-transform/transform-to-babel-extend/input.js