swc_ecma_compat_es2015/classes/
mod.rs

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/// `@babel/plugin-transform-classes`
42///
43/// # In
44/// ```js
45/// class Test {
46///   constructor(name) {
47///     this.name = name;
48///   }
49///
50///   logger () {
51///     console.log("Hello", this.name);
52///   }
53/// }
54/// ```
55///
56/// # Out
57/// ```js
58/// var Test = function () {
59///   function Test(name) {
60///     _class_call_check(this, Test);
61///
62///     this.name = name;
63///   }
64///
65///   Test.prototype.logger = function logger() {
66///     console.log("Hello", this.name);
67///   };
68///
69///   return Test;
70/// }();
71/// ```
72#[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        // will there be anything else in var name at this point?
244        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    /// `let { f = class /* f */ {} } = {};`
259    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    /// `let [c = class /* c */ {}] = [];`
270    /// `function foo(bar = class /* bar */ {}) {}`
271    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    /// {
282    ///     hello: class {},
283    ///     "foo": class {},
284    ///     ["x"]: class {}
285    /// }
286    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        // let VarDecl take every comments except pure
350        if let Expr::Call(call) = &mut rhs {
351            let mut span = Span {
352                // after class
353                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                // Foo in var Foo =
367                name: ident.into(),
368                definite: false,
369            }],
370            ..Default::default()
371        }
372    }
373
374    /// Turns class expression into iife.
375    ///
376    /// ```js
377    /// class Foo {}
378    /// ```
379    ///
380    /// ```js
381    /// function() {
382    ///   var Foo = function Foo(){
383    ///   };
384    /// }()
385    /// ```
386    fn fold_class(&mut self, class_name: Option<Ident>, class: Box<Class>) -> Expr {
387        let span = class.span;
388
389        // Ident of the super class *inside* function.
390        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            // Param should have a separate syntax context from arg.
397            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            //    class Foo {}
441            //
442            // should be
443            //
444            //    var Foo = function Foo() {
445            //        _class_call_check(this, Foo);
446            //    };
447            //
448            // instead of
449            //
450            //    var Foo = function(){
451            //      function Foo() {
452            //          _class_call_check(this, Foo);
453            //      }
454            //
455            //      return Foo;
456            //    }();
457
458            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    /// Returned `stmts` contains `return Foo`
504    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            // inject helper methods
535
536            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        // constructor
556        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        // convert class methods
568        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        // `return Foo`
606        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        /// { key: "prop" }
628        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        /// _create_class(Foo, [{}], [{}]);
716        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            // class is always strict, however computed key is not part of class
745            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                // https://github.com/swc-project/swc/issues/5029
839                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}