code-auditor-mcp 2.6.2 → 3.0.0
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.
- package/README.md +319 -147
- package/dist/analyzers/analyzerUtils.d.ts +13 -11
- package/dist/analyzers/analyzerUtils.d.ts.map +1 -1
- package/dist/analyzers/analyzerUtils.js +22 -56
- package/dist/analyzers/analyzerUtils.js.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
- package/dist/analyzers/dataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/dataAccessAnalyzer.js +50 -24
- package/dist/analyzers/dataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/documentationAnalyzer.d.ts +4 -1
- package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/documentationAnalyzer.js +210 -111
- package/dist/analyzers/documentationAnalyzer.js.map +1 -1
- package/dist/analyzers/dryAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/dryAnalyzer.js +190 -58
- package/dist/analyzers/dryAnalyzer.js.map +1 -1
- package/dist/analyzers/invariantsAnalyzer.d.ts +20 -0
- package/dist/analyzers/invariantsAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/invariantsAnalyzer.js +148 -0
- package/dist/analyzers/invariantsAnalyzer.js.map +1 -0
- package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/reactAnalyzer.js +2 -2
- package/dist/analyzers/reactAnalyzer.js.map +1 -1
- package/dist/analyzers/schemaAnalyzer.d.ts +2 -0
- package/dist/analyzers/schemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/schemaAnalyzer.js +306 -147
- package/dist/analyzers/schemaAnalyzer.js.map +1 -1
- package/dist/analyzers/solidAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/solidAnalyzer.js +145 -119
- package/dist/analyzers/solidAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +3 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js +41 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +8 -8
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +6 -6
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
- package/dist/api/index.js +1 -0
- package/dist/api/index.js.map +1 -1
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +367 -24
- package/dist/auditRunner.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +740 -18
- package/dist/cli.js.map +1 -1
- package/dist/codeIndexDB-enhanced.d.ts +35 -13
- package/dist/codeIndexDB-enhanced.d.ts.map +1 -1
- package/dist/codeIndexDB-enhanced.js +311 -214
- package/dist/codeIndexDB-enhanced.js.map +1 -1
- package/dist/{codeIndexDb.d.ts → codeIndexDB.d.ts} +51 -224
- package/dist/codeIndexDB.d.ts.map +1 -0
- package/dist/codeIndexDB.js +2042 -0
- package/dist/codeIndexDB.js.map +1 -0
- package/dist/codeIndexService.d.ts.map +1 -1
- package/dist/codeIndexService.js.map +1 -1
- package/dist/componentScanner.d.ts +14 -8
- package/dist/componentScanner.d.ts.map +1 -1
- package/dist/componentScanner.js +136 -113
- package/dist/componentScanner.js.map +1 -1
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +10 -7
- package/dist/config/configLoader.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/fingerprint.d.ts +15 -0
- package/dist/fingerprint.d.ts.map +1 -0
- package/dist/fingerprint.js +32 -0
- package/dist/fingerprint.js.map +1 -0
- package/dist/functionScanner.d.ts +5 -0
- package/dist/functionScanner.d.ts.map +1 -1
- package/dist/functionScanner.js +417 -341
- package/dist/functionScanner.js.map +1 -1
- package/dist/generators/AWSQConfigGenerator.d.ts.map +1 -1
- package/dist/generators/AiderConfigGenerator.d.ts.map +1 -1
- package/dist/generators/BaseConfigGenerator.d.ts.map +1 -1
- package/dist/generators/BaseConfigGenerator.js.map +1 -1
- package/dist/generators/ClaudeConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ClineConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CodeiumConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ConfigGeneratorFactory.d.ts.map +1 -1
- package/dist/generators/ConfigGeneratorFactory.js.map +1 -1
- package/dist/generators/ContinueConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CopilotConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CursorConfigGenerator.d.ts.map +1 -1
- package/dist/generators/JetBrainsConfigGenerator.d.ts.map +1 -1
- package/dist/generators/VSCodeConfigGenerator.d.ts.map +1 -1
- package/dist/grammars/tree-sitter-go.wasm +0 -0
- package/dist/grammars/tree-sitter-javascript.wasm +0 -0
- package/dist/grammars/tree-sitter-tsx.wasm +0 -0
- package/dist/grammars/tree-sitter-typescript.wasm +0 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/invariants/index.d.ts +9 -0
- package/dist/invariants/index.d.ts.map +1 -0
- package/dist/invariants/index.js +4 -0
- package/dist/invariants/index.js.map +1 -0
- package/dist/invariants/invariant-rules.schema.json +137 -0
- package/dist/invariants/ruleEngine.d.ts +32 -0
- package/dist/invariants/ruleEngine.d.ts.map +1 -0
- package/dist/invariants/ruleEngine.js +536 -0
- package/dist/invariants/ruleEngine.js.map +1 -0
- package/dist/invariants/ruleValidator.d.ts +17 -0
- package/dist/invariants/ruleValidator.d.ts.map +1 -0
- package/dist/invariants/ruleValidator.js +264 -0
- package/dist/invariants/ruleValidator.js.map +1 -0
- package/dist/invariants/types.d.ts +78 -0
- package/dist/invariants/types.d.ts.map +1 -0
- package/dist/invariants/types.js +5 -0
- package/dist/invariants/types.js.map +1 -0
- package/dist/languages/LanguageOrchestrator.d.ts.map +1 -1
- package/dist/languages/LanguageOrchestrator.js.map +1 -1
- package/dist/languages/LanguageRegistry.d.ts.map +1 -1
- package/dist/languages/RuntimeManager.d.ts.map +1 -1
- package/dist/languages/RuntimeManager.js +1 -1
- package/dist/languages/RuntimeManager.js.map +1 -1
- package/dist/languages/UniversalAnalyzer.d.ts.map +1 -1
- package/dist/languages/UniversalAnalyzer.js.map +1 -1
- package/dist/languages/adapterBridge.d.ts +102 -0
- package/dist/languages/adapterBridge.d.ts.map +1 -0
- package/dist/languages/adapterBridge.js +382 -0
- package/dist/languages/adapterBridge.js.map +1 -0
- package/dist/languages/go/GoAdapter.d.ts +37 -26
- package/dist/languages/go/GoAdapter.d.ts.map +1 -1
- package/dist/languages/go/GoAdapter.js +560 -281
- package/dist/languages/go/GoAdapter.js.map +1 -1
- package/dist/languages/index.d.ts +5 -2
- package/dist/languages/index.d.ts.map +1 -1
- package/dist/languages/index.js +11 -9
- package/dist/languages/index.js.map +1 -1
- package/dist/languages/tree-sitter/converter.d.ts +43 -0
- package/dist/languages/tree-sitter/converter.d.ts.map +1 -0
- package/dist/languages/tree-sitter/converter.js +150 -0
- package/dist/languages/tree-sitter/converter.js.map +1 -0
- package/dist/languages/tree-sitter/parser.d.ts +36 -0
- package/dist/languages/tree-sitter/parser.d.ts.map +1 -0
- package/dist/languages/tree-sitter/parser.js +114 -0
- package/dist/languages/tree-sitter/parser.js.map +1 -0
- package/dist/languages/types.d.ts +11 -0
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +92 -0
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -0
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +951 -0
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -0
- package/dist/mcp-tools/projectTasks.d.ts +58 -22
- package/dist/mcp-tools/projectTasks.d.ts.map +1 -1
- package/dist/mcp-tools/projectTasks.js +172 -10
- package/dist/mcp-tools/projectTasks.js.map +1 -1
- package/dist/mcp-tools/whitelistTools.d.ts +21 -21
- package/dist/mcp-tools/whitelistTools.d.ts.map +1 -1
- package/dist/mcp-tools/workflowGuide.d.ts.map +1 -1
- package/dist/mcp-tools/workflowGuide.js +130 -126
- package/dist/mcp-tools/workflowGuide.js.map +1 -1
- package/dist/mcp-tools-shared.d.ts.map +1 -1
- package/dist/mcp-tools-shared.js.map +1 -1
- package/dist/mcp-ui-simple.js +1 -1
- package/dist/mcp-ui-simple.js.map +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +1376 -1181
- package/dist/mcp.js.map +1 -1
- package/dist/mcpAuditJobs.d.ts +4 -0
- package/dist/mcpAuditJobs.d.ts.map +1 -1
- package/dist/mcpAuditJobs.js +49 -2
- package/dist/mcpAuditJobs.js.map +1 -1
- package/dist/mcpToolErrors.d.ts.map +1 -1
- package/dist/mcpToolErrors.js.map +1 -1
- package/dist/native/ast-grep-napi.darwin-arm64.node +0 -0
- package/dist/native/ast-grep-napi.darwin-x64.node +0 -0
- package/dist/native/ast-grep-napi.linux-arm64-gnu.node +0 -0
- package/dist/native/ast-grep-napi.linux-arm64-musl.node +0 -0
- package/dist/native/ast-grep-napi.linux-x64-gnu.node +0 -0
- package/dist/native/ast-grep-napi.linux-x64-musl.node +0 -0
- package/dist/native/ast-grep-napi.win32-x64-msvc.node +0 -0
- package/dist/native-bootstrap.d.ts +15 -0
- package/dist/native-bootstrap.d.ts.map +1 -0
- package/dist/native-bootstrap.js +75 -0
- package/dist/native-bootstrap.js.map +1 -0
- package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
- package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
- package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
- package/dist/reporting/reportGenerator.d.ts.map +1 -1
- package/dist/reporting/reportGenerator.js +4 -0
- package/dist/reporting/reportGenerator.js.map +1 -1
- package/dist/reporting/sarifReportGenerator.d.ts +39 -0
- package/dist/reporting/sarifReportGenerator.d.ts.map +1 -0
- package/dist/reporting/sarifReportGenerator.js +229 -0
- package/dist/reporting/sarifReportGenerator.js.map +1 -0
- package/dist/search/QueryParser.d.ts +41 -0
- package/dist/search/QueryParser.d.ts.map +1 -1
- package/dist/search/QueryParser.js +200 -19
- package/dist/search/QueryParser.js.map +1 -1
- package/dist/services/CodeMapGenerator.d.ts.map +1 -1
- package/dist/services/CodeMapGenerator.js +4 -4
- package/dist/services/CodeMapGenerator.js.map +1 -1
- package/dist/services/ProjectTaskRepository.d.ts +15 -2
- package/dist/services/ProjectTaskRepository.d.ts.map +1 -1
- package/dist/services/ProjectTaskRepository.js +16 -1
- package/dist/services/ProjectTaskRepository.js.map +1 -1
- package/dist/services/SchemaParser.d.ts.map +1 -1
- package/dist/services/SchemaParser.js +5 -8
- package/dist/services/SchemaParser.js.map +1 -1
- package/dist/services/auditJobService.d.ts +1 -1
- package/dist/services/projectTaskHelpers.js.map +1 -1
- package/dist/services/whitelistService.d.ts.map +1 -1
- package/dist/services/whitelistService.js +1 -1
- package/dist/services/whitelistService.js.map +1 -1
- package/dist/tool-registry.d.ts +79 -0
- package/dist/tool-registry.d.ts.map +1 -0
- package/dist/tool-registry.js +139 -0
- package/dist/tool-registry.js.map +1 -0
- package/dist/types/projectTask.d.ts +4 -0
- package/dist/types/projectTask.d.ts.map +1 -1
- package/dist/types.d.ts +26 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/ui/InteractivePrompts.d.ts.map +1 -1
- package/dist/ui/InteractivePrompts.js.map +1 -1
- package/dist/utils/astParser.d.ts +31 -38
- package/dist/utils/astParser.d.ts.map +1 -1
- package/dist/utils/astParser.js +138 -196
- package/dist/utils/astParser.js.map +1 -1
- package/dist/utils/astUtils.d.ts +87 -58
- package/dist/utils/astUtils.d.ts.map +1 -1
- package/dist/utils/astUtils.js +852 -581
- package/dist/utils/astUtils.js.map +1 -1
- package/dist/utils/componentPatterns.js.map +1 -1
- package/dist/utils/componentResponsibility.d.ts +13 -9
- package/dist/utils/componentResponsibility.d.ts.map +1 -1
- package/dist/utils/componentResponsibility.js +236 -206
- package/dist/utils/componentResponsibility.js.map +1 -1
- package/dist/utils/dependencyExtractor.d.ts +11 -6
- package/dist/utils/dependencyExtractor.d.ts.map +1 -1
- package/dist/utils/dependencyExtractor.js +200 -119
- package/dist/utils/dependencyExtractor.js.map +1 -1
- package/dist/utils/fileDiscovery.d.ts +1 -1
- package/dist/utils/fileDiscovery.d.ts.map +1 -1
- package/dist/utils/fileDiscovery.js.map +1 -1
- package/dist/utils/jsDocParser.d.ts +27 -30
- package/dist/utils/jsDocParser.d.ts.map +1 -1
- package/dist/utils/jsDocParser.js +271 -207
- package/dist/utils/jsDocParser.js.map +1 -1
- package/dist/utils/reactDetection.d.ts +27 -12
- package/dist/utils/reactDetection.d.ts.map +1 -1
- package/dist/utils/reactDetection.js +331 -219
- package/dist/utils/reactDetection.js.map +1 -1
- package/dist/workers/auditWorker.js +17 -3
- package/dist/workers/auditWorker.js.map +1 -1
- package/package.json +54 -51
- package/dist/adapters/GoAdapter.d.ts +0 -53
- package/dist/adapters/GoAdapter.d.ts.map +0 -1
- package/dist/adapters/GoAdapter.js +0 -611
- package/dist/adapters/GoAdapter.js.map +0 -1
- package/dist/adapters/GoConfig.d.ts +0 -35
- package/dist/adapters/GoConfig.d.ts.map +0 -1
- package/dist/adapters/GoConfig.js +0 -155
- package/dist/adapters/GoConfig.js.map +0 -1
- package/dist/adapters/LanguageAdapter.d.ts +0 -135
- package/dist/adapters/LanguageAdapter.d.ts.map +0 -1
- package/dist/adapters/LanguageAdapter.js +0 -47
- package/dist/adapters/LanguageAdapter.js.map +0 -1
- package/dist/adapters/TypeScriptAdapter.d.ts +0 -43
- package/dist/adapters/TypeScriptAdapter.d.ts.map +0 -1
- package/dist/adapters/TypeScriptAdapter.js +0 -386
- package/dist/adapters/TypeScriptAdapter.js.map +0 -1
- package/dist/adapters/UniversalAnalyzer.d.ts +0 -42
- package/dist/adapters/UniversalAnalyzer.d.ts.map +0 -1
- package/dist/adapters/UniversalAnalyzer.js +0 -103
- package/dist/adapters/UniversalAnalyzer.js.map +0 -1
- package/dist/adapters/UniversalSOLIDAnalyzer.d.ts +0 -48
- package/dist/adapters/UniversalSOLIDAnalyzer.d.ts.map +0 -1
- package/dist/adapters/UniversalSOLIDAnalyzer.js +0 -178
- package/dist/adapters/UniversalSOLIDAnalyzer.js.map +0 -1
- package/dist/adapters/solidAnalyzerCompat.d.ts +0 -21
- package/dist/adapters/solidAnalyzerCompat.d.ts.map +0 -1
- package/dist/adapters/solidAnalyzerCompat.js +0 -78
- package/dist/adapters/solidAnalyzerCompat.js.map +0 -1
- package/dist/analyzers/dataAccessAnalyzerCompat.d.ts +0 -20
- package/dist/analyzers/dataAccessAnalyzerCompat.d.ts.map +0 -1
- package/dist/analyzers/dataAccessAnalyzerCompat.js +0 -59
- package/dist/analyzers/dataAccessAnalyzerCompat.js.map +0 -1
- package/dist/analyzers/documentationAnalyzerCompat.d.ts +0 -20
- package/dist/analyzers/documentationAnalyzerCompat.d.ts.map +0 -1
- package/dist/analyzers/documentationAnalyzerCompat.js +0 -52
- package/dist/analyzers/documentationAnalyzerCompat.js.map +0 -1
- package/dist/analyzers/dryAnalyzerCompat.d.ts +0 -20
- package/dist/analyzers/dryAnalyzerCompat.d.ts.map +0 -1
- package/dist/analyzers/dryAnalyzerCompat.js +0 -44
- package/dist/analyzers/dryAnalyzerCompat.js.map +0 -1
- package/dist/analyzers/schemaAnalyzerCompat.d.ts +0 -20
- package/dist/analyzers/schemaAnalyzerCompat.d.ts.map +0 -1
- package/dist/analyzers/schemaAnalyzerCompat.js +0 -69
- package/dist/analyzers/schemaAnalyzerCompat.js.map +0 -1
- package/dist/analyzers/solidAnalyzerCompat.d.ts +0 -20
- package/dist/analyzers/solidAnalyzerCompat.d.ts.map +0 -1
- package/dist/analyzers/solidAnalyzerCompat.js +0 -78
- package/dist/analyzers/solidAnalyzerCompat.js.map +0 -1
- package/dist/codeIndexDb.d.ts.map +0 -1
- package/dist/codeIndexDb.js +0 -2102
- package/dist/codeIndexDb.js.map +0 -1
- package/dist/languages/typescript/TypeScriptAdapter.d.ts +0 -108
- package/dist/languages/typescript/TypeScriptAdapter.d.ts.map +0 -1
- package/dist/languages/typescript/TypeScriptAdapter.js +0 -567
- package/dist/languages/typescript/TypeScriptAdapter.js.map +0 -1
- package/dist/languages/typescript/test-extraction.d.ts +0 -2
- package/dist/languages/typescript/test-extraction.d.ts.map +0 -1
- package/dist/languages/typescript/test-extraction.js +0 -36
- package/dist/languages/typescript/test-extraction.js.map +0 -1
- package/dist/mcp-hybrid.d.ts +0 -10
- package/dist/mcp-hybrid.d.ts.map +0 -1
- package/dist/mcp-hybrid.js +0 -41
- package/dist/mcp-hybrid.js.map +0 -1
- package/dist/mcp-index.d.ts +0 -13
- package/dist/mcp-index.d.ts.map +0 -1
- package/dist/mcp-index.js +0 -136
- package/dist/mcp-index.js.map +0 -1
- package/dist/mcp-standalone.d.ts +0 -3
- package/dist/mcp-standalone.d.ts.map +0 -1
- package/dist/mcp-standalone.js +0 -1324
- package/dist/mcp-standalone.js.map +0 -1
- package/dist/mcp-ui-server.d.ts +0 -27
- package/dist/mcp-ui-server.d.ts.map +0 -1
- package/dist/mcp-ui-server.js +0 -484
- package/dist/mcp-ui-server.js.map +0 -1
- package/dist/test-multi-strategy-search.d.ts +0 -3
- package/dist/test-multi-strategy-search.d.ts.map +0 -1
- package/dist/test-multi-strategy-search.js +0 -170
- package/dist/test-multi-strategy-search.js.map +0 -1
|
@@ -0,0 +1,951 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree-sitter TypeScript/JavaScript adapter implementing the LanguageAdapter interface.
|
|
3
|
+
*
|
|
4
|
+
* Uses web-tree-sitter WASM parsers behind the adapter seam — no TypeScript compiler API.
|
|
5
|
+
* Supports .ts, .tsx, .js, and .jsx files.
|
|
6
|
+
*/
|
|
7
|
+
import { getParser } from '../tree-sitter/parser.js';
|
|
8
|
+
import { toASTNode, toSourceLocation } from '../tree-sitter/converter.js';
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Source code storage
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
/**
|
|
13
|
+
* Source code indexed by AST, so methods that only receive an AST
|
|
14
|
+
* can recover the original source text.
|
|
15
|
+
*/
|
|
16
|
+
const sourceCodeMap = new WeakMap();
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Adapter implementation
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
export class TreeSitterTypeScriptAdapter {
|
|
21
|
+
name = 'typescript';
|
|
22
|
+
fileExtensions = ['.ts', '.tsx', '.js', '.jsx'];
|
|
23
|
+
// -- File detection -------------------------------------------------------
|
|
24
|
+
supportsFile(filePath) {
|
|
25
|
+
return this.fileExtensions.some((ext) => filePath.endsWith(ext));
|
|
26
|
+
}
|
|
27
|
+
// -- Parsing --------------------------------------------------------------
|
|
28
|
+
async parse(filePath, content) {
|
|
29
|
+
const isTsx = filePath.endsWith('.tsx') || filePath.endsWith('.jsx');
|
|
30
|
+
const lang = isTsx ? 'tsx' : filePath.endsWith('.go') ? 'go' : 'typescript';
|
|
31
|
+
const parser = getParser(lang, isTsx);
|
|
32
|
+
const tree = parser.parse(content);
|
|
33
|
+
if (!tree)
|
|
34
|
+
throw new Error(`Failed to parse file: ${filePath}`);
|
|
35
|
+
const errors = [];
|
|
36
|
+
// Collect ERROR nodes as parse errors
|
|
37
|
+
this.collectErrors(tree.rootNode, errors);
|
|
38
|
+
const root = toASTNode(tree.rootNode, undefined, lang);
|
|
39
|
+
const ast = {
|
|
40
|
+
root,
|
|
41
|
+
language: lang,
|
|
42
|
+
filePath,
|
|
43
|
+
errors,
|
|
44
|
+
};
|
|
45
|
+
sourceCodeMap.set(ast, content);
|
|
46
|
+
return ast;
|
|
47
|
+
}
|
|
48
|
+
// -- AST Navigation -------------------------------------------------------
|
|
49
|
+
findNodes(ast, pattern) {
|
|
50
|
+
const results = [];
|
|
51
|
+
this.walk(ast.root, (node) => {
|
|
52
|
+
if (this.matchesPattern(node, pattern)) {
|
|
53
|
+
results.push(node);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return results;
|
|
57
|
+
}
|
|
58
|
+
getParent(node) {
|
|
59
|
+
return node.parent ?? null;
|
|
60
|
+
}
|
|
61
|
+
getChildren(node) {
|
|
62
|
+
return node.children ?? [];
|
|
63
|
+
}
|
|
64
|
+
getSiblings(node) {
|
|
65
|
+
if (!node.parent?.children)
|
|
66
|
+
return [];
|
|
67
|
+
return node.parent.children.filter((c) => c !== node);
|
|
68
|
+
}
|
|
69
|
+
// -- Node Information -----------------------------------------------------
|
|
70
|
+
getNodeType(node) {
|
|
71
|
+
return node.type;
|
|
72
|
+
}
|
|
73
|
+
getNodeText(node, sourceCode) {
|
|
74
|
+
return sourceCode.slice(node.range[0], node.range[1]);
|
|
75
|
+
}
|
|
76
|
+
getNodeName(node) {
|
|
77
|
+
const syntaxNode = node.raw;
|
|
78
|
+
return this.extractName(syntaxNode);
|
|
79
|
+
}
|
|
80
|
+
getNodeLocation(node) {
|
|
81
|
+
return node.location;
|
|
82
|
+
}
|
|
83
|
+
// -- Language-Specific Extraction -----------------------------------------
|
|
84
|
+
extractFunctions(ast) {
|
|
85
|
+
const sourceCode = sourceCodeMap.get(ast) ?? '';
|
|
86
|
+
const functions = [];
|
|
87
|
+
this.walk(ast.root, (node) => {
|
|
88
|
+
const syntaxNode = node.raw;
|
|
89
|
+
const type = syntaxNode.type;
|
|
90
|
+
if (type === 'function_declaration' ||
|
|
91
|
+
type === 'generator_function_declaration' ||
|
|
92
|
+
type === 'function_expression' ||
|
|
93
|
+
type === 'arrow_function' ||
|
|
94
|
+
type === 'method_definition') {
|
|
95
|
+
const fn = this.buildFunctionInfo(syntaxNode, sourceCode);
|
|
96
|
+
if (fn)
|
|
97
|
+
functions.push(fn);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
return functions;
|
|
101
|
+
}
|
|
102
|
+
extractClasses(ast) {
|
|
103
|
+
const sourceCode = sourceCodeMap.get(ast) ?? '';
|
|
104
|
+
const classes = [];
|
|
105
|
+
this.walk(ast.root, (node) => {
|
|
106
|
+
const syntaxNode = node.raw;
|
|
107
|
+
if (syntaxNode.type === 'class_declaration') {
|
|
108
|
+
const cls = this.buildClassInfo(syntaxNode, sourceCode);
|
|
109
|
+
if (cls)
|
|
110
|
+
classes.push(cls);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
return classes;
|
|
114
|
+
}
|
|
115
|
+
extractImports(ast) {
|
|
116
|
+
const sourceCode = sourceCodeMap.get(ast) ?? '';
|
|
117
|
+
const imports = [];
|
|
118
|
+
this.walk(ast.root, (node) => {
|
|
119
|
+
const syntaxNode = node.raw;
|
|
120
|
+
if (syntaxNode.type === 'import_statement') {
|
|
121
|
+
const imp = this.buildImportInfo(syntaxNode, sourceCode);
|
|
122
|
+
if (imp)
|
|
123
|
+
imports.push(imp);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
return imports;
|
|
127
|
+
}
|
|
128
|
+
extractExports(ast) {
|
|
129
|
+
const sourceCode = sourceCodeMap.get(ast) ?? '';
|
|
130
|
+
const exports = [];
|
|
131
|
+
this.walk(ast.root, (node) => {
|
|
132
|
+
const syntaxNode = node.raw;
|
|
133
|
+
if (syntaxNode.type === 'export_statement') {
|
|
134
|
+
const ex = this.buildExportInfo(syntaxNode, sourceCode);
|
|
135
|
+
if (ex)
|
|
136
|
+
exports.push(ex);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
return exports;
|
|
140
|
+
}
|
|
141
|
+
// -- Pattern Matching Helpers ---------------------------------------------
|
|
142
|
+
isClass(node) {
|
|
143
|
+
const type = node.raw.type;
|
|
144
|
+
return type === 'class_declaration' || type === 'class_expression';
|
|
145
|
+
}
|
|
146
|
+
isFunction(node) {
|
|
147
|
+
const type = node.raw.type;
|
|
148
|
+
return (type === 'function_declaration' ||
|
|
149
|
+
type === 'function_expression' ||
|
|
150
|
+
type === 'arrow_function' ||
|
|
151
|
+
type === 'generator_function_declaration' ||
|
|
152
|
+
type === 'generator_function_expression');
|
|
153
|
+
}
|
|
154
|
+
isMethod(node) {
|
|
155
|
+
return node.raw.type === 'method_definition';
|
|
156
|
+
}
|
|
157
|
+
isInterface(node) {
|
|
158
|
+
return node.raw.type === 'interface_declaration';
|
|
159
|
+
}
|
|
160
|
+
isImport(node) {
|
|
161
|
+
return node.raw.type === 'import_statement';
|
|
162
|
+
}
|
|
163
|
+
isExport(node) {
|
|
164
|
+
return node.raw.type === 'export_statement';
|
|
165
|
+
}
|
|
166
|
+
isLoop(node) {
|
|
167
|
+
const type = node.raw.type;
|
|
168
|
+
return (type === 'for_statement' ||
|
|
169
|
+
type === 'for_in_statement' ||
|
|
170
|
+
type === 'while_statement' ||
|
|
171
|
+
type === 'do_statement');
|
|
172
|
+
}
|
|
173
|
+
isConditional(node) {
|
|
174
|
+
const type = node.raw.type;
|
|
175
|
+
return (type === 'if_statement' ||
|
|
176
|
+
type === 'switch_statement' ||
|
|
177
|
+
type === 'ternary_expression' ||
|
|
178
|
+
type === 'switch_case');
|
|
179
|
+
}
|
|
180
|
+
isVariableDeclaration(node) {
|
|
181
|
+
const type = node.raw.type;
|
|
182
|
+
return (type === 'variable_declaration' ||
|
|
183
|
+
type === 'lexical_declaration' ||
|
|
184
|
+
type === 'variable_declarator');
|
|
185
|
+
}
|
|
186
|
+
// -- Advanced Features ----------------------------------------------------
|
|
187
|
+
getTypeInfo(node) {
|
|
188
|
+
const syntaxNode = node.raw;
|
|
189
|
+
return this.extractTypeAnnotation(syntaxNode);
|
|
190
|
+
}
|
|
191
|
+
getDocumentation(node) {
|
|
192
|
+
const syntaxNode = node.raw;
|
|
193
|
+
return this.extractDocumentation(syntaxNode);
|
|
194
|
+
}
|
|
195
|
+
getComplexity(node) {
|
|
196
|
+
const syntaxNode = node.raw;
|
|
197
|
+
return this.calculateCyclomaticComplexity(syntaxNode);
|
|
198
|
+
}
|
|
199
|
+
// -- Optional: Interfaces -------------------------------------------------
|
|
200
|
+
extractInterfaces(ast) {
|
|
201
|
+
const sourceCode = sourceCodeMap.get(ast) ?? '';
|
|
202
|
+
const interfaces = [];
|
|
203
|
+
this.walk(ast.root, (node) => {
|
|
204
|
+
const syntaxNode = node.raw;
|
|
205
|
+
if (syntaxNode.type === 'interface_declaration') {
|
|
206
|
+
const iface = this.buildInterfaceInfo(syntaxNode, sourceCode);
|
|
207
|
+
if (iface)
|
|
208
|
+
interfaces.push(iface);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
return interfaces;
|
|
212
|
+
}
|
|
213
|
+
// -- Optional: Raw imports (static, dynamic, require) ----------------------
|
|
214
|
+
extractRawImports(_filePath, content) {
|
|
215
|
+
const results = [];
|
|
216
|
+
const parser = getParser('typescript');
|
|
217
|
+
const tree = parser.parse(content);
|
|
218
|
+
if (!tree)
|
|
219
|
+
return results;
|
|
220
|
+
// Static imports
|
|
221
|
+
this.walkRaw(tree.rootNode, (node) => {
|
|
222
|
+
if (node.type === 'import_statement') {
|
|
223
|
+
const source = this.getChildByType(node, 'string');
|
|
224
|
+
if (source) {
|
|
225
|
+
const specifier = source.text.slice(1, -1); // strip quotes
|
|
226
|
+
results.push({
|
|
227
|
+
moduleSpecifier: specifier,
|
|
228
|
+
isStatic: true,
|
|
229
|
+
isDynamic: false,
|
|
230
|
+
isRequire: false,
|
|
231
|
+
line: source.startPosition.row,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// Dynamic import(): import('...')
|
|
236
|
+
if (node.type === 'call_expression') {
|
|
237
|
+
const fn = node.firstChild;
|
|
238
|
+
if (fn?.type === 'import') {
|
|
239
|
+
const args = this.getChildByType(node, 'arguments');
|
|
240
|
+
if (args) {
|
|
241
|
+
const strNode = this.findFirstNamedChild(args, 'string');
|
|
242
|
+
if (strNode) {
|
|
243
|
+
const specifier = strNode.text.slice(1, -1);
|
|
244
|
+
results.push({
|
|
245
|
+
moduleSpecifier: specifier,
|
|
246
|
+
isStatic: false,
|
|
247
|
+
isDynamic: true,
|
|
248
|
+
isRequire: false,
|
|
249
|
+
line: strNode.startPosition.row,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// require('...')
|
|
255
|
+
if (fn?.type === 'identifier' && fn.text === 'require') {
|
|
256
|
+
const args = this.getChildByType(node, 'arguments');
|
|
257
|
+
if (args) {
|
|
258
|
+
const strNode = this.findFirstNamedChild(args, 'string');
|
|
259
|
+
if (strNode) {
|
|
260
|
+
const specifier = strNode.text.slice(1, -1);
|
|
261
|
+
results.push({
|
|
262
|
+
moduleSpecifier: specifier,
|
|
263
|
+
isStatic: false,
|
|
264
|
+
isDynamic: false,
|
|
265
|
+
isRequire: true,
|
|
266
|
+
line: strNode.startPosition.row,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
return results;
|
|
274
|
+
}
|
|
275
|
+
// -- Optional: Exported symbol names --------------------------------------
|
|
276
|
+
extractExportedSymbols(ast) {
|
|
277
|
+
const symbols = [];
|
|
278
|
+
this.walk(ast.root, (astNode) => {
|
|
279
|
+
const node = astNode.raw;
|
|
280
|
+
if (node.type === 'export_statement') {
|
|
281
|
+
// export function foo / export class Foo / export const x
|
|
282
|
+
const declaration = this.findFirstNamedChild(node, [
|
|
283
|
+
'function_declaration',
|
|
284
|
+
'class_declaration',
|
|
285
|
+
'lexical_declaration',
|
|
286
|
+
'variable_declaration',
|
|
287
|
+
'interface_declaration',
|
|
288
|
+
'type_alias_declaration',
|
|
289
|
+
'enum_declaration',
|
|
290
|
+
]);
|
|
291
|
+
if (declaration) {
|
|
292
|
+
const name = this.extractName(declaration);
|
|
293
|
+
if (name) {
|
|
294
|
+
symbols.push({ name, line: declaration.startPosition.row });
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
// export { foo, bar } or export { default }
|
|
299
|
+
const clause = this.getChildByType(node, 'export_clause');
|
|
300
|
+
if (clause) {
|
|
301
|
+
for (const child of clause.namedChildren) {
|
|
302
|
+
if (child.type === 'export_specifier') {
|
|
303
|
+
const nameNode = this.getChildByType(child, 'identifier');
|
|
304
|
+
if (nameNode) {
|
|
305
|
+
symbols.push({
|
|
306
|
+
name: nameNode.text,
|
|
307
|
+
line: nameNode.startPosition.row,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// export default <expression>
|
|
314
|
+
if (node.childForFieldName?.('value')) {
|
|
315
|
+
symbols.push({
|
|
316
|
+
name: 'default',
|
|
317
|
+
line: node.startPosition.row,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
return symbols;
|
|
323
|
+
}
|
|
324
|
+
// ---------------------------------------------------------------------------
|
|
325
|
+
// Internal helpers
|
|
326
|
+
// ---------------------------------------------------------------------------
|
|
327
|
+
/** Walk all ASTNodes in a tree (depth-first). */
|
|
328
|
+
walk(node, visitor) {
|
|
329
|
+
visitor(node);
|
|
330
|
+
if (node.children) {
|
|
331
|
+
for (const child of node.children) {
|
|
332
|
+
this.walk(child, visitor);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
/** Walk all TreeSitterNodes in a tree (depth-first). */
|
|
337
|
+
walkRaw(node, visitor) {
|
|
338
|
+
visitor(node);
|
|
339
|
+
for (const child of node.children) {
|
|
340
|
+
this.walkRaw(child, visitor);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/** Collect ERROR nodes from tree-sitter's error recovery. */
|
|
344
|
+
collectErrors(node, errors) {
|
|
345
|
+
if (node.type === 'ERROR' || node.isError) {
|
|
346
|
+
errors.push({
|
|
347
|
+
message: `Parse error near "${node.text.slice(0, 50)}"`,
|
|
348
|
+
location: toSourceLocation(node),
|
|
349
|
+
severity: 'error',
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
for (const child of node.children) {
|
|
353
|
+
this.collectErrors(child, errors);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/** Match an ASTNode against a NodePattern. */
|
|
357
|
+
matchesPattern(node, pattern) {
|
|
358
|
+
const syntaxNode = node.raw;
|
|
359
|
+
// type matching
|
|
360
|
+
if (pattern.type !== undefined) {
|
|
361
|
+
const types = Array.isArray(pattern.type) ? pattern.type : [pattern.type];
|
|
362
|
+
if (!types.includes(syntaxNode.type))
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
// name matching
|
|
366
|
+
if (pattern.name !== undefined) {
|
|
367
|
+
const nodeName = this.extractName(syntaxNode);
|
|
368
|
+
if (nodeName === null)
|
|
369
|
+
return false;
|
|
370
|
+
if (typeof pattern.name === 'string') {
|
|
371
|
+
if (nodeName !== pattern.name)
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
else if (pattern.name instanceof RegExp) {
|
|
375
|
+
if (!pattern.name.test(nodeName))
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
// hasChild matching
|
|
380
|
+
if (pattern.hasChild !== undefined) {
|
|
381
|
+
const childNodes = node.children ?? [];
|
|
382
|
+
const hasMatch = childNodes.some((c) => this.matchesPattern(c, pattern.hasChild));
|
|
383
|
+
if (!hasMatch)
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
// hasParent matching
|
|
387
|
+
if (pattern.hasParent !== undefined) {
|
|
388
|
+
if (!node.parent)
|
|
389
|
+
return false;
|
|
390
|
+
if (!this.matchesPattern(node.parent, pattern.hasParent))
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
// custom predicate
|
|
394
|
+
if (pattern.custom !== undefined) {
|
|
395
|
+
if (!pattern.custom(node))
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
return true;
|
|
399
|
+
}
|
|
400
|
+
/** Extract the name/identifier from a tree-sitter TreeSitterNode. */
|
|
401
|
+
extractName(node) {
|
|
402
|
+
switch (node.type) {
|
|
403
|
+
case 'function_declaration':
|
|
404
|
+
case 'generator_function_declaration':
|
|
405
|
+
case 'class_declaration':
|
|
406
|
+
case 'interface_declaration':
|
|
407
|
+
case 'enum_declaration':
|
|
408
|
+
case 'type_alias_declaration': {
|
|
409
|
+
const nameNode = node.childForFieldName?.('name');
|
|
410
|
+
if (nameNode)
|
|
411
|
+
return nameNode.text;
|
|
412
|
+
// Fallback: find first identifier child
|
|
413
|
+
for (const child of node.namedChildren) {
|
|
414
|
+
if (child.type === 'identifier' || child.type === 'type_identifier') {
|
|
415
|
+
return child.text;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
case 'method_definition':
|
|
421
|
+
case 'public_field_definition': {
|
|
422
|
+
const nameNode = node.childForFieldName?.('name');
|
|
423
|
+
if (nameNode)
|
|
424
|
+
return nameNode.text;
|
|
425
|
+
// property_identifier or string
|
|
426
|
+
for (const child of node.namedChildren) {
|
|
427
|
+
if (child.type === 'property_identifier' ||
|
|
428
|
+
child.type === 'string') {
|
|
429
|
+
return child.text.replace(/^["']|["']$/g, '');
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
case 'variable_declarator': {
|
|
435
|
+
const nameNode = node.childForFieldName?.('name');
|
|
436
|
+
if (nameNode)
|
|
437
|
+
return nameNode.text;
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
case 'lexical_declaration':
|
|
441
|
+
case 'variable_declaration': {
|
|
442
|
+
// Find the declarator child and extract its name
|
|
443
|
+
for (const child of node.namedChildren) {
|
|
444
|
+
if (child.type === 'variable_declarator') {
|
|
445
|
+
return this.extractName(child);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
case 'arrow_function': {
|
|
451
|
+
// Check if assigned to a variable: const foo = () => {}
|
|
452
|
+
const parent = node.parent;
|
|
453
|
+
if (parent?.type === 'variable_declarator') {
|
|
454
|
+
return this.extractName(parent);
|
|
455
|
+
}
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
case 'function_expression': {
|
|
459
|
+
// Might have a name: const foo = function bar() {}
|
|
460
|
+
const nameNode = node.childForFieldName?.('name');
|
|
461
|
+
if (nameNode)
|
|
462
|
+
return nameNode.text;
|
|
463
|
+
// Check if assigned to a variable
|
|
464
|
+
const parent = node.parent;
|
|
465
|
+
if (parent?.type === 'variable_declarator') {
|
|
466
|
+
return this.extractName(parent);
|
|
467
|
+
}
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
case 'property_identifier':
|
|
471
|
+
return node.text;
|
|
472
|
+
case 'identifier':
|
|
473
|
+
return node.text;
|
|
474
|
+
default:
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
/** Extract a type annotation string from a node. */
|
|
479
|
+
extractTypeAnnotation(node) {
|
|
480
|
+
// Function return type
|
|
481
|
+
if (node.type === 'function_declaration' ||
|
|
482
|
+
node.type === 'method_definition' ||
|
|
483
|
+
node.type === 'arrow_function') {
|
|
484
|
+
const returnType = node.childForFieldName?.('return_type');
|
|
485
|
+
if (returnType) {
|
|
486
|
+
return returnType.text.replace(/^:\s*/, '').trim();
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// Variable / parameter type annotation
|
|
490
|
+
const typeAnnotation = node.childForFieldName?.('type');
|
|
491
|
+
if (typeAnnotation) {
|
|
492
|
+
return typeAnnotation.text.replace(/^:\s*/, '').trim();
|
|
493
|
+
}
|
|
494
|
+
// Named children that are type_annotation
|
|
495
|
+
for (const child of node.namedChildren) {
|
|
496
|
+
if (child.type === 'type_annotation') {
|
|
497
|
+
return child.text.replace(/^:\s*/, '').trim();
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
/** Extract JSDoc or leading comment from a node. */
|
|
503
|
+
extractDocumentation(node) {
|
|
504
|
+
const parent = node.parent;
|
|
505
|
+
if (!parent)
|
|
506
|
+
return null;
|
|
507
|
+
// Find the node's position among its parent's children and look for
|
|
508
|
+
// a preceding comment node.
|
|
509
|
+
const siblings = parent.children;
|
|
510
|
+
let myIndex = -1;
|
|
511
|
+
for (let i = 0; i < siblings.length; i++) {
|
|
512
|
+
if (siblings[i] === node) {
|
|
513
|
+
myIndex = i;
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (myIndex === -1)
|
|
518
|
+
return null;
|
|
519
|
+
// Look backwards through siblings for comment nodes
|
|
520
|
+
const comments = [];
|
|
521
|
+
for (let i = myIndex - 1; i >= 0; i--) {
|
|
522
|
+
const sibling = siblings[i];
|
|
523
|
+
if (sibling.type === 'comment') {
|
|
524
|
+
comments.unshift(sibling.text);
|
|
525
|
+
}
|
|
526
|
+
else if (sibling.isNamed) {
|
|
527
|
+
// Stop at the first non-comment, named sibling
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
530
|
+
// Continue past non-named tokens (whitespace, semicolons, etc.)
|
|
531
|
+
}
|
|
532
|
+
if (comments.length === 0)
|
|
533
|
+
return null;
|
|
534
|
+
const text = comments.join('\n').trim();
|
|
535
|
+
return this.cleanCommentText(text);
|
|
536
|
+
}
|
|
537
|
+
/** Strip comment syntax markers. */
|
|
538
|
+
cleanCommentText(comment) {
|
|
539
|
+
// Block comments: /* ... */ or /** ... */
|
|
540
|
+
if (comment.startsWith('/*')) {
|
|
541
|
+
let inner = comment.slice(2, -2);
|
|
542
|
+
// Strip leading asterisks
|
|
543
|
+
inner = inner
|
|
544
|
+
.split('\n')
|
|
545
|
+
.map((line) => line.replace(/^\s*\*\s?/, ''))
|
|
546
|
+
.join('\n');
|
|
547
|
+
return inner.trim();
|
|
548
|
+
}
|
|
549
|
+
// Line comments: //
|
|
550
|
+
if (comment.startsWith('//')) {
|
|
551
|
+
return comment.replace(/^\/\/\s*/, '').trim();
|
|
552
|
+
}
|
|
553
|
+
return comment.trim();
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Cyclomatic complexity: count decision points + 1.
|
|
557
|
+
* Decision points: if, for, while, do, switch_case, ternary, &&, ||.
|
|
558
|
+
*/
|
|
559
|
+
calculateCyclomaticComplexity(node) {
|
|
560
|
+
let complexity = 1;
|
|
561
|
+
this.walkRaw(node, (child) => {
|
|
562
|
+
if (child === node)
|
|
563
|
+
return; // don't count the root node itself
|
|
564
|
+
switch (child.type) {
|
|
565
|
+
case 'if_statement':
|
|
566
|
+
case 'for_statement':
|
|
567
|
+
case 'for_in_statement':
|
|
568
|
+
case 'while_statement':
|
|
569
|
+
case 'do_statement':
|
|
570
|
+
case 'switch_case':
|
|
571
|
+
case 'ternary_expression':
|
|
572
|
+
case 'catch_clause':
|
|
573
|
+
complexity++;
|
|
574
|
+
break;
|
|
575
|
+
case 'binary_expression': {
|
|
576
|
+
// && and || create branching paths
|
|
577
|
+
const op = this.getOperator(child);
|
|
578
|
+
if (op === '&&' || op === '||') {
|
|
579
|
+
complexity++;
|
|
580
|
+
}
|
|
581
|
+
break;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
return complexity;
|
|
586
|
+
}
|
|
587
|
+
/** Get the operator text from a binary expression node. */
|
|
588
|
+
getOperator(node) {
|
|
589
|
+
for (const child of node.children) {
|
|
590
|
+
if (!child.isNamed &&
|
|
591
|
+
['&&', '||', '+', '-', '*', '/', '%', '===', '!==', '==', '!='].includes(child.type)) {
|
|
592
|
+
return child.type;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
// -- Extraction helpers ----------------------------------------------------
|
|
598
|
+
buildFunctionInfo(node, sourceCode) {
|
|
599
|
+
const name = this.extractName(node);
|
|
600
|
+
if (name === null && node.type !== 'arrow_function')
|
|
601
|
+
return null;
|
|
602
|
+
const isAsync = this.hasModifier(node, 'async');
|
|
603
|
+
const isMethod = node.type === 'method_definition';
|
|
604
|
+
const isExported = this.isNodeExported(node);
|
|
605
|
+
const returnType = this.extractTypeAnnotation(node);
|
|
606
|
+
const jsDoc = this.extractDocumentation(node);
|
|
607
|
+
const className = isMethod ? this.getEnclosingClassName(node) : undefined;
|
|
608
|
+
const parameters = this.extractParameters(node, sourceCode);
|
|
609
|
+
return {
|
|
610
|
+
name: name ?? '<anonymous>',
|
|
611
|
+
location: toSourceLocation(node),
|
|
612
|
+
parameters,
|
|
613
|
+
returnType: returnType ?? undefined,
|
|
614
|
+
isAsync,
|
|
615
|
+
isExported,
|
|
616
|
+
isMethod,
|
|
617
|
+
className,
|
|
618
|
+
jsDoc: jsDoc ?? undefined,
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
buildClassInfo(node, sourceCode) {
|
|
622
|
+
const name = this.extractName(node);
|
|
623
|
+
if (!name)
|
|
624
|
+
return null;
|
|
625
|
+
const isAbstract = this.hasModifier(node, 'abstract');
|
|
626
|
+
const isExported = this.isNodeExported(node);
|
|
627
|
+
const jsDoc = this.extractDocumentation(node);
|
|
628
|
+
// Extract extends
|
|
629
|
+
let extendsName;
|
|
630
|
+
const extendsClause = node.childForFieldName?.('extends');
|
|
631
|
+
if (extendsClause) {
|
|
632
|
+
const firstTypeChild = extendsClause.namedChildren[0];
|
|
633
|
+
if (firstTypeChild) {
|
|
634
|
+
extendsName = firstTypeChild.text;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
// Extract implements
|
|
638
|
+
let implementsList;
|
|
639
|
+
const implementsClause = node.childForFieldName?.('implements');
|
|
640
|
+
if (implementsClause) {
|
|
641
|
+
implementsList = implementsClause.namedChildren.map((c) => c.text);
|
|
642
|
+
}
|
|
643
|
+
// Extract methods
|
|
644
|
+
const classBody = node.childForFieldName?.('body');
|
|
645
|
+
const methods = [];
|
|
646
|
+
const properties = [];
|
|
647
|
+
if (classBody) {
|
|
648
|
+
for (const member of classBody.namedChildren) {
|
|
649
|
+
if (member.type === 'method_definition') {
|
|
650
|
+
const fnInfo = this.buildFunctionInfo(member, sourceCode);
|
|
651
|
+
if (fnInfo) {
|
|
652
|
+
fnInfo.className = name;
|
|
653
|
+
fnInfo.isMethod = true;
|
|
654
|
+
methods.push(fnInfo);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
else if (member.type === 'public_field_definition' ||
|
|
658
|
+
member.type === 'field_definition') {
|
|
659
|
+
const propInfo = this.buildPropertyInfo(member);
|
|
660
|
+
if (propInfo)
|
|
661
|
+
properties.push(propInfo);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return {
|
|
666
|
+
name,
|
|
667
|
+
location: toSourceLocation(node),
|
|
668
|
+
methods,
|
|
669
|
+
properties,
|
|
670
|
+
extends: extendsName,
|
|
671
|
+
implements: implementsList,
|
|
672
|
+
isAbstract,
|
|
673
|
+
isExported,
|
|
674
|
+
jsDoc: jsDoc ?? undefined,
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
buildPropertyInfo(node) {
|
|
678
|
+
const name = this.extractName(node);
|
|
679
|
+
if (!name)
|
|
680
|
+
return null;
|
|
681
|
+
let visibility;
|
|
682
|
+
if (this.hasModifier(node, 'private'))
|
|
683
|
+
visibility = 'private';
|
|
684
|
+
else if (this.hasModifier(node, 'protected'))
|
|
685
|
+
visibility = 'protected';
|
|
686
|
+
else if (this.hasModifier(node, 'public'))
|
|
687
|
+
visibility = 'public';
|
|
688
|
+
const isStatic = this.hasModifier(node, 'static');
|
|
689
|
+
const isReadonly = this.hasModifier(node, 'readonly');
|
|
690
|
+
const type = this.extractTypeAnnotation(node);
|
|
691
|
+
return {
|
|
692
|
+
name,
|
|
693
|
+
type: type ?? undefined,
|
|
694
|
+
visibility,
|
|
695
|
+
isStatic,
|
|
696
|
+
isReadonly,
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
buildImportInfo(node, _sourceCode) {
|
|
700
|
+
// import source
|
|
701
|
+
const sourceNode = node.childForFieldName?.('source');
|
|
702
|
+
if (!sourceNode)
|
|
703
|
+
return null;
|
|
704
|
+
const source = sourceNode.text.slice(1, -1); // strip quotes
|
|
705
|
+
const specifiers = [];
|
|
706
|
+
// import defaultExport from 'module'
|
|
707
|
+
// import * as namespace from 'module'
|
|
708
|
+
// import { named } from 'module'
|
|
709
|
+
for (const child of node.namedChildren) {
|
|
710
|
+
if (child.type === 'import_specifier') {
|
|
711
|
+
const nameNode = child.childForFieldName?.('name');
|
|
712
|
+
const aliasNode = child.childForFieldName?.('alias');
|
|
713
|
+
if (nameNode) {
|
|
714
|
+
specifiers.push({
|
|
715
|
+
name: nameNode.text,
|
|
716
|
+
alias: aliasNode?.text,
|
|
717
|
+
isDefault: false,
|
|
718
|
+
isNamespace: false,
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
else if (child.type === 'namespace_import') {
|
|
723
|
+
const nameNode = child.childForFieldName?.('name');
|
|
724
|
+
if (nameNode) {
|
|
725
|
+
specifiers.push({
|
|
726
|
+
name: nameNode.text,
|
|
727
|
+
isDefault: false,
|
|
728
|
+
isNamespace: true,
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
else if (child.type === 'import') {
|
|
733
|
+
// default import: `import foo from '...'`
|
|
734
|
+
const nameNode = node.childForFieldName?.('name');
|
|
735
|
+
if (nameNode) {
|
|
736
|
+
specifiers.push({
|
|
737
|
+
name: nameNode.text,
|
|
738
|
+
isDefault: true,
|
|
739
|
+
isNamespace: false,
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return {
|
|
745
|
+
source,
|
|
746
|
+
specifiers,
|
|
747
|
+
location: toSourceLocation(node),
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
buildExportInfo(node, _sourceCode) {
|
|
751
|
+
// export default <expression>
|
|
752
|
+
const isDefault = this.hasChild(node, 'default');
|
|
753
|
+
// export { foo, bar } [from '...']
|
|
754
|
+
const clause = node.childForFieldName?.('clause');
|
|
755
|
+
if (clause?.type === 'export_clause') {
|
|
756
|
+
const sourceNode = node.childForFieldName?.('source');
|
|
757
|
+
const exports = [];
|
|
758
|
+
for (const spec of clause.namedChildren) {
|
|
759
|
+
if (spec.type === 'export_specifier') {
|
|
760
|
+
const nameNode = spec.childForFieldName?.('name');
|
|
761
|
+
if (nameNode) {
|
|
762
|
+
exports.push({
|
|
763
|
+
name: nameNode.text,
|
|
764
|
+
location: toSourceLocation(nameNode),
|
|
765
|
+
isDefault: false,
|
|
766
|
+
source: sourceNode?.text.slice(1, -1),
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
// Return first (handled by caller iterating export_statement nodes)
|
|
772
|
+
return exports[0] ?? null;
|
|
773
|
+
}
|
|
774
|
+
// export function/class/const/let/var name
|
|
775
|
+
const declaration = this.findFirstNamedChild(node, [
|
|
776
|
+
'function_declaration',
|
|
777
|
+
'class_declaration',
|
|
778
|
+
'lexical_declaration',
|
|
779
|
+
'variable_declaration',
|
|
780
|
+
]);
|
|
781
|
+
if (declaration) {
|
|
782
|
+
const name = this.extractName(declaration);
|
|
783
|
+
if (name) {
|
|
784
|
+
return {
|
|
785
|
+
name,
|
|
786
|
+
location: toSourceLocation(node),
|
|
787
|
+
isDefault,
|
|
788
|
+
source: node.childForFieldName?.('source')?.text.slice(1, -1),
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
// export default <name> (no declaration)
|
|
793
|
+
if (isDefault) {
|
|
794
|
+
const value = node.childForFieldName?.('value');
|
|
795
|
+
if (value) {
|
|
796
|
+
return {
|
|
797
|
+
name: value.text,
|
|
798
|
+
location: toSourceLocation(node),
|
|
799
|
+
isDefault: true,
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
return null;
|
|
804
|
+
}
|
|
805
|
+
buildInterfaceInfo(node, _sourceCode) {
|
|
806
|
+
const name = this.extractName(node);
|
|
807
|
+
if (!name)
|
|
808
|
+
return null;
|
|
809
|
+
const isExported = this.isNodeExported(node);
|
|
810
|
+
// Extends
|
|
811
|
+
let extendsList;
|
|
812
|
+
const extendsClause = node.childForFieldName?.('extends');
|
|
813
|
+
if (extendsClause) {
|
|
814
|
+
extendsList = extendsClause.namedChildren.map((c) => c.text);
|
|
815
|
+
}
|
|
816
|
+
// Members
|
|
817
|
+
const body = node.childForFieldName?.('body');
|
|
818
|
+
const members = [];
|
|
819
|
+
if (body) {
|
|
820
|
+
for (const member of body.namedChildren) {
|
|
821
|
+
if (member.type === 'method_signature') {
|
|
822
|
+
const memberName = member.childForFieldName?.('name')?.text;
|
|
823
|
+
if (memberName) {
|
|
824
|
+
members.push({
|
|
825
|
+
name: memberName,
|
|
826
|
+
type: 'method',
|
|
827
|
+
location: toSourceLocation(member),
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
else if (member.type === 'property_signature') {
|
|
832
|
+
const memberName = member.childForFieldName?.('name')?.text;
|
|
833
|
+
if (memberName) {
|
|
834
|
+
members.push({
|
|
835
|
+
name: memberName,
|
|
836
|
+
type: 'property',
|
|
837
|
+
location: toSourceLocation(member),
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
return {
|
|
844
|
+
name,
|
|
845
|
+
location: toSourceLocation(node),
|
|
846
|
+
members,
|
|
847
|
+
extends: extendsList,
|
|
848
|
+
isExported,
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
// -- Parameter extraction --------------------------------------------------
|
|
852
|
+
extractParameters(node, sourceCode) {
|
|
853
|
+
const params = [];
|
|
854
|
+
const formalParams = node.childForFieldName?.('parameters');
|
|
855
|
+
if (!formalParams)
|
|
856
|
+
return params;
|
|
857
|
+
for (const child of formalParams.namedChildren) {
|
|
858
|
+
if (child.type === 'required_parameter' || child.type === 'optional_parameter') {
|
|
859
|
+
const paramNode = child.childForFieldName?.('pattern');
|
|
860
|
+
const name = paramNode?.text ?? child.firstNamedChild?.text;
|
|
861
|
+
const isOptional = child.type === 'optional_parameter';
|
|
862
|
+
const typeNode = child.childForFieldName?.('type');
|
|
863
|
+
let defaultValue;
|
|
864
|
+
// Check for a default value
|
|
865
|
+
const valueNode = child.childForFieldName?.('value');
|
|
866
|
+
if (valueNode) {
|
|
867
|
+
defaultValue = valueNode.text;
|
|
868
|
+
}
|
|
869
|
+
if (name) {
|
|
870
|
+
params.push({
|
|
871
|
+
name,
|
|
872
|
+
type: typeNode?.text.replace(/^:\s*/, ''),
|
|
873
|
+
optional: isOptional,
|
|
874
|
+
defaultValue,
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return params;
|
|
880
|
+
}
|
|
881
|
+
// -- Context helpers -------------------------------------------------------
|
|
882
|
+
/** Check if a node has an `async` modifier keyword. */
|
|
883
|
+
hasModifier(node, modifier) {
|
|
884
|
+
for (const child of node.children) {
|
|
885
|
+
if (!child.isNamed && child.type === modifier)
|
|
886
|
+
return true;
|
|
887
|
+
// tree-sitter may represent modifiers as named decorator/modifier nodes
|
|
888
|
+
if (child.isNamed &&
|
|
889
|
+
(child.type === modifier ||
|
|
890
|
+
child.type === 'accessibility_modifier' ||
|
|
891
|
+
child.type === 'override_modifier' ||
|
|
892
|
+
child.type === 'readonly' ||
|
|
893
|
+
child.type === 'static' ||
|
|
894
|
+
child.type === 'abstract')) {
|
|
895
|
+
if (child.text === modifier)
|
|
896
|
+
return true;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
return false;
|
|
900
|
+
}
|
|
901
|
+
/** Check if a node is under an export_statement. */
|
|
902
|
+
isNodeExported(node) {
|
|
903
|
+
const parent = node.parent;
|
|
904
|
+
if (parent?.type === 'export_statement')
|
|
905
|
+
return true;
|
|
906
|
+
// Also check for `export` keyword on the node itself (TS module syntax)
|
|
907
|
+
for (const child of node.children) {
|
|
908
|
+
if (!child.isNamed && child.type === 'export')
|
|
909
|
+
return true;
|
|
910
|
+
}
|
|
911
|
+
return false;
|
|
912
|
+
}
|
|
913
|
+
/** Get the enclosing class name for a method. */
|
|
914
|
+
getEnclosingClassName(node) {
|
|
915
|
+
let current = node.parent;
|
|
916
|
+
while (current) {
|
|
917
|
+
if (current.type === 'class_declaration') {
|
|
918
|
+
return this.extractName(current) ?? undefined;
|
|
919
|
+
}
|
|
920
|
+
current = current.parent ?? null;
|
|
921
|
+
}
|
|
922
|
+
return undefined;
|
|
923
|
+
}
|
|
924
|
+
// -- Tree traversal helpers ------------------------------------------------
|
|
925
|
+
/** Find first child of node matching one of the given types. */
|
|
926
|
+
getChildByType(node, type) {
|
|
927
|
+
for (const child of node.children) {
|
|
928
|
+
if (child.type === type)
|
|
929
|
+
return child;
|
|
930
|
+
}
|
|
931
|
+
return null;
|
|
932
|
+
}
|
|
933
|
+
/** Find the first named child matching one of the given types. */
|
|
934
|
+
findFirstNamedChild(node, types) {
|
|
935
|
+
const typeSet = Array.isArray(types) ? new Set(types) : new Set([types]);
|
|
936
|
+
for (const child of node.namedChildren) {
|
|
937
|
+
if (typeSet.has(child.type))
|
|
938
|
+
return child;
|
|
939
|
+
}
|
|
940
|
+
return null;
|
|
941
|
+
}
|
|
942
|
+
/** Check if node has a direct child (named or anonymous) with the given text. */
|
|
943
|
+
hasChild(node, text) {
|
|
944
|
+
for (const child of node.children) {
|
|
945
|
+
if (child.text === text || child.type === text)
|
|
946
|
+
return true;
|
|
947
|
+
}
|
|
948
|
+
return false;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
//# sourceMappingURL=TreeSitterTypeScriptAdapter.js.map
|