projscan 0.10.0 → 0.12.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 +56 -19
- package/dist/analyzers/unusedDependencyCheck.js +69 -17
- package/dist/analyzers/unusedDependencyCheck.js.map +1 -1
- package/dist/cli/_shared.d.ts +16 -0
- package/dist/cli/_shared.js +210 -0
- package/dist/cli/_shared.js.map +1 -0
- package/dist/cli/commands/analyze.d.ts +1 -0
- package/dist/cli/commands/analyze.js +87 -0
- package/dist/cli/commands/analyze.js.map +1 -0
- package/dist/cli/commands/audit.d.ts +1 -0
- package/dist/cli/commands/audit.js +47 -0
- package/dist/cli/commands/audit.js.map +1 -0
- package/dist/cli/commands/badge.d.ts +1 -0
- package/dist/cli/commands/badge.js +45 -0
- package/dist/cli/commands/badge.js.map +1 -0
- package/dist/cli/commands/ci.d.ts +1 -0
- package/dist/cli/commands/ci.js +57 -0
- package/dist/cli/commands/ci.js.map +1 -0
- package/dist/cli/commands/coupling.d.ts +1 -0
- package/dist/cli/commands/coupling.js +83 -0
- package/dist/cli/commands/coupling.js.map +1 -0
- package/dist/cli/commands/coverage.d.ts +1 -0
- package/dist/cli/commands/coverage.js +63 -0
- package/dist/cli/commands/coverage.js.map +1 -0
- package/dist/cli/commands/dependencies.d.ts +1 -0
- package/dist/cli/commands/dependencies.js +45 -0
- package/dist/cli/commands/dependencies.js.map +1 -0
- package/dist/cli/commands/diagram.d.ts +1 -0
- package/dist/cli/commands/diagram.js +45 -0
- package/dist/cli/commands/diagram.js.map +1 -0
- package/dist/cli/commands/diff.d.ts +1 -0
- package/dist/cli/commands/diff.js +70 -0
- package/dist/cli/commands/diff.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +1 -0
- package/dist/cli/commands/doctor.js +62 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/explain.d.ts +1 -0
- package/dist/cli/commands/explain.js +42 -0
- package/dist/cli/commands/explain.js.map +1 -0
- package/dist/cli/commands/file.d.ts +1 -0
- package/dist/cli/commands/file.js +45 -0
- package/dist/cli/commands/file.js.map +1 -0
- package/dist/cli/commands/fix.d.ts +1 -0
- package/dist/cli/commands/fix.js +70 -0
- package/dist/cli/commands/fix.js.map +1 -0
- package/dist/cli/commands/help.d.ts +1 -0
- package/dist/cli/commands/help.js +11 -0
- package/dist/cli/commands/help.js.map +1 -0
- package/dist/cli/commands/hotspots.d.ts +1 -0
- package/dist/cli/commands/hotspots.js +74 -0
- package/dist/cli/commands/hotspots.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +1 -0
- package/dist/cli/commands/mcp.js +21 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/commands/outdated.d.ts +1 -0
- package/dist/cli/commands/outdated.js +51 -0
- package/dist/cli/commands/outdated.js.map +1 -0
- package/dist/cli/commands/prDiff.d.ts +1 -0
- package/dist/cli/commands/prDiff.js +59 -0
- package/dist/cli/commands/prDiff.js.map +1 -0
- package/dist/cli/commands/search.d.ts +1 -0
- package/dist/cli/commands/search.js +233 -0
- package/dist/cli/commands/search.js.map +1 -0
- package/dist/cli/commands/structure.d.ts +1 -0
- package/dist/cli/commands/structure.js +58 -0
- package/dist/cli/commands/structure.js.map +1 -0
- package/dist/cli/commands/upgrade.d.ts +1 -0
- package/dist/cli/commands/upgrade.js +44 -0
- package/dist/cli/commands/upgrade.js.map +1 -0
- package/dist/cli/commands/workspaces.d.ts +1 -0
- package/dist/cli/commands/workspaces.js +35 -0
- package/dist/cli/commands/workspaces.js.map +1 -0
- package/dist/cli/index.js +45 -1132
- package/dist/cli/index.js.map +1 -1
- package/dist/core/ast.d.ts +2 -0
- package/dist/core/ast.js +35 -2
- package/dist/core/ast.js.map +1 -1
- package/dist/core/codeGraph.d.ts +2 -0
- package/dist/core/codeGraph.js +2 -0
- package/dist/core/codeGraph.js.map +1 -1
- package/dist/core/couplingAnalyzer.d.ts +18 -0
- package/dist/core/couplingAnalyzer.js +174 -0
- package/dist/core/couplingAnalyzer.js.map +1 -0
- package/dist/core/fileInspector.d.ts +1 -1
- package/dist/core/fileInspector.js +31 -1
- package/dist/core/fileInspector.js.map +1 -1
- package/dist/core/hotspotAnalyzer.d.ts +13 -0
- package/dist/core/hotspotAnalyzer.js +29 -6
- package/dist/core/hotspotAnalyzer.js.map +1 -1
- package/dist/core/indexCache.js +6 -3
- package/dist/core/indexCache.js.map +1 -1
- package/dist/core/languages/LanguageAdapter.d.ts +1 -1
- package/dist/core/languages/goAdapter.d.ts +2 -0
- package/dist/core/languages/goAdapter.js +138 -0
- package/dist/core/languages/goAdapter.js.map +1 -0
- package/dist/core/languages/goCallSites.d.ts +20 -0
- package/dist/core/languages/goCallSites.js +42 -0
- package/dist/core/languages/goCallSites.js.map +1 -0
- package/dist/core/languages/goCyclomatic.d.ts +21 -0
- package/dist/core/languages/goCyclomatic.js +55 -0
- package/dist/core/languages/goCyclomatic.js.map +1 -0
- package/dist/core/languages/goExports.d.ts +26 -0
- package/dist/core/languages/goExports.js +89 -0
- package/dist/core/languages/goExports.js.map +1 -0
- package/dist/core/languages/goImports.d.ts +26 -0
- package/dist/core/languages/goImports.js +64 -0
- package/dist/core/languages/goImports.js.map +1 -0
- package/dist/core/languages/goManifests.d.ts +19 -0
- package/dist/core/languages/goManifests.js +56 -0
- package/dist/core/languages/goManifests.js.map +1 -0
- package/dist/core/languages/javaAdapter.d.ts +2 -0
- package/dist/core/languages/javaAdapter.js +148 -0
- package/dist/core/languages/javaAdapter.js.map +1 -0
- package/dist/core/languages/javaCallSites.d.ts +16 -0
- package/dist/core/languages/javaCallSites.js +45 -0
- package/dist/core/languages/javaCallSites.js.map +1 -0
- package/dist/core/languages/javaCyclomatic.d.ts +21 -0
- package/dist/core/languages/javaCyclomatic.js +49 -0
- package/dist/core/languages/javaCyclomatic.js.map +1 -0
- package/dist/core/languages/javaExports.d.ts +25 -0
- package/dist/core/languages/javaExports.js +80 -0
- package/dist/core/languages/javaExports.js.map +1 -0
- package/dist/core/languages/javaImports.d.ts +25 -0
- package/dist/core/languages/javaImports.js +49 -0
- package/dist/core/languages/javaImports.js.map +1 -0
- package/dist/core/languages/javaManifests.d.ts +25 -0
- package/dist/core/languages/javaManifests.js +86 -0
- package/dist/core/languages/javaManifests.js.map +1 -0
- package/dist/core/languages/pythonAdapter.js +8 -1
- package/dist/core/languages/pythonAdapter.js.map +1 -1
- package/dist/core/languages/pythonCallSites.d.ts +19 -0
- package/dist/core/languages/pythonCallSites.js +40 -0
- package/dist/core/languages/pythonCallSites.js.map +1 -0
- package/dist/core/languages/pythonCyclomatic.d.ts +18 -0
- package/dist/core/languages/pythonCyclomatic.js +45 -0
- package/dist/core/languages/pythonCyclomatic.js.map +1 -0
- package/dist/core/languages/registry.js +4 -1
- package/dist/core/languages/registry.js.map +1 -1
- package/dist/core/languages/rubyAdapter.d.ts +2 -0
- package/dist/core/languages/rubyAdapter.js +131 -0
- package/dist/core/languages/rubyAdapter.js.map +1 -0
- package/dist/core/languages/rubyCallSites.d.ts +16 -0
- package/dist/core/languages/rubyCallSites.js +34 -0
- package/dist/core/languages/rubyCallSites.js.map +1 -0
- package/dist/core/languages/rubyCyclomatic.d.ts +19 -0
- package/dist/core/languages/rubyCyclomatic.js +47 -0
- package/dist/core/languages/rubyCyclomatic.js.map +1 -0
- package/dist/core/languages/rubyExports.d.ts +24 -0
- package/dist/core/languages/rubyExports.js +53 -0
- package/dist/core/languages/rubyExports.js.map +1 -0
- package/dist/core/languages/rubyImports.d.ts +12 -0
- package/dist/core/languages/rubyImports.js +75 -0
- package/dist/core/languages/rubyImports.js.map +1 -0
- package/dist/core/languages/rubyManifests.d.ts +20 -0
- package/dist/core/languages/rubyManifests.js +55 -0
- package/dist/core/languages/rubyManifests.js.map +1 -0
- package/dist/core/languages/treeSitterLoader.js +4 -1
- package/dist/core/languages/treeSitterLoader.js.map +1 -1
- package/dist/core/monorepo.d.ts +20 -0
- package/dist/core/monorepo.js +270 -0
- package/dist/core/monorepo.js.map +1 -0
- package/dist/core/outdatedDetector.d.ts +13 -2
- package/dist/core/outdatedDetector.js +86 -16
- package/dist/core/outdatedDetector.js.map +1 -1
- package/dist/core/prDiff.d.ts +43 -0
- package/dist/core/prDiff.js +298 -0
- package/dist/core/prDiff.js.map +1 -0
- package/dist/core/telemetry.d.ts +90 -0
- package/dist/core/telemetry.js +199 -0
- package/dist/core/telemetry.js.map +1 -0
- package/dist/grammars/tree-sitter-go.wasm +0 -0
- package/dist/grammars/tree-sitter-java.wasm +0 -0
- package/dist/grammars/tree-sitter-ruby.wasm +0 -0
- package/dist/mcp/tools/_shared.d.ts +24 -0
- package/dist/mcp/tools/_shared.js +82 -0
- package/dist/mcp/tools/_shared.js.map +1 -0
- package/dist/mcp/tools/analyze.d.ts +2 -0
- package/dist/mcp/tools/analyze.js +55 -0
- package/dist/mcp/tools/analyze.js.map +1 -0
- package/dist/mcp/tools/audit.d.ts +2 -0
- package/dist/mcp/tools/audit.js +32 -0
- package/dist/mcp/tools/audit.js.map +1 -0
- package/dist/mcp/tools/coupling.d.ts +2 -0
- package/dist/mcp/tools/coupling.js +67 -0
- package/dist/mcp/tools/coupling.js.map +1 -0
- package/dist/mcp/tools/coverage.d.ts +2 -0
- package/dist/mcp/tools/coverage.js +53 -0
- package/dist/mcp/tools/coverage.js.map +1 -0
- package/dist/mcp/tools/dependencies.d.ts +2 -0
- package/dist/mcp/tools/dependencies.js +16 -0
- package/dist/mcp/tools/dependencies.js.map +1 -0
- package/dist/mcp/tools/doctor.d.ts +2 -0
- package/dist/mcp/tools/doctor.js +30 -0
- package/dist/mcp/tools/doctor.js.map +1 -0
- package/dist/mcp/tools/explain.d.ts +2 -0
- package/dist/mcp/tools/explain.js +30 -0
- package/dist/mcp/tools/explain.js.map +1 -0
- package/dist/mcp/tools/file.d.ts +2 -0
- package/dist/mcp/tools/file.js +22 -0
- package/dist/mcp/tools/file.js.map +1 -0
- package/dist/mcp/tools/graph.d.ts +2 -0
- package/dist/mcp/tools/graph.js +69 -0
- package/dist/mcp/tools/graph.js.map +1 -0
- package/dist/mcp/tools/hotspots.d.ts +2 -0
- package/dist/mcp/tools/hotspots.js +64 -0
- package/dist/mcp/tools/hotspots.js.map +1 -0
- package/dist/mcp/tools/outdated.d.ts +2 -0
- package/dist/mcp/tools/outdated.js +36 -0
- package/dist/mcp/tools/outdated.js.map +1 -0
- package/dist/mcp/tools/prDiff.d.ts +2 -0
- package/dist/mcp/tools/prDiff.js +38 -0
- package/dist/mcp/tools/prDiff.js.map +1 -0
- package/dist/mcp/tools/search.d.ts +2 -0
- package/dist/mcp/tools/search.js +167 -0
- package/dist/mcp/tools/search.js.map +1 -0
- package/dist/mcp/tools/structure.d.ts +2 -0
- package/dist/mcp/tools/structure.js +34 -0
- package/dist/mcp/tools/structure.js.map +1 -0
- package/dist/mcp/tools/upgrade.d.ts +2 -0
- package/dist/mcp/tools/upgrade.js +38 -0
- package/dist/mcp/tools/upgrade.js.map +1 -0
- package/dist/mcp/tools/workspaces.d.ts +2 -0
- package/dist/mcp/tools/workspaces.js +13 -0
- package/dist/mcp/tools/workspaces.js.map +1 -0
- package/dist/mcp/tools.d.ts +12 -6
- package/dist/mcp/tools.js +40 -605
- package/dist/mcp/tools.js.map +1 -1
- package/dist/reporters/consoleReporter.d.ts +4 -1
- package/dist/reporters/consoleReporter.js +113 -0
- package/dist/reporters/consoleReporter.js.map +1 -1
- package/dist/reporters/jsonReporter.d.ts +4 -1
- package/dist/reporters/jsonReporter.js +9 -0
- package/dist/reporters/jsonReporter.js.map +1 -1
- package/dist/reporters/markdownReporter.d.ts +4 -1
- package/dist/reporters/markdownReporter.js +103 -3
- package/dist/reporters/markdownReporter.js.map +1 -1
- package/dist/tool-manifest.json +358 -0
- package/dist/types.d.ts +115 -0
- package/dist/utils/cache.d.ts +3 -0
- package/dist/utils/cache.js +51 -0
- package/dist/utils/cache.js.map +1 -0
- package/package.json +7 -3
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract package-level "exports" from a Java AST.
|
|
3
|
+
*
|
|
4
|
+
* Java visibility rules:
|
|
5
|
+
* public - visible everywhere
|
|
6
|
+
* protected - visible to package + subclasses
|
|
7
|
+
* (none) - package-private
|
|
8
|
+
* private - file-only
|
|
9
|
+
*
|
|
10
|
+
* We treat `public` as the export surface (the typical "API") and skip
|
|
11
|
+
* everything else. Top-level declarations only - nested types are part of
|
|
12
|
+
* their enclosing class's surface, not separate exports.
|
|
13
|
+
*/
|
|
14
|
+
export function extractJavaExports(root) {
|
|
15
|
+
const out = [];
|
|
16
|
+
for (const child of root.namedChildren) {
|
|
17
|
+
visitTopLevel(child, out);
|
|
18
|
+
}
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
function visitTopLevel(node, out) {
|
|
22
|
+
switch (node.type) {
|
|
23
|
+
case 'class_declaration':
|
|
24
|
+
addPublic(node, 'class', out);
|
|
25
|
+
return;
|
|
26
|
+
case 'interface_declaration':
|
|
27
|
+
addPublic(node, 'interface', out);
|
|
28
|
+
return;
|
|
29
|
+
case 'enum_declaration':
|
|
30
|
+
addPublic(node, 'enum', out);
|
|
31
|
+
return;
|
|
32
|
+
case 'record_declaration':
|
|
33
|
+
// Java 14+ records - treat as a class for our purposes.
|
|
34
|
+
addPublic(node, 'class', out);
|
|
35
|
+
return;
|
|
36
|
+
case 'annotation_type_declaration':
|
|
37
|
+
addPublic(node, 'type', out);
|
|
38
|
+
return;
|
|
39
|
+
default:
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function addPublic(node, kind, out) {
|
|
44
|
+
if (!hasModifier(node, 'public'))
|
|
45
|
+
return;
|
|
46
|
+
const nameNode = node.childForFieldName?.('name') ?? findChild(node, 'identifier');
|
|
47
|
+
if (!nameNode)
|
|
48
|
+
return;
|
|
49
|
+
out.push({
|
|
50
|
+
name: nameNode.text,
|
|
51
|
+
kind,
|
|
52
|
+
typeOnly: false,
|
|
53
|
+
line: node.startPosition.row + 1,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function hasModifier(node, mod) {
|
|
57
|
+
const modsNode = findChild(node, 'modifiers');
|
|
58
|
+
if (!modsNode)
|
|
59
|
+
return false;
|
|
60
|
+
// tree-sitter-java emits keyword modifiers (`public`, `private`, etc.) as
|
|
61
|
+
// anonymous children - they're not in `namedChildren`. Walk the full child
|
|
62
|
+
// list and match by token type or text.
|
|
63
|
+
for (const c of modsNode.namedChildren) {
|
|
64
|
+
if (c.type === mod || c.text === mod)
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
// Anonymous-keyword fallback: scan tokens via text match. Modifiers contain
|
|
68
|
+
// only the keyword tokens plus annotations; annotations start with `@` so
|
|
69
|
+
// exact-match against `mod` is unambiguous.
|
|
70
|
+
const tokens = modsNode.text.split(/\s+/);
|
|
71
|
+
return tokens.includes(mod);
|
|
72
|
+
}
|
|
73
|
+
function findChild(node, type) {
|
|
74
|
+
for (const c of node.namedChildren) {
|
|
75
|
+
if (c.type === type)
|
|
76
|
+
return c;
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=javaExports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"javaExports.js","sourceRoot":"","sources":["../../../src/core/languages/javaExports.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,GAAG,GAAgB,EAAE,CAAC;IAC5B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACvC,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,aAAa,CAAC,IAAY,EAAE,GAAgB;IACnD,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,mBAAmB;YACtB,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;YAC9B,OAAO;QACT,KAAK,uBAAuB;YAC1B,SAAS,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,CAAC,CAAC;YAClC,OAAO;QACT,KAAK,kBAAkB;YACrB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;YAC7B,OAAO;QACT,KAAK,oBAAoB;YACvB,wDAAwD;YACxD,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;YAC9B,OAAO;QACT,KAAK,6BAA6B;YAChC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;YAC7B,OAAO;QACT;YACE,OAAO;IACX,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,IAAY,EAAE,IAAgB,EAAE,GAAgB;IACjE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC;QAAE,OAAO;IACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IACnF,IAAI,CAAC,QAAQ;QAAE,OAAO;IACtB,GAAG,CAAC,IAAI,CAAC;QACP,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,IAAI;QACJ,QAAQ,EAAE,KAAK;QACf,IAAI,EAAE,IAAI,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC;KACjC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,WAAW,CAAC,IAAY,EAAE,GAAW;IAC5C,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAC9C,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,0EAA0E;IAC1E,2EAA2E;IAC3E,wCAAwC;IACxC,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,aAAa,EAAE,CAAC;QACvC,IAAI,CAAC,CAAC,IAAI,KAAK,GAAG,IAAI,CAAC,CAAC,IAAI,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;IACpD,CAAC;IACD,4EAA4E;IAC5E,0EAA0E;IAC1E,4CAA4C;IAC5C,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1C,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,SAAS,CAAC,IAAY,EAAE,IAAY;IAC3C,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACnC,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI;YAAE,OAAO,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AstImport } from '../ast.js';
|
|
2
|
+
interface TsNode {
|
|
3
|
+
type: string;
|
|
4
|
+
text: string;
|
|
5
|
+
startPosition: {
|
|
6
|
+
row: number;
|
|
7
|
+
};
|
|
8
|
+
namedChildren: TsNode[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Extract import declarations from a tree-sitter-java AST.
|
|
12
|
+
*
|
|
13
|
+
* Java imports come in three shapes:
|
|
14
|
+
* import java.util.List;
|
|
15
|
+
* import java.util.*; // wildcard
|
|
16
|
+
* import static foo.Bar.X; // static
|
|
17
|
+
*
|
|
18
|
+
* The grammar emits `import_declaration` at the top level. The dotted path is
|
|
19
|
+
* captured as `scoped_identifier`; a wildcard appears as an `asterisk` child.
|
|
20
|
+
* We record the package portion (everything before the last segment for type
|
|
21
|
+
* imports, the whole path for wildcards/static imports) as the import source -
|
|
22
|
+
* matching how a resolver will look up the target.
|
|
23
|
+
*/
|
|
24
|
+
export declare function extractJavaImports(root: TsNode): AstImport[];
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract import declarations from a tree-sitter-java AST.
|
|
3
|
+
*
|
|
4
|
+
* Java imports come in three shapes:
|
|
5
|
+
* import java.util.List;
|
|
6
|
+
* import java.util.*; // wildcard
|
|
7
|
+
* import static foo.Bar.X; // static
|
|
8
|
+
*
|
|
9
|
+
* The grammar emits `import_declaration` at the top level. The dotted path is
|
|
10
|
+
* captured as `scoped_identifier`; a wildcard appears as an `asterisk` child.
|
|
11
|
+
* We record the package portion (everything before the last segment for type
|
|
12
|
+
* imports, the whole path for wildcards/static imports) as the import source -
|
|
13
|
+
* matching how a resolver will look up the target.
|
|
14
|
+
*/
|
|
15
|
+
export function extractJavaImports(root) {
|
|
16
|
+
const out = [];
|
|
17
|
+
for (const child of root.namedChildren) {
|
|
18
|
+
if (child.type === 'import_declaration') {
|
|
19
|
+
handleImport(child, out);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
function handleImport(node, out) {
|
|
25
|
+
const line = node.startPosition.row + 1;
|
|
26
|
+
// Collect the dotted path text. With wildcards the asterisk node sits next
|
|
27
|
+
// to the scoped_identifier; we keep `.*` on the source so the resolver can
|
|
28
|
+
// distinguish.
|
|
29
|
+
let path = null;
|
|
30
|
+
let wildcard = false;
|
|
31
|
+
for (const c of node.namedChildren) {
|
|
32
|
+
if (c.type === 'scoped_identifier' || c.type === 'identifier') {
|
|
33
|
+
path = c.text;
|
|
34
|
+
}
|
|
35
|
+
else if (c.type === 'asterisk') {
|
|
36
|
+
wildcard = true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!path)
|
|
40
|
+
return;
|
|
41
|
+
out.push({
|
|
42
|
+
source: wildcard ? `${path}.*` : path,
|
|
43
|
+
kind: 'static',
|
|
44
|
+
specifiers: [],
|
|
45
|
+
typeOnly: false,
|
|
46
|
+
line,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=javaImports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"javaImports.js","sourceRoot":"","sources":["../../../src/core/languages/javaImports.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,GAAG,GAAgB,EAAE,CAAC;IAC5B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACvC,IAAI,KAAK,CAAC,IAAI,KAAK,oBAAoB,EAAE,CAAC;YACxC,YAAY,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,YAAY,CAAC,IAAY,EAAE,GAAgB;IAClD,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC,CAAC;IACxC,2EAA2E;IAC3E,2EAA2E;IAC3E,eAAe;IACf,IAAI,IAAI,GAAkB,IAAI,CAAC;IAC/B,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACnC,IAAI,CAAC,CAAC,IAAI,KAAK,mBAAmB,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YAC9D,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;QAChB,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACjC,QAAQ,GAAG,IAAI,CAAC;QAClB,CAAC;IACH,CAAC;IACD,IAAI,CAAC,IAAI;QAAE,OAAO;IAClB,GAAG,CAAC,IAAI,CAAC;QACP,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI;QACrC,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,EAAE;QACd,QAAQ,EAAE,KAAK;QACf,IAAI;KACL,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { FileEntry } from '../../types.js';
|
|
2
|
+
export interface JavaProjectInfo {
|
|
3
|
+
/** Absolute paths to source roots - directories under which `package` paths resolve. */
|
|
4
|
+
sourceRoots: string[];
|
|
5
|
+
/** Build system detected, if any. Informational only. */
|
|
6
|
+
buildSystem: 'maven' | 'gradle' | 'unknown';
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Detect a Java project layout.
|
|
10
|
+
*
|
|
11
|
+
* Maven: presence of `pom.xml`. Default source roots are
|
|
12
|
+
* src/main/java/, src/test/java/.
|
|
13
|
+
* Gradle: presence of `build.gradle` or `build.gradle.kts`. Same defaults.
|
|
14
|
+
*
|
|
15
|
+
* Real projects may override the source root via Maven's <sourceDirectory>
|
|
16
|
+
* or Gradle's `sourceSets`. Parsing those out is deferred - the conventional
|
|
17
|
+
* defaults cover ≈95% of repos. Custom roots can still be picked up because
|
|
18
|
+
* `inferRootsFromFiles` walks the file list and takes any directory that
|
|
19
|
+
* contains a top-level `.java` file with a `package` declaration matching its
|
|
20
|
+
* directory path.
|
|
21
|
+
*
|
|
22
|
+
* Returns `null` only when no `.java` files exist and no build manifest is
|
|
23
|
+
* found - i.e. this is not a Java project.
|
|
24
|
+
*/
|
|
25
|
+
export declare function detectJavaProject(rootPath: string, files: FileEntry[]): Promise<JavaProjectInfo | null>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const MAVEN_DEFAULT_ROOTS = ['src/main/java', 'src/test/java'];
|
|
4
|
+
const GRADLE_DEFAULT_ROOTS = ['src/main/java', 'src/test/java'];
|
|
5
|
+
/**
|
|
6
|
+
* Detect a Java project layout.
|
|
7
|
+
*
|
|
8
|
+
* Maven: presence of `pom.xml`. Default source roots are
|
|
9
|
+
* src/main/java/, src/test/java/.
|
|
10
|
+
* Gradle: presence of `build.gradle` or `build.gradle.kts`. Same defaults.
|
|
11
|
+
*
|
|
12
|
+
* Real projects may override the source root via Maven's <sourceDirectory>
|
|
13
|
+
* or Gradle's `sourceSets`. Parsing those out is deferred - the conventional
|
|
14
|
+
* defaults cover ≈95% of repos. Custom roots can still be picked up because
|
|
15
|
+
* `inferRootsFromFiles` walks the file list and takes any directory that
|
|
16
|
+
* contains a top-level `.java` file with a `package` declaration matching its
|
|
17
|
+
* directory path.
|
|
18
|
+
*
|
|
19
|
+
* Returns `null` only when no `.java` files exist and no build manifest is
|
|
20
|
+
* found - i.e. this is not a Java project.
|
|
21
|
+
*/
|
|
22
|
+
export async function detectJavaProject(rootPath, files) {
|
|
23
|
+
const hasPom = await fileExists(path.join(rootPath, 'pom.xml'));
|
|
24
|
+
const hasGradle = (await fileExists(path.join(rootPath, 'build.gradle'))) ||
|
|
25
|
+
(await fileExists(path.join(rootPath, 'build.gradle.kts')));
|
|
26
|
+
const javaFiles = files.filter((f) => f.relativePath.endsWith('.java'));
|
|
27
|
+
if (!hasPom && !hasGradle && javaFiles.length === 0)
|
|
28
|
+
return null;
|
|
29
|
+
const buildSystem = hasPom
|
|
30
|
+
? 'maven'
|
|
31
|
+
: hasGradle
|
|
32
|
+
? 'gradle'
|
|
33
|
+
: 'unknown';
|
|
34
|
+
// Start with conventional roots (only the ones that actually exist).
|
|
35
|
+
const conventional = hasPom ? MAVEN_DEFAULT_ROOTS : GRADLE_DEFAULT_ROOTS;
|
|
36
|
+
const roots = new Set();
|
|
37
|
+
for (const r of conventional) {
|
|
38
|
+
if (await fileExists(path.join(rootPath, r))) {
|
|
39
|
+
roots.add(r);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// If neither convention exists, fall back to inferring from file layout.
|
|
43
|
+
if (roots.size === 0) {
|
|
44
|
+
const inferred = inferRootsFromFiles(javaFiles);
|
|
45
|
+
for (const r of inferred)
|
|
46
|
+
roots.add(r);
|
|
47
|
+
if (roots.size === 0)
|
|
48
|
+
roots.add('.');
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
sourceRoots: [...roots].map((r) => path.join(rootPath, r)),
|
|
52
|
+
buildSystem,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Best-effort source-root inference: for each .java file, scan its content
|
|
57
|
+
* for a `package` declaration and back out the source root by stripping the
|
|
58
|
+
* package path from the file's directory. We can't read content here cheaply
|
|
59
|
+
* (FileEntry doesn't carry it), so we approximate by looking at the deepest
|
|
60
|
+
* common prefix of `.java` directories.
|
|
61
|
+
*/
|
|
62
|
+
function inferRootsFromFiles(files) {
|
|
63
|
+
if (files.length === 0)
|
|
64
|
+
return [];
|
|
65
|
+
const dirs = new Set(files.map((f) => path.posix.dirname(f.relativePath)));
|
|
66
|
+
// Find the shortest directory - that's the most likely package root.
|
|
67
|
+
let shortest = '';
|
|
68
|
+
let shortestLen = Infinity;
|
|
69
|
+
for (const d of dirs) {
|
|
70
|
+
if (d.length < shortestLen) {
|
|
71
|
+
shortest = d;
|
|
72
|
+
shortestLen = d.length;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return [shortest || '.'];
|
|
76
|
+
}
|
|
77
|
+
async function fileExists(p) {
|
|
78
|
+
try {
|
|
79
|
+
await fs.access(p);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=javaManifests.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"javaManifests.js","sourceRoot":"","sources":["../../../src/core/languages/javaManifests.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,8 @@ import path from 'node:path';
|
|
|
2
2
|
import { createParserFor } from './treeSitterLoader.js';
|
|
3
3
|
import { extractPythonImports } from './pythonImports.js';
|
|
4
4
|
import { extractPythonExports } from './pythonExports.js';
|
|
5
|
+
import { extractPythonCyclomatic } from './pythonCyclomatic.js';
|
|
6
|
+
import { extractPythonCallSites } from './pythonCallSites.js';
|
|
5
7
|
import { detectPythonProject } from './pythonManifests.js';
|
|
6
8
|
// Pinned grammar: tree-sitter-python 0.25.0. See treeSitterLoader.ts.
|
|
7
9
|
const PY_EXTENSIONS = new Set(['.py', '.pyw', '.pyi']);
|
|
@@ -34,16 +36,20 @@ export const pythonAdapter = {
|
|
|
34
36
|
exports: [],
|
|
35
37
|
callSites: [],
|
|
36
38
|
lineCount: content ? content.split('\n').length : 0,
|
|
39
|
+
cyclomaticComplexity: 0,
|
|
37
40
|
};
|
|
38
41
|
}
|
|
39
42
|
const imports = extractPythonImports(tree.rootNode);
|
|
40
43
|
const exports = extractPythonExports(tree.rootNode);
|
|
44
|
+
const cyclomaticComplexity = extractPythonCyclomatic(tree.rootNode);
|
|
45
|
+
const callSites = extractPythonCallSites(tree.rootNode);
|
|
41
46
|
return {
|
|
42
47
|
ok: true,
|
|
43
48
|
imports,
|
|
44
49
|
exports,
|
|
45
|
-
callSites
|
|
50
|
+
callSites,
|
|
46
51
|
lineCount: content ? content.split('\n').length : 0,
|
|
52
|
+
cyclomaticComplexity,
|
|
47
53
|
};
|
|
48
54
|
}
|
|
49
55
|
catch (err) {
|
|
@@ -55,6 +61,7 @@ export const pythonAdapter = {
|
|
|
55
61
|
exports: [],
|
|
56
62
|
callSites: [],
|
|
57
63
|
lineCount: content ? content.split('\n').length : 0,
|
|
64
|
+
cyclomaticComplexity: 0,
|
|
58
65
|
};
|
|
59
66
|
}
|
|
60
67
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pythonAdapter.js","sourceRoot":"","sources":["../../../src/core/languages/pythonAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAO3D,sEAAsE;AAEtE,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AACvD,MAAM,WAAW,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhC,IAAI,aAAa,GAAqD,IAAI,CAAC;AAC3E,KAAK,UAAU,SAAS;IACtB,IAAI,CAAC,aAAa;QAAE,aAAa,GAAG,eAAe,CAAC,yBAAyB,CAAC,CAAC;IAC/E,OAAO,aAAa,CAAC;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAoB;IAC5C,EAAE,EAAE,QAAQ;IACZ,UAAU,EAAE,aAAa;IACzB,gBAAgB,EAAE,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC1C,eAAe,EAAE,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;IACtC,WAAW,EAAE,WAAW;IAExB,KAAK,CAAC,KAAK,CAAC,SAAiB,EAAE,OAAe;QAC5C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;YACjC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACnC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC5B,OAAO;oBACL,EAAE,EAAE,KAAK;oBACT,MAAM,EAAE,gCAAgC;oBACxC,OAAO,EAAE,EAAE;oBACX,OAAO,EAAE,EAAE;oBACX,SAAS,EAAE,EAAE;oBACb,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"pythonAdapter.js","sourceRoot":"","sources":["../../../src/core/languages/pythonAdapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract call-site identifiers from a tree-sitter-python AST.
|
|
3
|
+
*
|
|
4
|
+
* For `foo(...)` we record `foo`. For `obj.method(...)` (attribute call) we
|
|
5
|
+
* record `method` (the rightmost name). For more complex callees (subscripts,
|
|
6
|
+
* lambdas-immediately-invoked, decorators acting as callees, etc.) we skip -
|
|
7
|
+
* a name is needed to be useful for "who calls X" lookups.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors the JS/TS behaviour in `src/core/ast.ts`: identifiers and the final
|
|
10
|
+
* member-property name; uniqued before return.
|
|
11
|
+
*/
|
|
12
|
+
interface TsNode {
|
|
13
|
+
type: string;
|
|
14
|
+
text: string;
|
|
15
|
+
namedChildren: TsNode[];
|
|
16
|
+
childForFieldName?(name: string): TsNode | null;
|
|
17
|
+
}
|
|
18
|
+
export declare function extractPythonCallSites(root: TsNode): string[];
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract call-site identifiers from a tree-sitter-python AST.
|
|
3
|
+
*
|
|
4
|
+
* For `foo(...)` we record `foo`. For `obj.method(...)` (attribute call) we
|
|
5
|
+
* record `method` (the rightmost name). For more complex callees (subscripts,
|
|
6
|
+
* lambdas-immediately-invoked, decorators acting as callees, etc.) we skip -
|
|
7
|
+
* a name is needed to be useful for "who calls X" lookups.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors the JS/TS behaviour in `src/core/ast.ts`: identifiers and the final
|
|
10
|
+
* member-property name; uniqued before return.
|
|
11
|
+
*/
|
|
12
|
+
export function extractPythonCallSites(root) {
|
|
13
|
+
const seen = new Set();
|
|
14
|
+
walk(root, seen);
|
|
15
|
+
return [...seen];
|
|
16
|
+
}
|
|
17
|
+
function walk(node, out) {
|
|
18
|
+
if (node.type === 'call') {
|
|
19
|
+
const fn = node.childForFieldName?.('function') ?? null;
|
|
20
|
+
const name = nameFromCallee(fn);
|
|
21
|
+
if (name)
|
|
22
|
+
out.add(name);
|
|
23
|
+
}
|
|
24
|
+
for (const child of node.namedChildren)
|
|
25
|
+
walk(child, out);
|
|
26
|
+
}
|
|
27
|
+
function nameFromCallee(node) {
|
|
28
|
+
if (!node)
|
|
29
|
+
return null;
|
|
30
|
+
if (node.type === 'identifier')
|
|
31
|
+
return node.text;
|
|
32
|
+
if (node.type === 'attribute') {
|
|
33
|
+
// `obj.method` - the rightmost identifier is the called name.
|
|
34
|
+
const last = node.childForFieldName?.('attribute') ?? null;
|
|
35
|
+
if (last && last.type === 'identifier')
|
|
36
|
+
return last.text;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=pythonCallSites.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pythonCallSites.js","sourceRoot":"","sources":["../../../src/core/languages/pythonCallSites.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AASH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACjB,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;AACnB,CAAC;AAED,SAAS,IAAI,CAAC,IAAY,EAAE,GAAgB;IAC1C,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACzB,MAAM,EAAE,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC;QACxD,MAAM,IAAI,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,IAAI;YAAE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa;QAAE,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,cAAc,CAAC,IAAmB;IACzC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IACjD,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAC9B,8DAA8D;QAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC;QAC3D,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY;YAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IAC3D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Minimal tree-sitter node surface we depend on. Mirrors pythonImports.ts. */
|
|
2
|
+
interface TsNode {
|
|
3
|
+
type: string;
|
|
4
|
+
text: string;
|
|
5
|
+
namedChildren: TsNode[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Compute file-level McCabe cyclomatic complexity from a tree-sitter-python
|
|
9
|
+
* AST. Counts decision points across the entire file (module + nested
|
|
10
|
+
* functions/classes) and returns count + 1.
|
|
11
|
+
*
|
|
12
|
+
* Decision points: if/elif, for, while, except, conditional expression,
|
|
13
|
+
* boolean operators (and/or each add one), and `if` clauses inside
|
|
14
|
+
* comprehensions. The plain `else` branch is the fall-through path and does
|
|
15
|
+
* not count.
|
|
16
|
+
*/
|
|
17
|
+
export declare function extractPythonCyclomatic(root: TsNode): number;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute file-level McCabe cyclomatic complexity from a tree-sitter-python
|
|
3
|
+
* AST. Counts decision points across the entire file (module + nested
|
|
4
|
+
* functions/classes) and returns count + 1.
|
|
5
|
+
*
|
|
6
|
+
* Decision points: if/elif, for, while, except, conditional expression,
|
|
7
|
+
* boolean operators (and/or each add one), and `if` clauses inside
|
|
8
|
+
* comprehensions. The plain `else` branch is the fall-through path and does
|
|
9
|
+
* not count.
|
|
10
|
+
*/
|
|
11
|
+
export function extractPythonCyclomatic(root) {
|
|
12
|
+
let decisions = 0;
|
|
13
|
+
walk(root, (n) => {
|
|
14
|
+
if (isDecisionPoint(n))
|
|
15
|
+
decisions++;
|
|
16
|
+
});
|
|
17
|
+
return decisions + 1;
|
|
18
|
+
}
|
|
19
|
+
function isDecisionPoint(n) {
|
|
20
|
+
switch (n.type) {
|
|
21
|
+
case 'if_statement':
|
|
22
|
+
case 'elif_clause':
|
|
23
|
+
case 'for_statement':
|
|
24
|
+
case 'while_statement':
|
|
25
|
+
case 'except_clause':
|
|
26
|
+
case 'conditional_expression':
|
|
27
|
+
case 'for_in_clause': // comprehension `for`
|
|
28
|
+
case 'if_clause': // comprehension `if`
|
|
29
|
+
case 'case_clause': // match/case (Python 3.10+)
|
|
30
|
+
return true;
|
|
31
|
+
case 'boolean_operator':
|
|
32
|
+
// Each `and` / `or` introduces a branch. tree-sitter-python emits one
|
|
33
|
+
// boolean_operator node per logical operator, so each occurrence = +1.
|
|
34
|
+
return true;
|
|
35
|
+
default:
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function walk(node, visit) {
|
|
40
|
+
visit(node);
|
|
41
|
+
for (const child of node.namedChildren) {
|
|
42
|
+
walk(child, visit);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=pythonCyclomatic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pythonCyclomatic.js","sourceRoot":"","sources":["../../../src/core/languages/pythonCyclomatic.ts"],"names":[],"mappings":"AAOA;;;;;;;;;GASG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE;QACf,IAAI,eAAe,CAAC,CAAC,CAAC;YAAE,SAAS,EAAE,CAAC;IACtC,CAAC,CAAC,CAAC;IACH,OAAO,SAAS,GAAG,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,eAAe,CAAC,CAAS;IAChC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QACf,KAAK,cAAc,CAAC;QACpB,KAAK,aAAa,CAAC;QACnB,KAAK,eAAe,CAAC;QACrB,KAAK,iBAAiB,CAAC;QACvB,KAAK,eAAe,CAAC;QACrB,KAAK,wBAAwB,CAAC;QAC9B,KAAK,eAAe,CAAC,CAAC,sBAAsB;QAC5C,KAAK,WAAW,CAAC,CAAC,qBAAqB;QACvC,KAAK,aAAa,EAAE,4BAA4B;YAC9C,OAAO,IAAI,CAAC;QACd,KAAK,kBAAkB;YACrB,sEAAsE;YACtE,uEAAuE;YACvE,OAAO,IAAI,CAAC;QACd;YACE,OAAO,KAAK,CAAC;IACjB,CAAC;AACH,CAAC;AAED,SAAS,IAAI,CAAC,IAAY,EAAE,KAA0B;IACpD,KAAK,CAAC,IAAI,CAAC,CAAC;IACZ,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACvC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACrB,CAAC;AACH,CAAC"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { javascriptAdapter } from './javascriptAdapter.js';
|
|
3
3
|
import { pythonAdapter } from './pythonAdapter.js';
|
|
4
|
-
|
|
4
|
+
import { goAdapter } from './goAdapter.js';
|
|
5
|
+
import { javaAdapter } from './javaAdapter.js';
|
|
6
|
+
import { rubyAdapter } from './rubyAdapter.js';
|
|
7
|
+
const adapters = [javascriptAdapter, pythonAdapter, goAdapter, javaAdapter, rubyAdapter];
|
|
5
8
|
const byExtension = new Map();
|
|
6
9
|
function rebuildIndex() {
|
|
7
10
|
byExtension.clear();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/core/languages/registry.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/core/languages/registry.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,MAAM,QAAQ,GAAsB,CAAC,iBAAiB,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;AAC5G,MAAM,WAAW,GAAG,IAAI,GAAG,EAA2B,CAAC;AAEvD,SAAS,YAAY;IACnB,WAAW,CAAC,KAAK,EAAE,CAAC;IACpB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACrC,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;AACH,CAAC;AACD,YAAY,EAAE,CAAC;AAEf,MAAM,UAAU,eAAe,CAAC,OAAwB;IACtD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,YAAY,EAAE,CAAC;IACjB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,OAAO,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IACjD,OAAO,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { createParserFor } from './treeSitterLoader.js';
|
|
3
|
+
import { extractRubyImports } from './rubyImports.js';
|
|
4
|
+
import { extractRubyExports } from './rubyExports.js';
|
|
5
|
+
import { extractRubyCyclomatic } from './rubyCyclomatic.js';
|
|
6
|
+
import { extractRubyCallSites } from './rubyCallSites.js';
|
|
7
|
+
import { detectRubyProject } from './rubyManifests.js';
|
|
8
|
+
const RUBY_EXTENSIONS = new Set(['.rb']);
|
|
9
|
+
const MAX_RUBY_FILE = 1024 * 1024;
|
|
10
|
+
let parserPromise = null;
|
|
11
|
+
async function getParser() {
|
|
12
|
+
if (!parserPromise)
|
|
13
|
+
parserPromise = createParserFor('tree-sitter-ruby.wasm');
|
|
14
|
+
return parserPromise;
|
|
15
|
+
}
|
|
16
|
+
export const rubyAdapter = {
|
|
17
|
+
id: 'ruby',
|
|
18
|
+
extensions: RUBY_EXTENSIONS,
|
|
19
|
+
sourceExtensions: RUBY_EXTENSIONS,
|
|
20
|
+
// Ruby has no barrel-file convention.
|
|
21
|
+
barrelBasenames: new Set(),
|
|
22
|
+
maxFileSize: MAX_RUBY_FILE,
|
|
23
|
+
async parse(_filePath, content) {
|
|
24
|
+
try {
|
|
25
|
+
const parser = await getParser();
|
|
26
|
+
const tree = parser.parse(content);
|
|
27
|
+
if (!tree || !tree.rootNode) {
|
|
28
|
+
return {
|
|
29
|
+
ok: false,
|
|
30
|
+
reason: 'tree-sitter returned null tree',
|
|
31
|
+
imports: [],
|
|
32
|
+
exports: [],
|
|
33
|
+
callSites: [],
|
|
34
|
+
lineCount: content ? content.split('\n').length : 0,
|
|
35
|
+
cyclomaticComplexity: 0,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const root = tree.rootNode;
|
|
39
|
+
const imports = extractRubyImports(root);
|
|
40
|
+
const exports = extractRubyExports(root);
|
|
41
|
+
const cyclomaticComplexity = extractRubyCyclomatic(root);
|
|
42
|
+
const callSites = extractRubyCallSites(root);
|
|
43
|
+
return {
|
|
44
|
+
ok: true,
|
|
45
|
+
imports,
|
|
46
|
+
exports,
|
|
47
|
+
callSites,
|
|
48
|
+
lineCount: content ? content.split('\n').length : 0,
|
|
49
|
+
cyclomaticComplexity,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
reason: `ruby parse failure: ${msg.slice(0, 120)}`,
|
|
57
|
+
imports: [],
|
|
58
|
+
exports: [],
|
|
59
|
+
callSites: [],
|
|
60
|
+
lineCount: content ? content.split('\n').length : 0,
|
|
61
|
+
cyclomaticComplexity: 0,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
resolveImport(importingFile, source, graphFiles, context) {
|
|
66
|
+
return resolveRubyImport(importingFile, source, graphFiles, context);
|
|
67
|
+
},
|
|
68
|
+
toPackageName(source) {
|
|
69
|
+
if (!source)
|
|
70
|
+
return null;
|
|
71
|
+
// `./foo` and `../foo` are clearly local; let resolveImport handle.
|
|
72
|
+
if (source.startsWith('./') || source.startsWith('../'))
|
|
73
|
+
return null;
|
|
74
|
+
// Otherwise treat the whole specifier as the gem name (e.g. `json`, `active_support/core_ext`).
|
|
75
|
+
// Take only the first path segment for ranking purposes.
|
|
76
|
+
return source.split('/')[0];
|
|
77
|
+
},
|
|
78
|
+
async preparePackageRoots(rootPath, files) {
|
|
79
|
+
const info = await detectRubyProject(rootPath, files);
|
|
80
|
+
if (!info)
|
|
81
|
+
return { packageRoots: [], meta: undefined };
|
|
82
|
+
return {
|
|
83
|
+
packageRoots: info.sourceRoots.map((r) => path.relative(rootPath, r) || '.'),
|
|
84
|
+
meta: { rubyProject: info },
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Resolve a Ruby require / require_relative to a repo-local file path.
|
|
90
|
+
*
|
|
91
|
+
* require_relative 'sibling' → resolved against the importing file's dir.
|
|
92
|
+
* require_relative '../helpers' → walked up.
|
|
93
|
+
* require 'json' → null (stdlib / gem; no source-root match).
|
|
94
|
+
* require 'foo/bar' → for each sourceRoot, probe `foo/bar.rb`.
|
|
95
|
+
*
|
|
96
|
+
* Ruby `require` semantics depend on $LOAD_PATH at runtime; we approximate by
|
|
97
|
+
* checking sourceRoots (typically `lib/`). This catches the common case where
|
|
98
|
+
* a gem requires its own internal modules by their lib-relative path.
|
|
99
|
+
*/
|
|
100
|
+
function resolveRubyImport(importingFile, source, graphFiles, context) {
|
|
101
|
+
if (!source)
|
|
102
|
+
return null;
|
|
103
|
+
// The original call site distinguishes require vs require_relative, but by
|
|
104
|
+
// the time we're here we only have the source string. Heuristic: if the
|
|
105
|
+
// string starts with `./` or `../`, treat as relative; otherwise probe BOTH
|
|
106
|
+
// relative-to-importer AND relative-to-each-source-root, and pick the first
|
|
107
|
+
// that resolves. This captures both `require_relative` AND `require` of a
|
|
108
|
+
// gem-internal lib path.
|
|
109
|
+
const sourceWithExt = source.endsWith('.rb') ? source : `${source}.rb`;
|
|
110
|
+
if (source.startsWith('./') || source.startsWith('../')) {
|
|
111
|
+
const dir = path.posix.dirname(importingFile);
|
|
112
|
+
const joined = path.posix.normalize(path.posix.join(dir, sourceWithExt));
|
|
113
|
+
return graphFiles.has(joined) ? joined : null;
|
|
114
|
+
}
|
|
115
|
+
// Try each source root.
|
|
116
|
+
const sourceRootsRel = context.packageRoots ?? ['.'];
|
|
117
|
+
for (const root of sourceRootsRel) {
|
|
118
|
+
const rootSegs = root === '.' || root === '' ? [] : root.split('/').filter(Boolean);
|
|
119
|
+
const candidate = [...rootSegs, sourceWithExt].join('/');
|
|
120
|
+
if (graphFiles.has(candidate))
|
|
121
|
+
return candidate;
|
|
122
|
+
}
|
|
123
|
+
// Also try treating the source as relative to the importing file's directory
|
|
124
|
+
// (require_relative without leading `./`). Some style guides drop the prefix.
|
|
125
|
+
const dir = path.posix.dirname(importingFile);
|
|
126
|
+
const joined = path.posix.normalize(path.posix.join(dir, sourceWithExt));
|
|
127
|
+
if (graphFiles.has(joined))
|
|
128
|
+
return joined;
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=rubyAdapter.js.map
|