swc_ecma_compat_es2022/
static_blocks.rs

1use rustc_hash::FxHashSet;
2use swc_atoms::Atom;
3use swc_common::{source_map::PLACEHOLDER_SP, util::take::Take};
4use swc_ecma_ast::*;
5use swc_ecma_utils::ExprFactory;
6use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
7use swc_trace_macro::swc_trace;
8
9struct ClassStaticBlock;
10
11pub fn static_blocks() -> impl Pass {
12    visit_mut_pass(ClassStaticBlock)
13}
14
15#[swc_trace]
16impl ClassStaticBlock {
17    fn transform_static_block(
18        &mut self,
19        mut static_block: StaticBlock,
20        private_id: Atom,
21    ) -> PrivateProp {
22        let mut stmts = static_block.body.stmts.take();
23        let span = static_block.span;
24
25        // We special-case the single expression case to avoid the iife, since it's
26        // common.
27        let value = if stmts.len() == 1 && stmts[0].is_expr() {
28            stmts[0].take().expr().map(|expr_stmt| expr_stmt.expr)
29        } else {
30            static_block.body.stmts = stmts;
31
32            let expr = CallExpr {
33                callee: ArrowExpr {
34                    body: Box::new(BlockStmtOrExpr::BlockStmt(static_block.body)),
35                    ..Default::default()
36                }
37                .as_callee(),
38                ..Default::default()
39            }
40            .into();
41
42            Some(Box::new(expr))
43        };
44
45        PrivateProp {
46            span,
47            is_static: true,
48            key: PrivateName {
49                span: PLACEHOLDER_SP,
50                name: private_id,
51            },
52            value,
53            ..Default::default()
54        }
55    }
56}
57
58#[swc_trace]
59impl VisitMut for ClassStaticBlock {
60    noop_visit_mut_type!(fail);
61
62    fn visit_mut_class(&mut self, class: &mut Class) {
63        class.visit_mut_children_with(self);
64
65        let mut private_names = FxHashSet::default();
66        for member in &class.body {
67            if let ClassMember::PrivateProp(private_property) = member {
68                private_names.insert(private_property.key.name.clone());
69            }
70        }
71
72        let mut count = 0;
73        for member in class.body.iter_mut() {
74            if let ClassMember::StaticBlock(static_block) = member {
75                if static_block.body.stmts.is_empty() {
76                    *member = ClassMember::dummy();
77                    continue;
78                }
79
80                let static_block_private_id = generate_uid(&private_names, &mut count);
81                *member = self
82                    .transform_static_block(static_block.take(), static_block_private_id)
83                    .into();
84            };
85        }
86    }
87}
88
89fn generate_uid(deny_list: &FxHashSet<Atom>, i: &mut u32) -> Atom {
90    *i += 1;
91
92    let mut uid: Atom = if *i == 1 {
93        "_".to_string()
94    } else {
95        format!("_{i}")
96    }
97    .into();
98    while deny_list.contains(&uid) {
99        *i += 1;
100        uid = format!("_{i}").into();
101    }
102
103    uid
104}