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
16pub(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 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
132pub(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 if m.kind == MethodKind::Method {
258 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 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 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 let ty = match &**ty {
403 TsType::TsParenthesizedType(ty) => &ty.type_ann,
404 _ => ty,
405 };
406 match &**ty {
407 TsType::TsKeywordType(TsKeywordType {
409 kind: TsKeywordTypeKind::TsNeverKeyword,
410 ..
411 }) => {
412 continue;
413 }
414
415 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 if item.is_ident_ref_to("Object") {
435 return item;
436 }
437
438 match &u {
444 None => {
445 u = Some(item);
446 }
447
448 Some(prev) => {
449 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 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}