mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
fix(transform): implement transform-react-display-name with bottom-up lookup (#3183)
Sliced off from #3152. Re-implement `transform-react-display-name` using bottom-up lookup provided by `Traverse` trait. This fixes the 1 remaining failing test case for this plugin (see #2937). `Traverse` is not complete yet (see #3182), so this is also not ready to merge yet.
This commit is contained in:
parent
be2aaa1d7e
commit
9590eb0cf4
4 changed files with 64 additions and 94 deletions
|
|
@ -115,8 +115,9 @@ impl<'a> Traverse<'a> for Transformer<'a> {
|
||||||
self.x0_typescript.transform_binding_pattern(pat);
|
self.x0_typescript.transform_binding_pattern(pat);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_call_expression(&mut self, expr: &mut CallExpression<'a>, _ctx: &TraverseCtx<'a>) {
|
fn enter_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &TraverseCtx<'a>) {
|
||||||
self.x0_typescript.transform_call_expression(expr);
|
self.x0_typescript.transform_call_expression(expr);
|
||||||
|
self.x1_react.transform_call_expression(expr, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_class(&mut self, class: &mut Class<'a>, _ctx: &TraverseCtx<'a>) {
|
fn enter_class(&mut self, class: &mut Class<'a>, _ctx: &TraverseCtx<'a>) {
|
||||||
|
|
@ -132,14 +133,6 @@ impl<'a> Traverse<'a> for Transformer<'a> {
|
||||||
self.x0_typescript.transform_class_body(body);
|
self.x0_typescript.transform_class_body(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_export_default_declaration(
|
|
||||||
&mut self,
|
|
||||||
decl: &mut ExportDefaultDeclaration<'a>,
|
|
||||||
_ctx: &TraverseCtx<'a>,
|
|
||||||
) {
|
|
||||||
self.x1_react.transform_export_default_declaration(decl);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_export_named_declaration(
|
fn enter_export_named_declaration(
|
||||||
&mut self,
|
&mut self,
|
||||||
decl: &mut ExportNamedDeclaration<'a>,
|
decl: &mut ExportNamedDeclaration<'a>,
|
||||||
|
|
@ -191,10 +184,6 @@ impl<'a> Traverse<'a> for Transformer<'a> {
|
||||||
self.x0_typescript.transform_new_expression(expr);
|
self.x0_typescript.transform_new_expression(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_object_property(&mut self, prop: &mut ObjectProperty<'a>, _ctx: &TraverseCtx<'a>) {
|
|
||||||
self.x1_react.transform_object_property(prop);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_property_definition(
|
fn enter_property_definition(
|
||||||
&mut self,
|
&mut self,
|
||||||
def: &mut PropertyDefinition<'a>,
|
def: &mut PropertyDefinition<'a>,
|
||||||
|
|
@ -216,14 +205,6 @@ impl<'a> Traverse<'a> for Transformer<'a> {
|
||||||
self.x0_typescript.transform_tagged_template_expression(expr);
|
self.x0_typescript.transform_tagged_template_expression(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_variable_declarator(
|
|
||||||
&mut self,
|
|
||||||
declarator: &mut VariableDeclarator<'a>,
|
|
||||||
_ctx: &TraverseCtx<'a>,
|
|
||||||
) {
|
|
||||||
self.x1_react.transform_variable_declarator(declarator);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_identifier_reference(
|
fn enter_identifier_reference(
|
||||||
&mut self,
|
&mut self,
|
||||||
ident: &mut IdentifierReference<'a>,
|
ident: &mut IdentifierReference<'a>,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use std::rc::Rc;
|
||||||
use oxc_allocator::Box;
|
use oxc_allocator::Box;
|
||||||
use oxc_ast::ast::*;
|
use oxc_ast::ast::*;
|
||||||
use oxc_span::{Atom, SPAN};
|
use oxc_span::{Atom, SPAN};
|
||||||
|
use oxc_traverse::{Ancestor, FinderRet, TraverseCtx};
|
||||||
|
|
||||||
use crate::context::Ctx;
|
use crate::context::Ctx;
|
||||||
|
|
||||||
|
|
@ -14,10 +15,6 @@ use crate::context::Ctx;
|
||||||
///
|
///
|
||||||
/// In: `var bar = createReactClass({});`
|
/// In: `var bar = createReactClass({});`
|
||||||
/// Out: `var bar = createReactClass({ displayName: "bar" });`
|
/// Out: `var bar = createReactClass({ displayName: "bar" });`
|
||||||
///
|
|
||||||
/// NOTE: The current implementation uses the top-down approach on `AssignmentExpression`, `VariableDeclaration`,
|
|
||||||
/// but can be rewritten with a bottom-up approach.
|
|
||||||
/// See <https://github.com/babel/babel/blob/08b0472069cd207f043dd40a4d157addfdd36011/packages/babel-plugin-transform-react-display-name/src/index.ts#L88-L98>
|
|
||||||
pub struct ReactDisplayName<'a> {
|
pub struct ReactDisplayName<'a> {
|
||||||
ctx: Ctx<'a>,
|
ctx: Ctx<'a>,
|
||||||
}
|
}
|
||||||
|
|
@ -30,65 +27,71 @@ impl<'a> ReactDisplayName<'a> {
|
||||||
|
|
||||||
// Transforms
|
// Transforms
|
||||||
impl<'a> ReactDisplayName<'a> {
|
impl<'a> ReactDisplayName<'a> {
|
||||||
/// `foo = React.createClass({})`
|
pub fn transform_call_expression(
|
||||||
pub fn transform_assignment_expression(&self, assign_expr: &mut AssignmentExpression<'a>) {
|
&self,
|
||||||
let Some(obj_expr) = Self::get_object_from_create_class(&mut assign_expr.right) else {
|
call_expr: &mut CallExpression<'a>,
|
||||||
|
ctx: &TraverseCtx<'a>,
|
||||||
|
) {
|
||||||
|
let Some(obj_expr) = Self::get_object_from_create_class(call_expr) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let name = match &assign_expr.left {
|
|
||||||
AssignmentTarget::AssignmentTargetIdentifier(ident) => ident.name.clone(),
|
let name = ctx.find_ancestor(|ancestor| {
|
||||||
target => {
|
match ancestor {
|
||||||
if let Some(target) = target.as_member_expression() {
|
// `foo = React.createClass({})`
|
||||||
if let Some(name) = target.static_property_name() {
|
Ancestor::AssignmentExpressionRight(assign_expr) => match &assign_expr.left() {
|
||||||
self.ctx.ast.new_atom(name)
|
AssignmentTarget::AssignmentTargetIdentifier(ident) => {
|
||||||
} else {
|
FinderRet::Found(ident.name.clone())
|
||||||
return;
|
}
|
||||||
|
target => {
|
||||||
|
if let Some(target) = target.as_member_expression() {
|
||||||
|
if let Some(name) = target.static_property_name() {
|
||||||
|
FinderRet::Found(ctx.ast.new_atom(name))
|
||||||
|
} else {
|
||||||
|
FinderRet::Stop
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FinderRet::Stop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// `let foo = React.createClass({})`
|
||||||
|
Ancestor::VariableDeclaratorInit(declarator) => match &declarator.id().kind {
|
||||||
|
BindingPatternKind::BindingIdentifier(ident) => {
|
||||||
|
FinderRet::Found(ident.name.clone())
|
||||||
|
}
|
||||||
|
_ => FinderRet::Stop,
|
||||||
|
},
|
||||||
|
// `{foo: React.createClass({})}`
|
||||||
|
Ancestor::ObjectPropertyValue(prop) => {
|
||||||
|
if let Some(name) = prop.key().static_name() {
|
||||||
|
FinderRet::Found(ctx.ast.new_atom(&name))
|
||||||
|
} else {
|
||||||
|
FinderRet::Stop
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
// `export default React.createClass({})`
|
||||||
|
// Uses the current file name as the display name.
|
||||||
|
Ancestor::ExportDefaultDeclarationDeclaration(_) => {
|
||||||
|
FinderRet::Found(ctx.ast.new_atom(&self.ctx.filename))
|
||||||
|
}
|
||||||
|
// Stop crawling up when hit a statement
|
||||||
|
_ if ancestor.is_via_statement() => FinderRet::Stop,
|
||||||
|
_ => FinderRet::Continue,
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
self.add_display_name(obj_expr, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `let foo = React.createClass({})`
|
if let Some(name) = name {
|
||||||
pub fn transform_variable_declarator(&self, declarator: &mut VariableDeclarator<'a>) {
|
self.add_display_name(obj_expr, name);
|
||||||
let Some(init_expr) = declarator.init.as_mut() else { return };
|
}
|
||||||
let Some(obj_expr) = Self::get_object_from_create_class(init_expr) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let name = match &declarator.id.kind {
|
|
||||||
BindingPatternKind::BindingIdentifier(ident) => ident.name.clone(),
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
self.add_display_name(obj_expr, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `{foo: React.createClass({})}`
|
|
||||||
pub fn transform_object_property(&self, prop: &mut ObjectProperty<'a>) {
|
|
||||||
let Some(obj_expr) = Self::get_object_from_create_class(&mut prop.value) else { return };
|
|
||||||
let Some(name) = prop.key.static_name() else { return };
|
|
||||||
let name = self.ctx.ast.new_atom(&name);
|
|
||||||
self.add_display_name(obj_expr, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `export default React.createClass({})`
|
|
||||||
/// Uses the current file name as the display name.
|
|
||||||
pub fn transform_export_default_declaration(&self, decl: &mut ExportDefaultDeclaration<'a>) {
|
|
||||||
let Some(expr) = decl.declaration.as_expression_mut() else { return };
|
|
||||||
let Some(obj_expr) = Self::get_object_from_create_class(expr) else { return };
|
|
||||||
let name = self.ctx.ast.new_atom(&self.ctx.filename);
|
|
||||||
self.add_display_name(obj_expr, name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ReactDisplayName<'a> {
|
impl<'a> ReactDisplayName<'a> {
|
||||||
/// Get the object from `React.createClass({})` or `createReactClass({})`
|
/// Get the object from `React.createClass({})` or `createReactClass({})`
|
||||||
fn get_object_from_create_class<'b>(
|
fn get_object_from_create_class<'b>(
|
||||||
e: &'b mut Expression<'a>,
|
call_expr: &'b mut CallExpression<'a>,
|
||||||
) -> Option<&'b mut Box<'a, ObjectExpression<'a>>> {
|
) -> Option<&'b mut Box<'a, ObjectExpression<'a>>> {
|
||||||
let Expression::CallExpression(call_expr) = e else { return None };
|
|
||||||
if match &call_expr.callee {
|
if match &call_expr.callee {
|
||||||
callee @ match_member_expression!(Expression) => {
|
callee @ match_member_expression!(Expression) => {
|
||||||
!callee.to_member_expression().is_specific_member_access("React", "createClass")
|
!callee.to_member_expression().is_specific_member_access("React", "createClass")
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ mod utils;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use oxc_ast::ast::*;
|
use oxc_ast::ast::*;
|
||||||
|
use oxc_traverse::TraverseCtx;
|
||||||
|
|
||||||
use crate::context::Ctx;
|
use crate::context::Ctx;
|
||||||
|
|
||||||
|
|
@ -58,11 +59,6 @@ impl<'a> React<'a> {
|
||||||
|
|
||||||
pub fn transform_expression(&mut self, expr: &mut Expression<'a>) {
|
pub fn transform_expression(&mut self, expr: &mut Expression<'a>) {
|
||||||
match expr {
|
match expr {
|
||||||
Expression::AssignmentExpression(e) => {
|
|
||||||
if self.options.display_name_plugin {
|
|
||||||
self.display_name.transform_assignment_expression(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
||||||
|
|
@ -77,21 +73,13 @@ impl<'a> React<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transform_variable_declarator(&self, declarator: &mut VariableDeclarator<'a>) {
|
pub fn transform_call_expression(
|
||||||
|
&self,
|
||||||
|
call_expr: &mut CallExpression<'a>,
|
||||||
|
ctx: &TraverseCtx<'a>,
|
||||||
|
) {
|
||||||
if self.options.display_name_plugin {
|
if self.options.display_name_plugin {
|
||||||
self.display_name.transform_variable_declarator(declarator);
|
self.display_name.transform_call_expression(call_expr, ctx);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn transform_object_property(&self, prop: &mut ObjectProperty<'a>) {
|
|
||||||
if self.options.display_name_plugin {
|
|
||||||
self.display_name.transform_object_property(prop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn transform_export_default_declaration(&self, decl: &mut ExportDefaultDeclaration<'a>) {
|
|
||||||
if self.options.display_name_plugin {
|
|
||||||
self.display_name.transform_export_default_declaration(decl);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
Passed: 294/362
|
Passed: 295/362
|
||||||
|
|
||||||
# All Passed:
|
# All Passed:
|
||||||
|
* babel-plugin-transform-react-display-name
|
||||||
* babel-plugin-transform-react-jsx-source
|
* babel-plugin-transform-react-jsx-source
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -69,9 +70,6 @@ Passed: 294/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-display-name (15/16)
|
|
||||||
* display-name/nested/input.js
|
|
||||||
|
|
||||||
# babel-plugin-transform-react-jsx-self (1/3)
|
# babel-plugin-transform-react-jsx-self (1/3)
|
||||||
* react-source/arrow-function/input.js
|
* react-source/arrow-function/input.js
|
||||||
* react-source/disable-with-super/input.js
|
* react-source/disable-with-super/input.js
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue