projscan 0.9.2 → 0.11.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.
Files changed (115) hide show
  1. package/README.md +37 -7
  2. package/dist/analyzers/deadCodeCheck.d.ts +10 -12
  3. package/dist/analyzers/deadCodeCheck.js +41 -69
  4. package/dist/analyzers/deadCodeCheck.js.map +1 -1
  5. package/dist/analyzers/pythonDependencyRiskCheck.d.ts +2 -0
  6. package/dist/analyzers/pythonDependencyRiskCheck.js +114 -0
  7. package/dist/analyzers/pythonDependencyRiskCheck.js.map +1 -0
  8. package/dist/analyzers/pythonLinterCheck.d.ts +2 -0
  9. package/dist/analyzers/pythonLinterCheck.js +119 -0
  10. package/dist/analyzers/pythonLinterCheck.js.map +1 -0
  11. package/dist/analyzers/pythonTestCheck.d.ts +2 -0
  12. package/dist/analyzers/pythonTestCheck.js +97 -0
  13. package/dist/analyzers/pythonTestCheck.js.map +1 -0
  14. package/dist/analyzers/pythonUnusedDependencyCheck.d.ts +2 -0
  15. package/dist/analyzers/pythonUnusedDependencyCheck.js +76 -0
  16. package/dist/analyzers/pythonUnusedDependencyCheck.js.map +1 -0
  17. package/dist/cli/index.js +294 -10
  18. package/dist/cli/index.js.map +1 -1
  19. package/dist/core/ast.d.ts +2 -0
  20. package/dist/core/ast.js +35 -2
  21. package/dist/core/ast.js.map +1 -1
  22. package/dist/core/codeGraph.d.ts +8 -7
  23. package/dist/core/codeGraph.js +50 -72
  24. package/dist/core/codeGraph.js.map +1 -1
  25. package/dist/core/couplingAnalyzer.d.ts +18 -0
  26. package/dist/core/couplingAnalyzer.js +174 -0
  27. package/dist/core/couplingAnalyzer.js.map +1 -0
  28. package/dist/core/fileInspector.d.ts +3 -0
  29. package/dist/core/fileInspector.js +78 -3
  30. package/dist/core/fileInspector.js.map +1 -1
  31. package/dist/core/hotspotAnalyzer.d.ts +13 -0
  32. package/dist/core/hotspotAnalyzer.js +29 -6
  33. package/dist/core/hotspotAnalyzer.js.map +1 -1
  34. package/dist/core/indexCache.js +8 -1
  35. package/dist/core/indexCache.js.map +1 -1
  36. package/dist/core/issueEngine.js +10 -0
  37. package/dist/core/issueEngine.js.map +1 -1
  38. package/dist/core/languages/LanguageAdapter.d.ts +36 -0
  39. package/dist/core/languages/LanguageAdapter.js +2 -0
  40. package/dist/core/languages/LanguageAdapter.js.map +1 -0
  41. package/dist/core/languages/goAdapter.d.ts +2 -0
  42. package/dist/core/languages/goAdapter.js +136 -0
  43. package/dist/core/languages/goAdapter.js.map +1 -0
  44. package/dist/core/languages/goCyclomatic.d.ts +21 -0
  45. package/dist/core/languages/goCyclomatic.js +55 -0
  46. package/dist/core/languages/goCyclomatic.js.map +1 -0
  47. package/dist/core/languages/goExports.d.ts +26 -0
  48. package/dist/core/languages/goExports.js +89 -0
  49. package/dist/core/languages/goExports.js.map +1 -0
  50. package/dist/core/languages/goImports.d.ts +26 -0
  51. package/dist/core/languages/goImports.js +64 -0
  52. package/dist/core/languages/goImports.js.map +1 -0
  53. package/dist/core/languages/goManifests.d.ts +19 -0
  54. package/dist/core/languages/goManifests.js +56 -0
  55. package/dist/core/languages/goManifests.js.map +1 -0
  56. package/dist/core/languages/javascriptAdapter.d.ts +2 -0
  57. package/dist/core/languages/javascriptAdapter.js +68 -0
  58. package/dist/core/languages/javascriptAdapter.js.map +1 -0
  59. package/dist/core/languages/pythonAdapter.d.ts +6 -0
  60. package/dist/core/languages/pythonAdapter.js +147 -0
  61. package/dist/core/languages/pythonAdapter.js.map +1 -0
  62. package/dist/core/languages/pythonCyclomatic.d.ts +18 -0
  63. package/dist/core/languages/pythonCyclomatic.js +45 -0
  64. package/dist/core/languages/pythonCyclomatic.js.map +1 -0
  65. package/dist/core/languages/pythonExports.d.ts +28 -0
  66. package/dist/core/languages/pythonExports.js +169 -0
  67. package/dist/core/languages/pythonExports.js.map +1 -0
  68. package/dist/core/languages/pythonImports.d.ts +22 -0
  69. package/dist/core/languages/pythonImports.js +104 -0
  70. package/dist/core/languages/pythonImports.js.map +1 -0
  71. package/dist/core/languages/pythonManifests.d.ts +34 -0
  72. package/dist/core/languages/pythonManifests.js +344 -0
  73. package/dist/core/languages/pythonManifests.js.map +1 -0
  74. package/dist/core/languages/registry.d.ts +5 -0
  75. package/dist/core/languages/registry.js +31 -0
  76. package/dist/core/languages/registry.js.map +1 -0
  77. package/dist/core/languages/treeSitterLoader.d.ts +14 -0
  78. package/dist/core/languages/treeSitterLoader.js +76 -0
  79. package/dist/core/languages/treeSitterLoader.js.map +1 -0
  80. package/dist/core/monorepo.d.ts +20 -0
  81. package/dist/core/monorepo.js +270 -0
  82. package/dist/core/monorepo.js.map +1 -0
  83. package/dist/core/prDiff.d.ts +43 -0
  84. package/dist/core/prDiff.js +298 -0
  85. package/dist/core/prDiff.js.map +1 -0
  86. package/dist/core/searchIndex.js +8 -0
  87. package/dist/core/searchIndex.js.map +1 -1
  88. package/dist/core/telemetry.d.ts +90 -0
  89. package/dist/core/telemetry.js +199 -0
  90. package/dist/core/telemetry.js.map +1 -0
  91. package/dist/grammars/tree-sitter-go.wasm +0 -0
  92. package/dist/grammars/tree-sitter-python.wasm +0 -0
  93. package/dist/grammars/web-tree-sitter.wasm +0 -0
  94. package/dist/mcp/server.js +22 -0
  95. package/dist/mcp/server.js.map +1 -1
  96. package/dist/mcp/tools.js +317 -20
  97. package/dist/mcp/tools.js.map +1 -1
  98. package/dist/reporters/consoleReporter.d.ts +4 -1
  99. package/dist/reporters/consoleReporter.js +113 -0
  100. package/dist/reporters/consoleReporter.js.map +1 -1
  101. package/dist/reporters/jsonReporter.d.ts +4 -1
  102. package/dist/reporters/jsonReporter.js +9 -0
  103. package/dist/reporters/jsonReporter.js.map +1 -1
  104. package/dist/reporters/markdownReporter.d.ts +4 -1
  105. package/dist/reporters/markdownReporter.js +103 -3
  106. package/dist/reporters/markdownReporter.js.map +1 -1
  107. package/dist/types.d.ts +115 -0
  108. package/dist/utils/cache.d.ts +3 -0
  109. package/dist/utils/cache.js +51 -0
  110. package/dist/utils/cache.js.map +1 -0
  111. package/dist/utils/config.js +10 -0
  112. package/dist/utils/config.js.map +1 -1
  113. package/dist/utils/fileWalker.js +14 -0
  114. package/dist/utils/fileWalker.js.map +1 -1
  115. package/package.json +11 -5
