From cc3b0cf0099313129e1728b655089bc1e1667bc5 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Wed, 27 Aug 2025 21:56:54 +0800 Subject: [PATCH 1/2] fix(transformer/legacy-decorator): emit correct metadata types for enum --- .../src/decorator/legacy/metadata.rs | 103 +++++++++++++++++- .../src/decorator/legacy/mod.rs | 6 + crates/oxc_transformer/src/decorator/mod.rs | 6 + crates/oxc_transformer/src/lib.rs | 1 + .../snapshots/semantic_typescript.snap | 16 +-- .../snapshots/oxc.snap.md | 102 ++++++++++++++++- .../fixtures/oxc/metadata/enum-types/input.ts | 72 ++++++++++++ .../oxc/metadata/enum-types/output.js | 80 ++++++++++++++ 8 files changed, 374 insertions(+), 12 deletions(-) create mode 100644 tasks/transform_conformance/tests/legacy-decorators/test/fixtures/oxc/metadata/enum-types/input.ts create mode 100644 tasks/transform_conformance/tests/legacy-decorators/test/fixtures/oxc/metadata/enum-types/output.js diff --git a/crates/oxc_transformer/src/decorator/legacy/metadata.rs b/crates/oxc_transformer/src/decorator/legacy/metadata.rs index 1fe10273a81ad..9fcff78b6ecda 100644 --- a/crates/oxc_transformer/src/decorator/legacy/metadata.rs +++ b/crates/oxc_transformer/src/decorator/legacy/metadata.rs @@ -92,9 +92,10 @@ use std::collections::VecDeque; use oxc_allocator::{Box as ArenaBox, TakeIn}; use oxc_ast::ast::*; use oxc_data_structures::stack::NonEmptyStack; -use oxc_semantic::ReferenceFlags; +use oxc_semantic::{Reference, ReferenceFlags, SymbolId}; use oxc_span::{ContentEq, SPAN}; use oxc_traverse::{MaybeBoundIdentifier, Traverse}; +use rustc_hash::FxHashMap; use crate::{ Helper, @@ -103,6 +104,17 @@ use crate::{ utils::ast_builder::create_property_access, }; +/// Type of an enum inferred from its members +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum EnumType { + /// All members are string literals or template literals with string-only expressions + String, + /// All members are numeric, bigint, unary numeric, or auto-incremented + Number, + /// Mixed types or computed values + Object, +} + pub enum MethodMetadata<'a> { Constructor(Expression<'a>), Normal([Expression<'a>; 3]), @@ -111,15 +123,31 @@ pub enum MethodMetadata<'a> { pub struct LegacyDecoratorMetadata<'a, 'ctx> { ctx: &'ctx TransformCtx<'a>, metadata_stack: NonEmptyStack>>, + enum_types: FxHashMap, } impl<'a, 'ctx> LegacyDecoratorMetadata<'a, 'ctx> { pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self { - LegacyDecoratorMetadata { ctx, metadata_stack: NonEmptyStack::new(VecDeque::new()) } + LegacyDecoratorMetadata { + ctx, + metadata_stack: NonEmptyStack::new(VecDeque::new()), + enum_types: FxHashMap::default(), + } } } impl<'a> Traverse<'a, TransformState<'a>> for LegacyDecoratorMetadata<'a, '_> { + // `#[inline]` so compiler knows `stmt` is a `TSEnumDeclaration` + #[inline] + fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { + // Collect enum types here instead of in `enter_ts_enum_declaration` because the TypeScript + // plugin transforms enum declarations in `enter_statement`, and we need to collect the + // enum type before it gets transformed. + if let Statement::TSEnumDeclaration(decl) = stmt { + self.collect_enum_type(decl, ctx); + } + } + fn enter_class(&mut self, class: &mut Class<'a>, ctx: &mut TraverseCtx<'a>) { if class.is_expression() || class.declare { return; @@ -201,6 +229,63 @@ impl<'a> Traverse<'a, TransformState<'a>> for LegacyDecoratorMetadata<'a, '_> { } impl<'a> LegacyDecoratorMetadata<'a, '_> { + /// Collects enum type information for decorator metadata generation. + fn collect_enum_type(&mut self, decl: &TSEnumDeclaration<'a>, ctx: &TraverseCtx<'a>) { + let symbol_id = decl.id.symbol_id(); + + // Optimization: + // If the enum doesn't have any type references, that implies that no decorators + // refer to this enum, so there is no need to infer its type. + let has_type_reference = + ctx.scoping().get_resolved_references(symbol_id).any(Reference::is_type); + if has_type_reference { + let enum_type = Self::infer_enum_type(&decl.body.members); + self.enum_types.insert(symbol_id, enum_type); + } + } + + /// Check if an expression is a numeric expression (including unary expressions) + fn is_numeric_expression(expr: &Expression<'a>) -> bool { + expr.is_number_literal() + || matches!( + expr, Expression::UnaryExpression(unary) if + matches!( + // These operators still produce numeric results. + unary.operator, UnaryOperator::UnaryNegation | UnaryOperator::UnaryPlus | UnaryOperator::BitwiseNot + ) && unary.argument.is_number_literal() + ) + } + + /// Infer the type of an enum based on its members + fn infer_enum_type(members: &[TSEnumMember<'a>]) -> EnumType { + let mut enum_type = EnumType::Object; + + for member in members { + if let Some(init) = &member.initializer { + match init { + Expression::StringLiteral(_) | Expression::TemplateLiteral(_) + if enum_type != EnumType::Number => + { + enum_type = EnumType::String; + } + expr if Self::is_numeric_expression(expr) && enum_type != EnumType::String => { + enum_type = EnumType::Number; + } + // For other expressions, we can't determine the type statically + _ => return EnumType::Object, + } + } else { + // No initializer means numeric (auto-incrementing from previous member) + if enum_type == EnumType::String { + return EnumType::Object; + } + enum_type = EnumType::Number; + } + } + + enum_type + } + pub fn pop_method_metadata(&mut self) -> Option> { self.metadata_stack.last_mut().pop_front() } @@ -350,6 +435,20 @@ impl<'a> LegacyDecoratorMetadata<'a, '_> { name: &TSTypeName<'a>, ctx: &mut TraverseCtx<'a>, ) -> Expression<'a> { + // Check if this is an enum type reference - if so, return the primitive type directly + if let TSTypeName::IdentifierReference(ident) = name { + let symbol_id = ctx.scoping().get_reference(ident.reference_id()).symbol_id(); + if let Some(symbol_id) = symbol_id { + if let Some(enum_type) = self.enum_types.get(&symbol_id) { + return match enum_type { + EnumType::String => Self::global_string(ctx), + EnumType::Number => Self::global_number(ctx), + EnumType::Object => Self::global_object(ctx), + }; + } + } + } + let Some(serialized_type) = self.serialize_entity_name_as_expression_fallback(name, ctx) else { // Reach here means the referent is a type symbol, so use `Object` as fallback. diff --git a/crates/oxc_transformer/src/decorator/legacy/mod.rs b/crates/oxc_transformer/src/decorator/legacy/mod.rs index a97c9b5ad7221..42db1880b78d1 100644 --- a/crates/oxc_transformer/src/decorator/legacy/mod.rs +++ b/crates/oxc_transformer/src/decorator/legacy/mod.rs @@ -111,6 +111,12 @@ impl<'a, 'ctx> LegacyDecorator<'a, 'ctx> { } impl<'a> Traverse<'a, TransformState<'a>> for LegacyDecorator<'a, '_> { + fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { + if self.emit_decorator_metadata { + self.metadata.enter_statement(stmt, ctx); + } + } + #[inline] fn enter_class(&mut self, class: &mut Class<'a>, ctx: &mut TraverseCtx<'a>) { if self.emit_decorator_metadata { diff --git a/crates/oxc_transformer/src/decorator/mod.rs b/crates/oxc_transformer/src/decorator/mod.rs index 66a4574926065..1eab769cc0a8a 100644 --- a/crates/oxc_transformer/src/decorator/mod.rs +++ b/crates/oxc_transformer/src/decorator/mod.rs @@ -29,6 +29,12 @@ impl<'a, 'ctx> Decorator<'a, 'ctx> { } impl<'a> Traverse<'a, TransformState<'a>> for Decorator<'a, '_> { + fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { + if self.options.legacy { + self.legacy_decorator.enter_statement(stmt, ctx); + } + } + fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { if self.options.legacy { self.legacy_decorator.exit_statement(stmt, ctx); diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 44e8ee686bcf8..1b33b2b918685 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -580,6 +580,7 @@ impl<'a> Traverse<'a, TransformState<'a>> for TransformerImpl<'a, '_> { } fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { + self.decorator.enter_statement(stmt, ctx); if let Some(typescript) = self.x0_typescript.as_mut() { typescript.enter_statement(stmt, ctx); } diff --git a/tasks/coverage/snapshots/semantic_typescript.snap b/tasks/coverage/snapshots/semantic_typescript.snap index 58b0a876afbaa..16352dffe7c61 100644 --- a/tasks/coverage/snapshots/semantic_typescript.snap +++ b/tasks/coverage/snapshots/semantic_typescript.snap @@ -20221,22 +20221,22 @@ after transform: ScopeId(4): ScopeFlags(0x0) rebuilt : ScopeId(5): ScopeFlags(Function) Symbol reference IDs mismatch for "A": after transform: SymbolId(3): [ReferenceId(2)] -rebuilt : SymbolId(8): [] +rebuilt : SymbolId(7): [] Symbol flags mismatch for "E": after transform: SymbolId(5): SymbolFlags(RegularEnum) -rebuilt : SymbolId(10): SymbolFlags(FunctionScopedVariable) +rebuilt : SymbolId(9): SymbolFlags(FunctionScopedVariable) Symbol reference IDs mismatch for "E": -after transform: SymbolId(5): [ReferenceId(6), ReferenceId(8), ReferenceId(9), ReferenceId(11), ReferenceId(13), ReferenceId(42), ReferenceId(47), ReferenceId(48), ReferenceId(55), ReferenceId(56)] -rebuilt : SymbolId(10): [ReferenceId(30), ReferenceId(39), ReferenceId(40), ReferenceId(53), ReferenceId(54)] +after transform: SymbolId(5): [ReferenceId(6), ReferenceId(8), ReferenceId(9), ReferenceId(11), ReferenceId(13), ReferenceId(42), ReferenceId(47), ReferenceId(48)] +rebuilt : SymbolId(9): [ReferenceId(30), ReferenceId(39), ReferenceId(40)] Unresolved references mismatch: -after transform: ["Boolean", "Object", "String", "require"] -rebuilt : ["Boolean", "Object", "require"] +after transform: ["Boolean", "Number", "Object", "String", "require"] +rebuilt : ["Boolean", "Number", "Object", "require"] Unresolved reference IDs mismatch for "Boolean": after transform: [ReferenceId(20), ReferenceId(21), ReferenceId(24)] rebuilt : [ReferenceId(14)] Unresolved reference IDs mismatch for "Object": -after transform: [ReferenceId(0), ReferenceId(18), ReferenceId(25), ReferenceId(51), ReferenceId(53), ReferenceId(59), ReferenceId(61)] -rebuilt : [ReferenceId(9), ReferenceId(19), ReferenceId(42), ReferenceId(47), ReferenceId(56), ReferenceId(61)] +after transform: [ReferenceId(0), ReferenceId(18), ReferenceId(25), ReferenceId(51), ReferenceId(53), ReferenceId(57)] +rebuilt : [ReferenceId(9), ReferenceId(19), ReferenceId(42), ReferenceId(47), ReferenceId(57)] semantic Error: tasks/coverage/typescript/tests/cases/compiler/metadataOfUnionWithNull.ts Symbol reference IDs mismatch for "A": diff --git a/tasks/transform_conformance/snapshots/oxc.snap.md b/tasks/transform_conformance/snapshots/oxc.snap.md index 312255fe4140d..a8b971f4e6bbc 100644 --- a/tasks/transform_conformance/snapshots/oxc.snap.md +++ b/tasks/transform_conformance/snapshots/oxc.snap.md @@ -1,6 +1,6 @@ commit: 41d96516 -Passed: 183/304 +Passed: 183/305 # All Passed: * babel-plugin-transform-class-static-block @@ -539,7 +539,7 @@ x Output mismatch x Output mismatch -# legacy-decorators (6/80) +# legacy-decorators (6/81) * oxc/metadata/abstract-class/input.ts Symbol reference IDs mismatch for "Dependency": after transform: SymbolId(1): [ReferenceId(1), ReferenceId(2), ReferenceId(3)] @@ -562,6 +562,104 @@ Symbol span mismatch for "Example": after transform: SymbolId(4): Span { start: 0, end: 0 } rebuilt : SymbolId(3): Span { start: 87, end: 94 } +* oxc/metadata/enum-types/input.ts +Bindings mismatch: +after transform: ScopeId(1): ["StringEnum", "bar", "foo"] +rebuilt : ScopeId(1): ["StringEnum"] +Scope flags mismatch: +after transform: ScopeId(1): ScopeFlags(0x0) +rebuilt : ScopeId(1): ScopeFlags(Function) +Bindings mismatch: +after transform: ScopeId(2): ["TemplateStringEnum", "mixed", "template"] +rebuilt : ScopeId(2): ["TemplateStringEnum"] +Scope flags mismatch: +after transform: ScopeId(2): ScopeFlags(0x0) +rebuilt : ScopeId(2): ScopeFlags(Function) +Bindings mismatch: +after transform: ScopeId(3): ["NumberEnum", "a", "b"] +rebuilt : ScopeId(3): ["NumberEnum"] +Scope flags mismatch: +after transform: ScopeId(3): ScopeFlags(0x0) +rebuilt : ScopeId(3): ScopeFlags(Function) +Bindings mismatch: +after transform: ScopeId(4): ["BigIntEnum", "big", "bigger"] +rebuilt : ScopeId(4): ["BigIntEnum"] +Scope flags mismatch: +after transform: ScopeId(4): ScopeFlags(0x0) +rebuilt : ScopeId(4): ScopeFlags(Function) +Bindings mismatch: +after transform: ScopeId(5): ["UnaryEnum", "bitwise", "negative", "positive"] +rebuilt : ScopeId(5): ["UnaryEnum"] +Scope flags mismatch: +after transform: ScopeId(5): ScopeFlags(0x0) +rebuilt : ScopeId(5): ScopeFlags(Function) +Bindings mismatch: +after transform: ScopeId(6): ["AutoIncrementEnum", "first", "second", "third"] +rebuilt : ScopeId(6): ["AutoIncrementEnum"] +Scope flags mismatch: +after transform: ScopeId(6): ScopeFlags(0x0) +rebuilt : ScopeId(6): ScopeFlags(Function) +Bindings mismatch: +after transform: ScopeId(7): ["MixedEnum", "num", "str"] +rebuilt : ScopeId(7): ["MixedEnum"] +Scope flags mismatch: +after transform: ScopeId(7): ScopeFlags(0x0) +rebuilt : ScopeId(7): ScopeFlags(Function) +Bindings mismatch: +after transform: ScopeId(8): ["ComputedEnum", "computed", "expression"] +rebuilt : ScopeId(8): ["ComputedEnum"] +Scope flags mismatch: +after transform: ScopeId(8): ScopeFlags(0x0) +rebuilt : ScopeId(8): ScopeFlags(Function) +Symbol flags mismatch for "StringEnum": +after transform: SymbolId(0): SymbolFlags(RegularEnum) +rebuilt : SymbolId(0): SymbolFlags(FunctionScopedVariable) +Symbol reference IDs mismatch for "StringEnum": +after transform: SymbolId(0): [ReferenceId(2), ReferenceId(18), ReferenceId(24)] +rebuilt : SymbolId(0): [ReferenceId(3)] +Symbol flags mismatch for "TemplateStringEnum": +after transform: SymbolId(3): SymbolFlags(RegularEnum) +rebuilt : SymbolId(2): SymbolFlags(FunctionScopedVariable) +Symbol reference IDs mismatch for "TemplateStringEnum": +after transform: SymbolId(3): [ReferenceId(4), ReferenceId(28)] +rebuilt : SymbolId(2): [ReferenceId(7)] +Symbol flags mismatch for "NumberEnum": +after transform: SymbolId(6): SymbolFlags(RegularEnum) +rebuilt : SymbolId(4): SymbolFlags(FunctionScopedVariable) +Symbol reference IDs mismatch for "NumberEnum": +after transform: SymbolId(6): [ReferenceId(6), ReferenceId(19), ReferenceId(20), ReferenceId(34)] +rebuilt : SymbolId(4): [ReferenceId(13), ReferenceId(48)] +Symbol flags mismatch for "BigIntEnum": +after transform: SymbolId(9): SymbolFlags(RegularEnum) +rebuilt : SymbolId(6): SymbolFlags(FunctionScopedVariable) +Symbol reference IDs mismatch for "BigIntEnum": +after transform: SymbolId(9): [ReferenceId(8), ReferenceId(40)] +rebuilt : SymbolId(6): [ReferenceId(19)] +Symbol flags mismatch for "UnaryEnum": +after transform: SymbolId(12): SymbolFlags(RegularEnum) +rebuilt : SymbolId(8): SymbolFlags(FunctionScopedVariable) +Symbol reference IDs mismatch for "UnaryEnum": +after transform: SymbolId(12): [ReferenceId(10), ReferenceId(48)] +rebuilt : SymbolId(8): [ReferenceId(27)] +Symbol flags mismatch for "AutoIncrementEnum": +after transform: SymbolId(16): SymbolFlags(RegularEnum) +rebuilt : SymbolId(10): SymbolFlags(FunctionScopedVariable) +Symbol reference IDs mismatch for "AutoIncrementEnum": +after transform: SymbolId(16): [ReferenceId(12), ReferenceId(56)] +rebuilt : SymbolId(10): [ReferenceId(35)] +Symbol flags mismatch for "MixedEnum": +after transform: SymbolId(20): SymbolFlags(RegularEnum) +rebuilt : SymbolId(12): SymbolFlags(FunctionScopedVariable) +Symbol reference IDs mismatch for "MixedEnum": +after transform: SymbolId(20): [ReferenceId(14), ReferenceId(61)] +rebuilt : SymbolId(12): [ReferenceId(40)] +Symbol flags mismatch for "ComputedEnum": +after transform: SymbolId(23): SymbolFlags(RegularEnum) +rebuilt : SymbolId(14): SymbolFlags(FunctionScopedVariable) +Symbol reference IDs mismatch for "ComputedEnum": +after transform: SymbolId(23): [ReferenceId(16), ReferenceId(67)] +rebuilt : SymbolId(14): [ReferenceId(47)] + * oxc/metadata/imports/input.ts Bindings mismatch: after transform: ScopeId(0): ["Bar", "Cls", "Foo", "Zoo", "_ref", "dec"] diff --git a/tasks/transform_conformance/tests/legacy-decorators/test/fixtures/oxc/metadata/enum-types/input.ts b/tasks/transform_conformance/tests/legacy-decorators/test/fixtures/oxc/metadata/enum-types/input.ts new file mode 100644 index 0000000000000..cf430a9570d0a --- /dev/null +++ b/tasks/transform_conformance/tests/legacy-decorators/test/fixtures/oxc/metadata/enum-types/input.ts @@ -0,0 +1,72 @@ +enum StringEnum { + foo = 'string', + bar = 'another' +} + +enum TemplateStringEnum { + template = `template literal`, + mixed = `prefix_${'suffix'}` +} + +enum NumberEnum { + a = 1, + b = 2 +} + +enum BigIntEnum { + big = 100n, + bigger = 200n +} + +enum UnaryEnum { + negative = -1, + positive = +2, + bitwise = ~3 +} + +enum AutoIncrementEnum { + first, // 0 + second, // 1 + third // 2 +} + +enum MixedEnum { + str = 'string', + num = 1 +} + +enum ComputedEnum { + computed = Math.PI, + expression = 1 + 2 +} + +function decorate(target: any, property: string) {} + +export class Foo { + @decorate + stringProp: StringEnum; + + @decorate + templateProp: TemplateStringEnum; + + @decorate + numberProp: NumberEnum; + + @decorate + bigintProp: BigIntEnum; + + @decorate + unaryProp: UnaryEnum; + + @decorate + autoProp: AutoIncrementEnum; + + @decorate + mixedProp: MixedEnum; + + @decorate + computedProp: ComputedEnum; + + @decorate + method(param: StringEnum): NumberEnum { return NumberEnum.a; } +} \ No newline at end of file diff --git a/tasks/transform_conformance/tests/legacy-decorators/test/fixtures/oxc/metadata/enum-types/output.js b/tasks/transform_conformance/tests/legacy-decorators/test/fixtures/oxc/metadata/enum-types/output.js new file mode 100644 index 0000000000000..e34076a11c4cd --- /dev/null +++ b/tasks/transform_conformance/tests/legacy-decorators/test/fixtures/oxc/metadata/enum-types/output.js @@ -0,0 +1,80 @@ +var StringEnum = /* @__PURE__ */ function(StringEnum) { + StringEnum["foo"] = "string"; + StringEnum["bar"] = "another"; + return StringEnum; +}(StringEnum || {}); + +var TemplateStringEnum = /* @__PURE__ */ function(TemplateStringEnum) { + TemplateStringEnum["template"] = "template literal"; + TemplateStringEnum["mixed"] = "prefix_suffix"; + return TemplateStringEnum; +}(TemplateStringEnum || {}); + +var NumberEnum = /* @__PURE__ */ function(NumberEnum) { + NumberEnum[NumberEnum["a"] = 1] = "a"; + NumberEnum[NumberEnum["b"] = 2] = "b"; + return NumberEnum; +}(NumberEnum || {}); + +var BigIntEnum = /* @__PURE__ */ function(BigIntEnum) { + BigIntEnum[BigIntEnum["big"] = 100n] = "big"; + BigIntEnum[BigIntEnum["bigger"] = 200n] = "bigger"; + return BigIntEnum; +}(BigIntEnum || {}); + +var UnaryEnum = /* @__PURE__ */ function(UnaryEnum) { + UnaryEnum[UnaryEnum["negative"] = -1] = "negative"; + UnaryEnum[UnaryEnum["positive"] = 2] = "positive"; + UnaryEnum[UnaryEnum["bitwise"] = -4] = "bitwise"; + return UnaryEnum; +}(UnaryEnum || {}); + +var AutoIncrementEnum = /* @__PURE__ */ function(AutoIncrementEnum) { + AutoIncrementEnum[AutoIncrementEnum["first"] = 0] = "first"; + AutoIncrementEnum[AutoIncrementEnum["second"] = 1] = "second"; + AutoIncrementEnum[AutoIncrementEnum["third"] = 2] = "third"; + return AutoIncrementEnum; +}(AutoIncrementEnum || {}); + +var MixedEnum = /* @__PURE__ */ function(MixedEnum) { + MixedEnum["str"] = "string"; + MixedEnum[MixedEnum["num"] = 1] = "num"; + return MixedEnum; +}(MixedEnum || {}); + +var ComputedEnum = /* @__PURE__ */ function(ComputedEnum) { + ComputedEnum[ComputedEnum["computed"] = Math.PI] = "computed"; + ComputedEnum[ComputedEnum["expression"] = 3] = "expression"; + return ComputedEnum; +}(ComputedEnum || {}); + +function decorate(target, property) {} + +export class Foo { + stringProp; + templateProp; + numberProp; + bigintProp; + unaryProp; + autoProp; + mixedProp; + computedProp; + method(param) { + return NumberEnum.a; + } +} + +babelHelpers.decorate([decorate, babelHelpers.decorateMetadata("design:type", String)], Foo.prototype, "stringProp", void 0); +babelHelpers.decorate([decorate, babelHelpers.decorateMetadata("design:type", String)], Foo.prototype, "templateProp", void 0); +babelHelpers.decorate([decorate, babelHelpers.decorateMetadata("design:type", Number)], Foo.prototype, "numberProp", void 0); +babelHelpers.decorate([decorate, babelHelpers.decorateMetadata("design:type", Number)], Foo.prototype, "bigintProp", void 0); +babelHelpers.decorate([decorate, babelHelpers.decorateMetadata("design:type", Number)], Foo.prototype, "unaryProp", void 0); +babelHelpers.decorate([decorate, babelHelpers.decorateMetadata("design:type", Number)], Foo.prototype, "autoProp", void 0); +babelHelpers.decorate([decorate, babelHelpers.decorateMetadata("design:type", Object)], Foo.prototype, "mixedProp", void 0); +babelHelpers.decorate([decorate, babelHelpers.decorateMetadata("design:type", Object)], Foo.prototype, "computedProp", void 0); +babelHelpers.decorate([ + decorate, + babelHelpers.decorateMetadata("design:type", Function), + babelHelpers.decorateMetadata("design:paramtypes", [String]), + babelHelpers.decorateMetadata("design:returntype", Number) +], Foo.prototype, "method", null); From 0a353dd1ae5e321293b55ec4113f06115afe901d Mon Sep 17 00:00:00 2001 From: overlookmotel Date: Thu, 28 Aug 2025 15:46:14 +0100 Subject: [PATCH 2/2] Update comment about `#[inline]` --- crates/oxc_transformer/src/decorator/legacy/metadata.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/oxc_transformer/src/decorator/legacy/metadata.rs b/crates/oxc_transformer/src/decorator/legacy/metadata.rs index 9fcff78b6ecda..cba110c0fb1d8 100644 --- a/crates/oxc_transformer/src/decorator/legacy/metadata.rs +++ b/crates/oxc_transformer/src/decorator/legacy/metadata.rs @@ -137,7 +137,8 @@ impl<'a, 'ctx> LegacyDecoratorMetadata<'a, 'ctx> { } impl<'a> Traverse<'a, TransformState<'a>> for LegacyDecoratorMetadata<'a, '_> { - // `#[inline]` so compiler knows `stmt` is a `TSEnumDeclaration` + // `#[inline]` because this is a hot path and most `Statement`s are not `TSEnumDeclaration`s. + // We want to avoid overhead of a function call for the common case. #[inline] fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { // Collect enum types here instead of in `enter_ts_enum_declaration` because the TypeScript