tree-sitter-analyzer 1.6.0__py3-none-any.whl → 1.6.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of tree-sitter-analyzer might be problematic. Click here for more details.

@@ -0,0 +1,1553 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ TypeScript Language Plugin
4
+
5
+ Enhanced TypeScript-specific parsing and element extraction functionality.
6
+ Provides comprehensive support for TypeScript features including interfaces,
7
+ type aliases, enums, generics, decorators, and modern JavaScript features.
8
+ Equivalent to JavaScript plugin capabilities with TypeScript-specific enhancements.
9
+ """
10
+
11
+ import re
12
+ from typing import TYPE_CHECKING, Any, Optional
13
+
14
+ if TYPE_CHECKING:
15
+ import tree_sitter
16
+
17
+ try:
18
+ import tree_sitter
19
+
20
+ TREE_SITTER_AVAILABLE = True
21
+ except ImportError:
22
+ TREE_SITTER_AVAILABLE = False
23
+
24
+ from ..core.analysis_engine import AnalysisRequest
25
+ from ..encoding_utils import extract_text_slice, safe_encode
26
+ from ..language_loader import loader
27
+ from ..models import AnalysisResult, Class, CodeElement, Function, Import, Variable
28
+ from ..plugins.base import ElementExtractor, LanguagePlugin
29
+ from ..utils import log_debug, log_error, log_warning
30
+
31
+
32
+ class TypeScriptElementExtractor(ElementExtractor):
33
+ """Enhanced TypeScript-specific element extractor with comprehensive feature support"""
34
+
35
+ def __init__(self) -> None:
36
+ """Initialize the TypeScript element extractor."""
37
+ self.current_file: str = ""
38
+ self.source_code: str = ""
39
+ self.content_lines: list[str] = []
40
+ self.imports: list[str] = []
41
+ self.exports: list[dict[str, Any]] = []
42
+
43
+ # Performance optimization caches
44
+ self._node_text_cache: dict[int, str] = {}
45
+ self._processed_nodes: set[int] = set()
46
+ self._element_cache: dict[tuple[int, str], Any] = {}
47
+ self._file_encoding: str | None = None
48
+ self._tsdoc_cache: dict[int, str] = {}
49
+ self._complexity_cache: dict[int, int] = {}
50
+
51
+ # TypeScript-specific tracking
52
+ self.is_module: bool = False
53
+ self.is_tsx: bool = False
54
+ self.framework_type: str = "" # react, angular, vue, etc.
55
+ self.typescript_version: str = "4.0" # default
56
+
57
+ def extract_functions(
58
+ self, tree: "tree_sitter.Tree", source_code: str
59
+ ) -> list[Function]:
60
+ """Extract TypeScript function definitions with comprehensive details"""
61
+ self.source_code = source_code
62
+ self.content_lines = source_code.split("\n")
63
+ self._reset_caches()
64
+ self._detect_file_characteristics()
65
+
66
+ functions: list[Function] = []
67
+
68
+ # Use optimized traversal for multiple function types
69
+ extractors = {
70
+ "function_declaration": self._extract_function_optimized,
71
+ "function_expression": self._extract_function_optimized,
72
+ "arrow_function": self._extract_arrow_function_optimized,
73
+ "method_definition": self._extract_method_optimized,
74
+ "generator_function_declaration": self._extract_generator_function_optimized,
75
+ "method_signature": self._extract_method_signature_optimized,
76
+ }
77
+
78
+ self._traverse_and_extract_iterative(
79
+ tree.root_node, extractors, functions, "function"
80
+ )
81
+
82
+ log_debug(f"Extracted {len(functions)} TypeScript functions")
83
+ return functions
84
+
85
+ def extract_classes(
86
+ self, tree: "tree_sitter.Tree", source_code: str
87
+ ) -> list[Class]:
88
+ """Extract TypeScript class and interface definitions with detailed information"""
89
+ self.source_code = source_code
90
+ self.content_lines = source_code.split("\n")
91
+ self._reset_caches()
92
+
93
+ classes: list[Class] = []
94
+
95
+ # Extract classes, interfaces, and type aliases
96
+ extractors = {
97
+ "class_declaration": self._extract_class_optimized,
98
+ "interface_declaration": self._extract_interface_optimized,
99
+ "type_alias_declaration": self._extract_type_alias_optimized,
100
+ "enum_declaration": self._extract_enum_optimized,
101
+ "abstract_class_declaration": self._extract_class_optimized,
102
+ }
103
+
104
+ self._traverse_and_extract_iterative(
105
+ tree.root_node, extractors, classes, "class"
106
+ )
107
+
108
+ log_debug(f"Extracted {len(classes)} TypeScript classes/interfaces/types")
109
+ return classes
110
+
111
+ def extract_variables(
112
+ self, tree: "tree_sitter.Tree", source_code: str
113
+ ) -> list[Variable]:
114
+ """Extract TypeScript variable definitions with type annotations"""
115
+ self.source_code = source_code
116
+ self.content_lines = source_code.split("\n")
117
+ self._reset_caches()
118
+
119
+ variables: list[Variable] = []
120
+
121
+ # Handle all TypeScript variable declaration types
122
+ extractors = {
123
+ "variable_declaration": self._extract_variable_optimized,
124
+ "lexical_declaration": self._extract_lexical_variable_optimized,
125
+ "property_definition": self._extract_property_optimized,
126
+ "property_signature": self._extract_property_signature_optimized,
127
+ }
128
+
129
+ self._traverse_and_extract_iterative(
130
+ tree.root_node, extractors, variables, "variable"
131
+ )
132
+
133
+ log_debug(f"Extracted {len(variables)} TypeScript variables")
134
+ return variables
135
+
136
+ def extract_imports(
137
+ self, tree: "tree_sitter.Tree", source_code: str
138
+ ) -> list[Import]:
139
+ """Extract TypeScript import statements with ES6+ and type import support"""
140
+ self.source_code = source_code
141
+ self.content_lines = source_code.split("\n")
142
+
143
+ imports: list[Import] = []
144
+
145
+ # Extract imports efficiently
146
+ for child in tree.root_node.children:
147
+ if child.type == "import_statement":
148
+ import_info = self._extract_import_info_simple(child)
149
+ if import_info:
150
+ imports.append(import_info)
151
+ elif child.type == "expression_statement":
152
+ # Check for dynamic imports
153
+ dynamic_import = self._extract_dynamic_import(child)
154
+ if dynamic_import:
155
+ imports.append(dynamic_import)
156
+
157
+ # Also check for CommonJS requires (for compatibility)
158
+ commonjs_imports = self._extract_commonjs_requires(tree, source_code)
159
+ imports.extend(commonjs_imports)
160
+
161
+ log_debug(f"Extracted {len(imports)} TypeScript imports")
162
+ return imports
163
+
164
+ def _reset_caches(self) -> None:
165
+ """Reset performance caches"""
166
+ self._node_text_cache.clear()
167
+ self._processed_nodes.clear()
168
+ self._element_cache.clear()
169
+ self._tsdoc_cache.clear()
170
+ self._complexity_cache.clear()
171
+
172
+ def _detect_file_characteristics(self) -> None:
173
+ """Detect TypeScript file characteristics"""
174
+ # Check if it's a module
175
+ self.is_module = "import " in self.source_code or "export " in self.source_code
176
+
177
+ # Check if it contains TSX/JSX
178
+ self.is_tsx = "</" in self.source_code and self.current_file.lower().endswith(('.tsx', '.jsx'))
179
+
180
+ # Detect framework
181
+ if "react" in self.source_code.lower() or "jsx" in self.source_code:
182
+ self.framework_type = "react"
183
+ elif "angular" in self.source_code.lower() or "@angular" in self.source_code:
184
+ self.framework_type = "angular"
185
+ elif "vue" in self.source_code.lower():
186
+ self.framework_type = "vue"
187
+
188
+ def _traverse_and_extract_iterative(
189
+ self,
190
+ root_node: "tree_sitter.Node",
191
+ extractors: dict[str, Any],
192
+ results: list[Any],
193
+ element_type: str,
194
+ ) -> None:
195
+ """Iterative node traversal and extraction with caching"""
196
+ if not root_node:
197
+ return
198
+
199
+ target_node_types = set(extractors.keys())
200
+ container_node_types = {
201
+ "program",
202
+ "class_body",
203
+ "interface_body",
204
+ "statement_block",
205
+ "object_type",
206
+ "class_declaration",
207
+ "interface_declaration",
208
+ "function_declaration",
209
+ "method_definition",
210
+ "export_statement",
211
+ "variable_declaration",
212
+ "lexical_declaration",
213
+ "variable_declarator",
214
+ "assignment_expression",
215
+ "type_alias_declaration",
216
+ "enum_declaration",
217
+ }
218
+
219
+ node_stack = [(root_node, 0)]
220
+ processed_nodes = 0
221
+ max_depth = 50
222
+
223
+ while node_stack:
224
+ current_node, depth = node_stack.pop()
225
+
226
+ if depth > max_depth:
227
+ log_warning(f"Maximum traversal depth ({max_depth}) exceeded")
228
+ continue
229
+
230
+ processed_nodes += 1
231
+ node_type = current_node.type
232
+
233
+ # Early termination for irrelevant nodes
234
+ if (
235
+ depth > 0
236
+ and node_type not in target_node_types
237
+ and node_type not in container_node_types
238
+ ):
239
+ continue
240
+
241
+ # Process target nodes
242
+ if node_type in target_node_types:
243
+ node_id = id(current_node)
244
+
245
+ if node_id in self._processed_nodes:
246
+ continue
247
+
248
+ cache_key = (node_id, element_type)
249
+ if cache_key in self._element_cache:
250
+ element = self._element_cache[cache_key]
251
+ if element:
252
+ if isinstance(element, list):
253
+ results.extend(element)
254
+ else:
255
+ results.append(element)
256
+ self._processed_nodes.add(node_id)
257
+ continue
258
+
259
+ # Extract and cache
260
+ extractor = extractors.get(node_type)
261
+ if extractor:
262
+ element = extractor(current_node)
263
+ self._element_cache[cache_key] = element
264
+ if element:
265
+ if isinstance(element, list):
266
+ results.extend(element)
267
+ else:
268
+ results.append(element)
269
+ self._processed_nodes.add(node_id)
270
+
271
+ # Add children to stack
272
+ if current_node.children:
273
+ for child in reversed(current_node.children):
274
+ node_stack.append((child, depth + 1))
275
+
276
+ log_debug(f"Iterative traversal processed {processed_nodes} nodes")
277
+
278
+ def _get_node_text_optimized(self, node: "tree_sitter.Node") -> str:
279
+ """Get node text with optimized caching"""
280
+ node_id = id(node)
281
+
282
+ if node_id in self._node_text_cache:
283
+ return self._node_text_cache[node_id]
284
+
285
+ try:
286
+ start_byte = node.start_byte
287
+ end_byte = node.end_byte
288
+
289
+ encoding = self._file_encoding or "utf-8"
290
+ content_bytes = safe_encode("\n".join(self.content_lines), encoding)
291
+ text = extract_text_slice(content_bytes, start_byte, end_byte, encoding)
292
+
293
+ self._node_text_cache[node_id] = text
294
+ return text
295
+ except Exception as e:
296
+ log_error(f"Error in _get_node_text_optimized: {e}")
297
+ # Fallback to simple text extraction
298
+ try:
299
+ start_point = node.start_point
300
+ end_point = node.end_point
301
+
302
+ if start_point[0] == end_point[0]:
303
+ line = self.content_lines[start_point[0]]
304
+ return line[start_point[1] : end_point[1]]
305
+ else:
306
+ lines = []
307
+ for i in range(start_point[0], end_point[0] + 1):
308
+ if i < len(self.content_lines):
309
+ line = self.content_lines[i]
310
+ if i == start_point[0]:
311
+ lines.append(line[start_point[1] :])
312
+ elif i == end_point[0]:
313
+ lines.append(line[: end_point[1]])
314
+ else:
315
+ lines.append(line)
316
+ return "\n".join(lines)
317
+ except Exception as fallback_error:
318
+ log_error(f"Fallback text extraction also failed: {fallback_error}")
319
+ return ""
320
+
321
+ def _extract_function_optimized(self, node: "tree_sitter.Node") -> Function | None:
322
+ """Extract regular function information with detailed metadata"""
323
+ try:
324
+ start_line = node.start_point[0] + 1
325
+ end_line = node.end_point[0] + 1
326
+
327
+ # Extract function details
328
+ function_info = self._parse_function_signature_optimized(node)
329
+ if not function_info:
330
+ return None
331
+
332
+ name, parameters, is_async, is_generator, return_type, generics = function_info
333
+
334
+ # Extract TSDoc
335
+ tsdoc = self._extract_tsdoc_for_line(start_line)
336
+
337
+ # Calculate complexity
338
+ complexity_score = self._calculate_complexity_optimized(node)
339
+
340
+ # Extract raw text
341
+ start_line_idx = max(0, start_line - 1)
342
+ end_line_idx = min(len(self.content_lines), end_line)
343
+ raw_text = "\n".join(self.content_lines[start_line_idx:end_line_idx])
344
+
345
+ return Function(
346
+ name=name,
347
+ start_line=start_line,
348
+ end_line=end_line,
349
+ raw_text=raw_text,
350
+ language="typescript",
351
+ parameters=parameters,
352
+ return_type=return_type or "any",
353
+ is_async=is_async,
354
+ is_generator=is_generator,
355
+ docstring=tsdoc,
356
+ complexity_score=complexity_score,
357
+ # TypeScript-specific properties
358
+ is_arrow=False,
359
+ is_method=False,
360
+ framework_type=self.framework_type,
361
+ )
362
+ except Exception as e:
363
+ log_error(f"Failed to extract function info: {e}")
364
+ import traceback
365
+
366
+ traceback.print_exc()
367
+ return None
368
+
369
+ def _extract_arrow_function_optimized(
370
+ self, node: "tree_sitter.Node"
371
+ ) -> Function | None:
372
+ """Extract arrow function information"""
373
+ try:
374
+ start_line = node.start_point[0] + 1
375
+ end_line = node.end_point[0] + 1
376
+
377
+ # For arrow functions, we need to find the variable declaration
378
+ parent = node.parent
379
+ name = "anonymous"
380
+
381
+ if parent and parent.type == "variable_declarator":
382
+ for child in parent.children:
383
+ if child.type == "identifier":
384
+ name = self._get_node_text_optimized(child)
385
+ break
386
+
387
+ # Extract parameters and return type
388
+ parameters = []
389
+ return_type = None
390
+ is_async = False
391
+
392
+ for child in node.children:
393
+ if child.type == "formal_parameters":
394
+ parameters = self._extract_parameters_with_types(child)
395
+ elif child.type == "identifier":
396
+ # Single parameter without parentheses
397
+ param_name = self._get_node_text_optimized(child)
398
+ parameters = [param_name]
399
+ elif child.type == "type_annotation":
400
+ return_type = self._get_node_text_optimized(child).lstrip(": ")
401
+
402
+ # Check if async
403
+ node_text = self._get_node_text_optimized(node)
404
+ is_async = "async" in node_text
405
+
406
+ # Extract TSDoc (look at parent variable declaration)
407
+ tsdoc = self._extract_tsdoc_for_line(start_line)
408
+
409
+ # Calculate complexity
410
+ complexity_score = self._calculate_complexity_optimized(node)
411
+
412
+ # Extract raw text
413
+ raw_text = self._get_node_text_optimized(node)
414
+
415
+ return Function(
416
+ name=name,
417
+ start_line=start_line,
418
+ end_line=end_line,
419
+ raw_text=raw_text,
420
+ language="typescript",
421
+ parameters=parameters,
422
+ return_type=return_type or "any",
423
+ is_async=is_async,
424
+ is_generator=False,
425
+ docstring=tsdoc,
426
+ complexity_score=complexity_score,
427
+ # TypeScript-specific properties
428
+ is_arrow=True,
429
+ is_method=False,
430
+ framework_type=self.framework_type,
431
+ )
432
+ except Exception as e:
433
+ log_debug(f"Failed to extract arrow function info: {e}")
434
+ return None
435
+
436
+ def _extract_method_optimized(self, node: "tree_sitter.Node") -> Function | None:
437
+ """Extract method information from class"""
438
+ try:
439
+ start_line = node.start_point[0] + 1
440
+ end_line = node.end_point[0] + 1
441
+
442
+ # Extract method details
443
+ method_info = self._parse_method_signature_optimized(node)
444
+ if not method_info:
445
+ return None
446
+
447
+ (
448
+ name,
449
+ parameters,
450
+ is_async,
451
+ is_static,
452
+ is_getter,
453
+ is_setter,
454
+ is_constructor,
455
+ return_type,
456
+ visibility,
457
+ generics,
458
+ ) = method_info
459
+
460
+ # Extract TSDoc
461
+ tsdoc = self._extract_tsdoc_for_line(start_line)
462
+
463
+ # Calculate complexity
464
+ complexity_score = self._calculate_complexity_optimized(node)
465
+
466
+ # Extract raw text
467
+ raw_text = self._get_node_text_optimized(node)
468
+
469
+ return Function(
470
+ name=name,
471
+ start_line=start_line,
472
+ end_line=end_line,
473
+ raw_text=raw_text,
474
+ language="typescript",
475
+ parameters=parameters,
476
+ return_type=return_type or "any",
477
+ is_async=is_async,
478
+ is_static=is_static,
479
+ is_constructor=is_constructor,
480
+ docstring=tsdoc,
481
+ complexity_score=complexity_score,
482
+ # TypeScript-specific properties
483
+ is_arrow=False,
484
+ is_method=True,
485
+ framework_type=self.framework_type,
486
+ visibility=visibility,
487
+ )
488
+ except Exception as e:
489
+ log_debug(f"Failed to extract method info: {e}")
490
+ return None
491
+
492
+ def _extract_method_signature_optimized(
493
+ self, node: "tree_sitter.Node"
494
+ ) -> Function | None:
495
+ """Extract method signature information from interfaces"""
496
+ try:
497
+ start_line = node.start_point[0] + 1
498
+ end_line = node.end_point[0] + 1
499
+
500
+ # Extract method signature details
501
+ method_info = self._parse_method_signature_optimized(node)
502
+ if not method_info:
503
+ return None
504
+
505
+ (
506
+ name,
507
+ parameters,
508
+ is_async,
509
+ is_static,
510
+ is_getter,
511
+ is_setter,
512
+ is_constructor,
513
+ return_type,
514
+ visibility,
515
+ generics,
516
+ ) = method_info
517
+
518
+ # Extract TSDoc
519
+ tsdoc = self._extract_tsdoc_for_line(start_line)
520
+
521
+ # Extract raw text
522
+ raw_text = self._get_node_text_optimized(node)
523
+
524
+ return Function(
525
+ name=name,
526
+ start_line=start_line,
527
+ end_line=end_line,
528
+ raw_text=raw_text,
529
+ language="typescript",
530
+ parameters=parameters,
531
+ return_type=return_type or "any",
532
+ is_async=is_async,
533
+ is_static=is_static,
534
+ docstring=tsdoc,
535
+ complexity_score=0, # Signatures have no complexity
536
+ # TypeScript-specific properties
537
+ is_arrow=False,
538
+ is_method=True,
539
+ is_signature=True,
540
+ framework_type=self.framework_type,
541
+ visibility=visibility,
542
+ is_getter=is_getter,
543
+ is_setter=is_setter,
544
+ # TypeScript-specific properties handled above
545
+ )
546
+ except Exception as e:
547
+ log_debug(f"Failed to extract method signature info: {e}")
548
+ return None
549
+
550
+ def _extract_generator_function_optimized(
551
+ self, node: "tree_sitter.Node"
552
+ ) -> Function | None:
553
+ """Extract generator function information"""
554
+ try:
555
+ start_line = node.start_point[0] + 1
556
+ end_line = node.end_point[0] + 1
557
+
558
+ # Extract function details
559
+ function_info = self._parse_function_signature_optimized(node)
560
+ if not function_info:
561
+ return None
562
+
563
+ name, parameters, is_async, _, return_type, generics = function_info
564
+
565
+ # Extract TSDoc
566
+ tsdoc = self._extract_tsdoc_for_line(start_line)
567
+
568
+ # Calculate complexity
569
+ complexity_score = self._calculate_complexity_optimized(node)
570
+
571
+ # Extract raw text
572
+ raw_text = self._get_node_text_optimized(node)
573
+
574
+ return Function(
575
+ name=name,
576
+ start_line=start_line,
577
+ end_line=end_line,
578
+ raw_text=raw_text,
579
+ language="typescript",
580
+ parameters=parameters,
581
+ return_type=return_type or "Generator",
582
+ is_async=is_async,
583
+ is_generator=True,
584
+ docstring=tsdoc,
585
+ complexity_score=complexity_score,
586
+ # TypeScript-specific properties
587
+ is_arrow=False,
588
+ is_method=False,
589
+ framework_type=self.framework_type,
590
+ # TypeScript-specific properties handled above
591
+ )
592
+ except Exception as e:
593
+ log_debug(f"Failed to extract generator function info: {e}")
594
+ return None
595
+
596
+ def _extract_class_optimized(self, node: "tree_sitter.Node") -> Class | None:
597
+ """Extract class information with detailed metadata"""
598
+ try:
599
+ start_line = node.start_point[0] + 1
600
+ end_line = node.end_point[0] + 1
601
+
602
+ # Extract class name
603
+ class_name = None
604
+ superclass = None
605
+ interfaces = []
606
+ generics = []
607
+ is_abstract = node.type == "abstract_class_declaration"
608
+
609
+ for child in node.children:
610
+ if child.type == "type_identifier":
611
+ class_name = child.text.decode("utf8") if child.text else None
612
+ elif child.type == "class_heritage":
613
+ # Extract extends and implements clauses
614
+ heritage_text = self._get_node_text_optimized(child)
615
+ extends_match = re.search(r"extends\s+(\w+)", heritage_text)
616
+ if extends_match:
617
+ superclass = extends_match.group(1)
618
+
619
+ implements_matches = re.findall(r"implements\s+([\w,\s]+)", heritage_text)
620
+ if implements_matches:
621
+ interfaces = [iface.strip() for iface in implements_matches[0].split(",")]
622
+ elif child.type == "type_parameters":
623
+ generics = self._extract_generics(child)
624
+
625
+ if not class_name:
626
+ return None
627
+
628
+ # Extract TSDoc
629
+ tsdoc = self._extract_tsdoc_for_line(start_line)
630
+
631
+ # Check if it's a framework component
632
+ is_component = self._is_framework_component(node, class_name)
633
+
634
+ # Extract raw text
635
+ raw_text = self._get_node_text_optimized(node)
636
+
637
+ return Class(
638
+ name=class_name,
639
+ start_line=start_line,
640
+ end_line=end_line,
641
+ raw_text=raw_text,
642
+ language="typescript",
643
+ class_type="abstract_class" if is_abstract else "class",
644
+ superclass=superclass,
645
+ interfaces=interfaces,
646
+ docstring=tsdoc,
647
+ # TypeScript-specific properties
648
+ is_react_component=is_component,
649
+ framework_type=self.framework_type,
650
+ is_exported=self._is_exported_class(class_name),
651
+ is_abstract=is_abstract,
652
+ # TypeScript-specific properties handled above
653
+ )
654
+ except Exception as e:
655
+ log_debug(f"Failed to extract class info: {e}")
656
+ return None
657
+
658
+ def _extract_interface_optimized(self, node: "tree_sitter.Node") -> Class | None:
659
+ """Extract interface information"""
660
+ try:
661
+ start_line = node.start_point[0] + 1
662
+ end_line = node.end_point[0] + 1
663
+
664
+ # Extract interface name
665
+ interface_name = None
666
+ extends_interfaces = []
667
+ generics = []
668
+
669
+ for child in node.children:
670
+ if child.type == "type_identifier":
671
+ interface_name = child.text.decode("utf8") if child.text else None
672
+ elif child.type == "extends_clause":
673
+ # Extract extends clause for interfaces
674
+ extends_text = self._get_node_text_optimized(child)
675
+ extends_matches = re.findall(r"extends\s+([\w,\s]+)", extends_text)
676
+ if extends_matches:
677
+ extends_interfaces = [iface.strip() for iface in extends_matches[0].split(",")]
678
+ elif child.type == "type_parameters":
679
+ generics = self._extract_generics(child)
680
+
681
+ if not interface_name:
682
+ return None
683
+
684
+ # Extract TSDoc
685
+ tsdoc = self._extract_tsdoc_for_line(start_line)
686
+
687
+ # Extract raw text
688
+ raw_text = self._get_node_text_optimized(node)
689
+
690
+ return Class(
691
+ name=interface_name,
692
+ start_line=start_line,
693
+ end_line=end_line,
694
+ raw_text=raw_text,
695
+ language="typescript",
696
+ class_type="interface",
697
+ interfaces=extends_interfaces,
698
+ docstring=tsdoc,
699
+ # TypeScript-specific properties
700
+ framework_type=self.framework_type,
701
+ is_exported=self._is_exported_class(interface_name),
702
+ # TypeScript-specific properties handled above
703
+ )
704
+ except Exception as e:
705
+ log_debug(f"Failed to extract interface info: {e}")
706
+ return None
707
+
708
+ def _extract_type_alias_optimized(self, node: "tree_sitter.Node") -> Class | None:
709
+ """Extract type alias information"""
710
+ try:
711
+ start_line = node.start_point[0] + 1
712
+ end_line = node.end_point[0] + 1
713
+
714
+ # Extract type alias name
715
+ type_name = None
716
+ generics = []
717
+
718
+ for child in node.children:
719
+ if child.type == "type_identifier":
720
+ type_name = child.text.decode("utf8") if child.text else None
721
+ elif child.type == "type_parameters":
722
+ generics = self._extract_generics(child)
723
+
724
+ if not type_name:
725
+ return None
726
+
727
+ # Extract TSDoc
728
+ tsdoc = self._extract_tsdoc_for_line(start_line)
729
+
730
+ # Extract raw text
731
+ raw_text = self._get_node_text_optimized(node)
732
+
733
+ return Class(
734
+ name=type_name,
735
+ start_line=start_line,
736
+ end_line=end_line,
737
+ raw_text=raw_text,
738
+ language="typescript",
739
+ class_type="type",
740
+ docstring=tsdoc,
741
+ # TypeScript-specific properties
742
+ framework_type=self.framework_type,
743
+ is_exported=self._is_exported_class(type_name),
744
+ # TypeScript-specific properties handled above
745
+ )
746
+ except Exception as e:
747
+ log_debug(f"Failed to extract type alias info: {e}")
748
+ return None
749
+
750
+ def _extract_enum_optimized(self, node: "tree_sitter.Node") -> Class | None:
751
+ """Extract enum information"""
752
+ try:
753
+ start_line = node.start_point[0] + 1
754
+ end_line = node.end_point[0] + 1
755
+
756
+ # Extract enum name
757
+ enum_name = None
758
+
759
+ for child in node.children:
760
+ if child.type == "identifier":
761
+ enum_name = child.text.decode("utf8") if child.text else None
762
+
763
+ if not enum_name:
764
+ return None
765
+
766
+ # Extract TSDoc
767
+ tsdoc = self._extract_tsdoc_for_line(start_line)
768
+
769
+ # Extract raw text
770
+ raw_text = self._get_node_text_optimized(node)
771
+
772
+ return Class(
773
+ name=enum_name,
774
+ start_line=start_line,
775
+ end_line=end_line,
776
+ raw_text=raw_text,
777
+ language="typescript",
778
+ class_type="enum",
779
+ docstring=tsdoc,
780
+ # TypeScript-specific properties
781
+ framework_type=self.framework_type,
782
+ is_exported=self._is_exported_class(enum_name),
783
+ # TypeScript-specific properties handled above
784
+ )
785
+ except Exception as e:
786
+ log_debug(f"Failed to extract enum info: {e}")
787
+ return None
788
+
789
+ def _extract_variable_optimized(self, node: "tree_sitter.Node") -> list[Variable]:
790
+ """Extract var declaration variables"""
791
+ return self._extract_variables_from_declaration(node, "var")
792
+
793
+ def _extract_lexical_variable_optimized(
794
+ self, node: "tree_sitter.Node"
795
+ ) -> list[Variable]:
796
+ """Extract let/const declaration variables"""
797
+ # Determine if it's let or const
798
+ node_text = self._get_node_text_optimized(node)
799
+ kind = "let" if node_text.strip().startswith("let") else "const"
800
+ return self._extract_variables_from_declaration(node, kind)
801
+
802
+ def _extract_property_optimized(self, node: "tree_sitter.Node") -> Variable | None:
803
+ """Extract class property definition"""
804
+ try:
805
+ start_line = node.start_point[0] + 1
806
+ end_line = node.end_point[0] + 1
807
+
808
+ # Extract property name and type
809
+ prop_name = None
810
+ prop_type = None
811
+ prop_value = None
812
+ is_static = False
813
+ visibility = "public"
814
+
815
+ for child in node.children:
816
+ if child.type == "property_identifier":
817
+ prop_name = self._get_node_text_optimized(child)
818
+ elif child.type == "type_annotation":
819
+ prop_type = self._get_node_text_optimized(child).lstrip(": ")
820
+ elif child.type in ["string", "number", "true", "false", "null"]:
821
+ prop_value = self._get_node_text_optimized(child)
822
+
823
+ # Check modifiers from parent or node text
824
+ node_text = self._get_node_text_optimized(node)
825
+ is_static = "static" in node_text
826
+ if "private" in node_text:
827
+ visibility = "private"
828
+ elif "protected" in node_text:
829
+ visibility = "protected"
830
+
831
+ if not prop_name:
832
+ return None
833
+
834
+ # Extract raw text
835
+ raw_text = self._get_node_text_optimized(node)
836
+
837
+ return Variable(
838
+ name=prop_name,
839
+ start_line=start_line,
840
+ end_line=end_line,
841
+ raw_text=raw_text,
842
+ language="typescript",
843
+ variable_type=prop_type or "any",
844
+ value=prop_value,
845
+ is_static=is_static,
846
+ is_constant=False, # Class properties are not const
847
+ # TypeScript-specific properties
848
+ visibility=visibility,
849
+ )
850
+ except Exception as e:
851
+ log_debug(f"Failed to extract property info: {e}")
852
+ return None
853
+
854
+ def _extract_property_signature_optimized(
855
+ self, node: "tree_sitter.Node"
856
+ ) -> Variable | None:
857
+ """Extract property signature from interface"""
858
+ try:
859
+ start_line = node.start_point[0] + 1
860
+ end_line = node.end_point[0] + 1
861
+
862
+ # Extract property signature name and type
863
+ prop_name = None
864
+ prop_type = None
865
+ is_optional = False
866
+
867
+ for child in node.children:
868
+ if child.type == "property_identifier":
869
+ prop_name = self._get_node_text_optimized(child)
870
+ elif child.type == "type_annotation":
871
+ prop_type = self._get_node_text_optimized(child).lstrip(": ")
872
+
873
+ # Check for optional property
874
+ node_text = self._get_node_text_optimized(node)
875
+ is_optional = "?" in node_text
876
+
877
+ if not prop_name:
878
+ return None
879
+
880
+ # Extract raw text
881
+ raw_text = self._get_node_text_optimized(node)
882
+
883
+ return Variable(
884
+ name=prop_name,
885
+ start_line=start_line,
886
+ end_line=end_line,
887
+ raw_text=raw_text,
888
+ language="typescript",
889
+ variable_type=prop_type or "any",
890
+ is_constant=False,
891
+ # TypeScript-specific properties
892
+ visibility="public", # Interface properties are always public
893
+ )
894
+ except Exception as e:
895
+ log_debug(f"Failed to extract property signature info: {e}")
896
+ return None
897
+
898
+ def _extract_variables_from_declaration(
899
+ self, node: "tree_sitter.Node", kind: str
900
+ ) -> list[Variable]:
901
+ """Extract variables from declaration node"""
902
+ variables: list[Variable] = []
903
+
904
+ try:
905
+ start_line = node.start_point[0] + 1
906
+ end_line = node.end_point[0] + 1
907
+
908
+ # Find variable declarators
909
+ for child in node.children:
910
+ if child.type == "variable_declarator":
911
+ var_info = self._parse_variable_declarator(
912
+ child, kind, start_line, end_line
913
+ )
914
+ if var_info:
915
+ variables.append(var_info)
916
+
917
+ except Exception as e:
918
+ log_debug(f"Failed to extract variables from declaration: {e}")
919
+
920
+ return variables
921
+
922
+ def _parse_variable_declarator(
923
+ self, node: "tree_sitter.Node", kind: str, start_line: int, end_line: int
924
+ ) -> Variable | None:
925
+ """Parse individual variable declarator with TypeScript type annotations"""
926
+ try:
927
+ var_name = None
928
+ var_type = None
929
+ var_value = None
930
+
931
+ # Find identifier, type annotation, and value in children
932
+ for child in node.children:
933
+ if child.type == "identifier":
934
+ var_name = self._get_node_text_optimized(child)
935
+ elif child.type == "type_annotation":
936
+ var_type = self._get_node_text_optimized(child).lstrip(": ")
937
+ elif child.type == "=" and child.next_sibling:
938
+ # Get the value after the assignment operator
939
+ value_node = child.next_sibling
940
+ var_value = self._get_node_text_optimized(value_node)
941
+
942
+ if not var_name:
943
+ return None
944
+
945
+ # Skip variables that are arrow functions (handled by function extractor)
946
+ for child in node.children:
947
+ if child.type == "arrow_function":
948
+ return None
949
+
950
+ # Extract TSDoc
951
+ tsdoc = self._extract_tsdoc_for_line(start_line)
952
+
953
+ # Extract raw text
954
+ raw_text = self._get_node_text_optimized(node)
955
+
956
+ return Variable(
957
+ name=var_name,
958
+ start_line=start_line,
959
+ end_line=end_line,
960
+ raw_text=raw_text,
961
+ language="typescript",
962
+ variable_type=var_type or self._infer_type_from_value(var_value),
963
+ is_static=False,
964
+ is_constant=(kind == "const"),
965
+ docstring=tsdoc,
966
+ initializer=var_value,
967
+ # TypeScript-specific properties
968
+ visibility="public", # Variables are typically public in TypeScript
969
+ )
970
+ except Exception as e:
971
+ log_debug(f"Failed to parse variable declarator: {e}")
972
+ return None
973
+
974
+ def _parse_function_signature_optimized(
975
+ self, node: "tree_sitter.Node"
976
+ ) -> tuple[str, list[str], bool, bool, str | None, list[str]] | None:
977
+ """Parse function signature for TypeScript functions"""
978
+ try:
979
+ name = None
980
+ parameters = []
981
+ is_async = False
982
+ is_generator = False
983
+ return_type = None
984
+ generics = []
985
+
986
+ # Check for async/generator keywords
987
+ node_text = self._get_node_text_optimized(node)
988
+ is_async = "async" in node_text
989
+ is_generator = node.type == "generator_function_declaration"
990
+
991
+ for child in node.children:
992
+ if child.type == "identifier":
993
+ name = child.text.decode("utf8") if child.text else None
994
+ elif child.type == "formal_parameters":
995
+ parameters = self._extract_parameters_with_types(child)
996
+ elif child.type == "type_annotation":
997
+ return_type = self._get_node_text_optimized(child).lstrip(": ")
998
+ elif child.type == "type_parameters":
999
+ generics = self._extract_generics(child)
1000
+
1001
+ return name or "", parameters, is_async, is_generator, return_type, generics
1002
+ except Exception:
1003
+ return None
1004
+
1005
+ def _parse_method_signature_optimized(
1006
+ self, node: "tree_sitter.Node"
1007
+ ) -> tuple[str, list[str], bool, bool, bool, bool, bool, str | None, str, list[str]] | None:
1008
+ """Parse method signature for TypeScript class methods"""
1009
+ try:
1010
+ name = None
1011
+ parameters = []
1012
+ is_async = False
1013
+ is_static = False
1014
+ is_getter = False
1015
+ is_setter = False
1016
+ is_constructor = False
1017
+ return_type = None
1018
+ visibility = "public"
1019
+ generics = []
1020
+
1021
+ # Check for method type
1022
+ node_text = self._get_node_text_optimized(node)
1023
+ is_async = "async" in node_text
1024
+ is_static = "static" in node_text
1025
+
1026
+ # Check visibility
1027
+ if "private" in node_text:
1028
+ visibility = "private"
1029
+ elif "protected" in node_text:
1030
+ visibility = "protected"
1031
+
1032
+ for child in node.children:
1033
+ if child.type == "property_identifier":
1034
+ name = self._get_node_text_optimized(child)
1035
+ is_constructor = name == "constructor"
1036
+ elif child.type == "formal_parameters":
1037
+ parameters = self._extract_parameters_with_types(child)
1038
+ elif child.type == "type_annotation":
1039
+ return_type = self._get_node_text_optimized(child).lstrip(": ")
1040
+ elif child.type == "type_parameters":
1041
+ generics = self._extract_generics(child)
1042
+
1043
+ # Check for getter/setter
1044
+ if "get " in node_text:
1045
+ is_getter = True
1046
+ elif "set " in node_text:
1047
+ is_setter = True
1048
+
1049
+ return (
1050
+ name,
1051
+ parameters,
1052
+ is_async,
1053
+ is_static,
1054
+ is_getter,
1055
+ is_setter,
1056
+ is_constructor,
1057
+ return_type,
1058
+ visibility,
1059
+ generics,
1060
+ )
1061
+ except Exception:
1062
+ return None
1063
+
1064
+ def _extract_parameters_with_types(self, params_node: "tree_sitter.Node") -> list[str]:
1065
+ """Extract function parameters with TypeScript type annotations"""
1066
+ parameters = []
1067
+
1068
+ for child in params_node.children:
1069
+ if child.type == "identifier":
1070
+ param_name = self._get_node_text_optimized(child)
1071
+ parameters.append(param_name)
1072
+ elif child.type == "required_parameter":
1073
+ # Handle typed parameters
1074
+ param_text = self._get_node_text_optimized(child)
1075
+ parameters.append(param_text)
1076
+ elif child.type == "optional_parameter":
1077
+ # Handle optional parameters
1078
+ param_text = self._get_node_text_optimized(child)
1079
+ parameters.append(param_text)
1080
+ elif child.type == "rest_parameter":
1081
+ # Handle rest parameters (...args)
1082
+ rest_text = self._get_node_text_optimized(child)
1083
+ parameters.append(rest_text)
1084
+ elif child.type in ["object_pattern", "array_pattern"]:
1085
+ # Handle destructuring parameters
1086
+ destructure_text = self._get_node_text_optimized(child)
1087
+ parameters.append(destructure_text)
1088
+
1089
+ return parameters
1090
+
1091
+ def _extract_generics(self, type_params_node: "tree_sitter.Node") -> list[str]:
1092
+ """Extract generic type parameters"""
1093
+ generics = []
1094
+
1095
+ for child in type_params_node.children:
1096
+ if child.type == "type_parameter":
1097
+ generic_text = self._get_node_text_optimized(child)
1098
+ generics.append(generic_text)
1099
+
1100
+ return generics
1101
+
1102
+ def _extract_import_info_simple(self, node: "tree_sitter.Node") -> Import | None:
1103
+ """Extract import information from import_statement node"""
1104
+ try:
1105
+ start_line = node.start_point[0] + 1
1106
+ end_line = node.end_point[0] + 1
1107
+
1108
+ # Get raw text using byte positions
1109
+ start_byte = node.start_byte
1110
+ end_byte = node.end_byte
1111
+ source_bytes = self.source_code.encode("utf-8")
1112
+ raw_text = source_bytes[start_byte:end_byte].decode("utf-8")
1113
+
1114
+ # Extract import details from AST structure
1115
+ import_names = []
1116
+ module_path = ""
1117
+ is_type_import = "type" in raw_text
1118
+
1119
+ for child in node.children:
1120
+ if child.type == "import_clause":
1121
+ import_names.extend(self._extract_import_names(child))
1122
+ elif child.type == "string":
1123
+ # Module path
1124
+ module_text = source_bytes[
1125
+ child.start_byte : child.end_byte
1126
+ ].decode("utf-8")
1127
+ module_path = module_text.strip("\"'")
1128
+
1129
+ # Use first import name or "unknown"
1130
+ primary_name = import_names[0] if import_names else "unknown"
1131
+
1132
+ return Import(
1133
+ name=primary_name,
1134
+ start_line=start_line,
1135
+ end_line=end_line,
1136
+ raw_text=raw_text,
1137
+ language="typescript",
1138
+ module_path=module_path,
1139
+ module_name=module_path,
1140
+ imported_names=import_names,
1141
+ )
1142
+
1143
+ except Exception as e:
1144
+ log_debug(f"Failed to extract import info: {e}")
1145
+ return None
1146
+
1147
+ def _extract_import_names(
1148
+ self, import_clause_node: "tree_sitter.Node"
1149
+ ) -> list[str]:
1150
+ """Extract import names from import clause"""
1151
+ names = []
1152
+ source_bytes = self.source_code.encode("utf-8")
1153
+
1154
+ for child in import_clause_node.children:
1155
+ if child.type == "import_default_specifier":
1156
+ # Default import
1157
+ for grandchild in child.children:
1158
+ if grandchild.type == "identifier":
1159
+ name_text = source_bytes[
1160
+ grandchild.start_byte : grandchild.end_byte
1161
+ ].decode("utf-8")
1162
+ names.append(name_text)
1163
+ elif child.type == "named_imports":
1164
+ # Named imports
1165
+ for grandchild in child.children:
1166
+ if grandchild.type == "import_specifier":
1167
+ for ggchild in grandchild.children:
1168
+ if ggchild.type == "identifier":
1169
+ name_text = source_bytes[
1170
+ ggchild.start_byte : ggchild.end_byte
1171
+ ].decode("utf-8")
1172
+ names.append(name_text)
1173
+
1174
+ return names
1175
+
1176
+ def _extract_dynamic_import(self, node: "tree_sitter.Node") -> Import | None:
1177
+ """Extract dynamic import() calls"""
1178
+ try:
1179
+ node_text = self._get_node_text_optimized(node)
1180
+
1181
+ # Look for import() calls
1182
+ import_match = re.search(
1183
+ r"import\s*\(\s*[\"']([^\"']+)[\"']\s*\)", node_text
1184
+ )
1185
+ if not import_match:
1186
+ return None
1187
+
1188
+ source = import_match.group(1)
1189
+
1190
+ return Import(
1191
+ name="dynamic_import",
1192
+ start_line=node.start_point[0] + 1,
1193
+ end_line=node.end_point[0] + 1,
1194
+ raw_text=node_text,
1195
+ language="typescript",
1196
+ module_path=source,
1197
+ # TypeScript-specific properties
1198
+ import_type="dynamic",
1199
+ is_dynamic=True,
1200
+ framework_type=self.framework_type,
1201
+ )
1202
+ except Exception as e:
1203
+ log_debug(f"Failed to extract dynamic import: {e}")
1204
+ return None
1205
+
1206
+ def _extract_commonjs_requires(
1207
+ self, tree: "tree_sitter.Tree", source_code: str
1208
+ ) -> list[Import]:
1209
+ """Extract CommonJS require() statements (for compatibility)"""
1210
+ imports = []
1211
+
1212
+ try:
1213
+ # Use regex to find require statements
1214
+ require_pattern = r"(?:const|let|var)\s+(\w+)\s*=\s*require\s*\(\s*[\"']([^\"']+)[\"']\s*\)"
1215
+
1216
+ for match in re.finditer(require_pattern, source_code):
1217
+ var_name = match.group(1)
1218
+ module_path = match.group(2)
1219
+
1220
+ # Find line number
1221
+ line_num = source_code[: match.start()].count("\n") + 1
1222
+
1223
+ import_obj = Import(
1224
+ name=var_name,
1225
+ start_line=line_num,
1226
+ end_line=line_num,
1227
+ raw_text=match.group(0),
1228
+ language="typescript",
1229
+ module_path=module_path,
1230
+ module_name=module_path,
1231
+ imported_names=[var_name],
1232
+ # TypeScript-specific properties
1233
+ import_type="commonjs",
1234
+ framework_type=self.framework_type,
1235
+ )
1236
+ imports.append(import_obj)
1237
+
1238
+ except Exception as e:
1239
+ log_debug(f"Failed to extract CommonJS requires: {e}")
1240
+
1241
+ return imports
1242
+
1243
+ def _is_framework_component(self, node: "tree_sitter.Node", class_name: str) -> bool:
1244
+ """Check if class is a framework component"""
1245
+ if self.framework_type == "react":
1246
+ # Check if extends React.Component or Component
1247
+ node_text = self._get_node_text_optimized(node)
1248
+ return "extends" in node_text and (
1249
+ "Component" in node_text or "PureComponent" in node_text
1250
+ )
1251
+ elif self.framework_type == "angular":
1252
+ # Check for Angular component decorator
1253
+ return "@Component" in self.source_code
1254
+ elif self.framework_type == "vue":
1255
+ # Check for Vue component patterns
1256
+ return "Vue" in self.source_code or "@Component" in self.source_code
1257
+ return False
1258
+
1259
+ def _is_exported_class(self, class_name: str) -> bool:
1260
+ """Check if class is exported"""
1261
+ # Simple check for export statements
1262
+ return f"export class {class_name}" in self.source_code or f"export default {class_name}" in self.source_code
1263
+
1264
+ def _infer_type_from_value(self, value: str | None) -> str:
1265
+ """Infer TypeScript type from value"""
1266
+ if not value:
1267
+ return "any"
1268
+
1269
+ value = value.strip()
1270
+
1271
+ if value.startswith('"') or value.startswith("'") or value.startswith("`"):
1272
+ return "string"
1273
+ elif value in ["true", "false"]:
1274
+ return "boolean"
1275
+ elif value == "null":
1276
+ return "null"
1277
+ elif value == "undefined":
1278
+ return "undefined"
1279
+ elif value.startswith("[") and value.endswith("]"):
1280
+ return "array"
1281
+ elif value.startswith("{") and value.endswith("}"):
1282
+ return "object"
1283
+ elif value.replace(".", "").replace("-", "").isdigit():
1284
+ return "number"
1285
+ elif "function" in value or "=>" in value:
1286
+ return "function"
1287
+ else:
1288
+ return "any"
1289
+
1290
+ def _extract_tsdoc_for_line(self, target_line: int) -> str | None:
1291
+ """Extract TSDoc comment immediately before the specified line"""
1292
+ if target_line in self._tsdoc_cache:
1293
+ return self._tsdoc_cache[target_line]
1294
+
1295
+ try:
1296
+ if not self.content_lines or target_line <= 1:
1297
+ return None
1298
+
1299
+ # Search backwards from target_line
1300
+ tsdoc_lines = []
1301
+ current_line = target_line - 1
1302
+
1303
+ # Skip empty lines
1304
+ while current_line > 0:
1305
+ line = self.content_lines[current_line - 1].strip()
1306
+ if line:
1307
+ break
1308
+ current_line -= 1
1309
+
1310
+ # Check for TSDoc end
1311
+ if current_line > 0:
1312
+ line = self.content_lines[current_line - 1].strip()
1313
+ if line.endswith("*/"):
1314
+ tsdoc_lines.append(self.content_lines[current_line - 1])
1315
+ current_line -= 1
1316
+
1317
+ # Collect TSDoc content
1318
+ while current_line > 0:
1319
+ line_content = self.content_lines[current_line - 1]
1320
+ line_stripped = line_content.strip()
1321
+ tsdoc_lines.append(line_content)
1322
+
1323
+ if line_stripped.startswith("/**"):
1324
+ tsdoc_lines.reverse()
1325
+ tsdoc_text = "\n".join(tsdoc_lines)
1326
+ cleaned = self._clean_tsdoc(tsdoc_text)
1327
+ self._tsdoc_cache[target_line] = cleaned
1328
+ return cleaned
1329
+ current_line -= 1
1330
+
1331
+ self._tsdoc_cache[target_line] = None
1332
+ return None
1333
+
1334
+ except Exception as e:
1335
+ log_debug(f"Failed to extract TSDoc: {e}")
1336
+ return None
1337
+
1338
+ def _clean_tsdoc(self, tsdoc_text: str) -> str:
1339
+ """Clean TSDoc text by removing comment markers"""
1340
+ if not tsdoc_text:
1341
+ return ""
1342
+
1343
+ lines = tsdoc_text.split("\n")
1344
+ cleaned_lines = []
1345
+
1346
+ for line in lines:
1347
+ line = line.strip()
1348
+
1349
+ if line.startswith("/**"):
1350
+ line = line[3:].strip()
1351
+ elif line.startswith("*/"):
1352
+ line = line[2:].strip()
1353
+ elif line.startswith("*"):
1354
+ line = line[1:].strip()
1355
+
1356
+ if line:
1357
+ cleaned_lines.append(line)
1358
+
1359
+ return " ".join(cleaned_lines) if cleaned_lines else ""
1360
+
1361
+ def _calculate_complexity_optimized(self, node: "tree_sitter.Node") -> int:
1362
+ """Calculate cyclomatic complexity efficiently"""
1363
+ node_id = id(node)
1364
+ if node_id in self._complexity_cache:
1365
+ return self._complexity_cache[node_id]
1366
+
1367
+ complexity = 1
1368
+ try:
1369
+ node_text = self._get_node_text_optimized(node).lower()
1370
+ keywords = [
1371
+ "if",
1372
+ "else if",
1373
+ "while",
1374
+ "for",
1375
+ "catch",
1376
+ "case",
1377
+ "switch",
1378
+ "&&",
1379
+ "||",
1380
+ "?",
1381
+ ]
1382
+ for keyword in keywords:
1383
+ complexity += node_text.count(keyword)
1384
+ except Exception as e:
1385
+ log_debug(f"Failed to calculate complexity: {e}")
1386
+
1387
+ self._complexity_cache[node_id] = complexity
1388
+ return complexity
1389
+
1390
+
1391
+ class TypeScriptPlugin(LanguagePlugin):
1392
+ """Enhanced TypeScript language plugin with comprehensive feature support"""
1393
+
1394
+ def __init__(self) -> None:
1395
+ self._extractor = TypeScriptElementExtractor()
1396
+ self._language: tree_sitter.Language | None = None
1397
+
1398
+ @property
1399
+ def language_name(self) -> str:
1400
+ return "typescript"
1401
+
1402
+ @property
1403
+ def file_extensions(self) -> list[str]:
1404
+ return [".ts", ".tsx", ".d.ts"]
1405
+
1406
+ def get_language_name(self) -> str:
1407
+ """Return the name of the programming language this plugin supports"""
1408
+ return "typescript"
1409
+
1410
+ def get_file_extensions(self) -> list[str]:
1411
+ """Return list of file extensions this plugin supports"""
1412
+ return [".ts", ".tsx", ".d.ts"]
1413
+
1414
+ def create_extractor(self) -> ElementExtractor:
1415
+ """Create and return an element extractor for this language"""
1416
+ return TypeScriptElementExtractor()
1417
+
1418
+ def get_extractor(self) -> ElementExtractor:
1419
+ return self._extractor
1420
+
1421
+ def get_tree_sitter_language(self) -> Optional["tree_sitter.Language"]:
1422
+ """Load and return TypeScript tree-sitter language"""
1423
+ if self._language is None:
1424
+ self._language = loader.load_language("typescript")
1425
+ return self._language
1426
+
1427
+ def get_supported_queries(self) -> list[str]:
1428
+ """Get list of supported query names for this language"""
1429
+ return [
1430
+ "function",
1431
+ "class",
1432
+ "interface",
1433
+ "type_alias",
1434
+ "enum",
1435
+ "variable",
1436
+ "import",
1437
+ "export",
1438
+ "async_function",
1439
+ "arrow_function",
1440
+ "method",
1441
+ "constructor",
1442
+ "generic",
1443
+ "decorator",
1444
+ "signature",
1445
+ "react_component",
1446
+ "angular_component",
1447
+ "vue_component",
1448
+ ]
1449
+
1450
+ def is_applicable(self, file_path: str) -> bool:
1451
+ """Check if this plugin is applicable for the given file"""
1452
+ return any(
1453
+ file_path.lower().endswith(ext.lower())
1454
+ for ext in self.get_file_extensions()
1455
+ )
1456
+
1457
+ def get_plugin_info(self) -> dict:
1458
+ """Get information about this plugin"""
1459
+ return {
1460
+ "name": "TypeScript Plugin",
1461
+ "language": self.get_language_name(),
1462
+ "extensions": self.get_file_extensions(),
1463
+ "version": "2.0.0",
1464
+ "supported_queries": self.get_supported_queries(),
1465
+ "features": [
1466
+ "TypeScript syntax support",
1467
+ "Interface declarations",
1468
+ "Type aliases",
1469
+ "Enums",
1470
+ "Generics",
1471
+ "Decorators",
1472
+ "Async/await functions",
1473
+ "Arrow functions",
1474
+ "Classes and methods",
1475
+ "Module imports/exports",
1476
+ "TSX/JSX support",
1477
+ "React component detection",
1478
+ "Angular component detection",
1479
+ "Vue component detection",
1480
+ "Type annotations",
1481
+ "Method signatures",
1482
+ "TSDoc extraction",
1483
+ "Complexity analysis",
1484
+ ],
1485
+ }
1486
+
1487
+ async def analyze_file(
1488
+ self, file_path: str, request: AnalysisRequest
1489
+ ) -> AnalysisResult:
1490
+ """Analyze a TypeScript file and return the analysis results."""
1491
+ if not TREE_SITTER_AVAILABLE:
1492
+ return AnalysisResult(
1493
+ file_path=file_path,
1494
+ language=self.language_name,
1495
+ success=False,
1496
+ error_message="Tree-sitter library not available.",
1497
+ )
1498
+
1499
+ language = self.get_tree_sitter_language()
1500
+ if not language:
1501
+ return AnalysisResult(
1502
+ file_path=file_path,
1503
+ language=self.language_name,
1504
+ success=False,
1505
+ error_message="Could not load TypeScript language for parsing.",
1506
+ )
1507
+
1508
+ try:
1509
+ with open(file_path, encoding="utf-8") as f:
1510
+ source_code = f.read()
1511
+
1512
+ parser = tree_sitter.Parser()
1513
+ parser.language = language
1514
+ tree = parser.parse(bytes(source_code, "utf8"))
1515
+
1516
+ extractor = self.create_extractor()
1517
+ extractor.current_file = file_path # Set current file for context
1518
+
1519
+ elements: list[CodeElement] = []
1520
+
1521
+ # Extract all element types
1522
+ functions = extractor.extract_functions(tree, source_code)
1523
+ classes = extractor.extract_classes(tree, source_code)
1524
+ variables = extractor.extract_variables(tree, source_code)
1525
+ imports = extractor.extract_imports(tree, source_code)
1526
+
1527
+ elements.extend(functions)
1528
+ elements.extend(classes)
1529
+ elements.extend(variables)
1530
+ elements.extend(imports)
1531
+
1532
+ def count_nodes(node: "tree_sitter.Node") -> int:
1533
+ count = 1
1534
+ for child in node.children:
1535
+ count += count_nodes(child)
1536
+ return count
1537
+
1538
+ return AnalysisResult(
1539
+ file_path=file_path,
1540
+ language=self.language_name,
1541
+ success=True,
1542
+ elements=elements,
1543
+ line_count=len(source_code.splitlines()),
1544
+ node_count=count_nodes(tree.root_node),
1545
+ )
1546
+ except Exception as e:
1547
+ log_error(f"Error analyzing TypeScript file {file_path}: {e}")
1548
+ return AnalysisResult(
1549
+ file_path=file_path,
1550
+ language=self.language_name,
1551
+ success=False,
1552
+ error_message=str(e),
1553
+ )