From 5df43b67b2c8b4963b5b35e256cf412380b4cb50 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Thu, 7 Mar 2024 15:37:59 +0800 Subject: [PATCH] feat(transformer/decorators): handling the coexistence of class decorators and member decorators --- crates/oxc_ast/src/ast/js.rs | 7 + crates/oxc_ast/src/ast/ts.rs | 21 +++ crates/oxc_ast/src/ast_builder.rs | 29 ++++ .../src/proposals/decorators.rs | 140 +++++++++++++----- 4 files changed, 156 insertions(+), 41 deletions(-) diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index caa3301c4eae..53d8c08741c6 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -1004,6 +1004,13 @@ pub struct ObjectAssignmentTarget<'a> { } impl<'a> ObjectAssignmentTarget<'a> { + pub fn new_with_properties( + span: Span, + properties: Vec<'a, AssignmentTargetProperty<'a>>, + ) -> Self { + Self { span, properties, rest: None } + } + pub fn is_empty(&self) -> bool { self.properties.is_empty() && self.rest.is_none() } diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index 27dfbc9f7c1b..baeccfada9e1 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -947,6 +947,27 @@ pub struct Decorator<'a> { pub expression: Expression<'a>, } +impl<'a> Decorator<'a> { + /// Get the name of the decorator + /// ```ts + /// @decorator + /// @decorator.a.b + /// @decorator(xx) + /// @decorator.a.b(xx) + /// The name of the decorator is `decorator` + /// ``` + pub fn name(&self) -> Option<&str> { + match &self.expression { + Expression::Identifier(ident) => Some(&ident.name), + Expression::MemberExpression(member) => member.static_property_name(), + Expression::CallExpression(call) => { + call.callee.get_member_expr().map(|member| member.static_property_name())? + } + _ => None, + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize), serde(rename_all = "camelCase"))] #[cfg_attr(all(feature = "serde", feature = "wasm"), derive(tsify::Tsify))] diff --git a/crates/oxc_ast/src/ast_builder.rs b/crates/oxc_ast/src/ast_builder.rs index af7880a2474a..8bd4d83db38f 100644 --- a/crates/oxc_ast/src/ast_builder.rs +++ b/crates/oxc_ast/src/ast_builder.rs @@ -468,6 +468,35 @@ impl<'a> AstBuilder<'a> { )) } + pub fn array_assignment_target_maybe_default( + &self, + array: ArrayAssignmentTarget<'a>, + ) -> AssignmentTargetMaybeDefault<'a> { + AssignmentTargetMaybeDefault::AssignmentTarget(AssignmentTarget::AssignmentTargetPattern( + AssignmentTargetPattern::ArrayAssignmentTarget(self.alloc(array)), + )) + } + + pub fn object_assignment_target( + &self, + array: ObjectAssignmentTarget<'a>, + ) -> AssignmentTarget<'a> { + AssignmentTarget::AssignmentTargetPattern(AssignmentTargetPattern::ObjectAssignmentTarget( + self.alloc(array), + )) + } + + pub fn assignment_target_property_property( + &self, + span: Span, + name: PropertyKey<'a>, + binding: AssignmentTargetMaybeDefault<'a>, + ) -> AssignmentTargetProperty<'a> { + AssignmentTargetProperty::AssignmentTargetPropertyProperty( + self.alloc(AssignmentTargetPropertyProperty { span, name, binding }), + ) + } + pub fn simple_assignment_target_identifier( &self, ident: IdentifierReference<'a>, diff --git a/crates/oxc_transformer/src/proposals/decorators.rs b/crates/oxc_transformer/src/proposals/decorators.rs index ffc56c89563e..6eb7ec54f9c2 100644 --- a/crates/oxc_transformer/src/proposals/decorators.rs +++ b/crates/oxc_transformer/src/proposals/decorators.rs @@ -17,7 +17,7 @@ use crate::{context::TransformerCtx, options::TransformOptions}; /// * pub struct Decorators<'a> { ast: Rc>, - _ctx: TransformerCtx<'a>, + ctx: TransformerCtx<'a>, options: DecoratorsOptions, // Insert to the top of the program top_statements: Vec<'a, Statement<'a>>, @@ -113,7 +113,7 @@ impl<'a> Decorators<'a> { let bottom_statements = ast.new_vec(); options.decorators.map(|options| Self { ast, - _ctx: ctx, + ctx, options, top_statements, bottom_statements, @@ -348,40 +348,73 @@ impl<'a> Decorators<'a> { Argument::Expression(self.ast.array_expression(SPAN, self.ast.new_vec(), None)); if has_decorator { - let class_name = class_name.unwrap_or_else(|| self.get_unique_name("class")); + let class_name = class_name.unwrap_or_else(|| { + self.get_unique_name( + class.id.as_ref().map_or_else(|| "class".into(), |id| id.name.clone()).as_ref(), + ) + }); - let class_decs_name = self.get_unique_name("classDecs"); let init_class_name = self.get_unique_name("initClass"); { // insert var _initClass, _classDecs; declarations.push(self.get_variable_declarator(&init_class_name)); - declarations.push(self.get_variable_declarator(&class_decs_name)); } { - // insert _classDecs = decorators; - let left = self.ast.simple_assignment_target_identifier(IdentifierReference::new( - SPAN, - self.ast.new_atom(&class_decs_name), - )); + let decorators_exists = class.decorators.iter().any(|decorator| { + decorator + .name() + .is_some_and(|name| // TODO: We should use current node's scope id + self.ctx.scopes().has_binding(self.ctx.scopes().root_scope_id(), name)) + }); + + if decorators_exists { + let mut elements = self.ast.new_vec(); + + elements.extend(class.decorators.drain(..).map(|decorator| { + ArrayExpressionElement::Expression(self.ast.copy(&decorator.expression)) + })); + class_decorators_argument = + Argument::Expression(self.ast.array_expression(SPAN, elements, None)); + } else { + let class_decs_name = self.get_unique_name("classDecs"); - let right = self.ast.array_expression( - SPAN, - { - let mut elements = self.ast.new_vec(); - elements.extend(class.decorators.drain(..).map(|d| { - ArrayExpressionElement::Expression(self.ast.copy(&d.expression)) - })); - elements - }, - None, - ); - let assign_class_decs = self.ast.expression_statement( - SPAN, - self.ast.assignment_expression(SPAN, AssignmentOperator::Assign, left, right), - ); - self.top_statements.push(assign_class_decs); + // insert var _classDecs; + declarations.push(self.get_variable_declarator(&class_decs_name)); + + // insert _classDecs = decorators; + let left = self.ast.simple_assignment_target_identifier( + IdentifierReference::new(SPAN, self.ast.new_atom(&class_decs_name)), + ); + + let right = self.ast.array_expression( + SPAN, + { + let mut elements = self.ast.new_vec(); + elements.extend(class.decorators.drain(..).map(|d| { + ArrayExpressionElement::Expression(self.ast.copy(&d.expression)) + })); + elements + }, + None, + ); + let assign_class_decs = self.ast.expression_statement( + SPAN, + self.ast.assignment_expression( + SPAN, + AssignmentOperator::Assign, + left, + right, + ), + ); + self.top_statements.push(assign_class_decs); + + class_decorators_argument = + Argument::Expression(self.ast.identifier_reference_expression( + IdentifierReference::new(SPAN, self.ast.new_atom(&class_decs_name)), + )); + } }; { @@ -402,11 +435,6 @@ impl<'a> Decorators<'a> { c_elements.push(self.get_assignment_target_maybe_default(&class_name)); c_elements.push(self.get_assignment_target_maybe_default(&init_class_name)); - class_decorators_argument = - Argument::Expression(self.ast.identifier_reference_expression( - IdentifierReference::new(SPAN, self.ast.new_atom(&class_decs_name)), - )); - { // call _initClass let callee = self.ast.identifier_reference_expression(IdentifierReference::new( @@ -420,7 +448,9 @@ impl<'a> Decorators<'a> { let static_block = self.ast.static_block(SPAN, statements); class.body.body.insert(0, static_block); } - } else if has_member_decorator { + } + + if has_member_decorator { let mut is_proto = false; let mut is_static = false; @@ -705,7 +735,29 @@ impl<'a> Decorators<'a> { let mut call_expr = self.ast.call_expression(SPAN, callee, arguments, false, None); if has_decorator && has_decorator == has_member_decorator { - // TODO: support this case + let mut properties = self.ast.new_vec_with_capacity(2); + properties.push(self.ast.assignment_target_property_property( + SPAN, + self.ast.property_key_identifier(IdentifierName::new(SPAN, "e".into())), + self.ast.array_assignment_target_maybe_default( + ArrayAssignmentTarget::new_with_elements(SPAN, e_elements), + ), + )); + properties.push(self.ast.assignment_target_property_property( + SPAN, + self.ast.property_key_identifier(IdentifierName::new(SPAN, "c".into())), + self.ast.array_assignment_target_maybe_default( + ArrayAssignmentTarget::new_with_elements(SPAN, c_elements), + ), + )); + call_expr = self.ast.assignment_expression( + SPAN, + AssignmentOperator::Assign, + self.ast.object_assignment_target(ObjectAssignmentTarget::new_with_properties( + SPAN, properties, + )), + call_expr, + ); } else if has_decorator || has_member_decorator { call_expr = self.ast.static_member_expression( SPAN, @@ -716,17 +768,23 @@ impl<'a> Decorators<'a> { ), false, ); - } - let left = self.ast.array_assignment_target(ArrayAssignmentTarget::new_with_elements( - SPAN, - if c_elements.is_empty() { e_elements } else { c_elements }, - )); - let new_expr = - self.ast.assignment_expression(SPAN, AssignmentOperator::Assign, left, call_expr); + let left = + self.ast.array_assignment_target(ArrayAssignmentTarget::new_with_elements( + SPAN, + if has_decorator { c_elements } else { e_elements }, + )); + + call_expr = self.ast.assignment_expression( + SPAN, + AssignmentOperator::Assign, + left, + call_expr, + ); + } let mut statements = self.ast.new_vec(); - statements.push(self.ast.expression_statement(SPAN, new_expr)); + statements.push(self.ast.expression_statement(SPAN, call_expr)); if let Some(init_static_name) = init_static_name { // call initStatic