diff --git a/crates/oxc_transformer/examples/test.tsx b/crates/oxc_transformer/examples/test.tsx new file mode 100644 index 00000000000000..6c06d5d174b6ea --- /dev/null +++ b/crates/oxc_transformer/examples/test.tsx @@ -0,0 +1,5 @@ +function App() { + return ( + + ) +} \ No newline at end of file diff --git a/crates/oxc_transformer/examples/transformer.rs b/crates/oxc_transformer/examples/transformer.rs index d3531898e646d4..13f01074e9632b 100644 --- a/crates/oxc_transformer/examples/transformer.rs +++ b/crates/oxc_transformer/examples/transformer.rs @@ -49,6 +49,7 @@ fn main() { target: TransformTarget::ES5, react_jsx: Some(ReactJsxOptions { runtime: Some(ReactJsxRuntimeOption::Valid(ReactJsxRuntime::Classic)), + development: Some(true), ..ReactJsxOptions::default() }), ..TransformOptions::default() diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 37123a934de024..496f4595e34dbb 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -52,7 +52,7 @@ use crate::{ es2021::{LogicalAssignmentOperators, NumericSeparator}, es2022::ClassStaticBlock, es3::PropertyLiteral, - react_jsx::ReactJsx, + react_jsx::{JsxSelf, ReactJsx}, regexp::RegexpFlags, typescript::TypeScript, utils::CreateVars, @@ -72,6 +72,7 @@ pub struct Transformer<'a> { decorators: Option>, #[allow(unused)] typescript: Option>, + jsx_self: Option>, react_jsx: Option>, regexp_flags: Option>, // es2022 @@ -140,6 +141,7 @@ impl<'a> Transformer<'a> { es2015_new_target: NewTarget::new(ctx.clone()), // other es3_property_literal: PropertyLiteral::new(ctx.clone()), + jsx_self: JsxSelf::new(ctx.clone()), react_jsx: ReactJsx::new(ctx.clone()), // original context ctx, @@ -202,6 +204,10 @@ impl<'a> VisitMut<'a> for Transformer<'a> { self.decorators.as_mut().map(|t| t.transform_declaration(decl)); } + fn visit_jsx_opening_element(&mut self, elem: &mut JSXOpeningElement<'a>) { + self.jsx_self.as_mut().map(|t| t.transform_jsx_opening_element(elem)); + } + fn visit_expression(&mut self, expr: &mut Expression<'a>) { // self.typescript.as_mut().map(|t| t.transform_expression(expr)); self.react_jsx.as_mut().map(|t| t.transform_expression(expr)); diff --git a/crates/oxc_transformer/src/options.rs b/crates/oxc_transformer/src/options.rs index 1ce80b3625cf17..1d742f2713a17b 100644 --- a/crates/oxc_transformer/src/options.rs +++ b/crates/oxc_transformer/src/options.rs @@ -10,6 +10,8 @@ pub struct TransformOptions { pub target: TransformTarget, pub assumptions: CompilerAssumptions, + pub jsx_self: Option, + pub react_jsx: Option, pub typescript: Option, diff --git a/crates/oxc_transformer/src/react_jsx/jsx_self.rs b/crates/oxc_transformer/src/react_jsx/jsx_self.rs new file mode 100644 index 00000000000000..d89e40a10c159b --- /dev/null +++ b/crates/oxc_transformer/src/react_jsx/jsx_self.rs @@ -0,0 +1,35 @@ +use crate::{context::TransformerCtx, ReactJsxOptions}; +use oxc_ast::ast::*; +use oxc_span::SPAN; + +/// @babel/plugin-transform-react-jsx-self +/// +/// References: +/// * +/// * +/// +pub struct JsxSelf<'a> { + ctx: TransformerCtx<'a>, + options: ReactJsxOptions, +} + +impl<'a> JsxSelf<'a> { + pub fn new(ctx: TransformerCtx<'a>) -> Option { + let jsx_options = ctx.options.react_jsx.clone()?; + Some(Self { ctx, options: jsx_options }) + } + pub fn transform_jsx_opening_element(&mut self, elem: &mut JSXOpeningElement<'a>) { + if self.options.development != Some(true) { + return; + } + + elem.attributes.push(JSXAttributeItem::Attribute(self.ctx.ast.jsx_attribute( + SPAN, + JSXAttributeName::Identifier(self.ctx.ast.jsx_identifier(SPAN, "__self".into())), + Some(JSXAttributeValue::ExpressionContainer(self.ctx.ast.jsx_expression_container( + SPAN, + JSXExpression::Expression(self.ctx.ast.this_expression(SPAN)), + ))), + ))); + } +} diff --git a/crates/oxc_transformer/src/react_jsx/mod.rs b/crates/oxc_transformer/src/react_jsx/mod.rs index a1bbac5b369000..674c0f40cc79b5 100644 --- a/crates/oxc_transformer/src/react_jsx/mod.rs +++ b/crates/oxc_transformer/src/react_jsx/mod.rs @@ -1,3 +1,4 @@ +mod jsx_self; mod options; use oxc_allocator::Vec; @@ -12,6 +13,7 @@ use oxc_syntax::{ xml_entities::XML_ENTITIES, }; +pub use self::jsx_self::JsxSelf; pub use self::options::{ReactJsxOptions, ReactJsxRuntime, ReactJsxRuntimeOption}; use crate::context::TransformerCtx; diff --git a/crates/oxc_transformer/src/react_jsx/options.rs b/crates/oxc_transformer/src/react_jsx/options.rs index 9ac934efe8b9c7..ecee78c280775b 100644 --- a/crates/oxc_transformer/src/react_jsx/options.rs +++ b/crates/oxc_transformer/src/react_jsx/options.rs @@ -34,6 +34,10 @@ pub struct ReactJsxOptions { /// Use `Some` instead of `bool` because we want to know if user set this field explicitly, /// which used for creating warning, pub use_spread: Option, + + /// enable development transform for jsx transform + #[serde(default)] + pub development: Option, } fn default_throw_if_namespace() -> bool { @@ -62,6 +66,7 @@ impl Default for ReactJsxOptions { pragma_frag: default_pragma_frag(), use_built_ins: None, use_spread: None, + development: None, } } } diff --git a/tasks/transform_conformance/src/test_case.rs b/tasks/transform_conformance/src/test_case.rs index 23772a5446466e..5e246d95fa893f 100644 --- a/tasks/transform_conformance/src/test_case.rs +++ b/tasks/transform_conformance/src/test_case.rs @@ -92,6 +92,9 @@ pub trait TestCase { decorators: options .get_plugin("proposal-decorators") .map(get_options::), + jsx_self: options + .get_plugin("transform-react-jsx-self") + .map(get_options::), react_jsx: options .get_plugin("transform-react-jsx") .map(get_options::),