swc_ecma_transforms_proposal/decorators/legacy/
metadata.rs

1use std::ops::Deref;
2
3use rustc_hash::FxHashMap;
4use swc_atoms::{atom, Atom};
5use swc_common::{
6    util::{move_map::MoveMap, take::Take},
7    BytePos, Spanned, DUMMY_SP,
8};
9use swc_ecma_ast::*;
10use swc_ecma_transforms_base::helper;
11use swc_ecma_utils::{quote_ident, ExprFactory};
12use swc_ecma_visit::{VisitMut, VisitMutWith};
13
14use super::EnumKind;
15
16/// https://github.com/leonardfactory/babel-plugin-transform-typescript-metadata/blob/master/src/parameter/parameterVisitor.ts
17pub(super) struct ParamMetadata;
18
19impl VisitMut for ParamMetadata {
20    fn visit_mut_class(&mut self, cls: &mut Class) {
21        cls.visit_mut_children_with(self);
22
23        let mut decorators = cls.decorators.take();
24
25        cls.body = cls.body.take().move_map(|m| match m {
26            ClassMember::Constructor(mut c) => {
27                for (idx, param) in c.params.iter_mut().enumerate() {
28                    //
29                    match param {
30                        ParamOrTsParamProp::TsParamProp(p) => {
31                            for decorator in p.decorators.drain(..) {
32                                let new_dec = self.create_param_decorator(idx, decorator.expr);
33                                decorators.push(new_dec);
34                            }
35                        }
36                        ParamOrTsParamProp::Param(param) => {
37                            for decorator in param.decorators.drain(..) {
38                                let new_dec = self.create_param_decorator(idx, decorator.expr);
39                                decorators.push(new_dec);
40                            }
41                        }
42                        #[cfg(swc_ast_unknown)]
43                        _ => panic!("unable to access unknown nodes"),
44                    }
45                }
46
47                ClassMember::Constructor(c)
48            }
49            _ => m,
50        });
51        cls.decorators = decorators;
52    }
53
54    fn visit_mut_class_method(&mut self, m: &mut ClassMethod) {
55        for (idx, param) in m.function.params.iter_mut().enumerate() {
56            for decorator in param.decorators.drain(..) {
57                let new_dec = self.create_param_decorator(idx, decorator.expr);
58                m.function.decorators.push(new_dec);
59            }
60        }
61    }
62}
63
64impl ParamMetadata {
65    fn create_param_decorator(
66        &self,
67        param_index: usize,
68        mut decorator_expr: Box<Expr>,
69    ) -> Decorator {
70        remove_span(&mut decorator_expr);
71
72        Decorator {
73            span: DUMMY_SP,
74            expr: CallExpr {
75                span: DUMMY_SP,
76                callee: helper!(ts, ts_param),
77                args: vec![param_index.as_arg(), decorator_expr.as_arg()],
78                ..Default::default()
79            }
80            .into(),
81        }
82    }
83}
84
85pub(super) fn remove_span(e: &mut Expr) {
86    match e {
87        Expr::Member(m) => {
88            m.span = DUMMY_SP;
89            remove_span(&mut m.obj);
90        }
91        Expr::Call(c) => {
92            c.span = DUMMY_SP;
93            if let Callee::Expr(e) = &mut c.callee {
94                remove_span(e);
95            }
96            for arg in &mut c.args {
97                remove_span(&mut arg.expr);
98            }
99        }
100        _ => {
101            e.set_span(DUMMY_SP);
102        }
103    }
104}
105
106type EnumMapType = FxHashMap<Atom, EnumKind>;
107
108pub(super) struct EnumMap<'a>(&'a EnumMapType);
109
110impl Deref for EnumMap<'_> {
111    type Target = EnumMapType;
112
113    fn deref(&self) -> &Self::Target {
114        self.0
115    }
116}
117
118impl EnumMap<'_> {
119    fn get_kind_as_str(&self, param: Option<&TsTypeAnn>) -> Option<&'static str> {
120        param
121            .and_then(|t| t.type_ann.as_ts_type_ref())
122            .and_then(|t| t.type_name.as_ident())
123            .and_then(|t| self.get(&t.sym))
124            .map(|kind| match kind {
125                EnumKind::Mixed => "Object",
126                EnumKind::Str => "String",
127                EnumKind::Num => "Number",
128            })
129    }
130}
131
132/// https://github.com/leonardfactory/babel-plugin-transform-typescript-metadata/blob/master/src/metadata/metadataVisitor.ts
133pub(super) struct Metadata<'a> {
134    pub(super) enums: EnumMap<'a>,
135
136    pub(super) class_name: Option<&'a Ident>,
137}
138
139impl VisitMut for Metadata<'_> {
140    fn visit_mut_class(&mut self, c: &mut Class) {
141        c.visit_mut_children_with(self);
142
143        if c.decorators.is_empty() {
144            return;
145        }
146
147        let constructor = c.body.iter().find_map(|m| match m {
148            ClassMember::Constructor(c) => Some(c),
149            _ => None,
150        });
151        if constructor.is_none() {
152            return;
153        }
154
155        {
156            let dec = self
157                .create_metadata_design_decorator("design:type", quote_ident!("Function").as_arg());
158            c.decorators.push(dec);
159        }
160        {
161            let dec = self.create_metadata_design_decorator(
162                "design:paramtypes",
163                ArrayLit {
164                    span: DUMMY_SP,
165                    elems: constructor
166                        .as_ref()
167                        .unwrap()
168                        .params
169                        .iter()
170                        .map(|v| match v {
171                            ParamOrTsParamProp::TsParamProp(p) => {
172                                let ann = match &p.param {
173                                    TsParamPropParam::Ident(i) => i.type_ann.as_deref(),
174                                    TsParamPropParam::Assign(a) => get_type_ann_of_pat(&a.left),
175                                    #[cfg(swc_ast_unknown)]
176                                    _ => panic!("unable to access unknown nodes"),
177                                };
178                                Some(if let Some(kind) = self.enums.get_kind_as_str(ann) {
179                                    quote_ident!(kind).as_arg()
180                                } else {
181                                    serialize_type(self.class_name, ann).as_arg()
182                                })
183                            }
184                            ParamOrTsParamProp::Param(p) => {
185                                let param_type = get_type_ann_of_pat(&p.pat);
186                                Some(if let Some(kind) = self.enums.get_kind_as_str(param_type) {
187                                    quote_ident!(kind).as_arg()
188                                } else {
189                                    serialize_type(self.class_name, param_type).as_arg()
190                                })
191                            }
192                            #[cfg(swc_ast_unknown)]
193                            _ => panic!("unable to access unknown nodes"),
194                        })
195                        .collect(),
196                }
197                .as_arg(),
198            );
199            c.decorators.push(dec);
200        }
201    }
202
203    fn visit_mut_class_method(&mut self, m: &mut ClassMethod) {
204        if m.function.decorators.is_empty() {
205            return;
206        }
207
208        {
209            let type_arg = match m.kind {
210                MethodKind::Method => quote_ident!("Function").as_arg(),
211                MethodKind::Getter => {
212                    let return_type = m.function.return_type.as_deref();
213
214                    if let Some(kind) = self.enums.get_kind_as_str(return_type) {
215                        quote_ident!(kind).as_arg()
216                    } else {
217                        serialize_type(self.class_name, return_type).as_arg()
218                    }
219                }
220                MethodKind::Setter => serialize_type(
221                    self.class_name,
222                    get_type_ann_of_pat(&m.function.params[0].pat),
223                )
224                .as_arg(),
225                #[cfg(swc_ast_unknown)]
226                _ => panic!("unable to access unknown nodes"),
227            };
228
229            let dec = self.create_metadata_design_decorator("design:type", type_arg);
230            m.function.decorators.push(dec);
231        }
232        {
233            let dec = self.create_metadata_design_decorator(
234                "design:paramtypes",
235                ArrayLit {
236                    span: DUMMY_SP,
237                    elems: m
238                        .function
239                        .params
240                        .iter()
241                        .map(|v| {
242                            let param_type = get_type_ann_of_pat(&v.pat);
243                            Some(if let Some(kind) = self.enums.get_kind_as_str(param_type) {
244                                quote_ident!(kind).as_arg()
245                            } else {
246                                serialize_type(self.class_name, param_type).as_arg()
247                            })
248                        })
249                        .collect(),
250                }
251                .as_arg(),
252            );
253            m.function.decorators.push(dec);
254        }
255
256        // https://github.com/microsoft/TypeScript/blob/2a8865e6ba95c9bdcdb9e2c9c08f10c5f5c75391/src/compiler/transformers/ts.ts#L1180
257        if m.kind == MethodKind::Method {
258            // Copy tsc behaviour
259            // https://github.com/microsoft/TypeScript/blob/5e8c261b6ab746213f19ee3501eb8c48a6215dd7/src/compiler/transformers/typeSerializer.ts#L242
260            let dec = self.create_metadata_design_decorator(
261                "design:returntype",
262                if m.function.is_async {
263                    quote_ident!("Promise").as_arg()
264                } else {
265                    let return_type = m.function.return_type.as_deref();
266
267                    if let Some(kind) = self.enums.get_kind_as_str(return_type) {
268                        quote_ident!(kind).as_arg()
269                    } else {
270                        serialize_type(self.class_name, return_type).as_arg()
271                    }
272                },
273            );
274            m.function.decorators.push(dec);
275        }
276    }
277
278    fn visit_mut_class_prop(&mut self, p: &mut ClassProp) {
279        if p.decorators.is_empty() || p.type_ann.is_none() {
280            return;
281        }
282
283        let dec = self.create_metadata_design_decorator("design:type", {
284            let prop_type = p.type_ann.as_deref();
285
286            if let Some(kind) = self.enums.get_kind_as_str(prop_type) {
287                quote_ident!(kind).as_arg()
288            } else {
289                serialize_type(self.class_name, prop_type).as_arg()
290            }
291        });
292        p.decorators.push(dec);
293    }
294}
295
296impl<'a> Metadata<'a> {
297    pub(super) fn new(enums: &'a EnumMapType, class_name: Option<&'a Ident>) -> Self {
298        Self {
299            enums: EnumMap(enums),
300            class_name,
301        }
302    }
303
304    fn create_metadata_design_decorator(&self, design: &str, type_arg: ExprOrSpread) -> Decorator {
305        Decorator {
306            span: DUMMY_SP,
307            expr: CallExpr {
308                span: DUMMY_SP,
309                callee: helper!(ts, ts_metadata),
310                args: vec![design.as_arg(), type_arg],
311                ..Default::default()
312            }
313            .into(),
314        }
315    }
316}
317
318fn serialize_type(class_name: Option<&Ident>, param: Option<&TsTypeAnn>) -> Expr {
319    fn check_object_existed(expr: Box<Expr>) -> Box<Expr> {
320        match *expr {
321            Expr::Member(ref member_expr) => {
322                let obj_expr = member_expr.obj.clone();
323                BinExpr {
324                    span: DUMMY_SP,
325                    left: check_object_existed(obj_expr),
326                    op: op!("||"),
327                    right: Box::new(
328                        BinExpr {
329                            span: DUMMY_SP,
330                            left: Box::new(Expr::Unary(UnaryExpr {
331                                span: DUMMY_SP,
332                                op: op!("typeof"),
333                                arg: expr,
334                            })),
335                            op: op!("==="),
336                            right: Box::new(Expr::Lit(Lit::Str(Str {
337                                span: DUMMY_SP,
338                                value: atom!("undefined").into(),
339                                raw: None,
340                            }))),
341                        }
342                        .into(),
343                    ),
344                }
345                .into()
346            }
347            _ => BinExpr {
348                span: DUMMY_SP,
349                left: Box::new(
350                    UnaryExpr {
351                        span: DUMMY_SP,
352                        op: op!("typeof"),
353                        arg: expr,
354                    }
355                    .into(),
356                ),
357                op: op!("==="),
358                right: Box::new(
359                    Lit::Str(Str {
360                        span: DUMMY_SP,
361                        value: atom!("undefined").into(),
362                        raw: None,
363                    })
364                    .into(),
365                ),
366            }
367            .into(),
368        }
369    }
370
371    fn serialize_type_ref(class_name: &str, ty: &TsTypeRef) -> Expr {
372        match &ty.type_name {
373            // We should omit references to self (class) since it will throw a ReferenceError at
374            // runtime due to babel transpile output.
375            TsEntityName::Ident(i) if &*i.sym == class_name => {
376                return quote_ident!("Object").into()
377            }
378            _ => {}
379        }
380
381        let member_expr = ts_entity_to_member_expr(&ty.type_name);
382
383        // We don't know if type is just a type (interface, etc.) or a concrete value
384        // (class, etc.)
385        //
386        // `typeof` operator allows us to use the expression even if it is not defined,
387        // fallback is just `Object`.
388
389        CondExpr {
390            span: DUMMY_SP,
391            test: check_object_existed(Box::new(member_expr.clone())),
392            cons: Box::new(quote_ident!("Object").into()),
393            alt: Box::new(member_expr),
394        }
395        .into()
396    }
397
398    fn serialize_type_list(class_name: &str, types: &[Box<TsType>]) -> Expr {
399        let mut u = None;
400        for ty in types {
401            // Skip parens if need be
402            let ty = match &**ty {
403                TsType::TsParenthesizedType(ty) => &ty.type_ann,
404                _ => ty,
405            };
406            match &**ty {
407                // Always elide `never` from the union/intersection if possible
408                TsType::TsKeywordType(TsKeywordType {
409                    kind: TsKeywordTypeKind::TsNeverKeyword,
410                    ..
411                }) => {
412                    continue;
413                }
414
415                // Elide null and undefined from unions for metadata, just like what we did prior to
416                // the implementation of strict null checks
417                TsType::TsKeywordType(TsKeywordType {
418                    kind: TsKeywordTypeKind::TsNullKeyword,
419                    ..
420                })
421                | TsType::TsKeywordType(TsKeywordType {
422                    kind: TsKeywordTypeKind::TsUndefinedKeyword,
423                    ..
424                }) => {
425                    return quote_ident!("Object").into();
426                }
427
428                _ => {}
429            }
430
431            let item = serialize_type_node(class_name, ty);
432
433            // One of the individual is global object, return immediately
434            if item.is_ident_ref_to("Object") {
435                return item;
436            }
437
438            // If there exists union that is not void 0 expression, check if the
439            // the common type is identifier. anything more complex
440            // and we will just default to Object
441
442            //
443            match &u {
444                None => {
445                    u = Some(item);
446                }
447
448                Some(prev) => {
449                    // Check for different types
450                    match prev {
451                        Expr::Ident(prev) => match &item {
452                            Expr::Ident(item) if prev.sym == item.sym => {}
453                            _ => return quote_ident!("Object").into(),
454                        },
455
456                        _ => return quote_ident!("Object").into(),
457                    }
458                }
459            }
460        }
461
462        match u {
463            Some(i) => i,
464            _ => quote_ident!("Object").into(),
465        }
466    }
467
468    fn serialize_type_node(class_name: &str, ty: &TsType) -> Expr {
469        let span = ty.span();
470        match ty {
471            TsType::TsKeywordType(TsKeywordType {
472                kind: TsKeywordTypeKind::TsVoidKeyword,
473                ..
474            })
475            | TsType::TsKeywordType(TsKeywordType {
476                kind: TsKeywordTypeKind::TsUndefinedKeyword,
477                ..
478            })
479            | TsType::TsKeywordType(TsKeywordType {
480                kind: TsKeywordTypeKind::TsNullKeyword,
481                ..
482            })
483            | TsType::TsKeywordType(TsKeywordType {
484                kind: TsKeywordTypeKind::TsNeverKeyword,
485                ..
486            }) => *Expr::undefined(span),
487
488            TsType::TsParenthesizedType(ty) => serialize_type_node(class_name, &ty.type_ann),
489
490            TsType::TsFnOrConstructorType(_) => quote_ident!("Function").into(),
491
492            TsType::TsArrayType(_) | TsType::TsTupleType(_) => quote_ident!("Array").into(),
493
494            TsType::TsLitType(TsLitType {
495                lit: TsLit::Bool(..),
496                ..
497            })
498            | TsType::TsTypePredicate(_)
499            | TsType::TsKeywordType(TsKeywordType {
500                kind: TsKeywordTypeKind::TsBooleanKeyword,
501                ..
502            }) => quote_ident!("Boolean").into(),
503
504            ty if is_str(ty) => quote_ident!("String").into(),
505
506            TsType::TsKeywordType(TsKeywordType {
507                kind: TsKeywordTypeKind::TsObjectKeyword,
508                ..
509            }) => quote_ident!("Object").into(),
510
511            TsType::TsLitType(TsLitType {
512                lit: TsLit::Number(..),
513                ..
514            })
515            | TsType::TsKeywordType(TsKeywordType {
516                kind: TsKeywordTypeKind::TsNumberKeyword,
517                ..
518            }) => quote_ident!("Number").into(),
519
520            TsType::TsKeywordType(TsKeywordType {
521                kind: TsKeywordTypeKind::TsBigIntKeyword,
522                ..
523            }) => CondExpr {
524                span: DUMMY_SP,
525                test: check_object_existed(quote_ident!("BigInt").into()),
526                cons: quote_ident!("Object").into(),
527                alt: quote_ident!("BigInt").into(),
528            }
529            .into(),
530
531            TsType::TsLitType(ty) => {
532                // TODO: Proper error reporting
533                panic!("Bad type for decoration: {ty:?}");
534            }
535
536            TsType::TsKeywordType(TsKeywordType {
537                kind: TsKeywordTypeKind::TsSymbolKeyword,
538                ..
539            }) => quote_ident!("Symbol").into(),
540
541            TsType::TsTypeQuery(_)
542            | TsType::TsTypeOperator(_)
543            | TsType::TsIndexedAccessType(_)
544            | TsType::TsTypeLit(_)
545            | TsType::TsMappedType(_)
546            | TsType::TsKeywordType(TsKeywordType {
547                kind: TsKeywordTypeKind::TsAnyKeyword,
548                ..
549            })
550            | TsType::TsKeywordType(TsKeywordType {
551                kind: TsKeywordTypeKind::TsUnknownKeyword,
552                ..
553            })
554            | TsType::TsThisType(..) => quote_ident!("Object").into(),
555
556            TsType::TsUnionOrIntersectionType(ty) => match ty {
557                TsUnionOrIntersectionType::TsUnionType(ty) => {
558                    serialize_type_list(class_name, &ty.types)
559                }
560                TsUnionOrIntersectionType::TsIntersectionType(ty) => {
561                    serialize_type_list(class_name, &ty.types)
562                }
563                #[cfg(swc_ast_unknown)]
564                _ => panic!("unable to access unknown nodes"),
565            },
566
567            TsType::TsConditionalType(ty) => {
568                serialize_type_list(class_name, &[ty.true_type.clone(), ty.false_type.clone()])
569            }
570
571            TsType::TsTypeRef(ty) => serialize_type_ref(class_name, ty),
572
573            _ => panic!("Bad type for decorator: {ty:?}"),
574        }
575    }
576
577    let param = match param {
578        Some(v) => &v.type_ann,
579        None => return *Expr::undefined(DUMMY_SP),
580    };
581
582    serialize_type_node(class_name.map(|v| &*v.sym).unwrap_or(""), param)
583}
584
585fn ts_entity_to_member_expr(type_name: &TsEntityName) -> Expr {
586    match type_name {
587        TsEntityName::TsQualifiedName(q) => {
588            let obj = ts_entity_to_member_expr(&q.left);
589
590            MemberExpr {
591                span: DUMMY_SP,
592                obj: obj.into(),
593                prop: MemberProp::Ident(q.right.clone()),
594            }
595            .into()
596        }
597        TsEntityName::Ident(i) => i.clone().with_pos(BytePos::DUMMY, BytePos::DUMMY).into(),
598        #[cfg(swc_ast_unknown)]
599        _ => panic!("unable to access unknown nodes"),
600    }
601}
602
603fn get_type_ann_of_pat(p: &Pat) -> Option<&TsTypeAnn> {
604    match p {
605        Pat::Ident(p) => p.type_ann.as_deref(),
606        Pat::Array(p) => p.type_ann.as_deref(),
607        Pat::Rest(p) => p.type_ann.as_deref(),
608        Pat::Object(p) => p.type_ann.as_deref(),
609        Pat::Assign(p) => get_type_ann_of_pat(&p.left),
610        Pat::Invalid(_) => None,
611        Pat::Expr(_) => None,
612        #[cfg(swc_ast_unknown)]
613        _ => panic!("unable to access unknown nodes"),
614    }
615}
616
617fn is_str(ty: &TsType) -> bool {
618    match ty {
619        TsType::TsLitType(TsLitType {
620            lit: TsLit::Str(..) | TsLit::Tpl(..),
621            ..
622        })
623        | TsType::TsKeywordType(TsKeywordType {
624            kind: TsKeywordTypeKind::TsStringKeyword,
625            ..
626        }) => true,
627
628        TsType::TsUnionOrIntersectionType(TsUnionOrIntersectionType::TsUnionType(u)) => {
629            u.types.iter().all(|ty| is_str(ty))
630        }
631
632        _ => false,
633    }
634}