From d8eba8ec5d920b8164d5053ef1b4543376673e09 Mon Sep 17 00:00:00 2001 From: overlookmotel Date: Thu, 2 May 2024 01:30:38 +0100 Subject: [PATCH] Implement `transform-react-display-name` with bottom-up lookup --- crates/oxc_transformer/src/lib.rs | 23 +--- .../src/react/display_name/mod.rs | 105 +++++++++--------- crates/oxc_transformer/src/react/mod.rs | 27 ++--- tasks/transform_conformance/babel.snap.md | 6 +- 4 files changed, 66 insertions(+), 95 deletions(-) diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 4c5831004c20d9..aa3641596a3bec 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -118,8 +118,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>) { @@ -135,14 +136,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>, @@ -194,10 +187,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>, @@ -219,14 +208,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>, diff --git a/crates/oxc_transformer/src/react/display_name/mod.rs b/crates/oxc_transformer/src/react/display_name/mod.rs index b922a74e8e7af6..af9e803d314bfd 100644 --- a/crates/oxc_transformer/src/react/display_name/mod.rs +++ b/crates/oxc_transformer/src/react/display_name/mod.rs @@ -1,7 +1,10 @@ use std::rc::Rc; use oxc_allocator::Box; -use oxc_ast::ast::*; +use oxc_ast::{ + ast::*, + traverse::{Ancestor, FinderRet, TraverseCtx}, +}; use oxc_span::{Atom, SPAN}; use crate::context::Ctx; @@ -14,10 +17,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 pub struct ReactDisplayName<'a> { ctx: Ctx<'a>, } @@ -30,65 +29,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) + + 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 { - return; + 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_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") diff --git a/crates/oxc_transformer/src/react/mod.rs b/crates/oxc_transformer/src/react/mod.rs index d46515098e9e88..dc052d10f238ce 100644 --- a/crates/oxc_transformer/src/react/mod.rs +++ b/crates/oxc_transformer/src/react/mod.rs @@ -7,7 +7,7 @@ mod utils; use std::rc::Rc; -use oxc_ast::ast::*; +use oxc_ast::{ast::*, traverse::TraverseCtx}; use crate::context::Ctx; @@ -58,11 +58,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 +72,13 @@ impl<'a> React<'a> { } } - pub fn transform_variable_declarator(&self, declarator: &mut VariableDeclarator<'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>) { + pub fn transform_call_expression( + &self, + call_expr: &mut CallExpression<'a>, + ctx: &TraverseCtx<'a>, + ) { if self.options.display_name_plugin { - self.display_name.transform_export_default_declaration(decl); + self.display_name.transform_call_expression(call_expr, ctx); } } diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index ee724f74be6f26..f3df2707d46edd 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,6 +1,7 @@ -Passed: 294/363 +Passed: 295/363 # All Passed: +* babel-plugin-transform-react-display-name * babel-plugin-transform-react-jsx-source @@ -68,9 +69,6 @@ Passed: 294/363 * 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