diff --git a/crates/swc_ecma_transforms/tests/fixture/legacy-metadata/issues/3319/input.ts b/crates/swc_ecma_transforms/tests/fixture/legacy-metadata/issues/3319/input.ts index caa7e1e13806..53b10529d37d 100644 --- a/crates/swc_ecma_transforms/tests/fixture/legacy-metadata/issues/3319/input.ts +++ b/crates/swc_ecma_transforms/tests/fixture/legacy-metadata/issues/3319/input.ts @@ -6,6 +6,24 @@ function decorator(target: any, key: string | symbol, descriptor: PropertyDescri returnType = Reflect.getMetadata('design:returntype', target, key); } +enum NumericEnum { + A, + B, + C, +} + +enum StringEnum { + A = "A", + B = "B", + C = "C", +} + +enum ObjectEnum { + A = "A", + B = 2, + C = "C", +} + class Foo { @decorator public foo(x: string): string { @@ -31,4 +49,34 @@ class Foo { public async quux() { return 'quux'; } + + @decorator + public numeric_array(): number[] { + return [1, 2, 3]; + } + + @decorator + public string_array(): string[] { + return ['first', 'second', 'third']; + } + + @decorator + public numeric_enum(): NumericEnum { + return NumericEnum.A; + } + + @decorator + public string_enum(): StringEnum { + return StringEnum.A; + } + + @decorator + public object_enum(): ObjectEnum { + return ObjectEnum.A; + } + + @decorator + public array_enum(): StringEnum[] { + return [StringEnum.A, StringEnum.B, StringEnum.C]; + } } diff --git a/crates/swc_ecma_transforms/tests/fixture/legacy-metadata/issues/3319/output.ts b/crates/swc_ecma_transforms/tests/fixture/legacy-metadata/issues/3319/output.ts index 798e718c55cf..dcbd881c5345 100644 --- a/crates/swc_ecma_transforms/tests/fixture/legacy-metadata/issues/3319/output.ts +++ b/crates/swc_ecma_transforms/tests/fixture/legacy-metadata/issues/3319/output.ts @@ -6,6 +6,23 @@ function decorator(target: any, key: string | symbol, descriptor: PropertyDescri returnType = Reflect.getMetadata('design:returntype', target, key); } +enum NumericEnum { + A, + B, + C +} +enum StringEnum { + A = "A", + B = "B", + C = "C" +} + +enum ObjectEnum { + A = "A", + B = 2, + C = "C" +} + class Foo { public foo(x: string): string { return 'foo'; @@ -22,6 +39,24 @@ class Foo { public async quux() { return 'quux'; } + public numeric_array(): number[] { + return [1, 2, 3]; + } + public string_array(): string[] { + return ['first', 'second', 'third']; + } + public numeric_enum(): NumericEnum { + return NumericEnum.A; + } + public string_enum(): StringEnum { + return StringEnum.A; + } + public object_enum(): ObjectEnum { + return ObjectEnum.A; + } + public array_enum(): StringEnum[] { + return [StringEnum.A, StringEnum.B, StringEnum.C]; + } } _ts_decorate([ decorator, @@ -53,3 +88,39 @@ _ts_decorate([ _ts_metadata("design:paramtypes", []), _ts_metadata("design:returntype", Promise) ], Foo.prototype, "quux", null); +_ts_decorate([ + decorator, + _ts_metadata("design:type", Function), + _ts_metadata("design:paramtypes", []), + _ts_metadata("design:returntype", Array) +], Foo.prototype, "numeric_array", null); +_ts_decorate([ + decorator, + _ts_metadata("design:type", Function), + _ts_metadata("design:paramtypes", []), + _ts_metadata("design:returntype", Array) +], Foo.prototype, "string_array", null); +_ts_decorate([ + decorator, + _ts_metadata("design:type", Function), + _ts_metadata("design:paramtypes", []), + _ts_metadata("design:returntype", Number) +], Foo.prototype, "numeric_enum", null); +_ts_decorate([ + decorator, + _ts_metadata("design:type", Function), + _ts_metadata("design:paramtypes", []), + _ts_metadata("design:returntype", String) +], Foo.prototype, "string_enum", null); +_ts_decorate([ + decorator, + _ts_metadata("design:type", Function), + _ts_metadata("design:paramtypes", []), + _ts_metadata("design:returntype", Object) +], Foo.prototype, "object_enum", null); +_ts_decorate([ + decorator, + _ts_metadata("design:type", Function), + _ts_metadata("design:paramtypes", []), + _ts_metadata("design:returntype", Array) +], Foo.prototype, "array_enum", null); diff --git a/crates/swc_ecma_transforms_proposal/src/decorators/legacy/metadata.rs b/crates/swc_ecma_transforms_proposal/src/decorators/legacy/metadata.rs index 49f7f0abed8c..b09b8affea81 100644 --- a/crates/swc_ecma_transforms_proposal/src/decorators/legacy/metadata.rs +++ b/crates/swc_ecma_transforms_proposal/src/decorators/legacy/metadata.rs @@ -1,3 +1,5 @@ +use std::ops::Deref; + use swc_atoms::JsWord; use swc_common::{ collections::AHashMap, @@ -71,9 +73,35 @@ impl ParamMetadata { } } +type EnumMapType = AHashMap; + +pub(super) struct EnumMap<'a>(&'a EnumMapType); + +impl Deref for EnumMap<'_> { + type Target = EnumMapType; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl EnumMap<'_> { + fn get_kind_as_str(&self, param: Option<&TsTypeAnn>) -> Option<&'static str> { + param + .and_then(|t| t.type_ann.as_ts_type_ref()) + .and_then(|t| t.type_name.as_ident()) + .and_then(|t| self.get(&t.sym)) + .map(|kind| match kind { + EnumKind::Mixed => "Object", + EnumKind::Str => "String", + EnumKind::Num => "Number", + }) + } +} + /// https://github.com/leonardfactory/babel-plugin-transform-typescript-metadata/blob/master/src/metadata/metadataVisitor.ts pub(super) struct Metadata<'a> { - pub(super) enums: &'a AHashMap, + pub(super) enums: EnumMap<'a>, pub(super) class_name: Option<&'a Ident>, } @@ -169,7 +197,13 @@ impl VisitMut for Metadata<'_> { if m.function.is_async { quote_ident!("Promise").as_arg() } else { - serialize_type(self.class_name, m.function.return_type.as_deref()).as_arg() + let return_type = m.function.return_type.as_deref(); + + if let Some(kind) = self.enums.get_kind_as_str(return_type) { + quote_ident!(kind).as_arg() + } else { + serialize_type(self.class_name, return_type).as_arg() + } }, ); m.function.decorators.push(dec); @@ -177,49 +211,31 @@ impl VisitMut for Metadata<'_> { } fn visit_mut_class_prop(&mut self, p: &mut ClassProp) { - if p.decorators.is_empty() { + if p.decorators.is_empty() || p.type_ann.is_none() { return; } - if p.type_ann.is_none() { - return; - } - if let Some(name) = p - .type_ann - .as_ref() - .map(|ty| &ty.type_ann) - .and_then(|type_ann| match &**type_ann { - TsType::TsTypeRef(r) => Some(r), - _ => None, - }) - .and_then(|r| match &r.type_name { - TsEntityName::TsQualifiedName(_) => None, - TsEntityName::Ident(i) => Some(i), - }) - { - if let Some(kind) = self.enums.get(&name.sym) { - let dec = self.create_metadata_design_decorator( - "design:type", - match kind { - EnumKind::Mixed => quote_ident!("Object").as_arg(), - EnumKind::Str => quote_ident!("String").as_arg(), - EnumKind::Num => quote_ident!("Number").as_arg(), - }, - ); - p.decorators.push(dec); - return; - } - } + let dec = self.create_metadata_design_decorator("design:type", { + let prop_type = p.type_ann.as_deref(); - let dec = self.create_metadata_design_decorator( - "design:type", - serialize_type(self.class_name, p.type_ann.as_deref()).as_arg(), - ); + if let Some(kind) = self.enums.get_kind_as_str(prop_type) { + quote_ident!(kind).as_arg() + } else { + serialize_type(self.class_name, prop_type).as_arg() + } + }); p.decorators.push(dec); } } -impl Metadata<'_> { +impl<'a> Metadata<'a> { + pub(super) fn new(enums: &'a EnumMapType, class_name: Option<&'a Ident>) -> Self { + Self { + enums: EnumMap(enums), + class_name, + } + } + fn create_metadata_design_decorator(&self, design: &str, type_arg: ExprOrSpread) -> Decorator { Decorator { span: DUMMY_SP, diff --git a/crates/swc_ecma_transforms_proposal/src/decorators/legacy/mod.rs b/crates/swc_ecma_transforms_proposal/src/decorators/legacy/mod.rs index d305c3a55b7a..433f8be12243 100644 --- a/crates/swc_ecma_transforms_proposal/src/decorators/legacy/mod.rs +++ b/crates/swc_ecma_transforms_proposal/src/decorators/legacy/mod.rs @@ -224,10 +224,7 @@ impl VisitMut for TscDecorator { if self.metadata { let i = self.class_name.clone(); - n.visit_mut_with(&mut Metadata { - enums: &self.enums, - class_name: i.as_ref(), - }); + n.visit_mut_with(&mut Metadata::new(&self.enums, i.as_ref())); } n.visit_mut_children_with(self);