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);
|
||||
}
|
||||
|
||||
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.x1_react.transform_call_expression(expr, ctx);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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(
|
||||
&mut self,
|
||||
decl: &mut ExportNamedDeclaration<'a>,
|
||||
|
|
@ -191,10 +184,6 @@ impl<'a> Traverse<'a> for Transformer<'a> {
|
|||
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(
|
||||
&mut self,
|
||||
def: &mut PropertyDefinition<'a>,
|
||||
|
|
@ -216,14 +205,6 @@ impl<'a> Traverse<'a> for Transformer<'a> {
|
|||
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(
|
||||
&mut self,
|
||||
ident: &mut IdentifierReference<'a>,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use std::rc::Rc;
|
|||
use oxc_allocator::Box;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_span::{Atom, SPAN};
|
||||
use oxc_traverse::{Ancestor, FinderRet, TraverseCtx};
|
||||
|
||||
use crate::context::Ctx;
|
||||
|
||||
|
|
@ -14,10 +15,6 @@ use crate::context::Ctx;
|
|||
///
|
||||
/// In: `var bar = createReactClass({});`
|
||||
/// 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> {
|
||||
ctx: Ctx<'a>,
|
||||
}
|
||||
|
|
@ -30,65 +27,71 @@ impl<'a> ReactDisplayName<'a> {
|
|||
|
||||
// Transforms
|
||||
impl<'a> ReactDisplayName<'a> {
|
||||
/// `foo = React.createClass({})`
|
||||
pub fn transform_assignment_expression(&self, assign_expr: &mut AssignmentExpression<'a>) {
|
||||
let Some(obj_expr) = Self::get_object_from_create_class(&mut assign_expr.right) else {
|
||||
pub fn transform_call_expression(
|
||||
&self,
|
||||
call_expr: &mut CallExpression<'a>,
|
||||
ctx: &TraverseCtx<'a>,
|
||||
) {
|
||||
let Some(obj_expr) = Self::get_object_from_create_class(call_expr) else {
|
||||
return;
|
||||
};
|
||||
let name = match &assign_expr.left {
|
||||
AssignmentTarget::AssignmentTargetIdentifier(ident) => ident.name.clone(),
|
||||
target => {
|
||||
if let Some(target) = target.as_member_expression() {
|
||||
if let Some(name) = target.static_property_name() {
|
||||
self.ctx.ast.new_atom(name)
|
||||
} else {
|
||||
return;
|
||||
|
||||
let name = ctx.find_ancestor(|ancestor| {
|
||||
match ancestor {
|
||||
// `foo = React.createClass({})`
|
||||
Ancestor::AssignmentExpressionRight(assign_expr) => match &assign_expr.left() {
|
||||
AssignmentTarget::AssignmentTargetIdentifier(ident) => {
|
||||
FinderRet::Found(ident.name.clone())
|
||||
}
|
||||
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({})`
|
||||
pub fn transform_variable_declarator(&self, declarator: &mut VariableDeclarator<'a>) {
|
||||
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);
|
||||
if let Some(name) = name {
|
||||
self.add_display_name(obj_expr, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ReactDisplayName<'a> {
|
||||
/// Get the object from `React.createClass({})` or `createReactClass({})`
|
||||
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>>> {
|
||||
let Expression::CallExpression(call_expr) = e else { return None };
|
||||
if match &call_expr.callee {
|
||||
callee @ match_member_expression!(Expression) => {
|
||||
!callee.to_member_expression().is_specific_member_access("React", "createClass")
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ mod utils;
|
|||
use std::rc::Rc;
|
||||
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_traverse::TraverseCtx;
|
||||
|
||||
use crate::context::Ctx;
|
||||
|
||||
|
|
@ -58,11 +59,6 @@ impl<'a> React<'a> {
|
|||
|
||||
pub fn transform_expression(&mut self, expr: &mut Expression<'a>) {
|
||||
match expr {
|
||||
Expression::AssignmentExpression(e) => {
|
||||
if self.options.display_name_plugin {
|
||||
self.display_name.transform_assignment_expression(e);
|
||||
}
|
||||
}
|
||||
Expression::JSXElement(e) => {
|
||||
if self.options.is_jsx_plugin_enabled() {
|
||||
*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 {
|
||||
self.display_name.transform_variable_declarator(declarator);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
self.display_name.transform_call_expression(call_expr, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
Passed: 294/362
|
||||
Passed: 295/362
|
||||
|
||||
# All Passed:
|
||||
* babel-plugin-transform-react-display-name
|
||||
* babel-plugin-transform-react-jsx-source
|
||||
|
||||
|
||||
|
|
@ -69,9 +70,6 @@ Passed: 294/362
|
|||
* autoImport/complicated-scope-module/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)
|
||||
* react-source/arrow-function/input.js
|
||||
* react-source/disable-with-super/input.js
|
||||
|
|
|
|||
Loading…
Reference in a new issue