oxc_traverse/ast_operations/
gather_node_parts.rs1use oxc_ast::ast::*;
8use oxc_ecmascript::BoundNames;
9
10use super::to_identifier;
11
12pub fn get_var_name_from_node<'a, N: GatherNodeParts<'a>>(node: &N) -> String {
13 let mut name = String::new();
14 node.gather(&mut |mut part| {
15 if name.is_empty() {
16 part = part.trim_start_matches('_');
17 } else {
18 name.push('$');
19 }
20 name.push_str(part);
21 });
22
23 if name.is_empty() {
24 name = "ref".to_string();
25 } else if name.len() > 20 {
26 let bytes = name.as_bytes();
30 if bytes[19] < 0x80 {
31 name.truncate(20);
33 } else {
34 let mut truncate_at = 19;
37 while truncate_at > 0 && (bytes[truncate_at] & 0xC0) == 0x80 {
38 truncate_at -= 1;
39 }
40 name.truncate(truncate_at);
41 }
42 }
43
44 to_identifier(name)
45}
46
47pub trait GatherNodeParts<'a> {
48 fn gather<F: FnMut(&str)>(&self, f: &mut F);
49}
50
51impl<'a> GatherNodeParts<'a> for ImportDeclaration<'a> {
53 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
54 self.source.gather(f);
55 }
56}
57
58impl<'a> GatherNodeParts<'a> for ExportAllDeclaration<'a> {
59 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
60 self.source.gather(f);
61 }
62}
63
64impl<'a> GatherNodeParts<'a> for ExportNamedDeclaration<'a> {
65 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
66 if let Some(source) = &self.source {
67 source.gather(f);
68 } else if let Some(declaration) = &self.declaration {
69 declaration.gather(f);
70 } else {
71 for specifier in &self.specifiers {
72 specifier.gather(f);
73 }
74 }
75 }
76}
77
78impl<'a> GatherNodeParts<'a> for ExportDefaultDeclaration<'a> {
79 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
80 self.declaration.gather(f);
81 }
82}
83
84impl<'a> GatherNodeParts<'a> for ExportDefaultDeclarationKind<'a> {
85 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
86 match self {
87 ExportDefaultDeclarationKind::FunctionDeclaration(decl) => decl.gather(f),
88 ExportDefaultDeclarationKind::ClassDeclaration(decl) => decl.gather(f),
89 ExportDefaultDeclarationKind::TSInterfaceDeclaration(_) => {}
90 match_expression!(ExportDefaultDeclarationKind) => self.to_expression().gather(f),
91 }
92 }
93}
94
95impl<'a> GatherNodeParts<'a> for ExportSpecifier<'a> {
96 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
97 match &self.local {
98 ModuleExportName::IdentifierName(ident) => ident.gather(f),
99 ModuleExportName::IdentifierReference(ident) => ident.gather(f),
100 ModuleExportName::StringLiteral(lit) => lit.gather(f),
101 }
102 }
103}
104
105impl<'a> GatherNodeParts<'a> for ModuleExportName<'a> {
106 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
107 match self {
108 ModuleExportName::IdentifierName(ident) => ident.gather(f),
109 ModuleExportName::IdentifierReference(ident) => ident.gather(f),
110 ModuleExportName::StringLiteral(lit) => lit.gather(f),
111 }
112 }
113}
114
115impl<'a> GatherNodeParts<'a> for ImportSpecifier<'a> {
116 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
117 self.local.gather(f);
118 }
119}
120
121impl<'a> GatherNodeParts<'a> for ImportDefaultSpecifier<'a> {
122 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
123 self.local.gather(f);
124 }
125}
126
127impl<'a> GatherNodeParts<'a> for ImportNamespaceSpecifier<'a> {
128 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
129 self.local.gather(f);
130 }
131}
132
133impl<'a> GatherNodeParts<'a> for Declaration<'a> {
136 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
137 match self {
138 Self::FunctionDeclaration(decl) => decl.gather(f),
139 Self::ClassDeclaration(decl) => decl.gather(f),
140 _ => (),
141 }
142 }
143}
144
145impl<'a> GatherNodeParts<'a> for Function<'a> {
148 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
149 if let Some(id) = &self.id {
150 id.gather(f);
151 }
152 }
153}
154
155impl<'a> GatherNodeParts<'a> for BindingRestElement<'a> {
156 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
157 self.argument.gather(f);
158 }
159}
160
161impl<'a> GatherNodeParts<'a> for VariableDeclarator<'a> {
164 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
165 self.id.gather(f);
166 }
167}
168
169impl<'a> GatherNodeParts<'a> for BindingPattern<'a> {
170 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
171 self.bound_names(&mut |id| f(id.name.as_str()));
172 }
173}
174
175impl<'a> GatherNodeParts<'a> for ObjectPattern<'a> {
176 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
177 self.bound_names(&mut |id| f(id.name.as_str()));
178 }
179}
180
181impl<'a> GatherNodeParts<'a> for ArrayPattern<'a> {
182 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
183 self.bound_names(&mut |id| f(id.name.as_str()));
184 }
185}
186
187impl<'a> GatherNodeParts<'a> for AssignmentPattern<'a> {
188 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
189 self.bound_names(&mut |id| f(id.name.as_str()));
190 }
191}
192
193impl<'a> GatherNodeParts<'a> for Expression<'a> {
196 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
197 match self {
198 match_member_expression!(Self) => self.to_member_expression().gather(f),
199 Self::Identifier(ident) => ident.gather(f),
200 Self::CallExpression(expr) => expr.gather(f),
201 Self::NewExpression(expr) => expr.gather(f),
202 Self::ObjectExpression(expr) => expr.gather(f),
203 Self::ThisExpression(expr) => expr.gather(f),
204 Self::Super(expr) => expr.gather(f),
205 Self::ImportExpression(expr) => expr.gather(f),
206 Self::YieldExpression(expr) => expr.gather(f),
207 Self::AwaitExpression(expr) => expr.gather(f),
208 Self::AssignmentExpression(expr) => expr.gather(f),
209 Self::FunctionExpression(expr) => expr.gather(f),
210 Self::ClassExpression(expr) => expr.gather(f),
211 Self::ParenthesizedExpression(expr) => expr.gather(f),
212 Self::UnaryExpression(expr) => expr.gather(f),
213 Self::UpdateExpression(expr) => expr.gather(f),
214 Self::ChainExpression(expr) => expr.gather(f),
215 Self::MetaProperty(expr) => expr.gather(f),
216 Self::JSXElement(expr) => expr.gather(f),
217 Self::JSXFragment(expr) => expr.gather(f),
218 Self::StringLiteral(expr) => expr.gather(f),
219 Self::NumericLiteral(expr) => expr.gather(f),
220 Self::BooleanLiteral(expr) => expr.gather(f),
221 Self::BigIntLiteral(expr) => expr.gather(f),
222 _ => (),
223 }
224 }
225}
226
227impl<'a> GatherNodeParts<'a> for ChainExpression<'a> {
228 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
229 self.expression.gather(f);
230 }
231}
232
233impl<'a> GatherNodeParts<'a> for ChainElement<'a> {
234 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
235 match self {
236 ChainElement::CallExpression(expr) => expr.gather(f),
237 ChainElement::TSNonNullExpression(expr) => expr.expression.gather(f),
238 expr @ match_member_expression!(Self) => expr.to_member_expression().gather(f),
239 }
240 }
241}
242
243impl<'a> GatherNodeParts<'a> for MemberExpression<'a> {
244 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
245 match self {
246 MemberExpression::ComputedMemberExpression(expr) => {
247 expr.gather(f);
248 }
249 MemberExpression::StaticMemberExpression(expr) => {
250 expr.gather(f);
251 }
252 MemberExpression::PrivateFieldExpression(expr) => {
253 expr.gather(f);
254 }
255 }
256 }
257}
258
259impl<'a> GatherNodeParts<'a> for ComputedMemberExpression<'a> {
260 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
261 self.object.gather(f);
262 self.expression.gather(f);
263 }
264}
265
266impl<'a> GatherNodeParts<'a> for StaticMemberExpression<'a> {
267 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
268 self.object.gather(f);
269 self.property.gather(f);
270 }
271}
272
273impl<'a> GatherNodeParts<'a> for PrivateFieldExpression<'a> {
274 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
275 self.object.gather(f);
276 self.field.gather(f);
277 }
278}
279
280impl<'a> GatherNodeParts<'a> for CallExpression<'a> {
281 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
282 self.callee.gather(f);
283 }
284}
285
286impl<'a> GatherNodeParts<'a> for NewExpression<'a> {
287 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
288 self.callee.gather(f);
289 }
290}
291
292impl<'a> GatherNodeParts<'a> for ObjectExpression<'a> {
293 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
294 for prop in &self.properties {
295 prop.gather(f);
296 }
297 }
298}
299
300impl GatherNodeParts<'_> for ThisExpression {
301 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
302 f("this");
303 }
304}
305
306impl GatherNodeParts<'_> for Super {
307 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
308 f("super");
309 }
310}
311
312impl<'a> GatherNodeParts<'a> for ImportExpression<'a> {
313 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
314 f("import");
315 }
316}
317
318impl<'a> GatherNodeParts<'a> for YieldExpression<'a> {
319 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
320 f("yield");
321 if let Some(argument) = &self.argument {
322 argument.gather(f);
323 }
324 }
325}
326
327impl<'a> GatherNodeParts<'a> for AwaitExpression<'a> {
328 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
329 f("await");
330 self.argument.gather(f);
331 }
332}
333
334impl<'a> GatherNodeParts<'a> for AssignmentExpression<'a> {
335 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
336 self.left.gather(f);
337 }
338}
339
340impl<'a> GatherNodeParts<'a> for ParenthesizedExpression<'a> {
341 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
342 self.expression.gather(f);
343 }
344}
345
346impl<'a> GatherNodeParts<'a> for UnaryExpression<'a> {
347 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
348 self.argument.gather(f);
349 }
350}
351
352impl<'a> GatherNodeParts<'a> for UpdateExpression<'a> {
353 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
354 self.argument.gather(f);
355 }
356}
357
358impl<'a> GatherNodeParts<'a> for MetaProperty<'a> {
359 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
360 self.meta.gather(f);
361 self.property.gather(f);
362 }
363}
364
365impl<'a> GatherNodeParts<'a> for AssignmentTarget<'a> {
367 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
368 match self {
369 match_simple_assignment_target!(Self) => {
370 self.to_simple_assignment_target().gather(f);
371 }
372 match_assignment_target_pattern!(Self) => {}
373 }
374 }
375}
376
377impl<'a> GatherNodeParts<'a> for SimpleAssignmentTarget<'a> {
378 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
379 match self {
380 Self::AssignmentTargetIdentifier(ident) => ident.gather(f),
381 match_member_expression!(Self) => self.to_member_expression().gather(f),
382 _ => {}
383 }
384 }
385}
386
387impl<'a> GatherNodeParts<'a> for Class<'a> {
390 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
391 if let Some(id) = &self.id {
392 id.gather(f);
393 }
394 }
395}
396
397impl<'a> GatherNodeParts<'a> for ClassElement<'a> {
398 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
399 match self {
400 ClassElement::PropertyDefinition(def) => def.gather(f),
401 ClassElement::MethodDefinition(def) => def.gather(f),
402 ClassElement::AccessorProperty(def) => def.gather(f),
403 _ => (),
404 }
405 }
406}
407
408impl<'a> GatherNodeParts<'a> for PropertyDefinition<'a> {
409 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
410 self.key.gather(f);
411 }
412}
413
414impl<'a> GatherNodeParts<'a> for MethodDefinition<'a> {
415 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
416 self.key.gather(f);
417 }
418}
419
420impl<'a> GatherNodeParts<'a> for AccessorProperty<'a> {
421 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
422 self.key.gather(f);
423 }
424}
425
426impl<'a> GatherNodeParts<'a> for ObjectPropertyKind<'a> {
429 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
430 match self {
431 ObjectPropertyKind::ObjectProperty(prop) => prop.gather(f),
432 ObjectPropertyKind::SpreadProperty(prop) => prop.gather(f),
433 }
434 }
435}
436
437impl<'a> GatherNodeParts<'a> for ObjectProperty<'a> {
438 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
439 self.key.gather(f);
440 }
441}
442
443impl<'a> GatherNodeParts<'a> for PropertyKey<'a> {
444 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
445 match self {
446 PropertyKey::StaticIdentifier(ident) => ident.gather(f),
447 PropertyKey::PrivateIdentifier(ident) => ident.gather(f),
448 match_expression!(Self) => self.to_expression().gather(f),
449 }
450 }
451}
452
453impl<'a> GatherNodeParts<'a> for SpreadElement<'a> {
454 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
455 self.argument.gather(f);
456 }
457}
458
459impl<'a> GatherNodeParts<'a> for BindingIdentifier<'a> {
462 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
463 f(self.name.as_str());
464 }
465}
466
467impl<'a> GatherNodeParts<'a> for IdentifierReference<'a> {
468 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
469 f(self.name.as_str());
470 }
471}
472
473impl<'a> GatherNodeParts<'a> for IdentifierName<'a> {
474 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
475 f(self.name.as_str());
476 }
477}
478
479impl<'a> GatherNodeParts<'a> for PrivateIdentifier<'a> {
480 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
481 f(self.name.as_str());
482 }
483}
484
485impl<'a> GatherNodeParts<'a> for StringLiteral<'a> {
488 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
489 f(self.value.as_str());
490 }
491}
492
493impl<'a> GatherNodeParts<'a> for NumericLiteral<'a> {
494 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
495 f(&self.raw_str());
496 }
497}
498
499impl GatherNodeParts<'_> for BooleanLiteral {
500 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
501 if self.value {
502 f("true");
503 } else {
504 f("false");
505 }
506 }
507}
508
509impl<'a> GatherNodeParts<'a> for BigIntLiteral<'a> {
510 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
511 f(self.value.as_str());
512 }
513}
514
515impl<'a> GatherNodeParts<'a> for JSXElement<'a> {
518 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
519 self.opening_element.gather(f);
520 }
521}
522
523impl<'a> GatherNodeParts<'a> for JSXFragment<'a> {
524 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
525 self.opening_fragment.gather(f);
526 }
527}
528
529impl<'a> GatherNodeParts<'a> for JSXOpeningElement<'a> {
530 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
531 self.name.gather(f);
532 }
533}
534
535impl GatherNodeParts<'_> for JSXOpeningFragment {
536 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
537 f("Fragment");
538 }
539}
540
541impl<'a> GatherNodeParts<'a> for JSXElementName<'a> {
542 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
543 match self {
544 JSXElementName::Identifier(ident) => ident.gather(f),
545 JSXElementName::IdentifierReference(ident) => ident.gather(f),
546 JSXElementName::NamespacedName(ns) => ns.gather(f),
547 JSXElementName::MemberExpression(expr) => expr.gather(f),
548 JSXElementName::ThisExpression(expr) => expr.gather(f),
549 }
550 }
551}
552
553impl<'a> GatherNodeParts<'a> for JSXNamespacedName<'a> {
554 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
555 self.namespace.gather(f);
556 self.name.gather(f);
557 }
558}
559
560impl<'a> GatherNodeParts<'a> for JSXMemberExpression<'a> {
561 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
562 self.object.gather(f);
563 self.property.gather(f);
564 }
565}
566
567impl<'a> GatherNodeParts<'a> for JSXMemberExpressionObject<'a> {
568 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
569 match self {
570 JSXMemberExpressionObject::IdentifierReference(ident) => ident.gather(f),
571 JSXMemberExpressionObject::MemberExpression(expr) => expr.gather(f),
572 JSXMemberExpressionObject::ThisExpression(expr) => expr.gather(f),
573 }
574 }
575}
576
577impl<'a> GatherNodeParts<'a> for JSXIdentifier<'a> {
578 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
579 f(self.name.as_str());
580 }
581}
582
583#[cfg(test)]
584mod tests {
585 use super::*;
586
587 struct TestNode<'a>(&'a str);
589
590 impl<'a> GatherNodeParts<'a> for TestNode<'a> {
591 fn gather<F: FnMut(&str)>(&self, f: &mut F) {
592 f(self.0);
593 }
594 }
595
596 #[test]
597 fn test_get_var_name_truncation_limits_to_approximately_20_bytes() {
598 let node = TestNode("abcdefghijklmnopqrst");
600 assert_eq!(get_var_name_from_node(&node), "abcdefghijklmnopqrst");
601
602 let node = TestNode("abcdefghijklmnopqrstu");
604 assert_eq!(get_var_name_from_node(&node), "abcdefghijklmnopqrst");
605
606 let node = TestNode("αβγδεζηθικλμνξοπρστυφ");
608 assert_eq!(get_var_name_from_node(&node), "αβγδεζηθι");
609
610 let node = TestNode("가나다라마바사아자차");
612 assert_eq!(get_var_name_from_node(&node), "가나다라마바");
613
614 let node = TestNode("𠀀𠀁𠀂𠀃𠀄𠀅");
616 assert_eq!(get_var_name_from_node(&node), "𠀀𠀁𠀂𠀃");
617
618 let node = TestNode("test_αβγδεζηθ");
620 assert_eq!(get_var_name_from_node(&node), "test_αβγδεζη");
621
622 let node = TestNode("short");
624 assert_eq!(get_var_name_from_node(&node), "short");
625
626 let node = TestNode("αβγδεζηθικ");
628 assert_eq!(get_var_name_from_node(&node), "αβγδεζηθικ");
629 }
630
631 #[test]
632 fn test_get_var_name_empty_returns_ref() {
633 let node = TestNode("");
634 let result = get_var_name_from_node(&node);
635 assert_eq!(result, "ref");
636 }
637
638 #[test]
639 fn test_get_var_name_strips_leading_underscores() {
640 let node = TestNode("___foo");
641 let result = get_var_name_from_node(&node);
642 assert_eq!(result, "foo");
643 }
644}