swc_ecma_compat_es2022/class_properties/
mod.rs

1use rustc_hash::FxHashMap;
2use swc_atoms::{atom, Atom};
3use swc_common::{
4    errors::HANDLER, source_map::PURE_SP, util::take::Take, Mark, Span, Spanned, SyntaxContext,
5    DUMMY_SP,
6};
7use swc_ecma_ast::*;
8use swc_ecma_transforms_base::{helper, perf::Check};
9use swc_ecma_transforms_classes::super_field::SuperFieldAccessFolder;
10use swc_ecma_transforms_macros::fast_path;
11use swc_ecma_utils::{
12    alias_ident_for, alias_if_required, constructor::inject_after_super,
13    default_constructor_with_span, is_literal, prepend_stmt, private_ident, quote_ident,
14    replace_ident, ExprFactory, ModuleItemLike, StmtLike,
15};
16use swc_ecma_visit::{
17    noop_visit_mut_type, noop_visit_type, visit_mut_pass, Visit, VisitMut, VisitMutWith, VisitWith,
18};
19use swc_trace_macro::swc_trace;
20
21use self::{
22    class_name_tdz::ClassNameTdzFolder,
23    member_init::{MemberInit, MemberInitRecord, PrivAccessor, PrivMethod, PrivProp, PubProp},
24    private_field::{
25        dup_private_method, visit_private_in_expr, BrandCheckHandler, Private,
26        PrivateAccessVisitor, PrivateKind, PrivateRecord,
27    },
28    this_in_static::{NewTargetInProp, ThisInStaticFolder},
29    used_name::UsedNameCollector,
30};
31
32mod class_name_tdz;
33mod member_init;
34mod private_field;
35mod this_in_static;
36mod used_name;
37
38///
39///
40///
41///
42/// # Impl note
43///
44/// We use custom helper to handle export default class
45pub fn class_properties(config: Config, unresolved_mark: Mark) -> impl Pass {
46    visit_mut_pass(ClassProperties {
47        c: config,
48        private: PrivateRecord::new(),
49        extra: ClassExtra::default(),
50        unresolved_mark,
51    })
52}
53
54#[derive(Debug, Default, Clone, Copy)]
55pub struct Config {
56    pub private_as_properties: bool,
57    pub set_public_fields: bool,
58    pub constant_super: bool,
59    pub no_document_all: bool,
60    pub pure_getter: bool,
61}
62
63struct ClassProperties {
64    extra: ClassExtra,
65    c: Config,
66    private: PrivateRecord,
67    unresolved_mark: Mark,
68}
69
70#[derive(Default)]
71struct ClassExtra {
72    lets: Vec<VarDeclarator>,
73    vars: Vec<VarDeclarator>,
74    stmts: Vec<Stmt>,
75}
76
77#[swc_trace]
78impl ClassExtra {
79    fn prepend_with<T: StmtLike>(self, stmts: &mut Vec<T>) {
80        if !self.vars.is_empty() {
81            prepend_stmt(
82                stmts,
83                T::from(Stmt::from(VarDecl {
84                    span: DUMMY_SP,
85                    kind: VarDeclKind::Var,
86                    decls: self.vars,
87                    ..Default::default()
88                })),
89            )
90        }
91
92        if !self.lets.is_empty() {
93            prepend_stmt(
94                stmts,
95                T::from(Stmt::from(VarDecl {
96                    span: DUMMY_SP,
97                    kind: VarDeclKind::Let,
98                    decls: self.lets,
99                    ..Default::default()
100                })),
101            )
102        }
103
104        stmts.extend(self.stmts.into_iter().map(|stmt| stmt.into()))
105    }
106
107    fn merge_with<T: StmtLike>(self, stmts: &mut Vec<T>, class: T) {
108        if !self.vars.is_empty() {
109            stmts.push(T::from(Stmt::from(VarDecl {
110                span: DUMMY_SP,
111                kind: VarDeclKind::Var,
112                decls: self.vars,
113                ..Default::default()
114            })))
115        }
116
117        if !self.lets.is_empty() {
118            stmts.push(T::from(
119                VarDecl {
120                    span: DUMMY_SP,
121                    kind: VarDeclKind::Let,
122                    decls: self.lets,
123                    ..Default::default()
124                }
125                .into(),
126            ));
127        }
128
129        stmts.push(class);
130
131        stmts.extend(self.stmts.into_iter().map(|stmt| stmt.into()))
132    }
133}
134
135impl Take for ClassExtra {
136    fn dummy() -> Self {
137        Self::default()
138    }
139}
140
141#[swc_trace]
142#[fast_path(ShouldWork)]
143impl VisitMut for ClassProperties {
144    noop_visit_mut_type!(fail);
145
146    fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
147        self.visit_mut_stmt_like(n);
148
149        self.extra.take().prepend_with(n)
150    }
151
152    fn visit_mut_stmts(&mut self, n: &mut Vec<Stmt>) {
153        let old = self.extra.take();
154        self.visit_mut_stmt_like(n);
155
156        self.extra.take().prepend_with(n);
157
158        self.extra = old;
159    }
160
161    fn visit_mut_block_stmt_or_expr(&mut self, body: &mut BlockStmtOrExpr) {
162        match body {
163            BlockStmtOrExpr::Expr(expr) if expr.is_class() => {
164                let ClassExpr { ident, class } = expr.take().class().unwrap();
165
166                let mut stmts = Vec::new();
167                let ident = ident.unwrap_or_else(|| private_ident!("_class"));
168                let (decl, extra) = self.visit_mut_class_as_decl(ident.clone(), class);
169
170                extra.merge_with(&mut stmts, decl.into());
171
172                stmts.push(
173                    ReturnStmt {
174                        span: DUMMY_SP,
175                        arg: Some(ident.into()),
176                    }
177                    .into(),
178                );
179
180                *body = BlockStmtOrExpr::BlockStmt(BlockStmt {
181                    span: DUMMY_SP,
182                    stmts,
183                    ..Default::default()
184                });
185            }
186            _ => body.visit_mut_children_with(self),
187        };
188    }
189
190    fn visit_mut_expr(&mut self, expr: &mut Expr) {
191        if let Expr::Class(ClassExpr {
192            ident: orig_ident,
193            class,
194        }) = expr
195        {
196            let ident = private_ident!(orig_ident
197                .clone()
198                .map(|id| Atom::from(format!("_{}", id.sym)))
199                .unwrap_or(atom!("_class")));
200            let (decl, ClassExtra { lets, vars, stmts }) =
201                self.visit_mut_class_as_decl(ident.clone(), class.take());
202
203            let class = ClassExpr {
204                ident: orig_ident.clone(),
205                class: decl.class,
206            }
207            .into();
208            if vars.is_empty() && lets.is_empty() && stmts.is_empty() {
209                *expr = class;
210                return;
211            }
212
213            let mut exprs = Vec::new();
214
215            for mut var in vars {
216                let init = var.init.take();
217                if let Some(init) = init {
218                    exprs.push(
219                        AssignExpr {
220                            span: var.span,
221                            op: op!("="),
222                            left: var.name.clone().try_into().unwrap(),
223                            right: init,
224                        }
225                        .into(),
226                    )
227                }
228                self.extra.vars.push(var);
229            }
230
231            for mut var in lets {
232                let init = var.init.take();
233                if let Some(init) = init {
234                    exprs.push(
235                        AssignExpr {
236                            span: var.span,
237                            op: op!("="),
238                            left: var.name.clone().try_into().unwrap(),
239                            right: init,
240                        }
241                        .into(),
242                    )
243                }
244                self.extra.lets.push(var);
245            }
246
247            let mut extra_value = false;
248            if !stmts.is_empty() {
249                extra_value = true;
250                self.extra.vars.push(VarDeclarator {
251                    span: DUMMY_SP,
252                    name: ident.clone().into(),
253                    init: None,
254                    definite: false,
255                });
256                exprs.push(
257                    AssignExpr {
258                        span: DUMMY_SP,
259                        left: ident.clone().into(),
260                        op: op!("="),
261                        right: class.into(),
262                    }
263                    .into(),
264                );
265            } else {
266                exprs.push(class.into());
267            }
268
269            for mut stmt in stmts {
270                if let Some(orig_ident) = orig_ident {
271                    replace_ident(&mut stmt, orig_ident.clone().into(), &ident);
272                }
273                match stmt {
274                    Stmt::Expr(e) => exprs.push(e.expr),
275                    Stmt::Decl(Decl::Var(v)) => {
276                        for mut decl in v.decls {
277                            let init = decl.init.take();
278
279                            if let Some(init) = init {
280                                exprs.push(
281                                    AssignExpr {
282                                        span: decl.span,
283                                        op: op!("="),
284                                        left: decl.name.clone().try_into().unwrap(),
285                                        right: init,
286                                    }
287                                    .into(),
288                                )
289                            }
290
291                            self.extra.vars.push(decl)
292                        }
293                    }
294                    _ => self.extra.stmts.push(stmt),
295                }
296            }
297
298            if extra_value {
299                exprs.push(Box::new(ident.into()))
300            }
301
302            *expr = SeqExpr {
303                span: DUMMY_SP,
304                exprs,
305            }
306            .into()
307        } else {
308            expr.visit_mut_children_with(self);
309        };
310    }
311}
312
313#[swc_trace]
314impl ClassProperties {
315    fn visit_mut_stmt_like<T>(&mut self, stmts: &mut Vec<T>)
316    where
317        T: StmtLike + ModuleItemLike + VisitMutWith<Self> + From<Stmt>,
318    {
319        let mut buf = Vec::with_capacity(stmts.len());
320
321        for stmt in stmts.drain(..) {
322            match T::try_into_stmt(stmt) {
323                Err(node) => match node.try_into_module_decl() {
324                    Ok(mut decl) => {
325                        match decl {
326                            ModuleDecl::ExportDefaultDecl(ExportDefaultDecl {
327                                span,
328                                decl: DefaultDecl::Class(ClassExpr { ident, class }),
329                                ..
330                            }) => {
331                                let ident = ident.unwrap_or_else(|| private_ident!("_class"));
332
333                                let (decl, extra) =
334                                    self.visit_mut_class_as_decl(ident.clone(), class);
335
336                                extra.merge_with(&mut buf, T::from(decl.into()));
337
338                                buf.push(
339                                    match T::try_from_module_decl(
340                                        NamedExport {
341                                            span,
342                                            specifiers: vec![ExportNamedSpecifier {
343                                                span: DUMMY_SP,
344                                                orig: ModuleExportName::Ident(ident),
345                                                exported: Some(ModuleExportName::Ident(
346                                                    private_ident!("default"),
347                                                )),
348                                                is_type_only: false,
349                                            }
350                                            .into()],
351                                            src: None,
352                                            type_only: false,
353                                            with: None,
354                                        }
355                                        .into(),
356                                    ) {
357                                        Ok(t) => t,
358                                        Err(..) => unreachable!(),
359                                    },
360                                );
361                            }
362                            ModuleDecl::ExportDecl(ExportDecl {
363                                span,
364                                decl:
365                                    Decl::Class(ClassDecl {
366                                        ident,
367                                        declare: false,
368                                        class,
369                                    }),
370                                ..
371                            }) => {
372                                let (decl, extra) = self.visit_mut_class_as_decl(ident, class);
373                                extra.merge_with(
374                                    &mut buf,
375                                    match T::try_from_module_decl(
376                                        ExportDecl {
377                                            span,
378                                            decl: decl.into(),
379                                        }
380                                        .into(),
381                                    ) {
382                                        Ok(t) => t,
383                                        Err(..) => unreachable!(),
384                                    },
385                                )
386                            }
387                            _ => {
388                                decl.visit_mut_children_with(self);
389                                buf.push(match T::try_from_module_decl(decl) {
390                                    Ok(t) => t,
391                                    Err(..) => unreachable!(),
392                                })
393                            }
394                        };
395                    }
396                    Err(..) => unreachable!(),
397                },
398                Ok(mut stmt) => {
399                    // Fold class
400                    match stmt {
401                        Stmt::Decl(Decl::Class(ClassDecl {
402                            ident,
403                            class,
404                            declare: false,
405                        })) => {
406                            let (decl, extra) = self.visit_mut_class_as_decl(ident, class);
407                            extra.merge_with(&mut buf, T::from(decl.into()))
408                        }
409                        _ => {
410                            stmt.visit_mut_children_with(self);
411                            buf.push(T::from(stmt))
412                        }
413                    }
414                }
415            }
416        }
417
418        *stmts = buf;
419    }
420}
421
422#[swc_trace]
423impl ClassProperties {
424    fn visit_mut_class_as_decl(
425        &mut self,
426        class_ident: Ident,
427        mut class: Box<Class>,
428    ) -> (ClassDecl, ClassExtra) {
429        // Create one mark per class
430        let private = Private {
431            mark: Mark::fresh(Mark::root()),
432            class_name: class_ident.clone(),
433            ident: {
434                let mut private_map = FxHashMap::default();
435
436                for member in class.body.iter() {
437                    match member {
438                        ClassMember::PrivateMethod(method) => {
439                            if let Some(kind) = private_map.get_mut(&method.key.name) {
440                                if dup_private_method(kind, method) {
441                                    let error =
442                                        format!("duplicate private name #{}.", method.key.name);
443                                    HANDLER.with(|handler| {
444                                        handler.struct_span_err(method.key.span, &error).emit()
445                                    });
446                                } else {
447                                    match method.kind {
448                                        MethodKind::Getter => kind.has_getter = true,
449                                        MethodKind::Setter => kind.has_setter = true,
450                                        MethodKind::Method => unreachable!(),
451                                        #[cfg(swc_ast_unknown)]
452                                        _ => panic!("unable to access unknown nodes"),
453                                    }
454                                }
455                            } else {
456                                private_map.insert(
457                                    method.key.name.clone(),
458                                    PrivateKind {
459                                        is_method: true,
460                                        is_static: method.is_static,
461                                        has_getter: method.kind == MethodKind::Getter,
462                                        has_setter: method.kind == MethodKind::Setter,
463                                    },
464                                );
465                            }
466                        }
467
468                        ClassMember::PrivateProp(prop) => {
469                            if private_map.contains_key(&prop.key.name) {
470                                let error = format!("duplicate private name #{}.", prop.key.name);
471                                HANDLER.with(|handler| {
472                                    handler.struct_span_err(prop.key.span, &error).emit()
473                                });
474                            } else {
475                                private_map.insert(
476                                    prop.key.name.clone(),
477                                    PrivateKind {
478                                        is_method: false,
479                                        is_static: prop.is_static,
480                                        has_getter: false,
481                                        has_setter: false,
482                                    },
483                                );
484                            };
485                        }
486
487                        ClassMember::AutoAccessor(_) => {
488                            // AutoAccessor is preserved as-is, no private field
489                            // registration needed
490                        }
491
492                        _ => (),
493                    };
494                }
495
496                private_map
497            },
498        };
499
500        self.private.push(private);
501
502        // we must collect outer class's private first
503        class.visit_mut_children_with(self);
504
505        let has_super = class.super_class.is_some();
506
507        let mut constructor_inits = MemberInitRecord::new(self.c);
508        let mut vars = Vec::new();
509        let mut lets = Vec::new();
510        let mut extra_inits = MemberInitRecord::new(self.c);
511        let mut private_method_fn_decls = Vec::new();
512        let mut members = Vec::new();
513        let mut constructor = None;
514        let mut used_names = Vec::new();
515        let mut used_key_names = Vec::new();
516        let mut super_ident = None;
517
518        class.body.visit_mut_with(&mut BrandCheckHandler {
519            private: &self.private,
520        });
521
522        let should_create_vars_for_method_names = class.body.iter().any(|m| match m {
523            ClassMember::Constructor(_)
524            | ClassMember::PrivateMethod(_)
525            | ClassMember::TsIndexSignature(_)
526            | ClassMember::Empty(_)
527            | ClassMember::AutoAccessor(_) => false,
528
529            ClassMember::Method(m) => contains_super(&m.key),
530
531            ClassMember::ClassProp(_)
532            | ClassMember::PrivateProp(_)
533            | ClassMember::StaticBlock(_) => true,
534
535            #[cfg(swc_ast_unknown)]
536            _ => panic!("unable to access unknown nodes"),
537        });
538
539        for member in class.body {
540            match member {
541                ClassMember::Empty(..) | ClassMember::TsIndexSignature(..) => members.push(member),
542
543                ClassMember::Method(method) => {
544                    // we handle computed key here to preserve the execution order
545                    let key = match method.key {
546                        PropName::Computed(ComputedPropName {
547                            span: c_span,
548                            mut expr,
549                        }) if should_create_vars_for_method_names && !is_literal(&*expr) => {
550                            vars.extend(visit_private_in_expr(
551                                &mut expr,
552                                &self.private,
553                                self.c,
554                                self.unresolved_mark,
555                            ));
556
557                            expr.visit_mut_with(&mut ClassNameTdzFolder {
558                                class_name: &class_ident,
559                            });
560                            let ident = alias_ident_for(&expr, "tmp");
561                            // Handle computed property
562                            lets.push(VarDeclarator {
563                                span: DUMMY_SP,
564                                name: ident.clone().into(),
565                                init: Some(expr),
566                                definite: false,
567                            });
568                            // We use computed because `classes` pass converts PropName::Ident to
569                            // string.
570                            PropName::Computed(ComputedPropName {
571                                span: c_span,
572                                expr: ident.into(),
573                            })
574                        }
575                        _ => method.key,
576                    };
577                    members.push(ClassMember::Method(ClassMethod { key, ..method }))
578                }
579
580                ClassMember::ClassProp(mut prop) => {
581                    let prop_span = prop.span();
582                    prop.key.visit_mut_with(&mut ClassNameTdzFolder {
583                        class_name: &class_ident,
584                    });
585
586                    if !prop.is_static {
587                        prop.key.visit_with(&mut UsedNameCollector {
588                            used_names: &mut used_key_names,
589                        });
590
591                        prop.value.visit_with(&mut UsedNameCollector {
592                            used_names: &mut used_names,
593                        });
594                    }
595
596                    match &mut prop.key {
597                        PropName::Computed(key) if !is_literal(&key.expr) => {
598                            vars.extend(visit_private_in_expr(
599                                &mut key.expr,
600                                &self.private,
601                                self.c,
602                                self.unresolved_mark,
603                            ));
604                            let (ident, aliased) = if let Expr::Ident(i) = &*key.expr {
605                                if used_key_names.contains(&i.sym) {
606                                    (alias_ident_for(&key.expr, "_ref"), true)
607                                } else {
608                                    alias_if_required(&key.expr, "_ref")
609                                }
610                            } else {
611                                alias_if_required(&key.expr, "_ref")
612                            };
613                            if aliased {
614                                // Handle computed property
615                                lets.push(VarDeclarator {
616                                    span: DUMMY_SP,
617                                    name: ident.clone().into(),
618                                    init: Some(key.expr.take()),
619                                    definite: false,
620                                });
621                            }
622                            *key.expr = ident.into();
623                        }
624                        _ => (),
625                    };
626
627                    let mut value = prop.value.unwrap_or_else(|| Expr::undefined(prop_span));
628
629                    value.visit_mut_with(&mut NewTargetInProp);
630
631                    vars.extend(visit_private_in_expr(
632                        &mut value,
633                        &self.private,
634                        self.c,
635                        self.unresolved_mark,
636                    ));
637
638                    if prop.is_static {
639                        if let (Some(super_class), None) = (&mut class.super_class, &super_ident) {
640                            let (ident, aliased) = alias_if_required(&*super_class, "_ref");
641                            super_ident = Some(ident.clone());
642
643                            if aliased {
644                                vars.push(VarDeclarator {
645                                    span: DUMMY_SP,
646                                    name: ident.clone().into(),
647                                    init: None,
648                                    definite: false,
649                                });
650                                let span = super_class.span();
651                                **super_class = AssignExpr {
652                                    span,
653                                    op: op!("="),
654                                    left: ident.into(),
655                                    right: super_class.take(),
656                                }
657                                .into()
658                            }
659                        }
660
661                        value.visit_mut_with(&mut SuperFieldAccessFolder {
662                            class_name: &class_ident,
663                            constructor_this_mark: None,
664                            is_static: true,
665                            folding_constructor: false,
666                            in_injected_define_property_call: false,
667                            in_nested_scope: false,
668                            this_alias_mark: None,
669                            constant_super: self.c.constant_super,
670                            super_class: &super_ident,
671                            in_pat: false,
672                        });
673                        value.visit_mut_with(&mut ThisInStaticFolder {
674                            ident: class_ident.clone(),
675                        });
676                    }
677
678                    let init = MemberInit::PubProp(PubProp {
679                        span: prop_span,
680                        name: prop.key,
681                        value,
682                    });
683                    if prop.is_static {
684                        extra_inits.push(init);
685                    } else {
686                        constructor_inits.push(init);
687                    }
688                }
689                ClassMember::PrivateProp(mut prop) => {
690                    let prop_span = prop.span();
691
692                    let ident = Ident::new(
693                        format!("_{}", prop.key.name).into(),
694                        // We use `self.mark` for private variables.
695                        prop.key.span,
696                        SyntaxContext::empty().apply_mark(self.private.cur_mark()),
697                    );
698
699                    if let Some(value) = &mut prop.value {
700                        value.visit_mut_with(&mut NewTargetInProp);
701
702                        if prop.is_static {
703                            value.visit_mut_with(&mut SuperFieldAccessFolder {
704                                class_name: &class_ident,
705                                constructor_this_mark: None,
706                                is_static: true,
707                                folding_constructor: false,
708                                in_injected_define_property_call: false,
709                                in_nested_scope: false,
710                                this_alias_mark: None,
711                                constant_super: self.c.constant_super,
712                                super_class: &super_ident,
713                                in_pat: false,
714                            });
715                        }
716                        vars.extend(visit_private_in_expr(
717                            &mut *value,
718                            &self.private,
719                            self.c,
720                            self.unresolved_mark,
721                        ));
722                    }
723
724                    prop.value.visit_with(&mut UsedNameCollector {
725                        used_names: &mut used_names,
726                    });
727                    if prop.is_static {
728                        prop.value.visit_mut_with(&mut ThisInStaticFolder {
729                            ident: class_ident.clone(),
730                        });
731                    }
732
733                    let value = prop.value.unwrap_or_else(|| Expr::undefined(prop_span));
734
735                    if prop.is_static && prop.key.span.is_placeholder() {
736                        let init = MemberInit::StaticBlock(value);
737                        extra_inits.push(init);
738                        continue;
739                    }
740
741                    let init = MemberInit::PrivProp(PrivProp {
742                        span: prop_span,
743                        name: ident.clone(),
744                        value,
745                    });
746                    let span = PURE_SP;
747                    if self.c.private_as_properties {
748                        vars.push(VarDeclarator {
749                            span: DUMMY_SP,
750                            definite: false,
751                            name: ident.clone().into(),
752                            init: Some(
753                                CallExpr {
754                                    span,
755                                    callee: helper!(class_private_field_loose_key),
756                                    args: vec![ident.sym.as_arg()],
757                                    ..Default::default()
758                                }
759                                .into(),
760                            ),
761                        });
762                    } else if !prop.is_static {
763                        vars.push(VarDeclarator {
764                            span: DUMMY_SP,
765                            definite: false,
766                            name: ident.into(),
767                            init: Some(
768                                NewExpr {
769                                    span,
770                                    callee: Box::new(quote_ident!("WeakMap").into()),
771                                    args: Some(Default::default()),
772                                    ..Default::default()
773                                }
774                                .into(),
775                            ),
776                        });
777                    };
778                    if prop.is_static {
779                        extra_inits.push(init);
780                    } else {
781                        constructor_inits.push(init);
782                    };
783                }
784
785                ClassMember::Constructor(c) => {
786                    constructor = Some(c);
787                }
788
789                ClassMember::PrivateMethod(mut method) => {
790                    let is_static = method.is_static;
791                    let prop_span = method.span;
792
793                    let fn_name = Ident::new(
794                        match method.kind {
795                            MethodKind::Getter => format!("get_{}", method.key.name).into(),
796                            MethodKind::Setter => format!("set_{}", method.key.name).into(),
797                            MethodKind::Method => {
798                                if method.key.name.is_reserved_in_any() {
799                                    format!("__{}", method.key.name).into()
800                                } else {
801                                    method.key.name.clone()
802                                }
803                            }
804                            #[cfg(swc_ast_unknown)]
805                            _ => panic!("unable to access unknown nodes"),
806                        },
807                        method.span,
808                        SyntaxContext::empty().apply_mark(self.private.cur_mark()),
809                    );
810
811                    let weak_coll_var = Ident::new(
812                        format!("_{}", method.key.name).into(),
813                        // We use `self.mark` for private variables.
814                        method.key.span,
815                        SyntaxContext::empty().apply_mark(self.private.cur_mark()),
816                    );
817                    method.function.visit_with(&mut UsedNameCollector {
818                        used_names: &mut used_names,
819                    });
820
821                    let extra_collect = match (method.kind, is_static) {
822                        (MethodKind::Getter | MethodKind::Setter, false) => {
823                            let is_getter = method.kind == MethodKind::Getter;
824                            let inserted =
825                                constructor_inits.push(MemberInit::PrivAccessor(PrivAccessor {
826                                    span: prop_span,
827                                    name: weak_coll_var.clone(),
828                                    getter: if is_getter {
829                                        Some(fn_name.clone())
830                                    } else {
831                                        None
832                                    },
833                                    setter: if !is_getter {
834                                        Some(fn_name.clone())
835                                    } else {
836                                        None
837                                    },
838                                }));
839
840                            if inserted {
841                                Some(quote_ident!("WeakMap"))
842                            } else {
843                                None
844                            }
845                        }
846                        (MethodKind::Getter | MethodKind::Setter, true) => {
847                            let is_getter = method.kind == MethodKind::Getter;
848                            let inserted =
849                                extra_inits.push(MemberInit::PrivAccessor(PrivAccessor {
850                                    span: prop_span,
851                                    name: weak_coll_var.clone(),
852                                    getter: if is_getter {
853                                        Some(fn_name.clone())
854                                    } else {
855                                        None
856                                    },
857                                    setter: if !is_getter {
858                                        Some(fn_name.clone())
859                                    } else {
860                                        None
861                                    },
862                                }));
863                            if inserted && self.c.private_as_properties {
864                                Some(IdentName::default())
865                            } else {
866                                None
867                            }
868                        }
869
870                        (MethodKind::Method, false) => {
871                            constructor_inits.push(MemberInit::PrivMethod(PrivMethod {
872                                span: prop_span,
873                                name: weak_coll_var.clone(),
874                                fn_name: if self.c.private_as_properties {
875                                    fn_name.clone()
876                                } else {
877                                    Ident::dummy()
878                                },
879                            }));
880                            Some(quote_ident!("WeakSet"))
881                        }
882                        (MethodKind::Method, true) => {
883                            if self.c.private_as_properties {
884                                extra_inits.push(MemberInit::PrivMethod(PrivMethod {
885                                    span: prop_span,
886                                    name: weak_coll_var.clone(),
887                                    fn_name: fn_name.clone(),
888                                }));
889                                Some(Default::default())
890                            } else {
891                                None
892                            }
893                        }
894                        #[cfg(swc_ast_unknown)]
895                        _ => panic!("unable to access unknown nodes"),
896                    };
897
898                    if let Some(extra) = extra_collect {
899                        let span = PURE_SP;
900                        vars.push(VarDeclarator {
901                            span: DUMMY_SP,
902                            definite: false,
903                            name: weak_coll_var.clone().into(),
904                            init: Some(Box::new(if self.c.private_as_properties {
905                                CallExpr {
906                                    span,
907                                    callee: helper!(class_private_field_loose_key),
908                                    args: vec![weak_coll_var.sym.as_arg()],
909                                    ..Default::default()
910                                }
911                                .into()
912                            } else {
913                                NewExpr {
914                                    span,
915                                    callee: extra.into(),
916                                    args: Some(Default::default()),
917                                    ..Default::default()
918                                }
919                                .into()
920                            })),
921                        })
922                    };
923
924                    method.function.visit_mut_with(&mut SuperFieldAccessFolder {
925                        class_name: &class_ident,
926                        constructor_this_mark: None,
927                        is_static,
928                        folding_constructor: false,
929                        in_injected_define_property_call: false,
930                        in_nested_scope: false,
931                        this_alias_mark: None,
932                        constant_super: self.c.constant_super,
933                        super_class: &super_ident,
934                        in_pat: false,
935                    });
936
937                    private_method_fn_decls.push(
938                        FnDecl {
939                            ident: fn_name,
940                            function: method.function,
941                            declare: false,
942                        }
943                        .into(),
944                    )
945                }
946
947                ClassMember::StaticBlock(..) => {
948                    unreachable!("static_blocks pass should remove this")
949                }
950
951                ClassMember::AutoAccessor(accessor) => {
952                    // AutoAccessor nodes should be handled by the decorator transform.
953                    // If we encounter them here, it means decorators are not enabled,
954                    // so we preserve the AutoAccessor as-is. The output environment
955                    // is expected to support auto-accessors natively.
956                    members.push(ClassMember::AutoAccessor(accessor));
957                }
958
959                #[cfg(swc_ast_unknown)]
960                _ => panic!("unable to access unknown nodes"),
961            }
962        }
963
964        let constructor =
965            self.process_constructor(class.span, constructor, has_super, constructor_inits);
966        if let Some(c) = constructor {
967            members.push(ClassMember::Constructor(c));
968        }
969
970        private_method_fn_decls.visit_mut_with(&mut PrivateAccessVisitor {
971            private: &self.private,
972            vars: Vec::new(),
973            private_access_type: Default::default(),
974            c: self.c,
975            unresolved_mark: self.unresolved_mark,
976        });
977
978        let mut extra_stmts = extra_inits.into_init_static(class_ident.clone());
979
980        extra_stmts.extend(private_method_fn_decls);
981
982        members.visit_mut_with(&mut PrivateAccessVisitor {
983            private: &self.private,
984            vars: Vec::new(),
985            private_access_type: Default::default(),
986            c: self.c,
987            unresolved_mark: self.unresolved_mark,
988        });
989
990        self.private.pop();
991
992        (
993            ClassDecl {
994                ident: class_ident,
995                declare: false,
996                class: Class {
997                    body: members,
998                    ..*class
999                }
1000                .into(),
1001            },
1002            ClassExtra {
1003                vars,
1004                lets,
1005                stmts: extra_stmts,
1006            },
1007        )
1008    }
1009
1010    /// # Legacy support.
1011    ///
1012    /// ## Why is this required?
1013    ///
1014    /// Hygiene data of
1015    ///
1016    ///```ts
1017    /// class A {
1018    ///     b = this.a;
1019    ///     constructor(a){
1020    ///         this.a = a;
1021    ///     }
1022    /// }
1023    /// ```
1024    ///
1025    /// is
1026    ///
1027    ///```ts
1028    /// class A0 {
1029    ///     constructor(a1){
1030    ///         this.a0 = a0;
1031    ///         this.b0 = this.a0;
1032    ///     }
1033    /// }
1034    /// ```
1035    ///
1036    /// which is valid only for es2020 properties.
1037    ///
1038    /// Legacy proposal which is used by typescript requires different hygiene.
1039    #[allow(clippy::vec_box)]
1040    fn process_constructor(
1041        &mut self,
1042        class_span: Span,
1043        constructor: Option<Constructor>,
1044        has_super: bool,
1045        constructor_exprs: MemberInitRecord,
1046    ) -> Option<Constructor> {
1047        let constructor = constructor.or_else(|| {
1048            if constructor_exprs.record.is_empty() {
1049                None
1050            } else {
1051                Some(default_constructor_with_span(has_super, class_span))
1052            }
1053        });
1054
1055        if let Some(mut c) = constructor {
1056            let constructor_exprs = constructor_exprs.into_init();
1057            // Prepend properties
1058            inject_after_super(&mut c, constructor_exprs);
1059            Some(c)
1060        } else {
1061            None
1062        }
1063    }
1064}
1065
1066#[derive(Default)]
1067struct ShouldWork {
1068    found: bool,
1069}
1070
1071#[swc_trace]
1072impl Visit for ShouldWork {
1073    noop_visit_type!(fail);
1074
1075    fn visit_class_method(&mut self, _: &ClassMethod) {
1076        self.found = true;
1077    }
1078
1079    fn visit_class_prop(&mut self, _: &ClassProp) {
1080        self.found = true;
1081    }
1082
1083    fn visit_private_prop(&mut self, _: &PrivateProp) {
1084        self.found = true;
1085    }
1086
1087    fn visit_private_method(&mut self, _: &PrivateMethod) {
1088        self.found = true;
1089    }
1090
1091    fn visit_constructor(&mut self, _: &Constructor) {
1092        self.found = true;
1093    }
1094
1095    // AutoAccessor is preserved as-is, doesn't require transformation
1096    fn visit_auto_accessor(&mut self, _: &AutoAccessor) {
1097        // No-op: AutoAccessor is handled by decorator transform, not
1098        // class_properties
1099    }
1100}
1101
1102impl Check for ShouldWork {
1103    fn should_handle(&self) -> bool {
1104        self.found
1105    }
1106}
1107
1108// TODO: remove
1109struct SuperVisitor {
1110    found: bool,
1111}
1112
1113impl Visit for SuperVisitor {
1114    noop_visit_type!(fail);
1115
1116    /// Don't recurse into constructor
1117    fn visit_constructor(&mut self, _: &Constructor) {}
1118
1119    /// Don't recurse into fn
1120    fn visit_fn_decl(&mut self, _: &FnDecl) {}
1121
1122    /// Don't recurse into fn
1123    fn visit_fn_expr(&mut self, _: &FnExpr) {}
1124
1125    /// Don't recurse into fn
1126    fn visit_function(&mut self, _: &Function) {}
1127
1128    /// Don't recurse into fn
1129    fn visit_getter_prop(&mut self, n: &GetterProp) {
1130        n.key.visit_with(self);
1131    }
1132
1133    /// Don't recurse into fn
1134    fn visit_method_prop(&mut self, n: &MethodProp) {
1135        n.key.visit_with(self);
1136        n.function.visit_with(self);
1137    }
1138
1139    /// Don't recurse into fn
1140    fn visit_setter_prop(&mut self, n: &SetterProp) {
1141        n.key.visit_with(self);
1142        n.param.visit_with(self);
1143    }
1144
1145    fn visit_super(&mut self, _: &Super) {
1146        self.found = true;
1147    }
1148}
1149
1150fn contains_super<N>(body: &N) -> bool
1151where
1152    N: VisitWith<SuperVisitor>,
1153{
1154    let mut visitor = SuperVisitor { found: false };
1155    body.visit_with(&mut visitor);
1156    visitor.found
1157}