feat(transformer): do not add self attribute in react/jsx plugin (#3287)

follow-up: https://github.com/oxc-project/oxc/pull/3258.
This commit is contained in:
Dunqing 2024-05-15 17:59:57 +08:00 committed by GitHub
parent 61281711f0
commit b9d69ad665
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 80 additions and 46 deletions

View file

@ -140,9 +140,9 @@ impl<'a> Traverse<'a> for Transformer<'a> {
self.x0_typescript.transform_export_named_declaration(decl); 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.x0_typescript.transform_expression(expr);
self.x1_react.transform_expression(expr); self.x1_react.transform_expression(expr, ctx);
self.x3_es2015.transform_expression(expr); self.x3_es2015.transform_expression(expr);
} }

View file

@ -9,6 +9,7 @@ use oxc_syntax::{
identifier::{is_irregular_whitespace, is_line_terminator}, identifier::{is_irregular_whitespace, is_line_terminator},
xml_entities::XML_ENTITIES, xml_entities::XML_ENTITIES,
}; };
use oxc_traverse::TraverseCtx;
use crate::{context::Ctx, helpers::module_imports::NamedImport}; use crate::{context::Ctx, helpers::module_imports::NamedImport};
@ -82,12 +83,20 @@ impl<'a> ReactJsx<'a> {
self.add_runtime_imports(program); self.add_runtime_imports(program);
} }
pub fn transform_jsx_element(&mut self, e: &JSXElement<'a>) -> Expression<'a> { pub fn transform_jsx_element(
self.transform_jsx(&JSXElementOrFragment::Element(e)) &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> { pub fn transform_jsx_fragment(
self.transform_jsx(&JSXElementOrFragment::Fragment(e)) &mut self,
e: &JSXFragment<'a>,
ctx: &TraverseCtx<'a>,
) -> Expression<'a> {
self.transform_jsx(&JSXElementOrFragment::Fragment(e), ctx)
} }
fn is_script(&self) -> bool { fn is_script(&self) -> bool {
@ -307,7 +316,11 @@ impl<'a> ReactJsx<'a> {
/// ### Fragment /// ### Fragment
/// React.createElement(React.Fragment, null, ...children) /// 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 is_fragment = e.is_fragment();
let has_key_after_props_spread = e.has_key_after_props_spread(); 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 // If has_key_after_props_spread is true, we need to fallback to `createElement` same behavior as classic runtime
@ -372,7 +385,7 @@ impl<'a> ReactJsx<'a> {
} }
// Add attribute to prop object // Add attribute to prop object
self.transform_jsx_attribute_item(&mut properties, attribute); self.transform_jsx_attribute_item(&mut properties, attribute, ctx);
} }
} }
@ -384,7 +397,7 @@ impl<'a> ReactJsx<'a> {
if is_automatic { if is_automatic {
let allocator = self.ast().allocator; let allocator = self.ast().allocator;
let mut children = Vec::from_iter_in( 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, allocator,
); );
let children_len = children.len(); let children_len = children.len();
@ -411,7 +424,9 @@ impl<'a> ReactJsx<'a> {
// React.createElement's second argument // React.createElement's second argument
if !is_fragment && is_classic { 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 { if let Some(span) = self_attr_span {
self.jsx_self.report_error(span); self.jsx_self.report_error(span);
} else { } else {
@ -447,7 +462,7 @@ impl<'a> ReactJsx<'a> {
if is_automatic { if is_automatic {
// key // key
if key_prop.is_some() { 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 { } else if is_development {
arguments.push(Argument::from(self.ctx.ast.void_0())); arguments.push(Argument::from(self.ctx.ast.void_0()));
} }
@ -475,7 +490,9 @@ impl<'a> ReactJsx<'a> {
} }
// this // 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 { if let Some(span) = self_attr_span {
self.jsx_self.report_error(span); self.jsx_self.report_error(span);
} else { } else {
@ -489,7 +506,7 @@ impl<'a> ReactJsx<'a> {
arguments.extend( arguments.extend(
children children
.iter() .iter()
.filter_map(|child| self.transform_jsx_child(child)) .filter_map(|child| self.transform_jsx_child(child, ctx))
.map(Argument::from), .map(Argument::from),
); );
} }
@ -652,12 +669,13 @@ impl<'a> ReactJsx<'a> {
&mut self, &mut self,
properties: &mut Vec<'a, ObjectPropertyKind<'a>>, properties: &mut Vec<'a, ObjectPropertyKind<'a>>,
attribute: &JSXAttributeItem<'a>, attribute: &JSXAttributeItem<'a>,
ctx: &TraverseCtx<'a>,
) { ) {
match attribute { match attribute {
JSXAttributeItem::Attribute(attr) => { JSXAttributeItem::Attribute(attr) => {
let kind = PropertyKind::Init; let kind = PropertyKind::Init;
let key = self.get_attribute_name(&attr.name); 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 = let object_property =
self.ast().object_property(SPAN, kind, key, value, None, false, false, false); self.ast().object_property(SPAN, kind, key, value, None, false, false, false);
let object_property = ObjectPropertyKind::ObjectProperty(object_property); let object_property = ObjectPropertyKind::ObjectProperty(object_property);
@ -680,6 +698,7 @@ impl<'a> ReactJsx<'a> {
fn transform_jsx_attribute_value( fn transform_jsx_attribute_value(
&mut self, &mut self,
value: Option<&JSXAttributeValue<'a>>, value: Option<&JSXAttributeValue<'a>>,
ctx: &TraverseCtx<'a>,
) -> Expression<'a> { ) -> Expression<'a> {
match value { match value {
Some(JSXAttributeValue::StringLiteral(s)) => { Some(JSXAttributeValue::StringLiteral(s)) => {
@ -688,10 +707,10 @@ impl<'a> ReactJsx<'a> {
self.ast().literal_string_expression(literal) self.ast().literal_string_expression(literal)
} }
Some(JSXAttributeValue::Element(e)) => { Some(JSXAttributeValue::Element(e)) => {
self.transform_jsx(&JSXElementOrFragment::Element(e)) self.transform_jsx(&JSXElementOrFragment::Element(e), ctx)
} }
Some(JSXAttributeValue::Fragment(e)) => { Some(JSXAttributeValue::Fragment(e)) => {
self.transform_jsx(&JSXElementOrFragment::Fragment(e)) self.transform_jsx(&JSXElementOrFragment::Fragment(e), ctx)
} }
Some(JSXAttributeValue::ExpressionContainer(c)) => match &c.expression { Some(JSXAttributeValue::ExpressionContainer(c)) => match &c.expression {
e @ match_expression!(JSXExpression) => self.ast().copy(e.to_expression()), e @ match_expression!(JSXExpression) => self.ast().copy(e.to_expression()),
@ -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 { match child {
JSXChild::Text(text) => self.transform_jsx_text(text.value.as_str()), JSXChild::Text(text) => self.transform_jsx_text(text.value.as_str()),
JSXChild::ExpressionContainer(e) => match &e.expression { JSXChild::ExpressionContainer(e) => match &e.expression {
e @ match_expression!(JSXExpression) => Some(self.ast().copy(e.to_expression())), e @ match_expression!(JSXExpression) => Some(self.ast().copy(e.to_expression())),
JSXExpression::EmptyExpression(_) => None, JSXExpression::EmptyExpression(_) => None,
}, },
JSXChild::Element(e) => Some(self.transform_jsx(&JSXElementOrFragment::Element(e))), JSXChild::Element(e) => {
JSXChild::Fragment(e) => Some(self.transform_jsx(&JSXElementOrFragment::Fragment(e))), Some(self.transform_jsx(&JSXElementOrFragment::Element(e), ctx))
}
JSXChild::Fragment(e) => {
Some(self.transform_jsx(&JSXElementOrFragment::Fragment(e), ctx))
}
JSXChild::Spread(e) => { JSXChild::Spread(e) => {
self.ctx.error(diagnostics::spread_children_are_not_supported(e.span)); self.ctx.error(diagnostics::spread_children_are_not_supported(e.span));
None None

View file

@ -3,6 +3,7 @@ use std::rc::Rc;
use oxc_ast::ast::*; use oxc_ast::ast::*;
use oxc_diagnostics::OxcDiagnostic; use oxc_diagnostics::OxcDiagnostic;
use oxc_span::{Span, SPAN}; use oxc_span::{Span, SPAN};
use oxc_traverse::{Ancestor, FinderRet, TraverseCtx};
use crate::context::Ctx; use crate::context::Ctx;
@ -46,6 +47,29 @@ impl<'a> ReactJsxSelf<'a> {
let error = OxcDiagnostic::warning("Duplicate __self prop found.").with_label(span); let error = OxcDiagnostic::warning("Duplicate __self prop found.").with_label(span);
self.ctx.error(error); 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> { impl<'a> ReactJsxSelf<'a> {

View file

@ -8,7 +8,7 @@ mod utils;
use std::rc::Rc; use std::rc::Rc;
use oxc_ast::ast::*; use oxc_ast::ast::*;
use oxc_traverse::{Ancestor, FinderRet, TraverseCtx}; use oxc_traverse::TraverseCtx;
use crate::context::Ctx; use crate::context::Ctx;
@ -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 { match expr {
Expression::JSXElement(e) => { Expression::JSXElement(e) => {
if self.options.is_jsx_plugin_enabled() { 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) => { Expression::JSXFragment(e) => {
if self.options.is_jsx_plugin_enabled() { if self.options.is_jsx_plugin_enabled() {
*expr = self.jsx.transform_jsx_fragment(e); *expr = self.jsx.transform_jsx_fragment(e, ctx);
} }
} }
_ => {} _ => {}
@ -83,26 +83,10 @@ impl<'a> React<'a> {
elem: &mut JSXOpeningElement<'a>, elem: &mut JSXOpeningElement<'a>,
ctx: &TraverseCtx<'a>, ctx: &TraverseCtx<'a>,
) { ) {
if self.options.is_jsx_self_plugin_enabled() { if self.options.is_jsx_self_plugin_enabled()
let is_constructor = ctx && self.jsx.jsx_self.can_add_self_attribute(ctx)
.find_scope(|scope| { {
if scope.is_block() || scope.is_arrow() { self.jsx.jsx_self.transform_jsx_opening_element(elem);
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_source_plugin_enabled() { if self.options.is_jsx_source_plugin_enabled() {
self.jsx.jsx_source.transform_jsx_opening_element(elem); self.jsx.jsx_source.transform_jsx_opening_element(elem);

View file

@ -1,6 +1,6 @@
commit: 4bd1b2c2 commit: 4bd1b2c2
Passed: 309/362 Passed: 310/362
# All Passed: # All Passed:
* babel-preset-react * babel-preset-react
@ -66,9 +66,8 @@ Passed: 309/362
* autoImport/complicated-scope-module/input.js * autoImport/complicated-scope-module/input.js
* react-automatic/should-throw-when-filter-is-specified/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/self-inside-arrow/input.mjs
* cross-platform/source-and-self-defined/input.js * cross-platform/source-and-self-defined/input.js
* cross-platform/within-derived-classes-constructor/input.js
* cross-platform/within-ts-module-block/input.ts * cross-platform/within-ts-module-block/input.ts