Skip to content

Commit

Permalink
fix(es/decorators): Resolve enum for design:returntype (#8320)
Browse files Browse the repository at this point in the history
  • Loading branch information
ZakisM committed Nov 22, 2023
1 parent c9c3e7d commit 91ef7c9
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 41 deletions.
Expand Up @@ -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 {
Expand All @@ -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];
}
}
Expand Up @@ -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';
Expand All @@ -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,
Expand Down Expand Up @@ -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);
@@ -1,3 +1,5 @@
use std::ops::Deref;

use swc_atoms::JsWord;
use swc_common::{
collections::AHashMap,
Expand Down Expand Up @@ -71,9 +73,35 @@ impl ParamMetadata {
}
}

type EnumMapType = AHashMap<JsWord, EnumKind>;

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<JsWord, EnumKind>,
pub(super) enums: EnumMap<'a>,

pub(super) class_name: Option<&'a Ident>,
}
Expand Down Expand Up @@ -169,57 +197,45 @@ 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);
}
}

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,
Expand Down
Expand Up @@ -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);
Expand Down

0 comments on commit 91ef7c9

Please sign in to comment.