1use std::iter;
2
3use rustc_hash::FxBuildHasher;
4use serde::Deserialize;
5use swc_atoms::{atom, Atom};
6use swc_common::{util::take::Take, BytePos, Mark, Span, Spanned, SyntaxContext, DUMMY_SP};
7use swc_ecma_ast::*;
8use swc_ecma_transforms_base::{helper, native::is_native, perf::Check};
9use swc_ecma_transforms_classes::super_field::SuperFieldAccessFolder;
10use swc_ecma_transforms_macros::fast_path;
11use swc_ecma_utils::{
12 alias_if_required, contains_this_expr, is_valid_ident, is_valid_prop_ident, prepend_stmt,
13 private_ident, prop_name_to_expr, quote_expr, quote_ident, quote_str, replace_ident,
14 ExprFactory, ModuleItemLike, StmtLike,
15};
16use swc_ecma_visit::{
17 noop_visit_mut_type, noop_visit_type, visit_mut_pass, Visit, VisitMut, VisitMutWith,
18};
19use swc_trace_macro::swc_trace;
20
21use self::{
22 constructor::fold_constructor,
23 prop_name::{is_pure_prop_name, should_extract_class_prop_key, HashKey},
24};
25
26mod constructor;
27mod prop_name;
28
29pub fn classes(config: Config) -> impl Pass {
30 visit_mut_pass(Classes {
31 in_strict: false,
32 config,
33
34 params: Default::default(),
35 args: Default::default(),
36 })
37}
38
39type IndexMap<K, V> = indexmap::IndexMap<K, V, FxBuildHasher>;
40
41#[derive(Default, Clone)]
73struct Classes {
74 in_strict: bool,
75 config: Config,
76
77 params: Vec<Param>,
78 args: Vec<ExprOrSpread>,
79}
80
81#[derive(Debug, Clone, Copy, Default, Deserialize)]
82#[serde(rename_all = "camelCase")]
83pub struct Config {
84 #[serde(default)]
85 pub constant_super: bool,
86 #[serde(default)]
87 pub no_class_calls: bool,
88 #[serde(default)]
89 pub set_class_methods: bool,
90 #[serde(default)]
91 pub super_is_callable_constructor: bool,
92}
93
94struct Data {
95 key_prop: Box<PropName>,
96 method: Option<Box<Expr>>,
97 set: Option<Box<Expr>>,
98 get: Option<Box<Expr>>,
99}
100
101#[swc_trace]
102impl Classes {
103 fn visit_mut_stmt_like<T>(&mut self, stmts: &mut Vec<T>)
104 where
105 T: StmtLike + ModuleItemLike + VisitMutWith<Self> + Take,
106 {
107 let mut buf = Vec::with_capacity(stmts.len());
108 let mut first = true;
109 let old = self.in_strict;
110
111 for stmt in stmts.iter_mut() {
112 match T::try_into_stmt(stmt.take()) {
113 Err(node) => match node.try_into_module_decl() {
114 Ok(mut decl) => {
115 match decl {
116 ModuleDecl::ExportDefaultDecl(ExportDefaultDecl {
117 decl: DefaultDecl::Class(ClassExpr { ident, class }),
118 ..
119 }) => {
120 let ident =
121 ident.unwrap_or_else(|| quote_ident!("_default").into());
122
123 let mut decl = self.fold_class_as_var_decl(ident.clone(), class);
124 decl.visit_mut_children_with(self);
125 buf.push(T::from(decl.into()));
126
127 buf.push(
128 match T::try_from_module_decl(
129 NamedExport {
130 span: DUMMY_SP,
131 specifiers: vec![ExportNamedSpecifier {
132 span: DUMMY_SP,
133 orig: ModuleExportName::Ident(ident),
134 exported: Some(ModuleExportName::Ident(
135 quote_ident!("default").into(),
136 )),
137 is_type_only: false,
138 }
139 .into()],
140 src: None,
141 type_only: false,
142 with: None,
143 }
144 .into(),
145 ) {
146 Ok(t) => t,
147 Err(..) => unreachable!(),
148 },
149 );
150 }
151 ModuleDecl::ExportDecl(ExportDecl {
152 span,
153 decl:
154 Decl::Class(ClassDecl {
155 ident,
156 declare: false,
157 class,
158 }),
159 ..
160 }) => {
161 let mut decl = self.fold_class_as_var_decl(ident, class);
162 decl.visit_mut_children_with(self);
163 buf.push(
164 match T::try_from_module_decl(
165 ExportDecl {
166 span,
167 decl: decl.into(),
168 }
169 .into(),
170 ) {
171 Ok(t) => t,
172 Err(..) => unreachable!(),
173 },
174 );
175 }
176 _ => buf.push({
177 decl.visit_mut_children_with(self);
178 match T::try_from_module_decl(decl) {
179 Ok(t) => t,
180 Err(..) => unreachable!(),
181 }
182 }),
183 };
184 }
185 Err(..) => unreachable!(),
186 },
187 Ok(mut stmt) => {
188 if first {
189 self.in_strict |= stmt.is_use_strict();
190 }
191
192 stmt.visit_mut_children_with(self);
193 buf.push(T::from(stmt));
194 }
195 }
196 first = false;
197 }
198
199 self.in_strict = old;
200 *stmts = buf;
201 }
202}
203
204#[swc_trace]
205#[fast_path(ClassFinder)]
206impl VisitMut for Classes {
207 noop_visit_mut_type!(fail);
208
209 fn visit_mut_module_items(&mut self, items: &mut Vec<ModuleItem>) {
210 self.visit_mut_stmt_like(items)
211 }
212
213 fn visit_mut_stmts(&mut self, items: &mut Vec<Stmt>) {
214 self.visit_mut_stmt_like(items)
215 }
216
217 fn visit_mut_decl(&mut self, n: &mut Decl) {
218 if let Decl::Class(decl) = n {
219 *n = self
220 .fold_class_as_var_decl(decl.ident.take(), decl.class.take())
221 .into()
222 };
223
224 n.visit_mut_children_with(self);
225 }
226
227 fn visit_mut_expr(&mut self, n: &mut Expr) {
228 match n {
229 Expr::Class(e) => {
230 let mut class = self.fold_class(e.ident.take(), e.class.take());
231 if let Expr::Call(call) = &mut class {
232 self.add_pure_comments(&mut call.span.lo)
233 }
234 *n = class;
235 n.visit_mut_children_with(self)
236 }
237
238 _ => n.visit_mut_children_with(self),
239 }
240 }
241
242 fn visit_mut_var_declarator(&mut self, d: &mut VarDeclarator) {
243 if let VarDeclarator {
245 name: Pat::Ident(i),
246 init: Some(init),
247 ..
248 } = d
249 {
250 if let Expr::Class(c @ ClassExpr { ident: None, .. }) = &mut **init {
251 c.ident = Some(Ident::from(&*i).into_private())
252 }
253 }
254
255 d.visit_mut_children_with(self)
256 }
257
258 fn visit_mut_assign_pat_prop(&mut self, n: &mut AssignPatProp) {
260 if let Some(value) = &mut n.value {
261 if let Expr::Class(c @ ClassExpr { ident: None, .. }) = &mut **value {
262 c.ident = Some(Ident::from(&n.key).into_private());
263 }
264 }
265
266 n.visit_mut_children_with(self);
267 }
268
269 fn visit_mut_assign_pat(&mut self, n: &mut AssignPat) {
272 if let (Pat::Ident(id), Expr::Class(c @ ClassExpr { ident: None, .. })) =
273 (&*n.left, &mut *n.right)
274 {
275 c.ident = Some(Ident::from(id).into_private());
276 }
277
278 n.visit_mut_children_with(self);
279 }
280
281 fn visit_mut_key_value_prop(&mut self, n: &mut KeyValueProp) {
287 if let Expr::Class(c @ ClassExpr { ident: None, .. }) = &mut *n.value {
288 match &n.key {
289 PropName::Ident(ident) => {
290 c.ident = Some(Ident::from(ident.clone()).into_private());
291 }
292 PropName::Str(Str { value, span, .. }) => {
293 if let Some(value_str) = value.as_str() {
294 if is_valid_prop_ident(value_str) {
295 c.ident = Some(private_ident!(*span, value_str));
296 }
297 }
298 }
299 PropName::Computed(ComputedPropName { expr, .. }) => {
300 if let Expr::Lit(Lit::Str(Str { value, span, .. })) = &**expr {
301 if let Some(value_str) = value.as_str() {
302 if is_valid_prop_ident(value_str) {
303 c.ident = Some(private_ident!(*span, value_str));
304 }
305 }
306 }
307 }
308 _ => {}
309 }
310 }
311
312 n.visit_mut_children_with(self)
313 }
314
315 fn visit_mut_assign_expr(&mut self, a: &mut AssignExpr) {
316 if let AssignExpr {
317 op: op!("=") | op!("||=") | op!("??="),
318 left,
319 right,
320 ..
321 } = a
322 {
323 if let Expr::Class(c @ ClassExpr { ident: None, .. }) = &mut **right {
324 if let AssignTarget::Simple(SimpleAssignTarget::Ident(ident)) = left {
325 c.ident = Some(Ident::from(&*ident).into_private())
326 }
327 }
328 }
329
330 a.visit_mut_children_with(self)
331 }
332}
333
334#[swc_trace]
335impl Classes {
336 fn add_pure_comments(&mut self, start: &mut BytePos) {
337 *start = BytePos::PURE;
338 }
339
340 fn fold_class_as_var_decl(&mut self, ident: Ident, class: Box<Class>) -> VarDecl {
341 let span = class.span;
342 let mut rhs = self.fold_class(Some(ident.clone()), class);
343
344 let mut new_name = ident.clone();
345 new_name.ctxt = new_name.ctxt.apply_mark(Mark::new());
346
347 replace_ident(&mut rhs, ident.to_id(), &new_name);
348
349 if let Expr::Call(call) = &mut rhs {
351 let mut span = Span {
352 lo: span.lo + BytePos(5),
354 ..span
355 };
356 self.add_pure_comments(&mut span.lo);
357 call.span = span;
358 }
359
360 VarDecl {
361 span,
362 kind: VarDeclKind::Let,
363 decls: vec![VarDeclarator {
364 span,
365 init: Some(Box::new(rhs)),
366 name: ident.into(),
368 definite: false,
369 }],
370 ..Default::default()
371 }
372 }
373
374 fn fold_class(&mut self, class_name: Option<Ident>, class: Box<Class>) -> Expr {
387 let span = class.span;
388
389 let super_ident = class
391 .super_class
392 .as_ref()
393 .map(|e| alias_if_required(e, "_superClass").0);
394 let has_super = super_ident.is_some();
395 let (mut params, mut args, super_ident) = if let Some(ref super_ident) = super_ident {
396 let super_param = private_ident!(super_ident.sym.clone());
398 let params = vec![Param {
399 span: DUMMY_SP,
400 decorators: Default::default(),
401 pat: super_param.clone().into(),
402 }];
403
404 let super_class = class.super_class.clone().unwrap();
405 let is_super_native = match *super_class {
406 Expr::Ident(Ident { ref sym, .. }) => is_native(sym),
407 _ => false,
408 };
409 if is_super_native {
410 (
411 params,
412 vec![CallExpr {
413 span: DUMMY_SP,
414 callee: helper!(wrap_native_super),
415 args: vec![super_class.as_arg()],
416 ..Default::default()
417 }
418 .as_arg()],
419 Some(super_param),
420 )
421 } else {
422 (params, vec![super_class.as_arg()], Some(super_param))
423 }
424 } else {
425 (Vec::new(), Vec::new(), None)
426 };
427
428 let mut stmts = self.class_to_stmts(class_name, super_ident, class);
429 params.extend(self.params.take());
430 args.extend(self.args.take());
431
432 let cnt_of_non_directive = stmts
433 .iter()
434 .filter(|stmt| match stmt {
435 Stmt::Expr(ExprStmt { expr, .. }) => !matches!(&**expr, Expr::Lit(Lit::Str(..))),
436 _ => true,
437 })
438 .count();
439 if !has_super && cnt_of_non_directive == 1 {
440 let stmt = stmts.pop().unwrap();
459 match stmt {
460 Stmt::Decl(Decl::Fn(FnDecl {
461 ident,
462 mut function,
463 ..
464 })) => {
465 if let Some(use_strict) = stmts.pop() {
466 prepend_stmt(&mut function.body.as_mut().unwrap().stmts, use_strict);
467 }
468 function.span = span;
469 return FnExpr {
470 ident: Some(ident),
471 function,
472 }
473 .into();
474 }
475 _ => unreachable!(),
476 }
477 }
478
479 let body = BlockStmt {
480 span: DUMMY_SP,
481 stmts,
482 ..Default::default()
483 };
484
485 let call = CallExpr {
486 span,
487 callee: Function {
488 span,
489 is_async: false,
490 is_generator: false,
491 params,
492 body: Some(body),
493 ..Default::default()
494 }
495 .as_callee(),
496 args,
497 ..Default::default()
498 };
499
500 call.into()
501 }
502
503 fn class_to_stmts(
505 &mut self,
506 class_name: Option<Ident>,
507 super_class_ident: Option<Ident>,
508 class: Box<Class>,
509 ) -> Vec<Stmt> {
510 let class_name = class_name.unwrap_or_else(|| quote_ident!("_class").into());
511 let mut stmts = Vec::new();
512
513 let mut methods = Vec::new();
514 let mut constructor = None;
515 for member in class.body {
516 match member {
517 ClassMember::Constructor(c) => constructor = Some(c),
518 ClassMember::Method(m) => methods.push(m),
519
520 ClassMember::PrivateMethod(_)
521 | ClassMember::ClassProp(..)
522 | ClassMember::PrivateProp(..)
523 | ClassMember::TsIndexSignature(..)
524 | ClassMember::StaticBlock(..)
525 | ClassMember::AutoAccessor(..) => {}
526 ClassMember::Empty(..) => {}
527
528 #[cfg(swc_ast_unknown)]
529 _ => panic!("unable to access unknown nodes"),
530 }
531 }
532
533 if let Some(ref super_class_ident) = super_class_ident {
534 let mut class_name_sym = class_name.clone();
537 class_name_sym.span = DUMMY_SP;
538 class_name_sym.ctxt = class_name.ctxt;
539
540 let mut super_class_name_sym = super_class_ident.clone();
541 super_class_name_sym.span = DUMMY_SP;
542 super_class_name_sym.ctxt = super_class_ident.ctxt;
543
544 stmts.push(
545 CallExpr {
546 span: DUMMY_SP,
547 callee: helper!(inherits),
548 args: vec![class_name_sym.as_arg(), super_class_name_sym.as_arg()],
549 ..Default::default()
550 }
551 .into_stmt(),
552 );
553 }
554
555 stmts.push(
557 fold_constructor(
558 class.span,
559 constructor,
560 &class_name,
561 &super_class_ident,
562 self.config,
563 )
564 .into(),
565 );
566
567 stmts.extend(self.fold_class_methods(&class_name, &super_class_ident, methods));
569
570 if stmts.first().map(|v| !v.is_use_strict()).unwrap_or(false) && !self.in_strict {
571 prepend_stmt(
572 &mut stmts,
573 Lit::Str(Str {
574 span: DUMMY_SP,
575 value: atom!("use strict").into(),
576 raw: Some(atom!("\"use strict\"")),
577 })
578 .into_stmt(),
579 );
580
581 if stmts.len() == 2 {
582 return stmts;
583 }
584 }
585
586 if super_class_ident.is_none()
587 && stmts
588 .iter()
589 .filter(|stmt| match stmt {
590 Stmt::Expr(ExprStmt { expr, .. }) => {
591 !matches!(&**expr, Expr::Lit(Lit::Str(..)))
592 }
593 _ => true,
594 })
595 .count()
596 == 1
597 {
598 return stmts;
599 }
600
601 let mut class_name_sym = class_name.clone();
602 class_name_sym.span = DUMMY_SP;
603 class_name_sym.ctxt = class_name.ctxt;
604
605 stmts.push(
607 ReturnStmt {
608 span: DUMMY_SP,
609 arg: Some(class_name_sym.into()),
610 }
611 .into(),
612 );
613
614 stmts
615 }
616
617 fn fold_class_methods(
618 &mut self,
619 class_name: &Ident,
620 super_class_ident: &Option<Ident>,
621 methods: Vec<ClassMethod>,
622 ) -> Vec<Stmt> {
623 if methods.is_empty() {
624 return Vec::new();
625 }
626
627 fn mk_key_prop(key: PropName) -> Box<Prop> {
629 Box::new(Prop::KeyValue(KeyValueProp {
630 key: PropName::Ident(quote_ident!(Default::default(), key.span(), "key").into()),
631 value: match key {
632 PropName::Ident(i) => Lit::Str(quote_str!(i.span, i.sym)).into(),
633 PropName::Str(s) => s.into(),
634 PropName::Num(n) => n.into(),
635 PropName::BigInt(b) => Str {
636 span: b.span,
637 raw: None,
638 value: b.value.to_string().into(),
639 }
640 .into(),
641 PropName::Computed(c) => c.expr,
642 #[cfg(swc_ast_unknown)]
643 _ => panic!("unable to access unknown nodes"),
644 },
645 }))
646 }
647
648 fn mk_key_prop_member(key: PropName) -> MemberProp {
649 match key {
650 PropName::Ident(i) => MemberProp::Ident(i),
651 PropName::Str(s) => MemberProp::Computed(ComputedPropName {
652 span: s.span,
653 expr: Lit::Str(s).into(),
654 }),
655 PropName::Num(n) => MemberProp::Computed(ComputedPropName {
656 span: n.span,
657 expr: Lit::Num(n).into(),
658 }),
659 PropName::BigInt(b) => MemberProp::Computed(ComputedPropName {
660 span: b.span,
661 expr: Str {
662 span: b.span,
663 raw: None,
664 value: b.value.to_string().into(),
665 }
666 .into(),
667 }),
668 PropName::Computed(c) => MemberProp::Computed(c),
669 #[cfg(swc_ast_unknown)]
670 _ => panic!("unable to access unknown nodes"),
671 }
672 }
673
674 fn mk_arg_obj_for_create_class(props: IndexMap<HashKey, Data>) -> ExprOrSpread {
675 if props.is_empty() {
676 return quote_expr!(DUMMY_SP, null).as_arg();
677 }
678 ArrayLit {
679 span: DUMMY_SP,
680 elems: props
681 .into_iter()
682 .map(|(_, data)| {
683 let mut props = vec![PropOrSpread::Prop(mk_key_prop(*data.key_prop))];
684
685 macro_rules! add {
686 ($field:expr, $kind:expr, $s:literal) => {{
687 if let Some(value) = $field {
688 let value = escape_keywords(value);
689 props.push(PropOrSpread::Prop(Box::new(Prop::KeyValue(
690 KeyValueProp {
691 key: PropName::Ident(quote_ident!($s)),
692 value,
693 },
694 ))));
695 }
696 }};
697 }
698
699 add!(data.get, MethodKind::Getter, "get");
700 add!(data.set, MethodKind::Setter, "set");
701 add!(data.method, MethodKind::Method, "value");
702
703 ObjectLit {
704 span: DUMMY_SP,
705 props,
706 }
707 .as_arg()
708 })
709 .map(Some)
710 .collect(),
711 }
712 .as_arg()
713 }
714
715 fn mk_create_class_call(
717 class_name: Ident,
718 methods: ExprOrSpread,
719 static_methods: Option<ExprOrSpread>,
720 ) -> Stmt {
721 let mut class_name_sym = class_name.clone();
722 class_name_sym.span = DUMMY_SP;
723 class_name_sym.ctxt = class_name.ctxt;
724
725 CallExpr {
726 span: DUMMY_SP,
727 callee: helper!(create_class),
728 args: iter::once(class_name_sym.as_arg())
729 .chain(iter::once(methods))
730 .chain(static_methods)
731 .collect(),
732 ..Default::default()
733 }
734 .into_stmt()
735 }
736
737 let (mut props, mut static_props) = (IndexMap::default(), IndexMap::default());
738
739 let should_extract = should_extract_class_prop_key(&methods);
740
741 for mut m in methods {
742 let key = HashKey::from(&m.key);
743 let key_is_pure = is_pure_prop_name(&m.key);
744 let key_contain_this = !self.in_strict && contains_this_expr(&m.key);
746 let key_prop = Box::new(m.key.clone());
747 let computed = matches!(m.key, PropName::Computed(..));
748 let prop_name = prop_name_to_expr(m.key);
749
750 let key_prop = if should_extract && !key_is_pure || key_contain_this {
751 let ident = private_ident!("_prop");
752
753 self.params.push(ident.clone().into());
754 self.args.push(prop_name.clone().into());
755
756 Box::new(PropName::Computed(ComputedPropName {
757 span: DUMMY_SP,
758 expr: Box::new(ident.into()),
759 }))
760 } else {
761 key_prop
762 };
763
764 let append_to: &mut IndexMap<_, _> = if m.is_static {
765 &mut static_props
766 } else {
767 &mut props
768 };
769
770 let mut folder = SuperFieldAccessFolder {
771 class_name,
772
773 constructor_this_mark: None,
774 is_static: m.is_static,
775 folding_constructor: false,
776 in_nested_scope: false,
777 in_injected_define_property_call: false,
778 this_alias_mark: None,
779 constant_super: self.config.constant_super,
780 super_class: super_class_ident,
781 in_pat: false,
782 };
783 m.function.visit_mut_with(&mut folder);
784
785 if let Some(mark) = folder.this_alias_mark {
786 prepend_stmt(
787 &mut m.function.body.as_mut().unwrap().stmts,
788 VarDecl {
789 span: DUMMY_SP,
790 declare: false,
791 kind: VarDeclKind::Var,
792 decls: vec![VarDeclarator {
793 span: DUMMY_SP,
794 name: quote_ident!(SyntaxContext::empty().apply_mark(mark), "_this")
795 .into(),
796 init: Some(Box::new(Expr::This(ThisExpr { span: DUMMY_SP }))),
797 definite: false,
798 }],
799 ..Default::default()
800 }
801 .into(),
802 );
803 }
804
805 let value = FnExpr {
806 ident: if m.kind == MethodKind::Method && !computed {
807 match prop_name {
808 Expr::Ident(ident) => Some(private_ident!(ident.span, ident.sym)),
809 Expr::Lit(Lit::Str(Str { span, value, .. })) => {
810 value.as_str().and_then(|value_str| {
811 if is_valid_ident(value_str) {
812 Some(Ident::new(
813 Atom::from(value_str),
814 span,
815 SyntaxContext::empty().apply_mark(Mark::new()),
816 ))
817 } else {
818 None
819 }
820 })
821 }
822 _ => None,
823 }
824 } else {
825 None
826 },
827 function: m.function,
828 }
829 .into();
830
831 let data = append_to.entry(key).or_insert_with(|| Data {
832 key_prop,
833 get: None,
834 set: None,
835 method: None,
836 });
837 match m.kind {
838 MethodKind::Getter => {
840 data.method = None;
841 data.get = Some(value)
842 }
843 MethodKind::Setter => {
844 data.method = None;
845 data.set = Some(value)
846 }
847 MethodKind::Method => {
848 data.get = None;
849 data.set = None;
850 data.method = Some(value)
851 }
852 #[cfg(swc_ast_unknown)]
853 _ => panic!("unable to access unknown nodes"),
854 }
855 }
856
857 let mut res = Vec::new();
858
859 if self.config.set_class_methods {
860 let proto = private_ident!("_proto");
861 props.retain(|_, v| {
862 if let Some(method) = v.method.take() {
863 if res.is_empty() {
864 res.push(
865 VarDecl {
866 span: DUMMY_SP,
867 kind: VarDeclKind::Var,
868 declare: false,
869 decls: vec![VarDeclarator {
870 span: DUMMY_SP,
871 name: proto.clone().into(),
872 init: Some(
873 class_name
874 .clone()
875 .make_member(quote_ident!("prototype"))
876 .into(),
877 ),
878 definite: false,
879 }],
880 ..Default::default()
881 }
882 .into(),
883 );
884 }
885 let span = method.span();
886 let prop = *v.key_prop.clone();
887 res.push(
888 ExprStmt {
889 span,
890 expr: AssignExpr {
891 span,
892 op: op!("="),
893 left: MemberExpr {
894 span,
895 obj: Box::new(proto.clone().into()),
896 prop: mk_key_prop_member(prop),
897 }
898 .into(),
899 right: escape_keywords(method),
900 }
901 .into(),
902 }
903 .into(),
904 );
905 !(v.get.is_none() && v.set.is_none())
906 } else {
907 true
908 }
909 });
910
911 static_props.retain(|_, v| {
912 if let Some(method) = v.method.take() {
913 let span = method.span();
914 let prop = *v.key_prop.clone();
915 res.push(
916 ExprStmt {
917 span,
918 expr: AssignExpr {
919 span,
920 op: op!("="),
921 left: MemberExpr {
922 span,
923 obj: Box::new(class_name.clone().into()),
924 prop: mk_key_prop_member(prop),
925 }
926 .into(),
927 right: escape_keywords(method),
928 }
929 .into(),
930 }
931 .into(),
932 );
933 !(v.get.is_none() && v.set.is_none())
934 } else {
935 true
936 }
937 })
938 }
939
940 if props.is_empty() && static_props.is_empty() {
941 return res;
942 }
943
944 res.push(mk_create_class_call(
945 class_name.clone(),
946 mk_arg_obj_for_create_class(props),
947 if static_props.is_empty() {
948 None
949 } else {
950 Some(mk_arg_obj_for_create_class(static_props))
951 },
952 ));
953
954 res
955 }
956}
957
958fn escape_keywords(mut e: Box<Expr>) -> Box<Expr> {
959 if let Expr::Fn(f) = &mut *e {
960 if let Some(i) = &mut f.ident {
961 let sym = Ident::verify_symbol(&i.sym);
962
963 if let Err(new) = sym {
964 i.sym = new.into();
965 }
966 }
967 }
968
969 e
970}
971
972#[derive(Default)]
973struct ClassFinder {
974 found: bool,
975}
976
977impl Visit for ClassFinder {
978 noop_visit_type!(fail);
979
980 fn visit_class(&mut self, _: &Class) {
981 self.found = true
982 }
983}
984
985impl Check for ClassFinder {
986 fn should_handle(&self) -> bool {
987 self.found
988 }
989}