@@ -0,0 +1,36 @@
1
+ import type { FileEntry } from '../../types.js';
2
+ import type { AstResult } from '../ast.js';
3
+ export type LanguageId = 'javascript' | 'python' | 'go';
4
+ export interface LanguageResolveContext {
5
+ /** Language-specific root dirs used during import resolution. */
6
+ packageRoots?: string[];
7
+ meta?: Record<string, unknown>;
8
+ }
9
+ export interface GraphFileLike {
10
+ relativePath: string;
11
+ }
12
+ export interface LanguageAdapter {
13
+ id: LanguageId;
14
+ /** File extensions this adapter claims (lowercase, with dot). */
15
+ extensions: ReadonlySet<string>;
16
+ /** Max file size to parse. Defaults to 1 MB at the call site. */
17
+ maxFileSize?: number;
18
+ /** Barrel filenames (sans extension) to skip in dead-code analysis. */
19
+ barrelBasenames?: ReadonlySet<string>;
20
+ /** Source extensions used for dead-code analysis (a subset or equal of `extensions`). */
21
+ sourceExtensions?: ReadonlySet<string>;
22
+ /** Parse a file. Must never throw; returns ok:false on failure. */
23
+ parse(filePath: string, content: string): AstResult | Promise<AstResult>;
24
+ /**
25
+ * Resolve a single import source against the graph's known files.
26
+ * Called during graph-building AFTER all files are parsed.
27
+ */
28
+ resolveImport(importingFile: string, source: string, graphFiles: Map<string, GraphFileLike>, context: LanguageResolveContext): string | null;
29
+ /**
30
+ * Return the bare external package name for this specifier, or null for
31
+ * "local/relative, let resolveImport handle it."
32
+ */
33
+ toPackageName(source: string): string | null;
34
+ /** One-time per-scan setup. Called before any parse. */
35
+ preparePackageRoots(rootPath: string, files: FileEntry[]): Promise<LanguageResolveContext> | LanguageResolveContext;
36
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=LanguageAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LanguageAdapter.js","sourceRoot":"","sources":["../../../src/core/languages/LanguageAdapter.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ import type { LanguageAdapter } from './LanguageAdapter.js';
2
+ export declare const goAdapter: LanguageAdapter;
@@ -0,0 +1,136 @@
1
+ import path from 'node:path';
2
+ import { createParserFor } from './treeSitterLoader.js';
3
+ import { extractGoImports } from './goImports.js';
4
+ import { extractGoExports } from './goExports.js';
5
+ import { extractGoCyclomatic } from './goCyclomatic.js';
6
+ import { detectGoProject } from './goManifests.js';
7
+ const GO_EXTENSIONS = new Set(['.go']);
8
+ const MAX_GO_FILE = 1024 * 1024;
9
+ let parserPromise = null;
10
+ async function getParser() {
11
+ if (!parserPromise)
12
+ parserPromise = createParserFor('tree-sitter-go.wasm');
13
+ return parserPromise;
14
+ }
15
+ export const goAdapter = {
16
+ id: 'go',
17
+ extensions: GO_EXTENSIONS,
18
+ sourceExtensions: GO_EXTENSIONS,
19
+ // Go has no analogue to JS `index.ts` or Python `__init__.py` — every .go
20
+ // file in a directory contributes to the same package. Leave empty.
21
+ barrelBasenames: new Set(),
22
+ maxFileSize: MAX_GO_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 = extractGoImports(root);
40
+ const exports = extractGoExports(root);
41
+ const cyclomaticComplexity = extractGoCyclomatic(root);
42
+ return {
43
+ ok: true,
44
+ imports,
45
+ exports,
46
+ callSites: [],
47
+ lineCount: content ? content.split('\n').length : 0,
48
+ cyclomaticComplexity,
49
+ };
50
+ }
51
+ catch (err) {
52
+ const msg = err instanceof Error ? err.message : String(err);
53
+ return {
54
+ ok: false,
55
+ reason: `go parse failure: ${msg.slice(0, 120)}`,
56
+ imports: [],
57
+ exports: [],
58
+ callSites: [],
59
+ lineCount: content ? content.split('\n').length : 0,
60
+ cyclomaticComplexity: 0,
61
+ };
62
+ }
63
+ },
64
+ resolveImport(importingFile, source, graphFiles, context) {
65
+ return resolveGoImport(importingFile, source, graphFiles, context);
66
+ },
67
+ toPackageName(source) {
68
+ if (!source)
69
+ return null;
70
+ // Standard library imports like "fmt", "net/http" — package = first segment.
71
+ // Third-party like "github.com/foo/bar" — package = whole path.
72
+ if (source.startsWith('.'))
73
+ return null; // local
74
+ if (source.includes('/'))
75
+ return source; // module path; treat as a single "package"
76
+ return source;
77
+ },
78
+ async preparePackageRoots(rootPath, files) {
79
+ const info = await detectGoProject(rootPath, files);
80
+ return {
81
+ packageRoots: info ? [path.relative(rootPath, info.moduleRoot) || '.'] : [],
82
+ meta: info ? { goProject: info } : undefined,
83
+ };
84
+ },
85
+ };
86
+ function resolveGoImport(importingFile, source, graphFiles, context) {
87
+ if (!source)
88
+ return null;
89
+ const goProject = context.meta?.goProject;
90
+ if (!goProject)
91
+ return null;
92
+ // Local imports (rare and discouraged in modern Go): ./foo or ../bar.
93
+ if (source.startsWith('.')) {
94
+ const dir = path.posix.dirname(importingFile);
95
+ const joined = path.posix.normalize(path.posix.join(dir, source));
96
+ return findPackageDir(joined, graphFiles);
97
+ }
98
+ // Module-prefixed imports resolve into the repo. Strip the module path,
99
+ // anything left is a directory within the module.
100
+ if (source === goProject.modulePath) {
101
+ return findPackageDir('', graphFiles);
102
+ }
103
+ const prefix = goProject.modulePath + '/';
104
+ if (source.startsWith(prefix)) {
105
+ const sub = source.slice(prefix.length);
106
+ return findPackageDir(sub, graphFiles);
107
+ }
108
+ // Anything else (stdlib, third-party) is external.
109
+ return null;
110
+ }
111
+ /**
112
+ * Resolve an import to ANY .go file inside the target directory. Go packages
113
+ * are directory-scoped — a single file is enough to anchor the edge in the
114
+ * graph. We pick the lexicographically first match for determinism.
115
+ */
116
+ function findPackageDir(relDir, graphFiles) {
117
+ const dir = relDir === '' || relDir === '.' ? '' : relDir;
118
+ const prefix = dir ? dir + '/' : '';
119
+ const matches = [];
120
+ for (const file of graphFiles.keys()) {
121
+ if (!file.endsWith('.go'))
122
+ continue;
123
+ if (file.startsWith(prefix)) {
124
+ // Must be a direct child, not nested deeper. Go packages don't transitively
125
+ // include subdirectories.
126
+ const tail = file.slice(prefix.length);
127
+ if (!tail.includes('/'))
128
+ matches.push(file);
129
+ }
130
+ }
131
+ if (matches.length === 0)
132
+ return null;
133
+ matches.sort();
134
+ return matches[0];
135
+ }
136
+ //# sourceMappingURL=goAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goAdapter.js","sourceRoot":"","sources":["../../../src/core/languages/goAdapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ interface TsNode {
2
+ type: string;
3
+ text: string;
4
+ namedChildren: TsNode[];
5
+ }
6
+ /**
7
+ * File-level McCabe cyclomatic complexity for a tree-sitter-go AST.
8
+ * Counts decision points across the whole file (package + functions +
9
+ * methods) and returns count + 1.
10
+ *
11
+ * Decision points:
12
+ * if_statement +1
13
+ * for_statement +1
14
+ * case_clause / type_case (each `case` in a switch; `default` does not count)
15
+ * communication_case +1 (each non-default arm of a select)
16
+ * binary_expression with operator `&&` or `||` +1 each
17
+ *
18
+ * Goroutine `go` and `defer` are NOT branches — they don't count.
19
+ */
20
+ export declare function extractGoCyclomatic(root: TsNode): number;
21
+ export {};
@@ -0,0 +1,55 @@
1
+ /**
2
+ * File-level McCabe cyclomatic complexity for a tree-sitter-go AST.
3
+ * Counts decision points across the whole file (package + functions +
4
+ * methods) and returns count + 1.
5
+ *
6
+ * Decision points:
7
+ * if_statement +1
8
+ * for_statement +1
9
+ * case_clause / type_case (each `case` in a switch; `default` does not count)
10
+ * communication_case +1 (each non-default arm of a select)
11
+ * binary_expression with operator `&&` or `||` +1 each
12
+ *
13
+ * Goroutine `go` and `defer` are NOT branches — they don't count.
14
+ */
15
+ export function extractGoCyclomatic(root) {
16
+ let decisions = 0;
17
+ walk(root, (n) => {
18
+ if (isDecisionPoint(n))
19
+ decisions++;
20
+ });
21
+ return decisions + 1;
22
+ }
23
+ function isDecisionPoint(n) {
24
+ switch (n.type) {
25
+ case 'if_statement':
26
+ case 'for_statement':
27
+ case 'expression_case': // `case x:` in expression switch
28
+ case 'type_case': // `case T:` in type switch
29
+ case 'communication_case': // non-default `case` in select
30
+ return true;
31
+ case 'binary_expression': {
32
+ // tree-sitter-go represents the operator as text on the operator child
33
+ // (or in n.text we'd need to inspect tokens). Cheap approximation:
34
+ // scan the source text of the binary_expression for `&&` / `||`. Since
35
+ // each binary_expression node represents exactly one operator
36
+ // application, this is precise.
37
+ const t = n.text;
38
+ // Use small windows: an `&&` or `||` appears as two ASCII chars.
39
+ // No false positives possible — strings are themselves leaf nodes
40
+ // (interpreted_string_literal) and would parent-up through other paths.
41
+ // Still, guard against operators inside string literals by checking
42
+ // that this particular binary_expression's first/second child is not
43
+ // a string.
44
+ return /(\s|^)(\|\||&&)(\s|$)/.test(t);
45
+ }
46
+ default:
47
+ return false;
48
+ }
49
+ }
50
+ function walk(node, visit) {
51
+ visit(node);
52
+ for (const child of node.namedChildren)
53
+ walk(child, visit);
54
+ }
55
+ //# sourceMappingURL=goCyclomatic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goCyclomatic.js","sourceRoot":"","sources":["../../../src/core/languages/goCyclomatic.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,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,eAAe,CAAC;QACrB,KAAK,iBAAiB,CAAC,CAAC,iCAAiC;QACzD,KAAK,WAAW,CAAC,CAAC,2BAA2B;QAC7C,KAAK,oBAAoB,EAAE,+BAA+B;YACxD,OAAO,IAAI,CAAC;QACd,KAAK,mBAAmB,CAAC,CAAC,CAAC;YACzB,uEAAuE;YACvE,mEAAmE;YACnE,uEAAuE;YACvE,8DAA8D;YAC9D,gCAAgC;YAChC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACjB,iEAAiE;YACjE,kEAAkE;YAClE,wEAAwE;YACxE,oEAAoE;YACpE,qEAAqE;YACrE,YAAY;YACZ,OAAO,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzC,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;QAAE,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type { AstExport } from '../ast.js';
2
+ interface TsNode {
3
+ type: string;
4
+ text: string;
5
+ startPosition: {
6
+ row: number;
7
+ };
8
+ namedChildren: TsNode[];
9
+ childForFieldName?(name: string): TsNode | null;
10
+ }
11
+ /**
12
+ * Extract package-level "exports" from a Go AST.
13
+ *
14
+ * Go's export rule is mechanical: an identifier is exported iff it starts
15
+ * with an uppercase letter. We scan top-level declarations only — local
16
+ * symbols inside functions are not part of the package surface.
17
+ *
18
+ * Captured forms:
19
+ * func Foo() {} → function
20
+ * func (r *T) Foo() {} → function (method)
21
+ * type Foo struct {} | interface {} → type / interface
22
+ * var Foo = ... / var Foo Type → variable
23
+ * const Foo = ... → variable
24
+ */
25
+ export declare function extractGoExports(root: TsNode): AstExport[];
26
+ export {};
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Extract package-level "exports" from a Go AST.
3
+ *
4
+ * Go's export rule is mechanical: an identifier is exported iff it starts
5
+ * with an uppercase letter. We scan top-level declarations only — local
6
+ * symbols inside functions are not part of the package surface.
7
+ *
8
+ * Captured forms:
9
+ * func Foo() {} → function
10
+ * func (r *T) Foo() {} → function (method)
11
+ * type Foo struct {} | interface {} → type / interface
12
+ * var Foo = ... / var Foo Type → variable
13
+ * const Foo = ... → variable
14
+ */
15
+ export function extractGoExports(root) {
16
+ const out = [];
17
+ for (const child of root.namedChildren) {
18
+ visitTopLevel(child, out);
19
+ }
20
+ return out;
21
+ }
22
+ function visitTopLevel(node, out) {
23
+ switch (node.type) {
24
+ case 'function_declaration':
25
+ addIfExported(name(node), 'function', node, out);
26
+ return;
27
+ case 'method_declaration':
28
+ addIfExported(name(node), 'function', node, out);
29
+ return;
30
+ case 'type_declaration':
31
+ for (const spec of node.namedChildren) {
32
+ if (spec.type === 'type_spec' || spec.type === 'type_alias') {
33
+ const id = spec.childForFieldName?.('name') ?? firstIdent(spec);
34
+ const kind = detectTypeKind(spec);
35
+ addIfExported(id?.text, kind, spec, out);
36
+ }
37
+ }
38
+ return;
39
+ case 'var_declaration':
40
+ case 'const_declaration':
41
+ for (const spec of node.namedChildren) {
42
+ if (spec.type === 'var_spec' || spec.type === 'const_spec') {
43
+ // A spec can declare multiple names: `var X, Y int`.
44
+ for (const c of spec.namedChildren) {
45
+ if (c.type === 'identifier')
46
+ addIfExported(c.text, 'variable', spec, out);
47
+ }
48
+ }
49
+ }
50
+ return;
51
+ default:
52
+ return;
53
+ }
54
+ }
55
+ function name(node) {
56
+ return node.childForFieldName?.('name')?.text ?? firstIdent(node)?.text;
57
+ }
58
+ function firstIdent(node) {
59
+ for (const c of node.namedChildren) {
60
+ if (c.type === 'identifier' || c.type === 'field_identifier' || c.type === 'type_identifier')
61
+ return c;
62
+ }
63
+ return undefined;
64
+ }
65
+ function detectTypeKind(spec) {
66
+ // type_spec children: identifier + (struct_type | interface_type | ...).
67
+ for (const c of spec.namedChildren) {
68
+ if (c.type === 'interface_type')
69
+ return 'interface';
70
+ if (c.type === 'struct_type')
71
+ return 'class';
72
+ }
73
+ return 'type';
74
+ }
75
+ function addIfExported(name, kind, node, out) {
76
+ if (!name)
77
+ return;
78
+ // Go's export rule: leading character is an uppercase Unicode letter.
79
+ const first = name.charAt(0);
80
+ if (first !== first.toUpperCase() || first === first.toLowerCase())
81
+ return;
82
+ out.push({
83
+ name,
84
+ kind,
85
+ typeOnly: false,
86
+ line: node.startPosition.row + 1,
87
+ });
88
+ }
89
+ //# sourceMappingURL=goExports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goExports.js","sourceRoot":"","sources":["../../../src/core/languages/goExports.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
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 paths from a tree-sitter-go AST.
12
+ *
13
+ * Go imports come in two shapes:
14
+ * import "fmt"
15
+ * import (
16
+ * "fmt"
17
+ * "github.com/foo/bar"
18
+ * util "github.com/foo/util" // alias
19
+ * )
20
+ *
21
+ * The grammar emits `import_declaration` containing one or more
22
+ * `import_spec` nodes. Each spec has an `interpreted_string_literal` child
23
+ * holding the import path (with surrounding quotes that we strip).
24
+ */
25
+ export declare function extractGoImports(root: TsNode): AstImport[];
26
+ export {};
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Extract import paths from a tree-sitter-go AST.
3
+ *
4
+ * Go imports come in two shapes:
5
+ * import "fmt"
6
+ * import (
7
+ * "fmt"
8
+ * "github.com/foo/bar"
9
+ * util "github.com/foo/util" // alias
10
+ * )
11
+ *
12
+ * The grammar emits `import_declaration` containing one or more
13
+ * `import_spec` nodes. Each spec has an `interpreted_string_literal` child
14
+ * holding the import path (with surrounding quotes that we strip).
15
+ */
16
+ export function extractGoImports(root) {
17
+ const out = [];
18
+ visit(root, out);
19
+ return out;
20
+ }
21
+ function visit(node, out) {
22
+ for (const child of node.namedChildren) {
23
+ if (child.type === 'import_declaration') {
24
+ handleImportDecl(child, out);
25
+ }
26
+ else {
27
+ // Imports must be top-level in Go, but we still descend through any
28
+ // top-level wrappers (e.g. source_file).
29
+ visit(child, out);
30
+ }
31
+ }
32
+ }
33
+ function handleImportDecl(node, out) {
34
+ const line = node.startPosition.row + 1;
35
+ for (const c of node.namedChildren) {
36
+ if (c.type === 'import_spec') {
37
+ addSpec(c, line, out);
38
+ }
39
+ else if (c.type === 'import_spec_list') {
40
+ for (const s of c.namedChildren) {
41
+ if (s.type === 'import_spec')
42
+ addSpec(s, line, out);
43
+ }
44
+ }
45
+ }
46
+ }
47
+ function addSpec(spec, line, out) {
48
+ // Path is the interpreted_string_literal; we strip the surrounding quotes.
49
+ const pathNode = spec.namedChildren.find((c) => c.type === 'interpreted_string_literal' || c.type === 'raw_string_literal');
50
+ if (!pathNode)
51
+ return;
52
+ const raw = pathNode.text;
53
+ const source = raw.replace(/^["`]|["`]$/g, '');
54
+ if (!source)
55
+ return;
56
+ out.push({
57
+ source,
58
+ kind: 'static',
59
+ specifiers: [],
60
+ typeOnly: false,
61
+ line,
62
+ });
63
+ }
64
+ //# sourceMappingURL=goImports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goImports.js","sourceRoot":"","sources":["../../../src/core/languages/goImports.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,GAAG,GAAgB,EAAE,CAAC;IAC5B,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACjB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,KAAK,CAAC,IAAY,EAAE,GAAgB;IAC3C,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACvC,IAAI,KAAK,CAAC,IAAI,KAAK,oBAAoB,EAAE,CAAC;YACxC,gBAAgB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,oEAAoE;YACpE,yCAAyC;YACzC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY,EAAE,GAAgB;IACtD,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACnC,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;YAC7B,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;QACxB,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;YACzC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;gBAChC,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa;oBAAE,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;YACtD,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,OAAO,CAAC,IAAY,EAAE,IAAY,EAAE,GAAgB;IAC3D,2EAA2E;IAC3E,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CACtC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,4BAA4B,IAAI,CAAC,CAAC,IAAI,KAAK,oBAAoB,CAClF,CAAC;IACF,IAAI,CAAC,QAAQ;QAAE,OAAO;IACtB,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC;IAC1B,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IAC/C,IAAI,CAAC,MAAM;QAAE,OAAO;IACpB,GAAG,CAAC,IAAI,CAAC;QACP,MAAM;QACN,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,EAAE;QACd,QAAQ,EAAE,KAAK;QACf,IAAI;KACL,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { FileEntry } from '../../types.js';
2
+ export interface GoProjectInfo {
3
+ /**
4
+ * Module path declared in go.mod (e.g. "github.com/acme/widget"). Local
5
+ * imports starting with this prefix resolve into the repo.
6
+ */
7
+ modulePath: string;
8
+ /** Absolute directory containing go.mod. Imports resolve relative to this. */
9
+ moduleRoot: string;
10
+ }
11
+ /**
12
+ * Find the closest go.mod and read its module declaration. Searches the
13
+ * repository root first, then any directory that contains at least one .go
14
+ * file (handles nested modules / examples directories).
15
+ *
16
+ * Returns null if no go.mod exists — Go files outside any module are valid
17
+ * (e.g. snippets) but their imports can't be resolved to local files.
18
+ */
19
+ export declare function detectGoProject(rootPath: string, files: FileEntry[]): Promise<GoProjectInfo | null>;
@@ -0,0 +1,56 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ /**
4
+ * Find the closest go.mod and read its module declaration. Searches the
5
+ * repository root first, then any directory that contains at least one .go
6
+ * file (handles nested modules / examples directories).
7
+ *
8
+ * Returns null if no go.mod exists — Go files outside any module are valid
9
+ * (e.g. snippets) but their imports can't be resolved to local files.
10
+ */
11
+ export async function detectGoProject(rootPath, files) {
12
+ // 1) Try repo root first — by far the most common case.
13
+ const rootGoMod = await readGoMod(path.join(rootPath, 'go.mod'));
14
+ if (rootGoMod)
15
+ return { modulePath: rootGoMod, moduleRoot: rootPath };
16
+ // 2) Look for go.mod in any directory containing a .go file. Take the
17
+ // shortest path (closest-to-root) when multiple match.
18
+ const candidates = new Set();
19
+ for (const f of files) {
20
+ if (!f.relativePath.endsWith('.go'))
21
+ continue;
22
+ let dir = path.posix.dirname(f.relativePath);
23
+ while (dir && dir !== '.' && dir !== '/') {
24
+ candidates.add(dir);
25
+ dir = path.posix.dirname(dir);
26
+ }
27
+ }
28
+ const sorted = [...candidates].sort((a, b) => a.length - b.length);
29
+ for (const dir of sorted) {
30
+ const p = path.join(rootPath, dir, 'go.mod');
31
+ const mod = await readGoMod(p);
32
+ if (mod)
33
+ return { modulePath: mod, moduleRoot: path.join(rootPath, dir) };
34
+ }
35
+ return null;
36
+ }
37
+ async function readGoMod(absPath) {
38
+ let content;
39
+ try {
40
+ content = await fs.readFile(absPath, 'utf-8');
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ // First non-comment `module <path>` line wins.
46
+ for (const raw of content.split('\n')) {
47
+ const line = raw.trim();
48
+ if (!line || line.startsWith('//'))
49
+ continue;
50
+ const m = /^module\s+(\S+)/.exec(line);
51
+ if (m)
52
+ return m[1];
53
+ }
54
+ return null;
55
+ }
56
+ //# sourceMappingURL=goManifests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goManifests.js","sourceRoot":"","sources":["../../../src/core/languages/goManifests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAa7B;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,QAAgB,EAChB,KAAkB;IAElB,wDAAwD;IACxD,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IACjE,IAAI,SAAS;QAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;IAEtE,sEAAsE;IACtE,uDAAuD;IACvD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QAC9C,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;QAC7C,OAAO,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;YACzC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACpB,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IACnE,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC7C,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,GAAG;YAAE,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC;IAC5E,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,OAAe;IACtC,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,+CAA+C;IAC/C,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,SAAS;QAC7C,MAAM,CAAC,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC;YAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { LanguageAdapter } from './LanguageAdapter.js';
2
+ export declare const javascriptAdapter: LanguageAdapter;
@@ -0,0 +1,68 @@
1
+ import path from 'node:path';
2
+ import { parseSource } from '../ast.js';
3
+ const JS_EXTENSIONS = new Set([
4
+ '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.mts', '.cts',
5
+ ]);
6
+ const RESOLUTION_EXTS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.mts', '.cts'];
7
+ const NODE_BUILTINS = new Set([
8
+ 'assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'console', 'constants', 'crypto',
9
+ 'dgram', 'dns', 'domain', 'events', 'fs', 'fs/promises', 'http', 'http2', 'https', 'inspector',
10
+ 'module', 'net', 'os', 'path', 'perf_hooks', 'process', 'punycode', 'querystring', 'readline',
11
+ 'repl', 'stream', 'string_decoder', 'sys', 'timers', 'tls', 'trace_events', 'tty', 'url', 'util',
12
+ 'v8', 'vm', 'wasi', 'worker_threads', 'zlib',
13
+ ]);
14
+ export const javascriptAdapter = {
15
+ id: 'javascript',
16
+ extensions: JS_EXTENSIONS,
17
+ sourceExtensions: JS_EXTENSIONS,
18
+ barrelBasenames: new Set(['index']),
19
+ parse(filePath, content) {
20
+ return parseSource(filePath, content);
21
+ },
22
+ resolveImport(importingFile, source, graphFiles) {
23
+ if (!(source.startsWith('.') || source.startsWith('/')))
24
+ return null;
25
+ const importingDir = path.posix.dirname(importingFile);
26
+ const base = path.posix.normalize(path.posix.join(importingDir, source));
27
+ if (graphFiles.has(base))
28
+ return base;
29
+ for (const ext of RESOLUTION_EXTS) {
30
+ if (graphFiles.has(base + ext))
31
+ return base + ext;
32
+ }
33
+ for (const ext of RESOLUTION_EXTS) {
34
+ const barrel = `${base}/index${ext}`;
35
+ if (graphFiles.has(barrel))
36
+ return barrel;
37
+ }
38
+ if (base.endsWith('.js')) {
39
+ const trimmed = base.slice(0, -3);
40
+ if (graphFiles.has(`${trimmed}.ts`))
41
+ return `${trimmed}.ts`;
42
+ if (graphFiles.has(`${trimmed}.tsx`))
43
+ return `${trimmed}.tsx`;
44
+ }
45
+ return null;
46
+ },
47
+ toPackageName(specifier) {
48
+ if (!specifier)
49
+ return null;
50
+ if (specifier.startsWith('.') || specifier.startsWith('/'))
51
+ return null;
52
+ if (specifier.startsWith('node:'))
53
+ return null;
54
+ if (NODE_BUILTINS.has(specifier))
55
+ return null;
56
+ if (specifier.startsWith('@')) {
57
+ const segments = specifier.split('/');
58
+ if (segments.length < 2)
59
+ return null;
60
+ return `${segments[0]}/${segments[1]}`;
61
+ }
62
+ return specifier.split('/')[0];
63
+ },
64
+ preparePackageRoots(_rootPath, _files) {
65
+ return {};
66
+ },
67
+ };
68
+ //# sourceMappingURL=javascriptAdapter.js.map