next_custom_transforms/transforms/
debug_fn_name.rs

1use std::fmt::Write;
2
3use swc_core::{
4    atoms::Atom,
5    common::{util::take::Take, DUMMY_SP},
6    ecma::{
7        ast::{
8            CallExpr, Callee, ExportDefaultDecl, ExportDefaultExpr, Expr, FnDecl, FnExpr,
9            KeyValueProp, MemberProp, ObjectLit, Pass, PropOrSpread, VarDeclarator,
10        },
11        utils::ExprFactory,
12        visit::{visit_mut_pass, VisitMut, VisitMutWith},
13    },
14};
15
16pub fn debug_fn_name() -> impl VisitMut + Pass {
17    visit_mut_pass(DebugFnName::default())
18}
19
20#[derive(Default)]
21struct DebugFnName {
22    path: String,
23    in_target: bool,
24    in_var_target: bool,
25    in_default_export: bool,
26}
27
28impl VisitMut for DebugFnName {
29    fn visit_mut_call_expr(&mut self, n: &mut CallExpr) {
30        if self.in_var_target || (self.path.is_empty() && !self.in_default_export) {
31            n.visit_mut_children_with(self);
32            return;
33        }
34
35        if let Some(target) = is_target_callee(&n.callee) {
36            let old_in_target = self.in_target;
37            self.in_target = true;
38            let orig_len = self.path.len();
39            if !self.path.is_empty() {
40                self.path.push('.');
41            }
42            self.path.push_str(&target);
43
44            n.visit_mut_children_with(self);
45
46            self.path.truncate(orig_len);
47            self.in_target = old_in_target;
48        } else {
49            n.visit_mut_children_with(self);
50        }
51    }
52
53    fn visit_mut_export_default_expr(&mut self, n: &mut ExportDefaultExpr) {
54        let old_in_default_export = self.in_default_export;
55        self.in_default_export = true;
56
57        n.visit_mut_children_with(self);
58
59        self.in_default_export = old_in_default_export;
60    }
61
62    fn visit_mut_export_default_decl(&mut self, n: &mut ExportDefaultDecl) {
63        let old_in_default_export = self.in_default_export;
64        self.in_default_export = true;
65
66        n.visit_mut_children_with(self);
67
68        self.in_default_export = old_in_default_export;
69    }
70
71    fn visit_mut_expr(&mut self, n: &mut Expr) {
72        n.visit_mut_children_with(self);
73
74        if self.in_target {
75            match n {
76                Expr::Arrow(..) | Expr::Fn(FnExpr { ident: None, .. }) => {
77                    //   useLayoutEffect(() => ...);
78                    //
79                    // becomes
80                    //
81                    //
82                    //  useLayoutEffect({'MyComponent.useLayoutEffect': () =>
83                    // ...}['MyComponent.useLayoutEffect']);
84
85                    let orig = n.take();
86                    let key = Atom::from(&*self.path);
87
88                    *n = Expr::Object(ObjectLit {
89                        span: DUMMY_SP,
90                        props: vec![PropOrSpread::Prop(Box::new(
91                            swc_core::ecma::ast::Prop::KeyValue(KeyValueProp {
92                                key: swc_core::ecma::ast::PropName::Str(key.clone().into()),
93                                value: Box::new(orig),
94                            }),
95                        ))],
96                    })
97                    .computed_member(key)
98                    .into();
99                }
100
101                _ => {}
102            }
103        }
104    }
105
106    fn visit_mut_fn_decl(&mut self, n: &mut FnDecl) {
107        let orig_len = self.path.len();
108        if !self.path.is_empty() {
109            self.path.push('.');
110        }
111        self.path.push_str(n.ident.sym.as_str());
112
113        n.visit_mut_children_with(self);
114
115        self.path.truncate(orig_len);
116    }
117
118    fn visit_mut_fn_expr(&mut self, n: &mut FnExpr) {
119        if let Some(ident) = &n.ident {
120            let orig_len = self.path.len();
121            if !self.path.is_empty() {
122                self.path.push('.');
123            }
124            self.path.push_str(ident.sym.as_str());
125
126            n.visit_mut_children_with(self);
127
128            self.path.truncate(orig_len);
129            return;
130        }
131
132        n.visit_mut_children_with(self);
133    }
134
135    fn visit_mut_var_declarator(&mut self, n: &mut VarDeclarator) {
136        if let Some(Expr::Call(call)) = n.init.as_deref() {
137            let name = is_target_callee(&call.callee).and_then(|target| {
138                let name = n.name.as_ident()?;
139
140                Some((name.sym.clone(), target))
141            });
142
143            if let Some((name, target)) = name {
144                let old_in_var_target = self.in_var_target;
145                self.in_var_target = true;
146
147                let old_in_target = self.in_target;
148                self.in_target = true;
149                let orig_len = self.path.len();
150                if !self.path.is_empty() {
151                    self.path.push('.');
152                }
153                let _ = write!(self.path, "{target}[{name}]");
154
155                n.visit_mut_children_with(self);
156
157                self.path.truncate(orig_len);
158                self.in_target = old_in_target;
159                self.in_var_target = old_in_var_target;
160                return;
161            }
162        }
163
164        if let Some(Expr::Arrow(..) | Expr::Fn(FnExpr { ident: None, .. }) | Expr::Call(..)) =
165            n.init.as_deref()
166        {
167            let name = n.name.as_ident();
168
169            if let Some(name) = name {
170                let orig_len = self.path.len();
171                if !self.path.is_empty() {
172                    self.path.push('.');
173                }
174                self.path.push_str(name.sym.as_str());
175
176                n.visit_mut_children_with(self);
177
178                self.path.truncate(orig_len);
179                return;
180            }
181        }
182
183        n.visit_mut_children_with(self);
184    }
185}
186
187fn is_target_callee(e: &Callee) -> Option<Atom> {
188    match e {
189        Callee::Expr(e) => match &**e {
190            Expr::Ident(i) => {
191                if i.sym.starts_with("use") {
192                    Some(i.sym.clone())
193                } else {
194                    None
195                }
196            }
197            Expr::Member(me) => match &me.prop {
198                MemberProp::Ident(i) => {
199                    if i.sym.starts_with("use") {
200                        Some(i.sym.clone())
201                    } else {
202                        None
203                    }
204                }
205                _ => None,
206            },
207            _ => None,
208        },
209        _ => None,
210    }
211}