Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions crates/oxc_transformer/src/decorator/legacy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ use oxc_semantic::{ScopeFlags, SymbolFlags};
use oxc_span::SPAN;
use oxc_syntax::operator::AssignmentOperator;
use oxc_traverse::{Ancestor, BoundIdentifier, Traverse};
use rustc_hash::FxHashMap;

use crate::{
Helper,
Expand Down Expand Up @@ -92,6 +93,8 @@ pub struct LegacyDecorator<'a, 'ctx> {
/// we have to transforms decorators to `exit_class` otherwise after class is being transformed by
/// `class-properties` plugin, the decorators' nodes might be lost.
class_decorated_data: Option<ClassDecoratedData<'a>>,
/// Transformed decorators, they will be inserted in the statements at [`Self::exit_class_at_end`].
decorations: FxHashMap<Address, Vec<Statement<'a>>>,
ctx: &'ctx TransformCtx<'a>,
}

Expand All @@ -102,6 +105,7 @@ impl<'a, 'ctx> LegacyDecorator<'a, 'ctx> {
metadata: LegacyDecoratorMetadata::new(ctx),
class_decorated_data: None,
ctx,
decorations: FxHashMap::default(),
}
}
}
Expand Down Expand Up @@ -492,8 +496,8 @@ impl<'a> LegacyDecorator<'a, '_> {
};

if has_private_in_expression_in_decorator {
let stmts = mem::replace(&mut decoration_stmts, ctx.ast.vec());
Self::insert_decorations_into_class_static_block(class, stmts, ctx);
let decorations = mem::take(&mut decoration_stmts);
Self::insert_decorations_into_class_static_block(class, decorations, ctx);
} else {
let address = match ctx.parent() {
Ancestor::ExportDefaultDeclarationDeclaration(_)
Expand All @@ -503,7 +507,7 @@ impl<'a> LegacyDecorator<'a, '_> {
};

decoration_stmts.push(constructor_decoration);
self.ctx.statement_injector.insert_many_after(&address, decoration_stmts);
self.decorations.entry(address).or_default().append(&mut decoration_stmts);
self.class_decorated_data = Some(ClassDecoratedData {
binding: class_binding,
// If the class alias has reassigned to `this` in the static block, then
Expand Down Expand Up @@ -561,7 +565,7 @@ impl<'a> LegacyDecorator<'a, '_> {

/// Transforms a non-decorated class declaration.
fn transform_class_declaration_without_class_decorators(
&self,
&mut self,
class: &mut Class<'a>,
has_private_in_expression_in_decorator: bool,
ctx: &mut TraverseCtx<'a>,
Expand All @@ -575,7 +579,7 @@ impl<'a> LegacyDecorator<'a, '_> {
class_binding
};

let decoration_stmts =
let mut decoration_stmts =
self.transform_decorators_of_class_elements(class, &class_binding, ctx);

if has_private_in_expression_in_decorator {
Expand All @@ -587,7 +591,7 @@ impl<'a> LegacyDecorator<'a, '_> {
// `Class` is always stored in a `Box`, so has a stable memory location
_ => Address::from_ptr(class),
};
self.ctx.statement_injector.insert_many_after(&stmt_address, decoration_stmts);
self.decorations.entry(stmt_address).or_default().append(&mut decoration_stmts);
}
}

Expand All @@ -598,8 +602,8 @@ impl<'a> LegacyDecorator<'a, '_> {
class: &mut Class<'a>,
class_binding: &BoundIdentifier<'a>,
ctx: &mut TraverseCtx<'a>,
) -> ArenaVec<'a, Statement<'a>> {
let mut decoration_stmts = ctx.ast.vec_with_capacity(class.body.body.len());
) -> Vec<Statement<'a>> {
let mut decoration_stmts = Vec::with_capacity(class.body.body.len());

for element in &mut class.body.body {
let (is_static, key, descriptor, decorations) = match element {
Expand Down Expand Up @@ -738,10 +742,11 @@ impl<'a> LegacyDecorator<'a, '_> {
/// ```
fn insert_decorations_into_class_static_block(
class: &mut Class<'a>,
decorations: ArenaVec<'a, Statement<'a>>,
decorations: Vec<Statement<'a>>,
ctx: &mut TraverseCtx<'a>,
) {
let scope_id = ctx.create_child_scope(class.scope_id(), ScopeFlags::ClassStaticBlock);
let decorations = ctx.ast.vec_from_iter(decorations);
let element = ctx.ast.class_element_static_block_with_scope_id(SPAN, decorations, scope_id);
class.body.body.push(element);
}
Expand Down Expand Up @@ -780,6 +785,14 @@ impl<'a> LegacyDecorator<'a, '_> {
}
}

/// Injects the class decorator statements after class-properties plugin has run, ensuring that
/// all transformed fields are injected before the class decorator statements.
pub fn exit_class_at_end(&mut self, _class: &mut Class<'a>, _ctx: &mut TraverseCtx<'a>) {
for (address, stmts) in mem::take(&mut self.decorations) {
self.ctx.statement_injector.insert_many_after(&address, stmts);
}
}

/// Converts a vec of [`Decorator`] to [`Expression::ArrayExpression`].
fn convert_decorators_to_array_expression(
decorators_iter: impl Iterator<Item = Decorator<'a>>,
Expand Down
8 changes: 8 additions & 0 deletions crates/oxc_transformer/src/decorator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,11 @@ impl<'a> Traverse<'a, TransformState<'a>> for Decorator<'a, '_> {
}
}
}

impl<'a> Decorator<'a, '_> {
pub fn exit_class_at_end(&mut self, class: &mut Class<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.legacy {
self.legacy_decorator.exit_class_at_end(class, ctx);
}
}
}
2 changes: 2 additions & 0 deletions crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,8 @@ impl<'a> Traverse<'a, TransformState<'a>> for TransformerImpl<'a, '_> {
typescript.exit_class(class, ctx);
}
self.x2_es2022.exit_class(class, ctx);
// `decorator` has some statements should be inserted after `class-properties` plugin.
self.decorator.exit_class_at_end(class, ctx);
}

fn enter_class_body(&mut self, body: &mut ClassBody<'a>, ctx: &mut TraverseCtx<'a>) {
Expand Down
24 changes: 12 additions & 12 deletions napi/transform/test/transform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,18 +403,18 @@ describe('typescript', () => {
},
});
expect(ret.code).toMatchInlineSnapshot(`
"import _decorate from "@oxc-project/runtime/helpers/decorate";
class Foo {
constructor() {
this.b = 1;
}
}
_decorate([dec], Foo.prototype, "c", void 0);
class StaticFoo {}
_decorate([dec], StaticFoo, "c", void 0);
StaticFoo.b = 1;
"
`);
"import _decorate from "@oxc-project/runtime/helpers/decorate";
class Foo {
constructor() {
this.b = 1;
}
}
_decorate([dec], Foo.prototype, "c", void 0);
class StaticFoo {}
StaticFoo.b = 1;
_decorate([dec], StaticFoo, "c", void 0);
"
`);
});
});
});
Expand Down
Loading
Loading