tree-sitter-analyzer 0.2.0__py3-none-any.whl → 0.4.0__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.
- tree_sitter_analyzer/__init__.py +134 -121
- tree_sitter_analyzer/__main__.py +11 -12
- tree_sitter_analyzer/api.py +533 -539
- tree_sitter_analyzer/cli/__init__.py +39 -39
- tree_sitter_analyzer/cli/__main__.py +12 -13
- tree_sitter_analyzer/cli/commands/__init__.py +26 -27
- tree_sitter_analyzer/cli/commands/advanced_command.py +88 -88
- tree_sitter_analyzer/cli/commands/base_command.py +160 -155
- tree_sitter_analyzer/cli/commands/default_command.py +18 -19
- tree_sitter_analyzer/cli/commands/partial_read_command.py +141 -133
- tree_sitter_analyzer/cli/commands/query_command.py +81 -82
- tree_sitter_analyzer/cli/commands/structure_command.py +138 -121
- tree_sitter_analyzer/cli/commands/summary_command.py +101 -93
- tree_sitter_analyzer/cli/commands/table_command.py +235 -233
- tree_sitter_analyzer/cli/info_commands.py +120 -121
- tree_sitter_analyzer/cli_main.py +278 -276
- tree_sitter_analyzer/core/__init__.py +15 -20
- tree_sitter_analyzer/core/analysis_engine.py +555 -574
- tree_sitter_analyzer/core/cache_service.py +320 -330
- tree_sitter_analyzer/core/engine.py +559 -560
- tree_sitter_analyzer/core/parser.py +293 -288
- tree_sitter_analyzer/core/query.py +502 -502
- tree_sitter_analyzer/encoding_utils.py +456 -460
- tree_sitter_analyzer/exceptions.py +337 -340
- tree_sitter_analyzer/file_handler.py +210 -222
- tree_sitter_analyzer/formatters/__init__.py +1 -1
- tree_sitter_analyzer/formatters/base_formatter.py +167 -168
- tree_sitter_analyzer/formatters/formatter_factory.py +78 -74
- tree_sitter_analyzer/formatters/java_formatter.py +291 -270
- tree_sitter_analyzer/formatters/python_formatter.py +259 -235
- tree_sitter_analyzer/interfaces/__init__.py +9 -10
- tree_sitter_analyzer/interfaces/cli.py +528 -557
- tree_sitter_analyzer/interfaces/cli_adapter.py +343 -319
- tree_sitter_analyzer/interfaces/mcp_adapter.py +206 -170
- tree_sitter_analyzer/interfaces/mcp_server.py +405 -416
- tree_sitter_analyzer/java_analyzer.py +187 -219
- tree_sitter_analyzer/language_detector.py +398 -400
- tree_sitter_analyzer/language_loader.py +224 -228
- tree_sitter_analyzer/languages/__init__.py +10 -11
- tree_sitter_analyzer/languages/java_plugin.py +1174 -1113
- tree_sitter_analyzer/{plugins → languages}/javascript_plugin.py +446 -439
- tree_sitter_analyzer/languages/python_plugin.py +747 -712
- tree_sitter_analyzer/mcp/__init__.py +31 -32
- tree_sitter_analyzer/mcp/resources/__init__.py +44 -47
- tree_sitter_analyzer/mcp/resources/code_file_resource.py +209 -213
- tree_sitter_analyzer/mcp/resources/project_stats_resource.py +555 -550
- tree_sitter_analyzer/mcp/server.py +333 -345
- tree_sitter_analyzer/mcp/tools/__init__.py +30 -31
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +654 -557
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +247 -245
- tree_sitter_analyzer/mcp/tools/base_tool.py +54 -55
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +300 -302
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +362 -359
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +543 -476
- tree_sitter_analyzer/mcp/utils/__init__.py +107 -106
- tree_sitter_analyzer/mcp/utils/error_handler.py +549 -549
- tree_sitter_analyzer/models.py +470 -481
- tree_sitter_analyzer/output_manager.py +255 -264
- tree_sitter_analyzer/plugins/__init__.py +280 -334
- tree_sitter_analyzer/plugins/base.py +496 -446
- tree_sitter_analyzer/plugins/manager.py +379 -355
- tree_sitter_analyzer/queries/__init__.py +26 -27
- tree_sitter_analyzer/queries/java.py +391 -394
- tree_sitter_analyzer/queries/javascript.py +148 -149
- tree_sitter_analyzer/queries/python.py +285 -286
- tree_sitter_analyzer/queries/typescript.py +229 -230
- tree_sitter_analyzer/query_loader.py +257 -260
- tree_sitter_analyzer/table_formatter.py +471 -448
- tree_sitter_analyzer/utils.py +277 -277
- {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.4.0.dist-info}/METADATA +23 -8
- tree_sitter_analyzer-0.4.0.dist-info/RECORD +73 -0
- {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.4.0.dist-info}/entry_points.txt +2 -1
- tree_sitter_analyzer/plugins/java_plugin.py +0 -625
- tree_sitter_analyzer/plugins/plugin_loader.py +0 -83
- tree_sitter_analyzer/plugins/python_plugin.py +0 -598
- tree_sitter_analyzer/plugins/registry.py +0 -366
- tree_sitter_analyzer-0.2.0.dist-info/RECORD +0 -77
- {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.4.0.dist-info}/WHEEL +0 -0
|
@@ -1,439 +1,446 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
TREE_SITTER_AVAILABLE =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
from ..
|
|
23
|
-
from ..
|
|
24
|
-
from ..utils import
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"""
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"""
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"""
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
captures
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
"""
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
"""
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
"""
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
def
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
def
|
|
366
|
-
"""
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
return
|
|
375
|
-
|
|
376
|
-
def
|
|
377
|
-
"""
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
JavaScript Language Plugin
|
|
4
|
+
|
|
5
|
+
Provides JavaScript-specific parsing and element extraction functionality.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import TYPE_CHECKING, Optional
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
import tree_sitter
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
import tree_sitter
|
|
15
|
+
|
|
16
|
+
TREE_SITTER_AVAILABLE = True
|
|
17
|
+
except ImportError:
|
|
18
|
+
TREE_SITTER_AVAILABLE = False
|
|
19
|
+
|
|
20
|
+
from ..core.analysis_engine import AnalysisRequest
|
|
21
|
+
from ..language_loader import loader
|
|
22
|
+
from ..models import AnalysisResult, Class, CodeElement, Function, Import, Variable
|
|
23
|
+
from ..plugins.base import ElementExtractor, LanguagePlugin
|
|
24
|
+
from ..utils import log_error, log_warning
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class JavaScriptElementExtractor(ElementExtractor):
|
|
28
|
+
"""JavaScript-specific element extractor"""
|
|
29
|
+
|
|
30
|
+
def extract_functions(
|
|
31
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
32
|
+
) -> list[Function]:
|
|
33
|
+
"""Extract JavaScript function definitions"""
|
|
34
|
+
functions = []
|
|
35
|
+
|
|
36
|
+
# Multiple function patterns in JavaScript
|
|
37
|
+
queries = [
|
|
38
|
+
# Regular function declarations
|
|
39
|
+
"""
|
|
40
|
+
(function_declaration
|
|
41
|
+
name: (identifier) @func.name
|
|
42
|
+
parameters: (formal_parameters) @func.params
|
|
43
|
+
body: (statement_block) @func.body) @func.declaration
|
|
44
|
+
""",
|
|
45
|
+
# Method definitions (class methods)
|
|
46
|
+
"""
|
|
47
|
+
(method_definition
|
|
48
|
+
name: (property_identifier) @func.name
|
|
49
|
+
parameters: (formal_parameters) @func.params
|
|
50
|
+
body: (statement_block) @func.body) @func.method
|
|
51
|
+
""",
|
|
52
|
+
# Arrow functions
|
|
53
|
+
"""
|
|
54
|
+
(variable_declarator
|
|
55
|
+
name: (identifier) @func.name
|
|
56
|
+
value: (arrow_function
|
|
57
|
+
parameters: (formal_parameters) @func.params
|
|
58
|
+
body: (_) @func.body)) @func.arrow
|
|
59
|
+
""",
|
|
60
|
+
# Function expressions
|
|
61
|
+
"""
|
|
62
|
+
(variable_declarator
|
|
63
|
+
name: (identifier) @func.name
|
|
64
|
+
value: (function_expression
|
|
65
|
+
parameters: (formal_parameters) @func.params
|
|
66
|
+
body: (statement_block) @func.body)) @func.expression
|
|
67
|
+
""",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
language = tree.language if hasattr(tree, "language") else None
|
|
72
|
+
if language:
|
|
73
|
+
for query_string in queries:
|
|
74
|
+
query = language.query(query_string)
|
|
75
|
+
captures = query.captures(tree.root_node)
|
|
76
|
+
|
|
77
|
+
if isinstance(captures, dict):
|
|
78
|
+
# Handle different function types
|
|
79
|
+
for capture_key in [
|
|
80
|
+
"func.declaration",
|
|
81
|
+
"func.method",
|
|
82
|
+
"func.arrow",
|
|
83
|
+
"func.expression",
|
|
84
|
+
]:
|
|
85
|
+
func_nodes = captures.get(capture_key, [])
|
|
86
|
+
for node in func_nodes:
|
|
87
|
+
function = self._extract_function_info(
|
|
88
|
+
node, source_code
|
|
89
|
+
)
|
|
90
|
+
if function:
|
|
91
|
+
functions.append(function)
|
|
92
|
+
|
|
93
|
+
except Exception as e:
|
|
94
|
+
log_warning(f"Could not extract JavaScript functions: {e}")
|
|
95
|
+
|
|
96
|
+
return functions
|
|
97
|
+
|
|
98
|
+
def extract_classes(
|
|
99
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
100
|
+
) -> list[Class]:
|
|
101
|
+
"""Extract JavaScript class definitions"""
|
|
102
|
+
classes = []
|
|
103
|
+
|
|
104
|
+
query_string = """
|
|
105
|
+
(class_declaration
|
|
106
|
+
name: (identifier) @class.name
|
|
107
|
+
body: (class_body) @class.body) @class.declaration
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
language = tree.language if hasattr(tree, "language") else None
|
|
112
|
+
if language:
|
|
113
|
+
query = language.query(query_string)
|
|
114
|
+
captures = query.captures(tree.root_node)
|
|
115
|
+
|
|
116
|
+
if isinstance(captures, dict):
|
|
117
|
+
class_nodes = captures.get("class.declaration", [])
|
|
118
|
+
for node in class_nodes:
|
|
119
|
+
cls = self._extract_class_info(node, source_code)
|
|
120
|
+
if cls:
|
|
121
|
+
classes.append(cls)
|
|
122
|
+
|
|
123
|
+
except Exception as e:
|
|
124
|
+
log_warning(f"Could not extract JavaScript classes: {e}")
|
|
125
|
+
|
|
126
|
+
return classes
|
|
127
|
+
|
|
128
|
+
def extract_variables(
|
|
129
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
130
|
+
) -> list[Variable]:
|
|
131
|
+
"""Extract JavaScript variable definitions"""
|
|
132
|
+
variables = []
|
|
133
|
+
|
|
134
|
+
# Variable declarations (let, const, var)
|
|
135
|
+
queries = [
|
|
136
|
+
# var declarations
|
|
137
|
+
"""
|
|
138
|
+
(variable_declaration
|
|
139
|
+
(variable_declarator
|
|
140
|
+
name: (identifier) @var.name
|
|
141
|
+
value: (_)? @var.value)) @var.declaration
|
|
142
|
+
""",
|
|
143
|
+
# let/const declarations
|
|
144
|
+
"""
|
|
145
|
+
(lexical_declaration
|
|
146
|
+
(variable_declarator
|
|
147
|
+
name: (identifier) @var.name
|
|
148
|
+
value: (_)? @var.value)) @var.lexical
|
|
149
|
+
""",
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
language = tree.language if hasattr(tree, "language") else None
|
|
154
|
+
if language:
|
|
155
|
+
for query_string in queries:
|
|
156
|
+
query = language.query(query_string)
|
|
157
|
+
captures = query.captures(tree.root_node)
|
|
158
|
+
|
|
159
|
+
if isinstance(captures, dict):
|
|
160
|
+
# Handle both var and lexical declarations
|
|
161
|
+
for capture_key in ["var.declaration", "var.lexical"]:
|
|
162
|
+
var_nodes = captures.get(capture_key, [])
|
|
163
|
+
for node in var_nodes:
|
|
164
|
+
variable = self._extract_variable_info(
|
|
165
|
+
node, source_code
|
|
166
|
+
)
|
|
167
|
+
if variable:
|
|
168
|
+
variables.append(variable)
|
|
169
|
+
|
|
170
|
+
except Exception as e:
|
|
171
|
+
log_warning(f"Could not extract JavaScript variables: {e}")
|
|
172
|
+
|
|
173
|
+
return variables
|
|
174
|
+
|
|
175
|
+
def extract_imports(
|
|
176
|
+
self, tree: "tree_sitter.Tree", source_code: str
|
|
177
|
+
) -> list[Import]:
|
|
178
|
+
"""Extract JavaScript import statements"""
|
|
179
|
+
imports = []
|
|
180
|
+
|
|
181
|
+
# ES6 import statements
|
|
182
|
+
query_string = """
|
|
183
|
+
(import_statement
|
|
184
|
+
source: (string) @import.source) @import.declaration
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
language = tree.language if hasattr(tree, "language") else None
|
|
189
|
+
if language:
|
|
190
|
+
query = language.query(query_string)
|
|
191
|
+
captures = query.captures(tree.root_node)
|
|
192
|
+
|
|
193
|
+
if isinstance(captures, dict):
|
|
194
|
+
import_nodes = captures.get("import.declaration", [])
|
|
195
|
+
for node in import_nodes:
|
|
196
|
+
imp = self._extract_import_info(node, source_code)
|
|
197
|
+
if imp:
|
|
198
|
+
imports.append(imp)
|
|
199
|
+
|
|
200
|
+
except Exception as e:
|
|
201
|
+
log_warning(f"Could not extract JavaScript imports: {e}")
|
|
202
|
+
|
|
203
|
+
return imports
|
|
204
|
+
|
|
205
|
+
def _extract_function_info(
|
|
206
|
+
self, node: "tree_sitter.Node", source_code: str
|
|
207
|
+
) -> Function | None:
|
|
208
|
+
"""Extract function information from AST node"""
|
|
209
|
+
try:
|
|
210
|
+
name_node = None
|
|
211
|
+
params_node = None
|
|
212
|
+
|
|
213
|
+
# Navigate the node structure to find name and parameters
|
|
214
|
+
for child in node.children:
|
|
215
|
+
if child.type == "identifier":
|
|
216
|
+
name_node = child
|
|
217
|
+
elif child.type == "property_identifier": # For method definitions
|
|
218
|
+
name_node = child
|
|
219
|
+
elif child.type == "formal_parameters":
|
|
220
|
+
params_node = child
|
|
221
|
+
elif child.type == "variable_declarator":
|
|
222
|
+
# For arrow functions and function expressions
|
|
223
|
+
for subchild in child.children:
|
|
224
|
+
if subchild.type == "identifier":
|
|
225
|
+
name_node = subchild
|
|
226
|
+
elif subchild.type in ["arrow_function", "function_expression"]:
|
|
227
|
+
for funcchild in subchild.children:
|
|
228
|
+
if funcchild.type == "formal_parameters":
|
|
229
|
+
params_node = funcchild
|
|
230
|
+
|
|
231
|
+
if not name_node:
|
|
232
|
+
return None
|
|
233
|
+
|
|
234
|
+
name = source_code[name_node.start_byte : name_node.end_byte]
|
|
235
|
+
|
|
236
|
+
# Extract parameters
|
|
237
|
+
parameters = []
|
|
238
|
+
if params_node:
|
|
239
|
+
for child in params_node.children:
|
|
240
|
+
if child.type == "identifier":
|
|
241
|
+
param_name = source_code[child.start_byte : child.end_byte]
|
|
242
|
+
parameters.append(param_name)
|
|
243
|
+
|
|
244
|
+
return Function(
|
|
245
|
+
name=name,
|
|
246
|
+
parameters=parameters,
|
|
247
|
+
start_line=node.start_point[0] + 1,
|
|
248
|
+
end_line=node.end_point[0] + 1,
|
|
249
|
+
raw_text=source_code[node.start_byte : node.end_byte],
|
|
250
|
+
language="javascript",
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
except Exception as e:
|
|
254
|
+
log_warning(f"Could not extract function info: {e}")
|
|
255
|
+
return None
|
|
256
|
+
|
|
257
|
+
def _extract_class_info(
|
|
258
|
+
self, node: "tree_sitter.Node", source_code: str
|
|
259
|
+
) -> Class | None:
|
|
260
|
+
"""Extract class information from AST node"""
|
|
261
|
+
try:
|
|
262
|
+
name_node = None
|
|
263
|
+
|
|
264
|
+
for child in node.children:
|
|
265
|
+
if child.type == "identifier":
|
|
266
|
+
name_node = child
|
|
267
|
+
break
|
|
268
|
+
|
|
269
|
+
if not name_node:
|
|
270
|
+
return None
|
|
271
|
+
|
|
272
|
+
name = source_code[name_node.start_byte : name_node.end_byte]
|
|
273
|
+
|
|
274
|
+
return Class(
|
|
275
|
+
name=name,
|
|
276
|
+
class_type="class",
|
|
277
|
+
start_line=node.start_point[0] + 1,
|
|
278
|
+
end_line=node.end_point[0] + 1,
|
|
279
|
+
raw_text=source_code[node.start_byte : node.end_byte],
|
|
280
|
+
language="javascript",
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
except Exception as e:
|
|
284
|
+
log_warning(f"Could not extract class info: {e}")
|
|
285
|
+
return None
|
|
286
|
+
|
|
287
|
+
def _extract_variable_info(
|
|
288
|
+
self, node: "tree_sitter.Node", source_code: str
|
|
289
|
+
) -> Variable | None:
|
|
290
|
+
"""Extract variable information from AST node"""
|
|
291
|
+
try:
|
|
292
|
+
name_node = None
|
|
293
|
+
|
|
294
|
+
# Find the identifier in variable declarator
|
|
295
|
+
for child in node.children:
|
|
296
|
+
if child.type == "variable_declarator":
|
|
297
|
+
for subchild in child.children:
|
|
298
|
+
if subchild.type == "identifier":
|
|
299
|
+
name_node = subchild
|
|
300
|
+
break
|
|
301
|
+
break
|
|
302
|
+
|
|
303
|
+
if not name_node:
|
|
304
|
+
return None
|
|
305
|
+
|
|
306
|
+
name = source_code[name_node.start_byte : name_node.end_byte]
|
|
307
|
+
|
|
308
|
+
return Variable(
|
|
309
|
+
name=name,
|
|
310
|
+
start_line=node.start_point[0] + 1,
|
|
311
|
+
end_line=node.end_point[0] + 1,
|
|
312
|
+
raw_text=source_code[node.start_byte : node.end_byte],
|
|
313
|
+
language="javascript",
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
except Exception as e:
|
|
317
|
+
log_warning(f"Could not extract variable info: {e}")
|
|
318
|
+
return None
|
|
319
|
+
|
|
320
|
+
def _extract_import_info(
|
|
321
|
+
self, node: "tree_sitter.Node", source_code: str
|
|
322
|
+
) -> Import | None:
|
|
323
|
+
"""Extract import information from AST node"""
|
|
324
|
+
try:
|
|
325
|
+
source_node = None
|
|
326
|
+
|
|
327
|
+
for child in node.children:
|
|
328
|
+
if child.type == "string":
|
|
329
|
+
source_node = child
|
|
330
|
+
break
|
|
331
|
+
|
|
332
|
+
if not source_node:
|
|
333
|
+
return None
|
|
334
|
+
|
|
335
|
+
module_path = source_code[source_node.start_byte : source_node.end_byte]
|
|
336
|
+
# Remove quotes from string
|
|
337
|
+
module_path = module_path.strip("\"'")
|
|
338
|
+
|
|
339
|
+
return Import(
|
|
340
|
+
name="import",
|
|
341
|
+
module_path=module_path,
|
|
342
|
+
start_line=node.start_point[0] + 1,
|
|
343
|
+
end_line=node.end_point[0] + 1,
|
|
344
|
+
raw_text=source_code[node.start_byte : node.end_byte],
|
|
345
|
+
language="javascript",
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
except Exception as e:
|
|
349
|
+
log_warning(f"Could not extract import info: {e}")
|
|
350
|
+
return None
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
class JavaScriptPlugin(LanguagePlugin):
|
|
354
|
+
"""JavaScript language plugin"""
|
|
355
|
+
|
|
356
|
+
def __init__(self) -> None:
|
|
357
|
+
self._extractor = JavaScriptElementExtractor()
|
|
358
|
+
self._language: tree_sitter.Language | None = None
|
|
359
|
+
|
|
360
|
+
@property
|
|
361
|
+
def language_name(self) -> str:
|
|
362
|
+
return "javascript"
|
|
363
|
+
|
|
364
|
+
@property
|
|
365
|
+
def file_extensions(self) -> list[str]:
|
|
366
|
+
return [".js", ".mjs", ".jsx"]
|
|
367
|
+
|
|
368
|
+
def get_language_name(self) -> str:
|
|
369
|
+
"""Return the name of the programming language this plugin supports"""
|
|
370
|
+
return "javascript"
|
|
371
|
+
|
|
372
|
+
def get_file_extensions(self) -> list[str]:
|
|
373
|
+
"""Return list of file extensions this plugin supports"""
|
|
374
|
+
return [".js", ".mjs", ".jsx"]
|
|
375
|
+
|
|
376
|
+
def create_extractor(self) -> ElementExtractor:
|
|
377
|
+
"""Create and return an element extractor for this language"""
|
|
378
|
+
return JavaScriptElementExtractor()
|
|
379
|
+
|
|
380
|
+
def get_extractor(self) -> ElementExtractor:
|
|
381
|
+
return self._extractor
|
|
382
|
+
|
|
383
|
+
def get_tree_sitter_language(self) -> Optional["tree_sitter.Language"]:
|
|
384
|
+
"""Load and return JavaScript tree-sitter language"""
|
|
385
|
+
if self._language is None:
|
|
386
|
+
self._language = loader.load_language("javascript")
|
|
387
|
+
return self._language
|
|
388
|
+
|
|
389
|
+
async def analyze_file(
|
|
390
|
+
self, file_path: str, request: AnalysisRequest
|
|
391
|
+
) -> AnalysisResult:
|
|
392
|
+
"""Analyze a JavaScript file and return the analysis results."""
|
|
393
|
+
if not TREE_SITTER_AVAILABLE:
|
|
394
|
+
return AnalysisResult(
|
|
395
|
+
file_path=file_path,
|
|
396
|
+
language=self.language_name,
|
|
397
|
+
success=False,
|
|
398
|
+
error_message="Tree-sitter library not available.",
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
language = self.get_tree_sitter_language()
|
|
402
|
+
if not language:
|
|
403
|
+
return AnalysisResult(
|
|
404
|
+
file_path=file_path,
|
|
405
|
+
language=self.language_name,
|
|
406
|
+
success=False,
|
|
407
|
+
error_message="Could not load JavaScript language for parsing.",
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
try:
|
|
411
|
+
with open(file_path, encoding="utf-8") as f:
|
|
412
|
+
source_code = f.read()
|
|
413
|
+
|
|
414
|
+
parser = tree_sitter.Parser()
|
|
415
|
+
parser.language = language
|
|
416
|
+
tree = parser.parse(bytes(source_code, "utf8"))
|
|
417
|
+
|
|
418
|
+
extractor = self.create_extractor()
|
|
419
|
+
elements: list[CodeElement] = []
|
|
420
|
+
elements.extend(extractor.extract_functions(tree, source_code))
|
|
421
|
+
elements.extend(extractor.extract_classes(tree, source_code))
|
|
422
|
+
elements.extend(extractor.extract_variables(tree, source_code))
|
|
423
|
+
elements.extend(extractor.extract_imports(tree, source_code))
|
|
424
|
+
|
|
425
|
+
def count_nodes(node: "tree_sitter.Node") -> int:
|
|
426
|
+
count = 1
|
|
427
|
+
for child in node.children:
|
|
428
|
+
count += count_nodes(child)
|
|
429
|
+
return count
|
|
430
|
+
|
|
431
|
+
return AnalysisResult(
|
|
432
|
+
file_path=file_path,
|
|
433
|
+
language=self.language_name,
|
|
434
|
+
success=True,
|
|
435
|
+
elements=elements,
|
|
436
|
+
line_count=len(source_code.splitlines()),
|
|
437
|
+
node_count=count_nodes(tree.root_node),
|
|
438
|
+
)
|
|
439
|
+
except Exception as e:
|
|
440
|
+
log_error(f"Error analyzing JavaScript file {file_path}: {e}")
|
|
441
|
+
return AnalysisResult(
|
|
442
|
+
file_path=file_path,
|
|
443
|
+
language=self.language_name,
|
|
444
|
+
success=False,
|
|
445
|
+
error_message=str(e),
|
|
446
|
+
)
|