swc_ecma_transforms_proposal/decorators/legacy/
mod.rs1use std::{iter, mem};
2
3use metadata::remove_span;
4use rustc_hash::FxHashMap;
5use swc_atoms::Atom;
6use swc_common::{util::take::Take, BytePos, DUMMY_SP};
7use swc_ecma_ast::*;
8use swc_ecma_transforms_base::helper;
9use swc_ecma_utils::{private_ident, prop_name_to_expr_value, quote_ident, ExprFactory, StmtLike};
10use swc_ecma_visit::{Visit, VisitMut, VisitMutWith, VisitWith};
11
12use self::metadata::{Metadata, ParamMetadata};
13use super::contains_decorator;
14
15mod metadata;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18enum EnumKind {
19 Mixed,
20 Str,
21 Num,
22}
23
24pub(super) fn new(metadata: bool) -> TscDecorator {
25 TscDecorator {
26 metadata,
27 enums: Default::default(),
28 vars: Default::default(),
29 appended_exprs: Default::default(),
30 appended_private_access_exprs: Default::default(),
31 prepended_exprs: Default::default(),
32 class_name: Default::default(),
33
34 assign_class_expr_to: Default::default(),
35 }
36}
37
38pub(super) struct TscDecorator {
39 metadata: bool,
40
41 enums: FxHashMap<Atom, EnumKind>,
42
43 vars: Vec<VarDeclarator>,
45 appended_exprs: Vec<Box<Expr>>,
46 appended_private_access_exprs: Vec<Box<Expr>>,
47 prepended_exprs: Vec<Box<Expr>>,
48
49 class_name: Option<Ident>,
50
51 assign_class_expr_to: Option<Ident>,
52}
53
54impl TscDecorator {
55 fn visit_mut_stmt_likes<T>(&mut self, stmts: &mut Vec<T>)
56 where
57 T: StmtLike + VisitMutWith<Self>,
58 {
59 let old_vars = self.vars.take();
60 let old_appended_exprs = self.appended_exprs.take();
61 let old_prepended_exprs = self.prepended_exprs.take();
62
63 let mut new = Vec::new();
64
65 for mut s in stmts.take() {
66 debug_assert!(self.appended_exprs.is_empty());
67
68 s.visit_mut_with(self);
69
70 if !self.vars.is_empty() {
71 new.push(T::from(
72 VarDecl {
73 span: DUMMY_SP,
74 kind: VarDeclKind::Var,
75 declare: Default::default(),
76 decls: self.vars.take(),
77 ..Default::default()
78 }
79 .into(),
80 ));
81 }
82
83 new.extend(
84 self.prepended_exprs
85 .drain(..)
86 .map(|expr| {
87 ExprStmt {
88 span: DUMMY_SP,
89 expr,
90 }
91 .into()
92 })
93 .map(T::from),
94 );
95
96 new.push(s);
97
98 new.extend(
99 self.appended_exprs
100 .drain(..)
101 .map(|expr| {
102 ExprStmt {
103 span: DUMMY_SP,
104 expr,
105 }
106 .into()
107 })
108 .map(T::from),
109 );
110 }
111
112 *stmts = new;
113
114 self.prepended_exprs = old_prepended_exprs;
115 self.appended_exprs = old_appended_exprs;
116 self.vars = old_vars;
117 }
118
119 fn key(&mut self, k: &mut PropName) -> Expr {
120 match k {
121 PropName::Computed(k) if !k.expr.is_lit() => {
122 let var_name = private_ident!(k.span, "_key");
123
124 self.vars.push(VarDeclarator {
126 span: DUMMY_SP,
127 name: var_name.clone().into(),
128 init: None,
129 definite: Default::default(),
130 });
131
132 self.prepended_exprs.push(
134 AssignExpr {
135 span: DUMMY_SP,
136 op: op!("="),
137 left: var_name.clone().into(),
138 right: k.expr.take(),
139 }
140 .into(),
141 );
142
143 k.expr = var_name.clone().into();
144
145 return var_name.into();
146 }
147 PropName::Ident(i) => {
148 return Lit::Str(Str {
149 span: DUMMY_SP,
150 raw: None,
151 value: i.sym.clone().into(),
152 })
153 .into()
154 }
155 _ => {}
156 }
157
158 prop_name_to_expr_value(k.clone())
159 }
160
161 fn has_private_access(mut expr: &Expr) -> bool {
162 while let Some(MemberExpr { obj, prop, .. }) = expr.as_member() {
163 if prop.is_private_name() {
164 return true;
165 }
166 expr = obj;
167 }
168
169 false
170 }
171
172 fn add_decorate_call(
174 &mut self,
175 decorators: impl IntoIterator<Item = Box<Expr>>,
176 mut target: ExprOrSpread,
177 key: ExprOrSpread,
178 mut desc: ExprOrSpread,
179 ) {
180 let mut has_private_access = false;
181 let decorators = ArrayLit {
182 span: DUMMY_SP,
183 elems: decorators
184 .into_iter()
185 .inspect(|e| {
186 if has_private_access {
187 return;
188 }
189 has_private_access = Self::has_private_access(e);
190 })
191 .map(|mut v| {
192 remove_span(&mut v);
193
194 v.as_arg()
195 })
196 .map(Some)
197 .collect(),
198 }
199 .as_arg();
200
201 remove_span(&mut target.expr);
202 remove_span(&mut desc.expr);
203
204 let expr = CallExpr {
205 callee: helper!(ts, ts_decorate),
206 args: vec![decorators, target, key, desc],
207 ..Default::default()
208 }
209 .into();
210
211 if has_private_access {
212 self.appended_private_access_exprs.push(expr);
213 } else {
214 self.appended_exprs.push(expr);
215 }
216 }
217}
218
219impl Visit for TscDecorator {
220 fn visit_ts_enum_decl(&mut self, e: &TsEnumDecl) {
221 let enum_kind = e
222 .members
223 .iter()
224 .map(|member| member.init.as_ref())
225 .map(|init| match init {
226 Some(e) => match &**e {
227 Expr::Unary(UnaryExpr {
228 op: op!(unary, "-"),
229 ..
230 }) => EnumKind::Num,
231 Expr::Lit(lit) => match lit {
232 Lit::Str(_) => EnumKind::Str,
233 Lit::Num(_) => EnumKind::Num,
234 _ => EnumKind::Mixed,
235 },
236 _ => EnumKind::Mixed,
237 },
238 None => EnumKind::Num,
239 })
240 .fold(None, |opt: Option<EnumKind>, item| {
241 let a = match item {
243 EnumKind::Mixed => return Some(EnumKind::Mixed),
244 _ => item,
245 };
246
247 let b = match opt {
248 Some(EnumKind::Mixed) => return Some(EnumKind::Mixed),
249 Some(v) => v,
250 None => return Some(item),
251 };
252 if a == b {
253 Some(a)
254 } else {
255 Some(EnumKind::Mixed)
256 }
257 });
258 if let Some(kind) = enum_kind {
259 self.enums.insert(e.id.sym.clone(), kind);
260 }
261 }
262}
263
264impl VisitMut for TscDecorator {
265 fn visit_mut_class(&mut self, n: &mut Class) {
266 let appended_private = self.appended_private_access_exprs.take();
267
268 n.visit_mut_with(&mut ParamMetadata);
269
270 if self.metadata {
271 let i = self.class_name.clone();
272
273 n.visit_mut_with(&mut Metadata::new(&self.enums, i.as_ref()));
274 }
275
276 n.visit_mut_children_with(self);
277
278 let appended_private =
279 mem::replace(&mut self.appended_private_access_exprs, appended_private);
280
281 if !appended_private.is_empty() {
282 let expr = if appended_private.len() == 1 {
283 *appended_private.into_iter().next().unwrap()
284 } else {
285 SeqExpr {
286 exprs: appended_private,
287 ..Default::default()
288 }
289 .into()
290 };
291
292 n.body.push(
293 StaticBlock {
294 body: BlockStmt {
295 stmts: vec![expr.into_stmt()],
296 ..Default::default()
297 },
298 ..Default::default()
299 }
300 .into(),
301 )
302 }
303
304 if let Some(class_name) = self.class_name.clone() {
305 if !n.decorators.is_empty() {
306 let decorators = ArrayLit {
307 span: DUMMY_SP,
308 elems: n
309 .decorators
310 .take()
311 .into_iter()
312 .map(|mut v| {
313 remove_span(&mut v.expr);
314
315 v.expr.as_arg()
316 })
317 .map(Some)
318 .collect(),
319 }
320 .as_arg();
321
322 let decorated = CallExpr {
323 span: DUMMY_SP,
324 callee: helper!(ts, ts_decorate),
325 args: vec![
326 decorators,
327 class_name
328 .clone()
329 .with_pos(BytePos::DUMMY, BytePos::DUMMY)
330 .as_arg(),
331 ],
332 ..Default::default()
333 }
334 .into();
335 self.appended_exprs.push(
336 AssignExpr {
337 span: DUMMY_SP,
338 op: op!("="),
339 left: class_name.with_pos(BytePos::DUMMY, BytePos::DUMMY).into(),
340 right: decorated,
341 }
342 .into(),
343 );
344 }
345 }
346 }
347
348 fn visit_mut_class_decl(&mut self, n: &mut ClassDecl) {
349 let old = self.class_name.replace(n.ident.clone());
350
351 n.visit_mut_children_with(self);
352
353 self.class_name = old;
354 }
355
356 fn visit_mut_expr(&mut self, e: &mut Expr) {
357 let appended_exprs = mem::take(&mut self.appended_exprs);
358 e.visit_mut_children_with(self);
359 let appended_exprs = mem::replace(&mut self.appended_exprs, appended_exprs);
360
361 if let Some(var_name) = self.assign_class_expr_to.take() {
362 self.vars.push(VarDeclarator {
363 span: DUMMY_SP,
364 name: var_name.clone().into(),
365 init: None,
366 definite: Default::default(),
367 });
368
369 *e = SeqExpr {
370 span: DUMMY_SP,
371 exprs: iter::once(AssignExpr {
372 span: DUMMY_SP,
373 op: op!("="),
374 left: var_name.clone().into(),
375 right: Box::new(e.take()),
376 })
377 .map(Into::into)
378 .chain(appended_exprs)
379 .chain(iter::once(var_name.into()))
380 .collect(),
381 }
382 .into();
383 }
384 }
385
386 fn visit_mut_class_expr(&mut self, n: &mut ClassExpr) {
387 if !contains_decorator(n) {
388 return;
389 }
390
391 let ident = n
392 .ident
393 .get_or_insert_with(|| private_ident!("_class"))
394 .clone();
395
396 let old = self.class_name.replace(ident.clone());
397
398 n.visit_mut_children_with(self);
399
400 self.assign_class_expr_to = Some(ident);
401
402 self.class_name = old;
403 }
404
405 fn visit_mut_export_default_decl(&mut self, n: &mut ExportDefaultDecl) {
406 n.visit_mut_children_with(self);
407 self.assign_class_expr_to = None;
409 }
410
411 fn visit_mut_class_method(&mut self, c: &mut ClassMethod) {
412 c.visit_mut_children_with(self);
413
414 if let Some(class_name) = self.class_name.clone() {
415 if !c.function.decorators.is_empty() {
416 let key = self.key(&mut c.key);
417
418 let target = if c.is_static {
419 class_name.as_arg()
420 } else {
421 class_name.make_member(quote_ident!("prototype")).as_arg()
422 };
423
424 self.add_decorate_call(
425 c.function.decorators.drain(..).map(|d| d.expr),
426 target,
427 key.as_arg(),
428 Lit::Null(Null::dummy()).as_arg(),
429 );
430 }
431 }
432 }
433
434 fn visit_mut_class_prop(&mut self, c: &mut ClassProp) {
435 c.visit_mut_children_with(self);
436
437 if let Some(class_name) = self.class_name.clone() {
438 if !c.decorators.is_empty() {
439 let key = self.key(&mut c.key);
440
441 let target = if c.is_static {
442 class_name.as_arg()
443 } else {
444 class_name.make_member(quote_ident!("prototype")).as_arg()
445 };
446
447 self.add_decorate_call(
448 c.decorators.drain(..).map(|d| d.expr),
449 target,
450 key.as_arg(),
451 Expr::undefined(DUMMY_SP).as_arg(),
452 );
453 }
454 }
455 }
456
457 fn visit_mut_module(&mut self, n: &mut Module) {
458 n.visit_with(self);
459
460 n.visit_mut_children_with(self);
461 }
462
463 fn visit_mut_module_items(&mut self, s: &mut Vec<ModuleItem>) {
464 self.visit_mut_stmt_likes(s);
465 }
466
467 fn visit_mut_script(&mut self, n: &mut Script) {
468 n.visit_with(self);
469
470 n.visit_mut_children_with(self);
471 }
472
473 fn visit_mut_stmts(&mut self, s: &mut Vec<Stmt>) {
474 self.visit_mut_stmt_likes(s)
475 }
476}