Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ impl<'a> Traverse<'a> for Transformer<'a> {
self.x0_typescript.transform_export_named_declaration(decl);
}

fn enter_expression(&mut self, expr: &mut Expression<'a>, _ctx: &TraverseCtx<'a>) {
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &TraverseCtx<'a>) {
self.x0_typescript.transform_expression(expr);
self.x1_react.transform_expression(expr);
self.x1_react.transform_expression(expr, ctx);
self.x3_es2015.transform_expression(expr);
}

Expand Down
61 changes: 44 additions & 17 deletions crates/oxc_transformer/src/react/jsx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use oxc_syntax::{
identifier::{is_irregular_whitespace, is_line_terminator},
xml_entities::XML_ENTITIES,
};
use oxc_traverse::TraverseCtx;

use crate::{context::Ctx, helpers::module_imports::NamedImport};

Expand Down Expand Up @@ -82,12 +83,20 @@ impl<'a> ReactJsx<'a> {
self.add_runtime_imports(program);
}

pub fn transform_jsx_element(&mut self, e: &JSXElement<'a>) -> Expression<'a> {
self.transform_jsx(&JSXElementOrFragment::Element(e))
pub fn transform_jsx_element(
&mut self,
e: &JSXElement<'a>,
ctx: &TraverseCtx<'a>,
) -> Expression<'a> {
self.transform_jsx(&JSXElementOrFragment::Element(e), ctx)
}

pub fn transform_jsx_fragment(&mut self, e: &JSXFragment<'a>) -> Expression<'a> {
self.transform_jsx(&JSXElementOrFragment::Fragment(e))
pub fn transform_jsx_fragment(
&mut self,
e: &JSXFragment<'a>,
ctx: &TraverseCtx<'a>,
) -> Expression<'a> {
self.transform_jsx(&JSXElementOrFragment::Fragment(e), ctx)
}

fn is_script(&self) -> bool {
Expand Down Expand Up @@ -307,7 +316,11 @@ impl<'a> ReactJsx<'a> {
/// ### Fragment
/// React.createElement(React.Fragment, null, ...children)
///
fn transform_jsx<'b>(&mut self, e: &JSXElementOrFragment<'a, 'b>) -> Expression<'a> {
fn transform_jsx<'b>(
&mut self,
e: &JSXElementOrFragment<'a, 'b>,
ctx: &TraverseCtx<'a>,
) -> Expression<'a> {
let is_fragment = e.is_fragment();
let has_key_after_props_spread = e.has_key_after_props_spread();
// If has_key_after_props_spread is true, we need to fallback to `createElement` same behavior as classic runtime
Expand Down Expand Up @@ -372,7 +385,7 @@ impl<'a> ReactJsx<'a> {
}

// Add attribute to prop object
self.transform_jsx_attribute_item(&mut properties, attribute);
self.transform_jsx_attribute_item(&mut properties, attribute, ctx);
}
}

Expand All @@ -384,7 +397,7 @@ impl<'a> ReactJsx<'a> {
if is_automatic {
let allocator = self.ast().allocator;
let mut children = Vec::from_iter_in(
children.iter().filter_map(|child| self.transform_jsx_child(child)),
children.iter().filter_map(|child| self.transform_jsx_child(child, ctx)),
allocator,
);
let children_len = children.len();
Expand All @@ -411,7 +424,9 @@ impl<'a> ReactJsx<'a> {

// React.createElement's second argument
if !is_fragment && is_classic {
if self.options.is_jsx_self_plugin_enabled() {
if self.options.is_jsx_self_plugin_enabled()
&& self.jsx_self.can_add_self_attribute(ctx)
{
if let Some(span) = self_attr_span {
self.jsx_self.report_error(span);
} else {
Expand Down Expand Up @@ -447,7 +462,7 @@ impl<'a> ReactJsx<'a> {
if is_automatic {
// key
if key_prop.is_some() {
arguments.push(Argument::from(self.transform_jsx_attribute_value(key_prop)));
arguments.push(Argument::from(self.transform_jsx_attribute_value(key_prop, ctx)));
} else if is_development {
arguments.push(Argument::from(self.ctx.ast.void_0()));
}
Expand Down Expand Up @@ -475,7 +490,9 @@ impl<'a> ReactJsx<'a> {
}

// this
if self.options.is_jsx_self_plugin_enabled() {
if self.options.is_jsx_self_plugin_enabled()
&& self.jsx_self.can_add_self_attribute(ctx)
{
if let Some(span) = self_attr_span {
self.jsx_self.report_error(span);
} else {
Expand All @@ -489,7 +506,7 @@ impl<'a> ReactJsx<'a> {
arguments.extend(
children
.iter()
.filter_map(|child| self.transform_jsx_child(child))
.filter_map(|child| self.transform_jsx_child(child, ctx))
.map(Argument::from),
);
}
Expand Down Expand Up @@ -652,12 +669,13 @@ impl<'a> ReactJsx<'a> {
&mut self,
properties: &mut Vec<'a, ObjectPropertyKind<'a>>,
attribute: &JSXAttributeItem<'a>,
ctx: &TraverseCtx<'a>,
) {
match attribute {
JSXAttributeItem::Attribute(attr) => {
let kind = PropertyKind::Init;
let key = self.get_attribute_name(&attr.name);
let value = self.transform_jsx_attribute_value(attr.value.as_ref());
let value = self.transform_jsx_attribute_value(attr.value.as_ref(), ctx);
let object_property =
self.ast().object_property(SPAN, kind, key, value, None, false, false, false);
let object_property = ObjectPropertyKind::ObjectProperty(object_property);
Expand All @@ -680,6 +698,7 @@ impl<'a> ReactJsx<'a> {
fn transform_jsx_attribute_value(
&mut self,
value: Option<&JSXAttributeValue<'a>>,
ctx: &TraverseCtx<'a>,
) -> Expression<'a> {
match value {
Some(JSXAttributeValue::StringLiteral(s)) => {
Expand All @@ -688,10 +707,10 @@ impl<'a> ReactJsx<'a> {
self.ast().literal_string_expression(literal)
}
Some(JSXAttributeValue::Element(e)) => {
self.transform_jsx(&JSXElementOrFragment::Element(e))
self.transform_jsx(&JSXElementOrFragment::Element(e), ctx)
}
Some(JSXAttributeValue::Fragment(e)) => {
self.transform_jsx(&JSXElementOrFragment::Fragment(e))
self.transform_jsx(&JSXElementOrFragment::Fragment(e), ctx)
}
Some(JSXAttributeValue::ExpressionContainer(c)) => match &c.expression {
e @ match_expression!(JSXExpression) => self.ast().copy(e.to_expression()),
Expand All @@ -703,15 +722,23 @@ impl<'a> ReactJsx<'a> {
}
}

fn transform_jsx_child(&mut self, child: &JSXChild<'a>) -> Option<Expression<'a>> {
fn transform_jsx_child(
&mut self,
child: &JSXChild<'a>,
ctx: &TraverseCtx<'a>,
) -> Option<Expression<'a>> {
match child {
JSXChild::Text(text) => self.transform_jsx_text(text.value.as_str()),
JSXChild::ExpressionContainer(e) => match &e.expression {
e @ match_expression!(JSXExpression) => Some(self.ast().copy(e.to_expression())),
JSXExpression::EmptyExpression(_) => None,
},
JSXChild::Element(e) => Some(self.transform_jsx(&JSXElementOrFragment::Element(e))),
JSXChild::Fragment(e) => Some(self.transform_jsx(&JSXElementOrFragment::Fragment(e))),
JSXChild::Element(e) => {
Some(self.transform_jsx(&JSXElementOrFragment::Element(e), ctx))
}
JSXChild::Fragment(e) => {
Some(self.transform_jsx(&JSXElementOrFragment::Fragment(e), ctx))
}
JSXChild::Spread(e) => {
self.ctx.error(diagnostics::spread_children_are_not_supported(e.span));
None
Expand Down
24 changes: 24 additions & 0 deletions crates/oxc_transformer/src/react/jsx_self/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::rc::Rc;
use oxc_ast::ast::*;
use oxc_diagnostics::OxcDiagnostic;
use oxc_span::{Span, SPAN};
use oxc_traverse::{Ancestor, FinderRet, TraverseCtx};

use crate::context::Ctx;

Expand Down Expand Up @@ -46,6 +47,29 @@ impl<'a> ReactJsxSelf<'a> {
let error = OxcDiagnostic::warning("Duplicate __self prop found.").with_label(span);
self.ctx.error(error);
}

#[allow(clippy::unused_self)]
fn is_inside_constructor(&self, ctx: &TraverseCtx<'a>) -> bool {
ctx.find_scope(|scope| {
if scope.is_block() || scope.is_arrow() {
return FinderRet::Continue;
}
FinderRet::Found(scope.is_constructor())
})
.unwrap_or(false)
}

fn has_no_super_class(ctx: &TraverseCtx<'a>) -> bool {
ctx.find_ancestor(|ancestor| match ancestor {
Ancestor::ClassBody(class) => FinderRet::Found(class.super_class().is_none()),
_ => FinderRet::Continue,
})
.unwrap_or(true)
}

pub fn can_add_self_attribute(&self, ctx: &TraverseCtx<'a>) -> bool {
!self.is_inside_constructor(ctx) || Self::has_no_super_class(ctx)
}
}

impl<'a> ReactJsxSelf<'a> {
Expand Down
32 changes: 8 additions & 24 deletions crates/oxc_transformer/src/react/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ mod utils;
use std::rc::Rc;

use oxc_ast::ast::*;
use oxc_traverse::{Ancestor, FinderRet, TraverseCtx};
use oxc_traverse::TraverseCtx;

use crate::context::Ctx;

Expand Down Expand Up @@ -52,16 +52,16 @@ impl<'a> React<'a> {
}
}

pub fn transform_expression(&mut self, expr: &mut Expression<'a>) {
pub fn transform_expression(&mut self, expr: &mut Expression<'a>, ctx: &TraverseCtx<'a>) {
match expr {
Expression::JSXElement(e) => {
if self.options.is_jsx_plugin_enabled() {
*expr = self.jsx.transform_jsx_element(e);
*expr = self.jsx.transform_jsx_element(e, ctx);
}
}
Expression::JSXFragment(e) => {
if self.options.is_jsx_plugin_enabled() {
*expr = self.jsx.transform_jsx_fragment(e);
*expr = self.jsx.transform_jsx_fragment(e, ctx);
}
}
_ => {}
Expand All @@ -83,26 +83,10 @@ impl<'a> React<'a> {
elem: &mut JSXOpeningElement<'a>,
ctx: &TraverseCtx<'a>,
) {
if self.options.is_jsx_self_plugin_enabled() {
let is_constructor = ctx
.find_scope(|scope| {
if scope.is_block() || scope.is_arrow() {
return FinderRet::Continue;
}
FinderRet::Found(scope.is_constructor())
})
.unwrap_or(false);
if !is_constructor
// no super class
|| ctx
.find_ancestor(|ancestor| match ancestor {
Ancestor::ClassBody(class) => FinderRet::Found(class.super_class().is_none()),
_ => FinderRet::Continue,
})
.unwrap_or(true)
{
self.jsx.jsx_self.transform_jsx_opening_element(elem);
}
if self.options.is_jsx_self_plugin_enabled()
&& self.jsx.jsx_self.can_add_self_attribute(ctx)
{
self.jsx.jsx_self.transform_jsx_opening_element(elem);
}
if self.options.is_jsx_source_plugin_enabled() {
self.jsx.jsx_source.transform_jsx_opening_element(elem);
Expand Down
5 changes: 2 additions & 3 deletions tasks/transform_conformance/babel.snap.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
commit: 4bd1b2c2

Passed: 309/362
Passed: 310/362

# All Passed:
* babel-preset-react
Expand Down Expand Up @@ -66,9 +66,8 @@ Passed: 309/362
* autoImport/complicated-scope-module/input.js
* react-automatic/should-throw-when-filter-is-specified/input.js

# babel-plugin-transform-react-jsx-development (8/12)
# babel-plugin-transform-react-jsx-development (9/12)
* cross-platform/self-inside-arrow/input.mjs
* cross-platform/source-and-self-defined/input.js
* cross-platform/within-derived-classes-constructor/input.js
* cross-platform/within-ts-module-block/input.ts