tree-sitter-analyzer 1.9.17.1__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.
- tree_sitter_analyzer/__init__.py +132 -0
- tree_sitter_analyzer/__main__.py +11 -0
- tree_sitter_analyzer/api.py +853 -0
- tree_sitter_analyzer/cli/__init__.py +39 -0
- tree_sitter_analyzer/cli/__main__.py +12 -0
- tree_sitter_analyzer/cli/argument_validator.py +89 -0
- tree_sitter_analyzer/cli/commands/__init__.py +26 -0
- tree_sitter_analyzer/cli/commands/advanced_command.py +226 -0
- tree_sitter_analyzer/cli/commands/base_command.py +181 -0
- tree_sitter_analyzer/cli/commands/default_command.py +18 -0
- tree_sitter_analyzer/cli/commands/find_and_grep_cli.py +188 -0
- tree_sitter_analyzer/cli/commands/list_files_cli.py +133 -0
- tree_sitter_analyzer/cli/commands/partial_read_command.py +139 -0
- tree_sitter_analyzer/cli/commands/query_command.py +109 -0
- tree_sitter_analyzer/cli/commands/search_content_cli.py +161 -0
- tree_sitter_analyzer/cli/commands/structure_command.py +156 -0
- tree_sitter_analyzer/cli/commands/summary_command.py +116 -0
- tree_sitter_analyzer/cli/commands/table_command.py +414 -0
- tree_sitter_analyzer/cli/info_commands.py +124 -0
- tree_sitter_analyzer/cli_main.py +472 -0
- tree_sitter_analyzer/constants.py +85 -0
- tree_sitter_analyzer/core/__init__.py +15 -0
- tree_sitter_analyzer/core/analysis_engine.py +580 -0
- tree_sitter_analyzer/core/cache_service.py +333 -0
- tree_sitter_analyzer/core/engine.py +585 -0
- tree_sitter_analyzer/core/parser.py +293 -0
- tree_sitter_analyzer/core/query.py +605 -0
- tree_sitter_analyzer/core/query_filter.py +200 -0
- tree_sitter_analyzer/core/query_service.py +340 -0
- tree_sitter_analyzer/encoding_utils.py +530 -0
- tree_sitter_analyzer/exceptions.py +747 -0
- tree_sitter_analyzer/file_handler.py +246 -0
- tree_sitter_analyzer/formatters/__init__.py +1 -0
- tree_sitter_analyzer/formatters/base_formatter.py +201 -0
- tree_sitter_analyzer/formatters/csharp_formatter.py +367 -0
- tree_sitter_analyzer/formatters/formatter_config.py +197 -0
- tree_sitter_analyzer/formatters/formatter_factory.py +84 -0
- tree_sitter_analyzer/formatters/formatter_registry.py +377 -0
- tree_sitter_analyzer/formatters/formatter_selector.py +96 -0
- tree_sitter_analyzer/formatters/go_formatter.py +368 -0
- tree_sitter_analyzer/formatters/html_formatter.py +498 -0
- tree_sitter_analyzer/formatters/java_formatter.py +423 -0
- tree_sitter_analyzer/formatters/javascript_formatter.py +611 -0
- tree_sitter_analyzer/formatters/kotlin_formatter.py +268 -0
- tree_sitter_analyzer/formatters/language_formatter_factory.py +123 -0
- tree_sitter_analyzer/formatters/legacy_formatter_adapters.py +228 -0
- tree_sitter_analyzer/formatters/markdown_formatter.py +725 -0
- tree_sitter_analyzer/formatters/php_formatter.py +301 -0
- tree_sitter_analyzer/formatters/python_formatter.py +830 -0
- tree_sitter_analyzer/formatters/ruby_formatter.py +278 -0
- tree_sitter_analyzer/formatters/rust_formatter.py +233 -0
- tree_sitter_analyzer/formatters/sql_formatter_wrapper.py +689 -0
- tree_sitter_analyzer/formatters/sql_formatters.py +536 -0
- tree_sitter_analyzer/formatters/typescript_formatter.py +543 -0
- tree_sitter_analyzer/formatters/yaml_formatter.py +462 -0
- tree_sitter_analyzer/interfaces/__init__.py +9 -0
- tree_sitter_analyzer/interfaces/cli.py +535 -0
- tree_sitter_analyzer/interfaces/cli_adapter.py +359 -0
- tree_sitter_analyzer/interfaces/mcp_adapter.py +224 -0
- tree_sitter_analyzer/interfaces/mcp_server.py +428 -0
- tree_sitter_analyzer/language_detector.py +553 -0
- tree_sitter_analyzer/language_loader.py +271 -0
- tree_sitter_analyzer/languages/__init__.py +10 -0
- tree_sitter_analyzer/languages/csharp_plugin.py +1076 -0
- tree_sitter_analyzer/languages/css_plugin.py +449 -0
- tree_sitter_analyzer/languages/go_plugin.py +836 -0
- tree_sitter_analyzer/languages/html_plugin.py +496 -0
- tree_sitter_analyzer/languages/java_plugin.py +1299 -0
- tree_sitter_analyzer/languages/javascript_plugin.py +1622 -0
- tree_sitter_analyzer/languages/kotlin_plugin.py +656 -0
- tree_sitter_analyzer/languages/markdown_plugin.py +1928 -0
- tree_sitter_analyzer/languages/php_plugin.py +862 -0
- tree_sitter_analyzer/languages/python_plugin.py +1636 -0
- tree_sitter_analyzer/languages/ruby_plugin.py +757 -0
- tree_sitter_analyzer/languages/rust_plugin.py +673 -0
- tree_sitter_analyzer/languages/sql_plugin.py +2444 -0
- tree_sitter_analyzer/languages/typescript_plugin.py +1892 -0
- tree_sitter_analyzer/languages/yaml_plugin.py +695 -0
- tree_sitter_analyzer/legacy_table_formatter.py +860 -0
- tree_sitter_analyzer/mcp/__init__.py +34 -0
- tree_sitter_analyzer/mcp/resources/__init__.py +43 -0
- tree_sitter_analyzer/mcp/resources/code_file_resource.py +208 -0
- tree_sitter_analyzer/mcp/resources/project_stats_resource.py +586 -0
- tree_sitter_analyzer/mcp/server.py +869 -0
- tree_sitter_analyzer/mcp/tools/__init__.py +28 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +779 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +291 -0
- tree_sitter_analyzer/mcp/tools/base_tool.py +139 -0
- tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +816 -0
- tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +686 -0
- tree_sitter_analyzer/mcp/tools/list_files_tool.py +413 -0
- tree_sitter_analyzer/mcp/tools/output_format_validator.py +148 -0
- tree_sitter_analyzer/mcp/tools/query_tool.py +443 -0
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +464 -0
- tree_sitter_analyzer/mcp/tools/search_content_tool.py +836 -0
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +572 -0
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +653 -0
- tree_sitter_analyzer/mcp/utils/__init__.py +113 -0
- tree_sitter_analyzer/mcp/utils/error_handler.py +569 -0
- tree_sitter_analyzer/mcp/utils/file_output_factory.py +217 -0
- tree_sitter_analyzer/mcp/utils/file_output_manager.py +322 -0
- tree_sitter_analyzer/mcp/utils/gitignore_detector.py +358 -0
- tree_sitter_analyzer/mcp/utils/path_resolver.py +414 -0
- tree_sitter_analyzer/mcp/utils/search_cache.py +343 -0
- tree_sitter_analyzer/models.py +840 -0
- tree_sitter_analyzer/mypy_current_errors.txt +2 -0
- tree_sitter_analyzer/output_manager.py +255 -0
- tree_sitter_analyzer/platform_compat/__init__.py +3 -0
- tree_sitter_analyzer/platform_compat/adapter.py +324 -0
- tree_sitter_analyzer/platform_compat/compare.py +224 -0
- tree_sitter_analyzer/platform_compat/detector.py +67 -0
- tree_sitter_analyzer/platform_compat/fixtures.py +228 -0
- tree_sitter_analyzer/platform_compat/profiles.py +217 -0
- tree_sitter_analyzer/platform_compat/record.py +55 -0
- tree_sitter_analyzer/platform_compat/recorder.py +155 -0
- tree_sitter_analyzer/platform_compat/report.py +92 -0
- tree_sitter_analyzer/plugins/__init__.py +280 -0
- tree_sitter_analyzer/plugins/base.py +647 -0
- tree_sitter_analyzer/plugins/manager.py +384 -0
- tree_sitter_analyzer/project_detector.py +328 -0
- tree_sitter_analyzer/queries/__init__.py +27 -0
- tree_sitter_analyzer/queries/csharp.py +216 -0
- tree_sitter_analyzer/queries/css.py +615 -0
- tree_sitter_analyzer/queries/go.py +275 -0
- tree_sitter_analyzer/queries/html.py +543 -0
- tree_sitter_analyzer/queries/java.py +402 -0
- tree_sitter_analyzer/queries/javascript.py +724 -0
- tree_sitter_analyzer/queries/kotlin.py +192 -0
- tree_sitter_analyzer/queries/markdown.py +258 -0
- tree_sitter_analyzer/queries/php.py +95 -0
- tree_sitter_analyzer/queries/python.py +859 -0
- tree_sitter_analyzer/queries/ruby.py +92 -0
- tree_sitter_analyzer/queries/rust.py +223 -0
- tree_sitter_analyzer/queries/sql.py +555 -0
- tree_sitter_analyzer/queries/typescript.py +871 -0
- tree_sitter_analyzer/queries/yaml.py +236 -0
- tree_sitter_analyzer/query_loader.py +272 -0
- tree_sitter_analyzer/security/__init__.py +22 -0
- tree_sitter_analyzer/security/boundary_manager.py +277 -0
- tree_sitter_analyzer/security/regex_checker.py +297 -0
- tree_sitter_analyzer/security/validator.py +599 -0
- tree_sitter_analyzer/table_formatter.py +782 -0
- tree_sitter_analyzer/utils/__init__.py +53 -0
- tree_sitter_analyzer/utils/logging.py +433 -0
- tree_sitter_analyzer/utils/tree_sitter_compat.py +289 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/METADATA +485 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/RECORD +149 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/WHEEL +4 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/entry_points.txt +25 -0
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
JavaScript-specific table formatter.
|
|
4
|
+
|
|
5
|
+
Provides specialized formatting for JavaScript code analysis results,
|
|
6
|
+
handling modern JavaScript features like ES6+ syntax, async/await,
|
|
7
|
+
classes, modules, and framework-specific patterns.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from .base_formatter import BaseTableFormatter
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class JavaScriptTableFormatter(BaseTableFormatter):
|
|
16
|
+
"""Table formatter specialized for JavaScript"""
|
|
17
|
+
|
|
18
|
+
def format(self, data: dict[str, Any] | None, format_type: str = "full") -> str:
|
|
19
|
+
"""Format data using the configured format type"""
|
|
20
|
+
# Handle None data gracefully
|
|
21
|
+
if data is None:
|
|
22
|
+
data = {}
|
|
23
|
+
|
|
24
|
+
# Ensure data is a dictionary
|
|
25
|
+
if not isinstance(data, dict):
|
|
26
|
+
raise TypeError(f"Expected dict, got {type(data)}")
|
|
27
|
+
|
|
28
|
+
if format_type:
|
|
29
|
+
# Check for supported format types
|
|
30
|
+
supported_formats = ["full", "compact", "csv", "json"]
|
|
31
|
+
if format_type not in supported_formats:
|
|
32
|
+
raise ValueError(
|
|
33
|
+
f"Unsupported format type: {format_type}. Supported formats: {supported_formats}"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Handle json format separately
|
|
37
|
+
if format_type == "json":
|
|
38
|
+
return self._format_json(data)
|
|
39
|
+
|
|
40
|
+
# Temporarily change format type for this call
|
|
41
|
+
original_format = self.format_type
|
|
42
|
+
self.format_type = format_type
|
|
43
|
+
result = self.format_structure(data)
|
|
44
|
+
self.format_type = original_format
|
|
45
|
+
return result
|
|
46
|
+
return self.format_structure(data)
|
|
47
|
+
|
|
48
|
+
def _format_full_table(self, data: dict[str, Any]) -> str:
|
|
49
|
+
"""Full table format for JavaScript"""
|
|
50
|
+
if not isinstance(data, dict):
|
|
51
|
+
raise TypeError(f"Expected dict, got {type(data)}")
|
|
52
|
+
|
|
53
|
+
lines = []
|
|
54
|
+
|
|
55
|
+
# Header - JavaScript (module/file based)
|
|
56
|
+
file_path = data.get("file_path", "Unknown")
|
|
57
|
+
if file_path is None:
|
|
58
|
+
file_path = "Unknown"
|
|
59
|
+
file_name = str(file_path).split("/")[-1].split("\\")[-1]
|
|
60
|
+
module_name = (
|
|
61
|
+
file_name.replace(".js", "").replace(".jsx", "").replace(".mjs", "")
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Check if this is a module (has exports)
|
|
65
|
+
exports = data.get("exports", [])
|
|
66
|
+
if exports is None:
|
|
67
|
+
exports = []
|
|
68
|
+
is_module = len(exports) > 0
|
|
69
|
+
|
|
70
|
+
if is_module:
|
|
71
|
+
lines.append(f"# Module: {module_name}")
|
|
72
|
+
else:
|
|
73
|
+
lines.append(f"# Script: {module_name}")
|
|
74
|
+
lines.append("")
|
|
75
|
+
|
|
76
|
+
# Imports
|
|
77
|
+
imports = data.get("imports", [])
|
|
78
|
+
if imports:
|
|
79
|
+
lines.append("## Imports")
|
|
80
|
+
lines.append("```javascript")
|
|
81
|
+
for imp in imports:
|
|
82
|
+
if isinstance(imp, str):
|
|
83
|
+
# Handle malformed data where import is a string
|
|
84
|
+
import_statement = imp
|
|
85
|
+
elif isinstance(imp, dict):
|
|
86
|
+
import_statement = imp.get("statement", "")
|
|
87
|
+
if not import_statement:
|
|
88
|
+
# Construct import statement from parts
|
|
89
|
+
source = imp.get("source", "")
|
|
90
|
+
name = imp.get("name", "")
|
|
91
|
+
if name and source:
|
|
92
|
+
import_statement = f"import {name} from {source};"
|
|
93
|
+
else:
|
|
94
|
+
import_statement = str(imp)
|
|
95
|
+
lines.append(import_statement)
|
|
96
|
+
lines.append("```")
|
|
97
|
+
lines.append("")
|
|
98
|
+
|
|
99
|
+
# Module Info
|
|
100
|
+
stats = data.get("statistics", {})
|
|
101
|
+
if stats is None or not isinstance(stats, dict):
|
|
102
|
+
stats = {}
|
|
103
|
+
classes = data.get("classes", [])
|
|
104
|
+
if classes is None:
|
|
105
|
+
classes = []
|
|
106
|
+
|
|
107
|
+
lines.append("## Module Info")
|
|
108
|
+
lines.append("| Property | Value |")
|
|
109
|
+
lines.append("|----------|-------|")
|
|
110
|
+
lines.append(f"| File | {file_name} |")
|
|
111
|
+
lines.append(f"| Type | {'ES6 Module' if is_module else 'Script'} |")
|
|
112
|
+
lines.append(f"| Functions | {stats.get('function_count', 0)} |")
|
|
113
|
+
lines.append(f"| Classes | {len(classes)} |")
|
|
114
|
+
lines.append(f"| Variables | {stats.get('variable_count', 0)} |")
|
|
115
|
+
lines.append(f"| Exports | {len(exports)} |")
|
|
116
|
+
lines.append("")
|
|
117
|
+
|
|
118
|
+
# Classes (if any)
|
|
119
|
+
if classes:
|
|
120
|
+
lines.append("## Classes")
|
|
121
|
+
lines.append("| Class | Type | Extends | Lines | Methods | Properties |")
|
|
122
|
+
lines.append("|-------|------|---------|-------|---------|------------|")
|
|
123
|
+
|
|
124
|
+
for class_info in classes:
|
|
125
|
+
name = str(class_info.get("name", "Unknown"))
|
|
126
|
+
class_type = "class" # JavaScript only has classes
|
|
127
|
+
extends = str(class_info.get("superclass", "")) or "-"
|
|
128
|
+
line_range = class_info.get("line_range", {})
|
|
129
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
130
|
+
|
|
131
|
+
# Count methods within the class
|
|
132
|
+
class_methods = [
|
|
133
|
+
m
|
|
134
|
+
for m in data.get("methods", [])
|
|
135
|
+
if line_range.get("start", 0)
|
|
136
|
+
<= m.get("line_range", {}).get("start", 0)
|
|
137
|
+
<= line_range.get("end", 0)
|
|
138
|
+
]
|
|
139
|
+
|
|
140
|
+
# Count properties (class fields)
|
|
141
|
+
class_properties = [
|
|
142
|
+
v
|
|
143
|
+
for v in data.get("variables", [])
|
|
144
|
+
if line_range.get("start", 0)
|
|
145
|
+
<= v.get("line_range", {}).get("start", 0)
|
|
146
|
+
<= line_range.get("end", 0)
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
lines.append(
|
|
150
|
+
f"| {name} | {class_type} | {extends} | {lines_str} | {len(class_methods)} | {len(class_properties)} |"
|
|
151
|
+
)
|
|
152
|
+
lines.append("")
|
|
153
|
+
|
|
154
|
+
# Variables/Constants
|
|
155
|
+
variables = data.get("variables", [])
|
|
156
|
+
if variables:
|
|
157
|
+
lines.append("## Variables")
|
|
158
|
+
lines.append("| Name | Type | Scope | Kind | Line | Value |")
|
|
159
|
+
lines.append("|------|------|-------|------|------|-------|")
|
|
160
|
+
|
|
161
|
+
for var in variables:
|
|
162
|
+
name = str(var.get("name", ""))
|
|
163
|
+
# Try to get value from initializer or value field
|
|
164
|
+
var_value = var.get("initializer") or var.get("value", "")
|
|
165
|
+
var_type = self._infer_js_type(var_value)
|
|
166
|
+
scope = self._determine_scope(var)
|
|
167
|
+
kind = self._get_variable_kind(var)
|
|
168
|
+
line = var.get("line_range", {}).get("start", 0)
|
|
169
|
+
value = str(var_value)[:30] + (
|
|
170
|
+
"..." if len(str(var_value)) > 30 else ""
|
|
171
|
+
)
|
|
172
|
+
value = value.replace("\n", " ").replace("|", "\\|")
|
|
173
|
+
|
|
174
|
+
lines.append(
|
|
175
|
+
f"| {name} | {var_type} | {scope} | {kind} | {line} | {value} |"
|
|
176
|
+
)
|
|
177
|
+
lines.append("")
|
|
178
|
+
|
|
179
|
+
# Functions
|
|
180
|
+
functions = data.get("functions", [])
|
|
181
|
+
if functions:
|
|
182
|
+
# Group functions by type
|
|
183
|
+
regular_functions = [
|
|
184
|
+
f
|
|
185
|
+
for f in functions
|
|
186
|
+
if not self._is_method(f) and not f.get("is_async", False)
|
|
187
|
+
]
|
|
188
|
+
async_functions = [
|
|
189
|
+
f
|
|
190
|
+
for f in functions
|
|
191
|
+
if not self._is_method(f) and f.get("is_async", False)
|
|
192
|
+
]
|
|
193
|
+
methods = [f for f in functions if self._is_method(f)]
|
|
194
|
+
|
|
195
|
+
# Regular Functions
|
|
196
|
+
if regular_functions:
|
|
197
|
+
lines.append("## Functions")
|
|
198
|
+
lines.append(
|
|
199
|
+
"| Function | Parameters | Type | Lines | Complexity | JSDoc |"
|
|
200
|
+
)
|
|
201
|
+
lines.append(
|
|
202
|
+
"|----------|------------|------|-------|------------|-------|"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
for func in regular_functions:
|
|
206
|
+
lines.append(self._format_function_row(func))
|
|
207
|
+
lines.append("")
|
|
208
|
+
|
|
209
|
+
# Async Functions
|
|
210
|
+
if async_functions:
|
|
211
|
+
lines.append("## Async Functions")
|
|
212
|
+
lines.append(
|
|
213
|
+
"| Function | Parameters | Type | Lines | Complexity | JSDoc |"
|
|
214
|
+
)
|
|
215
|
+
lines.append(
|
|
216
|
+
"|----------|------------|------|-------|------------|-------|"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
for func in async_functions:
|
|
220
|
+
lines.append(self._format_function_row(func))
|
|
221
|
+
lines.append("")
|
|
222
|
+
|
|
223
|
+
# Methods (class methods)
|
|
224
|
+
if methods:
|
|
225
|
+
lines.append("## Methods")
|
|
226
|
+
lines.append(
|
|
227
|
+
"| Method | Class | Parameters | Type | Lines | Complexity | JSDoc |"
|
|
228
|
+
)
|
|
229
|
+
lines.append(
|
|
230
|
+
"|--------|-------|------------|------|-------|------------|-------|"
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
for method in methods:
|
|
234
|
+
lines.append(self._format_method_row(method))
|
|
235
|
+
lines.append("")
|
|
236
|
+
|
|
237
|
+
# Exports
|
|
238
|
+
if exports:
|
|
239
|
+
lines.append("## Exports")
|
|
240
|
+
lines.append("| Export | Type | Name | Default |")
|
|
241
|
+
lines.append("|--------|------|------|---------|")
|
|
242
|
+
|
|
243
|
+
# Handle malformed exports data
|
|
244
|
+
if isinstance(exports, list):
|
|
245
|
+
for export in exports:
|
|
246
|
+
try:
|
|
247
|
+
export_type = self._get_export_type(export)
|
|
248
|
+
if isinstance(export, dict):
|
|
249
|
+
name = str(export.get("name", ""))
|
|
250
|
+
is_default = "✓" if export.get("is_default", False) else "-"
|
|
251
|
+
else:
|
|
252
|
+
name = str(export)
|
|
253
|
+
is_default = "-"
|
|
254
|
+
export_type = "unknown"
|
|
255
|
+
|
|
256
|
+
lines.append(f"| {export_type} | {name} | {is_default} |")
|
|
257
|
+
except (TypeError, AttributeError):
|
|
258
|
+
# Handle malformed export data gracefully
|
|
259
|
+
lines.append(f"| unknown | {str(export)} | - |")
|
|
260
|
+
else:
|
|
261
|
+
# Handle case where exports is not a list (malformed data)
|
|
262
|
+
lines.append(f"| unknown | {str(exports)} | - |")
|
|
263
|
+
lines.append("")
|
|
264
|
+
|
|
265
|
+
# Trim trailing blank lines
|
|
266
|
+
while lines and lines[-1] == "":
|
|
267
|
+
lines.pop()
|
|
268
|
+
|
|
269
|
+
return "\n".join(lines)
|
|
270
|
+
|
|
271
|
+
def _format_compact_table(self, data: dict[str, Any]) -> str:
|
|
272
|
+
"""Compact table format for JavaScript"""
|
|
273
|
+
lines = []
|
|
274
|
+
|
|
275
|
+
# Header
|
|
276
|
+
file_path = data.get("file_path", "Unknown")
|
|
277
|
+
file_name = file_path.split("/")[-1].split("\\")[-1]
|
|
278
|
+
module_name = (
|
|
279
|
+
file_name.replace(".js", "").replace(".jsx", "").replace(".mjs", "")
|
|
280
|
+
)
|
|
281
|
+
lines.append(f"# {module_name}")
|
|
282
|
+
lines.append("")
|
|
283
|
+
|
|
284
|
+
# Info
|
|
285
|
+
stats = data.get("statistics", {})
|
|
286
|
+
lines.append("## Info")
|
|
287
|
+
lines.append("| Property | Value |")
|
|
288
|
+
lines.append("|----------|-------|")
|
|
289
|
+
lines.append(f"| Functions | {stats.get('function_count', 0)} |")
|
|
290
|
+
lines.append(f"| Classes | {len(data.get('classes', []))} |")
|
|
291
|
+
lines.append(f"| Variables | {stats.get('variable_count', 0)} |")
|
|
292
|
+
lines.append(f"| Exports | {len(data.get('exports', []))} |")
|
|
293
|
+
lines.append("")
|
|
294
|
+
|
|
295
|
+
# Functions (compact)
|
|
296
|
+
functions = data.get("functions", [])
|
|
297
|
+
if functions:
|
|
298
|
+
lines.append("## Functions")
|
|
299
|
+
lines.append("| Function | Params | Type | L | Cx | Doc |")
|
|
300
|
+
lines.append("|----------|--------|------|---|----|----|")
|
|
301
|
+
|
|
302
|
+
for func in functions:
|
|
303
|
+
name = str(func.get("name", ""))
|
|
304
|
+
params = self._create_compact_params(func)
|
|
305
|
+
func_type = self._get_function_type_short(func)
|
|
306
|
+
line_range = func.get("line_range", {})
|
|
307
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
308
|
+
complexity = func.get("complexity_score", 0)
|
|
309
|
+
doc = self._clean_csv_text(
|
|
310
|
+
self._extract_doc_summary(str(func.get("jsdoc", "")))
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
lines.append(
|
|
314
|
+
f"| {name} | {params} | {func_type} | {lines_str} | {complexity} | {doc} |"
|
|
315
|
+
)
|
|
316
|
+
lines.append("")
|
|
317
|
+
|
|
318
|
+
# Trim trailing blank lines
|
|
319
|
+
while lines and lines[-1] == "":
|
|
320
|
+
lines.pop()
|
|
321
|
+
|
|
322
|
+
return "\n".join(lines)
|
|
323
|
+
|
|
324
|
+
def _format_function_row(self, func: dict[str, Any]) -> str:
|
|
325
|
+
"""Format a function table row for JavaScript"""
|
|
326
|
+
name = str(func.get("name", ""))
|
|
327
|
+
params = self._create_full_params(func)
|
|
328
|
+
func_type = self._get_function_type(func)
|
|
329
|
+
line_range = func.get("line_range", {})
|
|
330
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
331
|
+
complexity = func.get("complexity_score", 0)
|
|
332
|
+
doc = self._clean_csv_text(
|
|
333
|
+
self._extract_doc_summary(str(func.get("jsdoc", "")))
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
return (
|
|
337
|
+
f"| {name} | {params} | {func_type} | {lines_str} | {complexity} | {doc} |"
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
def _format_method_row(self, method: dict[str, Any]) -> str:
|
|
341
|
+
"""Format a method table row for JavaScript"""
|
|
342
|
+
name = str(method.get("name", ""))
|
|
343
|
+
class_name = self._get_method_class(method)
|
|
344
|
+
params = self._create_full_params(method)
|
|
345
|
+
method_type = self._get_method_type(method)
|
|
346
|
+
line_range = method.get("line_range", {})
|
|
347
|
+
lines_str = f"{line_range.get('start', 0)}-{line_range.get('end', 0)}"
|
|
348
|
+
complexity = method.get("complexity_score", 0)
|
|
349
|
+
doc = self._clean_csv_text(
|
|
350
|
+
self._extract_doc_summary(str(method.get("jsdoc", "")))
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
return f"| {name} | {class_name} | {params} | {method_type} | {lines_str} | {complexity} | {doc} |"
|
|
354
|
+
|
|
355
|
+
def _create_full_params(self, func: dict[str, Any]) -> str:
|
|
356
|
+
"""Create full parameter list for JavaScript functions"""
|
|
357
|
+
params = func.get("parameters", [])
|
|
358
|
+
if not params:
|
|
359
|
+
return "()"
|
|
360
|
+
|
|
361
|
+
# Handle malformed data where parameters might be a string
|
|
362
|
+
if isinstance(params, str):
|
|
363
|
+
# If parameters is a malformed string, return empty params
|
|
364
|
+
return "()"
|
|
365
|
+
|
|
366
|
+
param_strs = []
|
|
367
|
+
for param in params:
|
|
368
|
+
if isinstance(param, dict):
|
|
369
|
+
param_name = param.get("name", "")
|
|
370
|
+
param_type = param.get("type", "")
|
|
371
|
+
if param_type:
|
|
372
|
+
param_strs.append(f"{param_name}: {param_type}")
|
|
373
|
+
else:
|
|
374
|
+
param_strs.append(param_name)
|
|
375
|
+
else:
|
|
376
|
+
param_strs.append(str(param))
|
|
377
|
+
|
|
378
|
+
params_str = ", ".join(param_strs)
|
|
379
|
+
if len(params_str) > 50:
|
|
380
|
+
params_str = params_str[:47] + "..."
|
|
381
|
+
|
|
382
|
+
return f"({params_str})"
|
|
383
|
+
|
|
384
|
+
def _create_compact_params(self, func: dict[str, Any]) -> str:
|
|
385
|
+
"""Create compact parameter list for JavaScript functions"""
|
|
386
|
+
params = func.get("parameters", [])
|
|
387
|
+
if not params:
|
|
388
|
+
return "()"
|
|
389
|
+
|
|
390
|
+
# Handle malformed data where parameters might be a string
|
|
391
|
+
if isinstance(params, str):
|
|
392
|
+
# If parameters is a malformed string, return empty params
|
|
393
|
+
return "()"
|
|
394
|
+
|
|
395
|
+
param_count = len(params)
|
|
396
|
+
if param_count <= 3:
|
|
397
|
+
param_names = [
|
|
398
|
+
param.get("name", str(param)) if isinstance(param, dict) else str(param)
|
|
399
|
+
for param in params
|
|
400
|
+
]
|
|
401
|
+
return f"({','.join(param_names)})"
|
|
402
|
+
else:
|
|
403
|
+
return f"({param_count} params)"
|
|
404
|
+
|
|
405
|
+
def _get_function_type(self, func: dict[str, Any]) -> str:
|
|
406
|
+
"""Get full function type for JavaScript"""
|
|
407
|
+
if func.get("is_async", False):
|
|
408
|
+
return "async function"
|
|
409
|
+
elif func.get("is_generator", False):
|
|
410
|
+
return "generator"
|
|
411
|
+
elif func.get("is_arrow", False):
|
|
412
|
+
return "arrow"
|
|
413
|
+
elif self._is_method(func):
|
|
414
|
+
if func.get("is_constructor", False):
|
|
415
|
+
return "constructor"
|
|
416
|
+
elif func.get("is_getter", False):
|
|
417
|
+
return "getter"
|
|
418
|
+
elif func.get("is_setter", False):
|
|
419
|
+
return "setter"
|
|
420
|
+
elif func.get("is_static", False):
|
|
421
|
+
return "static method"
|
|
422
|
+
else:
|
|
423
|
+
return "method"
|
|
424
|
+
else:
|
|
425
|
+
return "function"
|
|
426
|
+
|
|
427
|
+
def _get_function_type_short(self, func: dict[str, Any]) -> str:
|
|
428
|
+
"""Get short function type for JavaScript"""
|
|
429
|
+
if func.get("is_async", False):
|
|
430
|
+
return "async"
|
|
431
|
+
elif func.get("is_generator", False):
|
|
432
|
+
return "gen"
|
|
433
|
+
elif func.get("is_arrow", False):
|
|
434
|
+
return "arrow"
|
|
435
|
+
elif self._is_method(func):
|
|
436
|
+
return "method"
|
|
437
|
+
else:
|
|
438
|
+
return "func"
|
|
439
|
+
|
|
440
|
+
def _get_method_type(self, method: dict[str, Any]) -> str:
|
|
441
|
+
"""Get method type for JavaScript"""
|
|
442
|
+
if method.get("is_constructor", False):
|
|
443
|
+
return "constructor"
|
|
444
|
+
elif method.get("is_getter", False):
|
|
445
|
+
return "getter"
|
|
446
|
+
elif method.get("is_setter", False):
|
|
447
|
+
return "setter"
|
|
448
|
+
elif method.get("is_static", False):
|
|
449
|
+
return "static"
|
|
450
|
+
elif method.get("is_async", False):
|
|
451
|
+
return "async"
|
|
452
|
+
else:
|
|
453
|
+
return "method"
|
|
454
|
+
|
|
455
|
+
def _is_method(self, func: dict[str, Any]) -> bool:
|
|
456
|
+
"""Check if function is a class method"""
|
|
457
|
+
return func.get("is_method", False) or func.get("class_name") is not None
|
|
458
|
+
|
|
459
|
+
def _get_method_class(self, method: dict[str, Any]) -> str:
|
|
460
|
+
"""Get the class name for a method"""
|
|
461
|
+
return str(method.get("class_name", "Unknown"))
|
|
462
|
+
|
|
463
|
+
def _infer_js_type(self, value: Any) -> str:
|
|
464
|
+
"""Infer JavaScript type from value"""
|
|
465
|
+
if value is None:
|
|
466
|
+
return "undefined"
|
|
467
|
+
|
|
468
|
+
value_str = str(value).strip()
|
|
469
|
+
|
|
470
|
+
# Check for specific patterns
|
|
471
|
+
if value_str == "undefined":
|
|
472
|
+
return "undefined"
|
|
473
|
+
elif value_str == "NaN":
|
|
474
|
+
return "number" # NaN is a number type in JavaScript
|
|
475
|
+
elif value_str in ["Infinity", "-Infinity"]:
|
|
476
|
+
return "number" # Infinity is a number type in JavaScript
|
|
477
|
+
elif (
|
|
478
|
+
value_str.startswith('"')
|
|
479
|
+
or value_str.startswith("'")
|
|
480
|
+
or value_str.startswith("`")
|
|
481
|
+
):
|
|
482
|
+
return "string"
|
|
483
|
+
elif value_str in ["true", "false"]:
|
|
484
|
+
return "boolean"
|
|
485
|
+
elif value_str == "null":
|
|
486
|
+
return "null"
|
|
487
|
+
elif value_str.startswith("[") and value_str.endswith("]"):
|
|
488
|
+
return "array"
|
|
489
|
+
elif value_str.startswith("{") and value_str.endswith("}"):
|
|
490
|
+
return "object"
|
|
491
|
+
elif (
|
|
492
|
+
value_str.startswith("function")
|
|
493
|
+
or value_str.startswith("async function")
|
|
494
|
+
or value_str.startswith("new Function")
|
|
495
|
+
or "=>" in value_str
|
|
496
|
+
):
|
|
497
|
+
return "function"
|
|
498
|
+
elif value_str.startswith("class"):
|
|
499
|
+
return "class"
|
|
500
|
+
elif value_str.replace(".", "").replace("-", "").isdigit():
|
|
501
|
+
return "number"
|
|
502
|
+
else:
|
|
503
|
+
return "unknown"
|
|
504
|
+
|
|
505
|
+
def _determine_scope(self, var: dict[str, Any]) -> str:
|
|
506
|
+
"""Determine variable scope"""
|
|
507
|
+
# This would need more context from the parser
|
|
508
|
+
# For now, return basic scope info
|
|
509
|
+
kind = self._get_variable_kind(var)
|
|
510
|
+
if kind == "const" or kind == "let":
|
|
511
|
+
return "block"
|
|
512
|
+
elif kind == "var":
|
|
513
|
+
return "function"
|
|
514
|
+
else:
|
|
515
|
+
return "unknown"
|
|
516
|
+
|
|
517
|
+
def _get_variable_kind(self, var: dict[str, Any]) -> str:
|
|
518
|
+
"""Get variable declaration kind (const, let, var)"""
|
|
519
|
+
# Check if variable has is_constant flag
|
|
520
|
+
if var.get("is_constant", False):
|
|
521
|
+
return "const"
|
|
522
|
+
|
|
523
|
+
# Fall back to parsing raw text
|
|
524
|
+
raw_text = str(var.get("raw_text", "")).strip()
|
|
525
|
+
if raw_text.startswith("const"):
|
|
526
|
+
return "const"
|
|
527
|
+
elif raw_text.startswith("let"):
|
|
528
|
+
return "let"
|
|
529
|
+
elif raw_text.startswith("var"):
|
|
530
|
+
return "var"
|
|
531
|
+
else:
|
|
532
|
+
return "unknown"
|
|
533
|
+
|
|
534
|
+
def _get_export_type(self, export: Any) -> str:
|
|
535
|
+
"""Get export type"""
|
|
536
|
+
if not isinstance(export, dict):
|
|
537
|
+
return "unknown"
|
|
538
|
+
if export.get("is_default", False):
|
|
539
|
+
return "default"
|
|
540
|
+
elif export.get("is_named", False):
|
|
541
|
+
return "named"
|
|
542
|
+
elif export.get("is_all", False):
|
|
543
|
+
return "all"
|
|
544
|
+
else:
|
|
545
|
+
return "unknown"
|
|
546
|
+
|
|
547
|
+
def _get_function_signature(self, func: dict[str, Any]) -> str:
|
|
548
|
+
"""Get function signature"""
|
|
549
|
+
name = str(func.get("name", ""))
|
|
550
|
+
params = self._create_full_params(func)
|
|
551
|
+
return_type = func.get("return_type", "")
|
|
552
|
+
if return_type:
|
|
553
|
+
return f"{name}{params} -> {return_type}"
|
|
554
|
+
return f"{name}{params}"
|
|
555
|
+
|
|
556
|
+
def _get_class_info(self, cls: dict[str, Any]) -> str:
|
|
557
|
+
"""Get class information as formatted string"""
|
|
558
|
+
if cls is None:
|
|
559
|
+
raise TypeError("Cannot format None data")
|
|
560
|
+
|
|
561
|
+
if not isinstance(cls, dict):
|
|
562
|
+
raise TypeError(f"Expected dict, got {type(cls)}")
|
|
563
|
+
|
|
564
|
+
name = str(cls.get("name", "Unknown"))
|
|
565
|
+
methods = cls.get("methods", [])
|
|
566
|
+
method_count = len(methods) if isinstance(methods, list) else 0
|
|
567
|
+
|
|
568
|
+
return f"{name} ({method_count} methods)"
|
|
569
|
+
|
|
570
|
+
def _format_json(self, data: dict[str, Any]) -> str:
|
|
571
|
+
"""Format data as JSON"""
|
|
572
|
+
import json
|
|
573
|
+
|
|
574
|
+
try:
|
|
575
|
+
return json.dumps(data, indent=2, ensure_ascii=False)
|
|
576
|
+
except (TypeError, ValueError) as e:
|
|
577
|
+
return f"# JSON serialization error: {e}\n"
|
|
578
|
+
|
|
579
|
+
def format_table(
|
|
580
|
+
self, analysis_result: dict[str, Any], table_type: str = "full"
|
|
581
|
+
) -> str:
|
|
582
|
+
"""Format table output for JavaScript"""
|
|
583
|
+
# Set the format type based on table_type parameter
|
|
584
|
+
original_format_type = self.format_type
|
|
585
|
+
self.format_type = table_type
|
|
586
|
+
|
|
587
|
+
try:
|
|
588
|
+
# Use the existing format_structure method
|
|
589
|
+
return self.format_structure(analysis_result)
|
|
590
|
+
finally:
|
|
591
|
+
# Restore original format type
|
|
592
|
+
self.format_type = original_format_type
|
|
593
|
+
|
|
594
|
+
def format_summary(self, analysis_result: dict[str, Any]) -> str:
|
|
595
|
+
"""Format summary output for JavaScript"""
|
|
596
|
+
return self._format_compact_table(analysis_result)
|
|
597
|
+
|
|
598
|
+
def format_structure(self, analysis_result: dict[str, Any]) -> str:
|
|
599
|
+
"""Format structure analysis output for JavaScript"""
|
|
600
|
+
return super().format_structure(analysis_result)
|
|
601
|
+
|
|
602
|
+
def format_advanced(
|
|
603
|
+
self, analysis_result: dict[str, Any], output_format: str = "json"
|
|
604
|
+
) -> str:
|
|
605
|
+
"""Format advanced analysis output for JavaScript"""
|
|
606
|
+
if output_format == "json":
|
|
607
|
+
return self._format_json(analysis_result)
|
|
608
|
+
elif output_format == "csv":
|
|
609
|
+
return self._format_csv(analysis_result)
|
|
610
|
+
else:
|
|
611
|
+
return self._format_full_table(analysis_result)
|