mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
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:
parent
61281711f0
commit
b9d69ad665
5 changed files with 80 additions and 46 deletions
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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> {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue