next_custom_transforms/transforms/
debug_fn_name.rs1use 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 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}