projscan 0.11.0 → 0.14.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 (278) hide show
  1. package/README.md +57 -33
  2. package/dist/analyzers/crossPackageImportCheck.d.ts +13 -0
  3. package/dist/analyzers/crossPackageImportCheck.js +136 -0
  4. package/dist/analyzers/crossPackageImportCheck.js.map +1 -0
  5. package/dist/analyzers/cycleCheck.d.ts +12 -0
  6. package/dist/analyzers/cycleCheck.js +65 -0
  7. package/dist/analyzers/cycleCheck.js.map +1 -0
  8. package/dist/analyzers/unusedDependencyCheck.js +69 -17
  9. package/dist/analyzers/unusedDependencyCheck.js.map +1 -1
  10. package/dist/cli/_shared.d.ts +16 -0
  11. package/dist/cli/_shared.js +210 -0
  12. package/dist/cli/_shared.js.map +1 -0
  13. package/dist/cli/commands/analyze.d.ts +1 -0
  14. package/dist/cli/commands/analyze.js +87 -0
  15. package/dist/cli/commands/analyze.js.map +1 -0
  16. package/dist/cli/commands/audit.d.ts +1 -0
  17. package/dist/cli/commands/audit.js +53 -0
  18. package/dist/cli/commands/audit.js.map +1 -0
  19. package/dist/cli/commands/badge.d.ts +1 -0
  20. package/dist/cli/commands/badge.js +45 -0
  21. package/dist/cli/commands/badge.js.map +1 -0
  22. package/dist/cli/commands/ci.d.ts +1 -0
  23. package/dist/cli/commands/ci.js +57 -0
  24. package/dist/cli/commands/ci.js.map +1 -0
  25. package/dist/cli/commands/coupling.d.ts +1 -0
  26. package/dist/cli/commands/coupling.js +83 -0
  27. package/dist/cli/commands/coupling.js.map +1 -0
  28. package/dist/cli/commands/coverage.d.ts +1 -0
  29. package/dist/cli/commands/coverage.js +63 -0
  30. package/dist/cli/commands/coverage.js.map +1 -0
  31. package/dist/cli/commands/dependencies.d.ts +1 -0
  32. package/dist/cli/commands/dependencies.js +46 -0
  33. package/dist/cli/commands/dependencies.js.map +1 -0
  34. package/dist/cli/commands/diagram.d.ts +1 -0
  35. package/dist/cli/commands/diagram.js +45 -0
  36. package/dist/cli/commands/diagram.js.map +1 -0
  37. package/dist/cli/commands/diff.d.ts +1 -0
  38. package/dist/cli/commands/diff.js +70 -0
  39. package/dist/cli/commands/diff.js.map +1 -0
  40. package/dist/cli/commands/doctor.d.ts +1 -0
  41. package/dist/cli/commands/doctor.js +62 -0
  42. package/dist/cli/commands/doctor.js.map +1 -0
  43. package/dist/cli/commands/explain.d.ts +1 -0
  44. package/dist/cli/commands/explain.js +42 -0
  45. package/dist/cli/commands/explain.js.map +1 -0
  46. package/dist/cli/commands/explainIssue.d.ts +1 -0
  47. package/dist/cli/commands/explainIssue.js +49 -0
  48. package/dist/cli/commands/explainIssue.js.map +1 -0
  49. package/dist/cli/commands/file.d.ts +1 -0
  50. package/dist/cli/commands/file.js +45 -0
  51. package/dist/cli/commands/file.js.map +1 -0
  52. package/dist/cli/commands/fix.d.ts +1 -0
  53. package/dist/cli/commands/fix.js +70 -0
  54. package/dist/cli/commands/fix.js.map +1 -0
  55. package/dist/cli/commands/fixSuggest.d.ts +1 -0
  56. package/dist/cli/commands/fixSuggest.js +71 -0
  57. package/dist/cli/commands/fixSuggest.js.map +1 -0
  58. package/dist/cli/commands/help.d.ts +1 -0
  59. package/dist/cli/commands/help.js +11 -0
  60. package/dist/cli/commands/help.js.map +1 -0
  61. package/dist/cli/commands/hotspots.d.ts +1 -0
  62. package/dist/cli/commands/hotspots.js +74 -0
  63. package/dist/cli/commands/hotspots.js.map +1 -0
  64. package/dist/cli/commands/mcp.d.ts +1 -0
  65. package/dist/cli/commands/mcp.js +21 -0
  66. package/dist/cli/commands/mcp.js.map +1 -0
  67. package/dist/cli/commands/outdated.d.ts +1 -0
  68. package/dist/cli/commands/outdated.js +51 -0
  69. package/dist/cli/commands/outdated.js.map +1 -0
  70. package/dist/cli/commands/prDiff.d.ts +1 -0
  71. package/dist/cli/commands/prDiff.js +59 -0
  72. package/dist/cli/commands/prDiff.js.map +1 -0
  73. package/dist/cli/commands/review.d.ts +1 -0
  74. package/dist/cli/commands/review.js +66 -0
  75. package/dist/cli/commands/review.js.map +1 -0
  76. package/dist/cli/commands/search.d.ts +1 -0
  77. package/dist/cli/commands/search.js +233 -0
  78. package/dist/cli/commands/search.js.map +1 -0
  79. package/dist/cli/commands/structure.d.ts +1 -0
  80. package/dist/cli/commands/structure.js +58 -0
  81. package/dist/cli/commands/structure.js.map +1 -0
  82. package/dist/cli/commands/upgrade.d.ts +1 -0
  83. package/dist/cli/commands/upgrade.js +44 -0
  84. package/dist/cli/commands/upgrade.js.map +1 -0
  85. package/dist/cli/commands/workspaces.d.ts +1 -0
  86. package/dist/cli/commands/workspaces.js +35 -0
  87. package/dist/cli/commands/workspaces.js.map +1 -0
  88. package/dist/cli/index.js +51 -1416
  89. package/dist/cli/index.js.map +1 -1
  90. package/dist/core/ast.d.ts +20 -0
  91. package/dist/core/ast.js +190 -0
  92. package/dist/core/ast.js.map +1 -1
  93. package/dist/core/auditRunner.d.ts +8 -0
  94. package/dist/core/auditRunner.js +50 -1
  95. package/dist/core/auditRunner.js.map +1 -1
  96. package/dist/core/codeGraph.d.ts +7 -1
  97. package/dist/core/codeGraph.js +2 -0
  98. package/dist/core/codeGraph.js.map +1 -1
  99. package/dist/core/couplingAnalyzer.d.ts +1 -1
  100. package/dist/core/couplingAnalyzer.js +3 -3
  101. package/dist/core/dependencyAnalyzer.d.ts +15 -1
  102. package/dist/core/dependencyAnalyzer.js +115 -18
  103. package/dist/core/dependencyAnalyzer.js.map +1 -1
  104. package/dist/core/explainIssue.d.ts +9 -0
  105. package/dist/core/explainIssue.js +106 -0
  106. package/dist/core/explainIssue.js.map +1 -0
  107. package/dist/core/fileInspector.js +12 -0
  108. package/dist/core/fileInspector.js.map +1 -1
  109. package/dist/core/fixSuggest.d.ts +41 -0
  110. package/dist/core/fixSuggest.js +327 -0
  111. package/dist/core/fixSuggest.js.map +1 -0
  112. package/dist/core/hotspotAnalyzer.js +2 -2
  113. package/dist/core/indexCache.js +5 -1
  114. package/dist/core/indexCache.js.map +1 -1
  115. package/dist/core/issueEngine.js +18 -0
  116. package/dist/core/issueEngine.js.map +1 -1
  117. package/dist/core/languages/LanguageAdapter.d.ts +1 -1
  118. package/dist/core/languages/goAdapter.js +12 -5
  119. package/dist/core/languages/goAdapter.js.map +1 -1
  120. package/dist/core/languages/goCallSites.d.ts +20 -0
  121. package/dist/core/languages/goCallSites.js +42 -0
  122. package/dist/core/languages/goCallSites.js.map +1 -0
  123. package/dist/core/languages/goCyclomatic.d.ts +1 -1
  124. package/dist/core/languages/goCyclomatic.js +2 -2
  125. package/dist/core/languages/goExports.d.ts +1 -1
  126. package/dist/core/languages/goExports.js +1 -1
  127. package/dist/core/languages/goFunctions.d.ts +24 -0
  128. package/dist/core/languages/goFunctions.js +99 -0
  129. package/dist/core/languages/goFunctions.js.map +1 -0
  130. package/dist/core/languages/goManifests.d.ts +1 -1
  131. package/dist/core/languages/goManifests.js +2 -2
  132. package/dist/core/languages/javaAdapter.d.ts +2 -0
  133. package/dist/core/languages/javaAdapter.js +153 -0
  134. package/dist/core/languages/javaAdapter.js.map +1 -0
  135. package/dist/core/languages/javaCallSites.d.ts +16 -0
  136. package/dist/core/languages/javaCallSites.js +45 -0
  137. package/dist/core/languages/javaCallSites.js.map +1 -0
  138. package/dist/core/languages/javaCyclomatic.d.ts +21 -0
  139. package/dist/core/languages/javaCyclomatic.js +49 -0
  140. package/dist/core/languages/javaCyclomatic.js.map +1 -0
  141. package/dist/core/languages/javaExports.d.ts +25 -0
  142. package/dist/core/languages/javaExports.js +80 -0
  143. package/dist/core/languages/javaExports.js.map +1 -0
  144. package/dist/core/languages/javaFunctions.d.ts +22 -0
  145. package/dist/core/languages/javaFunctions.js +87 -0
  146. package/dist/core/languages/javaFunctions.js.map +1 -0
  147. package/dist/core/languages/javaImports.d.ts +25 -0
  148. package/dist/core/languages/javaImports.js +49 -0
  149. package/dist/core/languages/javaImports.js.map +1 -0
  150. package/dist/core/languages/javaManifests.d.ts +25 -0
  151. package/dist/core/languages/javaManifests.js +86 -0
  152. package/dist/core/languages/javaManifests.js.map +1 -0
  153. package/dist/core/languages/pythonAdapter.js +8 -1
  154. package/dist/core/languages/pythonAdapter.js.map +1 -1
  155. package/dist/core/languages/pythonCallSites.d.ts +19 -0
  156. package/dist/core/languages/pythonCallSites.js +40 -0
  157. package/dist/core/languages/pythonCallSites.js.map +1 -0
  158. package/dist/core/languages/pythonFunctions.d.ts +23 -0
  159. package/dist/core/languages/pythonFunctions.js +87 -0
  160. package/dist/core/languages/pythonFunctions.js.map +1 -0
  161. package/dist/core/languages/registry.js +3 -1
  162. package/dist/core/languages/registry.js.map +1 -1
  163. package/dist/core/languages/rubyAdapter.d.ts +2 -0
  164. package/dist/core/languages/rubyAdapter.js +136 -0
  165. package/dist/core/languages/rubyAdapter.js.map +1 -0
  166. package/dist/core/languages/rubyCallSites.d.ts +16 -0
  167. package/dist/core/languages/rubyCallSites.js +34 -0
  168. package/dist/core/languages/rubyCallSites.js.map +1 -0
  169. package/dist/core/languages/rubyCyclomatic.d.ts +19 -0
  170. package/dist/core/languages/rubyCyclomatic.js +47 -0
  171. package/dist/core/languages/rubyCyclomatic.js.map +1 -0
  172. package/dist/core/languages/rubyExports.d.ts +24 -0
  173. package/dist/core/languages/rubyExports.js +53 -0
  174. package/dist/core/languages/rubyExports.js.map +1 -0
  175. package/dist/core/languages/rubyFunctions.d.ts +22 -0
  176. package/dist/core/languages/rubyFunctions.js +91 -0
  177. package/dist/core/languages/rubyFunctions.js.map +1 -0
  178. package/dist/core/languages/rubyImports.d.ts +12 -0
  179. package/dist/core/languages/rubyImports.js +75 -0
  180. package/dist/core/languages/rubyImports.js.map +1 -0
  181. package/dist/core/languages/rubyManifests.d.ts +20 -0
  182. package/dist/core/languages/rubyManifests.js +55 -0
  183. package/dist/core/languages/rubyManifests.js.map +1 -0
  184. package/dist/core/languages/treeSitterLoader.js +3 -1
  185. package/dist/core/languages/treeSitterLoader.js.map +1 -1
  186. package/dist/core/monorepo.js +5 -5
  187. package/dist/core/outdatedDetector.d.ts +13 -2
  188. package/dist/core/outdatedDetector.js +86 -16
  189. package/dist/core/outdatedDetector.js.map +1 -1
  190. package/dist/core/prDiff.d.ts +1 -1
  191. package/dist/core/prDiff.js +2 -2
  192. package/dist/core/review.d.ts +21 -0
  193. package/dist/core/review.js +457 -0
  194. package/dist/core/review.js.map +1 -0
  195. package/dist/grammars/tree-sitter-java.wasm +0 -0
  196. package/dist/grammars/tree-sitter-ruby.wasm +0 -0
  197. package/dist/index.d.ts +5 -1
  198. package/dist/index.js +3 -0
  199. package/dist/index.js.map +1 -1
  200. package/dist/mcp/server.js +0 -22
  201. package/dist/mcp/server.js.map +1 -1
  202. package/dist/mcp/tools/_shared.d.ts +24 -0
  203. package/dist/mcp/tools/_shared.js +82 -0
  204. package/dist/mcp/tools/_shared.js.map +1 -0
  205. package/dist/mcp/tools/analyze.d.ts +2 -0
  206. package/dist/mcp/tools/analyze.js +55 -0
  207. package/dist/mcp/tools/analyze.js.map +1 -0
  208. package/dist/mcp/tools/audit.d.ts +2 -0
  209. package/dist/mcp/tools/audit.js +37 -0
  210. package/dist/mcp/tools/audit.js.map +1 -0
  211. package/dist/mcp/tools/coupling.d.ts +2 -0
  212. package/dist/mcp/tools/coupling.js +67 -0
  213. package/dist/mcp/tools/coupling.js.map +1 -0
  214. package/dist/mcp/tools/coverage.d.ts +2 -0
  215. package/dist/mcp/tools/coverage.js +53 -0
  216. package/dist/mcp/tools/coverage.js.map +1 -0
  217. package/dist/mcp/tools/dependencies.d.ts +2 -0
  218. package/dist/mcp/tools/dependencies.js +22 -0
  219. package/dist/mcp/tools/dependencies.js.map +1 -0
  220. package/dist/mcp/tools/doctor.d.ts +2 -0
  221. package/dist/mcp/tools/doctor.js +30 -0
  222. package/dist/mcp/tools/doctor.js.map +1 -0
  223. package/dist/mcp/tools/explain.d.ts +2 -0
  224. package/dist/mcp/tools/explain.js +30 -0
  225. package/dist/mcp/tools/explain.js.map +1 -0
  226. package/dist/mcp/tools/explainIssue.d.ts +2 -0
  227. package/dist/mcp/tools/explainIssue.js +30 -0
  228. package/dist/mcp/tools/explainIssue.js.map +1 -0
  229. package/dist/mcp/tools/file.d.ts +2 -0
  230. package/dist/mcp/tools/file.js +22 -0
  231. package/dist/mcp/tools/file.js.map +1 -0
  232. package/dist/mcp/tools/fixSuggest.d.ts +2 -0
  233. package/dist/mcp/tools/fixSuggest.js +57 -0
  234. package/dist/mcp/tools/fixSuggest.js.map +1 -0
  235. package/dist/mcp/tools/graph.d.ts +2 -0
  236. package/dist/mcp/tools/graph.js +69 -0
  237. package/dist/mcp/tools/graph.js.map +1 -0
  238. package/dist/mcp/tools/hotspots.d.ts +2 -0
  239. package/dist/mcp/tools/hotspots.js +103 -0
  240. package/dist/mcp/tools/hotspots.js.map +1 -0
  241. package/dist/mcp/tools/outdated.d.ts +2 -0
  242. package/dist/mcp/tools/outdated.js +36 -0
  243. package/dist/mcp/tools/outdated.js.map +1 -0
  244. package/dist/mcp/tools/prDiff.d.ts +2 -0
  245. package/dist/mcp/tools/prDiff.js +38 -0
  246. package/dist/mcp/tools/prDiff.js.map +1 -0
  247. package/dist/mcp/tools/review.d.ts +2 -0
  248. package/dist/mcp/tools/review.js +54 -0
  249. package/dist/mcp/tools/review.js.map +1 -0
  250. package/dist/mcp/tools/search.d.ts +2 -0
  251. package/dist/mcp/tools/search.js +167 -0
  252. package/dist/mcp/tools/search.js.map +1 -0
  253. package/dist/mcp/tools/structure.d.ts +2 -0
  254. package/dist/mcp/tools/structure.js +34 -0
  255. package/dist/mcp/tools/structure.js.map +1 -0
  256. package/dist/mcp/tools/upgrade.d.ts +2 -0
  257. package/dist/mcp/tools/upgrade.js +38 -0
  258. package/dist/mcp/tools/upgrade.js.map +1 -0
  259. package/dist/mcp/tools/workspaces.d.ts +2 -0
  260. package/dist/mcp/tools/workspaces.js +13 -0
  261. package/dist/mcp/tools/workspaces.js.map +1 -0
  262. package/dist/mcp/tools.d.ts +12 -6
  263. package/dist/mcp/tools.js +46 -854
  264. package/dist/mcp/tools.js.map +1 -1
  265. package/dist/reporters/consoleReporter.d.ts +9 -1
  266. package/dist/reporters/consoleReporter.js +177 -0
  267. package/dist/reporters/consoleReporter.js.map +1 -1
  268. package/dist/reporters/jsonReporter.d.ts +9 -1
  269. package/dist/reporters/jsonReporter.js +9 -0
  270. package/dist/reporters/jsonReporter.js.map +1 -1
  271. package/dist/reporters/markdownReporter.d.ts +9 -1
  272. package/dist/reporters/markdownReporter.js +141 -0
  273. package/dist/reporters/markdownReporter.js.map +1 -1
  274. package/dist/tool-manifest.json +446 -0
  275. package/dist/types.d.ts +235 -5
  276. package/dist/utils/config.js +26 -9
  277. package/dist/utils/config.js.map +1 -1
  278. package/package.json +8 -3
@@ -0,0 +1,136 @@
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 { extractRubyFunctions } from './rubyFunctions.js';
7
+ import { extractRubyCallSites } from './rubyCallSites.js';
8
+ import { detectRubyProject } from './rubyManifests.js';
9
+ const RUBY_EXTENSIONS = new Set(['.rb']);
10
+ const MAX_RUBY_FILE = 1024 * 1024;
11
+ let parserPromise = null;
12
+ async function getParser() {
13
+ if (!parserPromise)
14
+ parserPromise = createParserFor('tree-sitter-ruby.wasm');
15
+ return parserPromise;
16
+ }
17
+ export const rubyAdapter = {
18
+ id: 'ruby',
19
+ extensions: RUBY_EXTENSIONS,
20
+ sourceExtensions: RUBY_EXTENSIONS,
21
+ // Ruby has no barrel-file convention.
22
+ barrelBasenames: new Set(),
23
+ maxFileSize: MAX_RUBY_FILE,
24
+ async parse(_filePath, content) {
25
+ try {
26
+ const parser = await getParser();
27
+ const tree = parser.parse(content);
28
+ if (!tree || !tree.rootNode) {
29
+ return {
30
+ ok: false,
31
+ reason: 'tree-sitter returned null tree',
32
+ imports: [],
33
+ exports: [],
34
+ callSites: [],
35
+ lineCount: content ? content.split('\n').length : 0,
36
+ cyclomaticComplexity: 0,
37
+ functions: [],
38
+ };
39
+ }
40
+ const root = tree.rootNode;
41
+ const imports = extractRubyImports(root);
42
+ const exports = extractRubyExports(root);
43
+ const cyclomaticComplexity = extractRubyCyclomatic(root);
44
+ const callSites = extractRubyCallSites(root);
45
+ const functions = extractRubyFunctions(root);
46
+ return {
47
+ ok: true,
48
+ imports,
49
+ exports,
50
+ callSites,
51
+ lineCount: content ? content.split('\n').length : 0,
52
+ cyclomaticComplexity,
53
+ functions,
54
+ };
55
+ }
56
+ catch (err) {
57
+ const msg = err instanceof Error ? err.message : String(err);
58
+ return {
59
+ ok: false,
60
+ reason: `ruby parse failure: ${msg.slice(0, 120)}`,
61
+ imports: [],
62
+ exports: [],
63
+ callSites: [],
64
+ lineCount: content ? content.split('\n').length : 0,
65
+ cyclomaticComplexity: 0,
66
+ functions: [],
67
+ };
68
+ }
69
+ },
70
+ resolveImport(importingFile, source, graphFiles, context) {
71
+ return resolveRubyImport(importingFile, source, graphFiles, context);
72
+ },
73
+ toPackageName(source) {
74
+ if (!source)
75
+ return null;
76
+ // `./foo` and `../foo` are clearly local; let resolveImport handle.
77
+ if (source.startsWith('./') || source.startsWith('../'))
78
+ return null;
79
+ // Otherwise treat the whole specifier as the gem name (e.g. `json`, `active_support/core_ext`).
80
+ // Take only the first path segment for ranking purposes.
81
+ return source.split('/')[0];
82
+ },
83
+ async preparePackageRoots(rootPath, files) {
84
+ const info = await detectRubyProject(rootPath, files);
85
+ if (!info)
86
+ return { packageRoots: [], meta: undefined };
87
+ return {
88
+ packageRoots: info.sourceRoots.map((r) => path.relative(rootPath, r) || '.'),
89
+ meta: { rubyProject: info },
90
+ };
91
+ },
92
+ };
93
+ /**
94
+ * Resolve a Ruby require / require_relative to a repo-local file path.
95
+ *
96
+ * require_relative 'sibling' → resolved against the importing file's dir.
97
+ * require_relative '../helpers' → walked up.
98
+ * require 'json' → null (stdlib / gem; no source-root match).
99
+ * require 'foo/bar' → for each sourceRoot, probe `foo/bar.rb`.
100
+ *
101
+ * Ruby `require` semantics depend on $LOAD_PATH at runtime; we approximate by
102
+ * checking sourceRoots (typically `lib/`). This catches the common case where
103
+ * a gem requires its own internal modules by their lib-relative path.
104
+ */
105
+ function resolveRubyImport(importingFile, source, graphFiles, context) {
106
+ if (!source)
107
+ return null;
108
+ // The original call site distinguishes require vs require_relative, but by
109
+ // the time we're here we only have the source string. Heuristic: if the
110
+ // string starts with `./` or `../`, treat as relative; otherwise probe BOTH
111
+ // relative-to-importer AND relative-to-each-source-root, and pick the first
112
+ // that resolves. This captures both `require_relative` AND `require` of a
113
+ // gem-internal lib path.
114
+ const sourceWithExt = source.endsWith('.rb') ? source : `${source}.rb`;
115
+ if (source.startsWith('./') || source.startsWith('../')) {
116
+ const dir = path.posix.dirname(importingFile);
117
+ const joined = path.posix.normalize(path.posix.join(dir, sourceWithExt));
118
+ return graphFiles.has(joined) ? joined : null;
119
+ }
120
+ // Try each source root.
121
+ const sourceRootsRel = context.packageRoots ?? ['.'];
122
+ for (const root of sourceRootsRel) {
123
+ const rootSegs = root === '.' || root === '' ? [] : root.split('/').filter(Boolean);
124
+ const candidate = [...rootSegs, sourceWithExt].join('/');
125
+ if (graphFiles.has(candidate))
126
+ return candidate;
127
+ }
128
+ // Also try treating the source as relative to the importing file's directory
129
+ // (require_relative without leading `./`). Some style guides drop the prefix.
130
+ const dir = path.posix.dirname(importingFile);
131
+ const joined = path.posix.normalize(path.posix.join(dir, sourceWithExt));
132
+ if (graphFiles.has(joined))
133
+ return joined;
134
+ return null;
135
+ }
136
+ //# sourceMappingURL=rubyAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rubyAdapter.js","sourceRoot":"","sources":["../../../src/core/languages/rubyAdapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Extract call-site identifiers from a tree-sitter-ruby AST.
3
+ *
4
+ * Ruby's `call` node covers BOTH bare method calls (`foo(...)`) and
5
+ * receiver-method calls (`obj.method(...)`). The method identifier is on the
6
+ * `method` field. We exclude `require` family calls so they don't pollute the
7
+ * "who calls foo" lookup with import noise.
8
+ */
9
+ interface TsNode {
10
+ type: string;
11
+ text: string;
12
+ namedChildren: TsNode[];
13
+ childForFieldName?(name: string): TsNode | null;
14
+ }
15
+ export declare function extractRubyCallSites(root: TsNode): string[];
16
+ export {};
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Extract call-site identifiers from a tree-sitter-ruby AST.
3
+ *
4
+ * Ruby's `call` node covers BOTH bare method calls (`foo(...)`) and
5
+ * receiver-method calls (`obj.method(...)`). The method identifier is on the
6
+ * `method` field. We exclude `require` family calls so they don't pollute the
7
+ * "who calls foo" lookup with import noise.
8
+ */
9
+ const REQUIRE_NAMES = new Set(['require', 'require_relative', 'load', 'autoload']);
10
+ export function extractRubyCallSites(root) {
11
+ const seen = new Set();
12
+ walk(root, seen);
13
+ return [...seen];
14
+ }
15
+ function walk(node, out) {
16
+ if (node.type === 'call') {
17
+ const methodNode = node.childForFieldName?.('method') ?? findChild(node, 'identifier');
18
+ if (methodNode &&
19
+ (methodNode.type === 'identifier' || methodNode.type === 'constant') &&
20
+ !REQUIRE_NAMES.has(methodNode.text)) {
21
+ out.add(methodNode.text);
22
+ }
23
+ }
24
+ for (const child of node.namedChildren)
25
+ walk(child, out);
26
+ }
27
+ function findChild(node, type) {
28
+ for (const c of node.namedChildren) {
29
+ if (c.type === type)
30
+ return c;
31
+ }
32
+ return null;
33
+ }
34
+ //# sourceMappingURL=rubyCallSites.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rubyCallSites.js","sourceRoot":"","sources":["../../../src/core/languages/rubyCallSites.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,kBAAkB,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;AAEnF,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,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,UAAU,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QACvF,IACE,UAAU;YACV,CAAC,UAAU,CAAC,IAAI,KAAK,YAAY,IAAI,UAAU,CAAC,IAAI,KAAK,UAAU,CAAC;YACpE,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EACnC,CAAC;YACD,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,aAAa;QAAE,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAC3D,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,19 @@
1
+ interface TsNode {
2
+ type: string;
3
+ text: string;
4
+ namedChildren: TsNode[];
5
+ }
6
+ /**
7
+ * File-level McCabe cyclomatic complexity for a tree-sitter-ruby AST.
8
+ *
9
+ * Decision points:
10
+ * if, elsif, unless +1 each (else does not count)
11
+ * while, until, for +1 each
12
+ * case + each `when` +1 per `when` (the `case` itself does NOT
13
+ * add - only the branch labels do)
14
+ * rescue +1
15
+ * conditional (ternary `?:`) +1
16
+ * binary with `&&` / `||` / `and` / `or` +1 each
17
+ */
18
+ export declare function extractRubyCyclomatic(root: TsNode): number;
19
+ export {};
@@ -0,0 +1,47 @@
1
+ /**
2
+ * File-level McCabe cyclomatic complexity for a tree-sitter-ruby AST.
3
+ *
4
+ * Decision points:
5
+ * if, elsif, unless +1 each (else does not count)
6
+ * while, until, for +1 each
7
+ * case + each `when` +1 per `when` (the `case` itself does NOT
8
+ * add - only the branch labels do)
9
+ * rescue +1
10
+ * conditional (ternary `?:`) +1
11
+ * binary with `&&` / `||` / `and` / `or` +1 each
12
+ */
13
+ export function extractRubyCyclomatic(root) {
14
+ let decisions = 0;
15
+ walk(root, (n) => {
16
+ if (isDecisionPoint(n))
17
+ decisions++;
18
+ });
19
+ return decisions + 1;
20
+ }
21
+ function isDecisionPoint(n) {
22
+ switch (n.type) {
23
+ case 'if':
24
+ case 'elsif':
25
+ case 'unless':
26
+ case 'while':
27
+ case 'until':
28
+ case 'for':
29
+ case 'when':
30
+ case 'rescue':
31
+ case 'conditional':
32
+ return true;
33
+ case 'binary': {
34
+ const t = n.text;
35
+ // Ruby supports both symbolic (&&, ||) and word (and, or) operators.
36
+ return /(\s|^)(\|\||&&)(\s|$)/.test(t) || /\b(and|or)\b/.test(t);
37
+ }
38
+ default:
39
+ return false;
40
+ }
41
+ }
42
+ function walk(node, visit) {
43
+ visit(node);
44
+ for (const child of node.namedChildren)
45
+ walk(child, visit);
46
+ }
47
+ //# sourceMappingURL=rubyCyclomatic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rubyCyclomatic.js","sourceRoot":"","sources":["../../../src/core/languages/rubyCyclomatic.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,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,IAAI,CAAC;QACV,KAAK,OAAO,CAAC;QACb,KAAK,QAAQ,CAAC;QACd,KAAK,OAAO,CAAC;QACb,KAAK,OAAO,CAAC;QACb,KAAK,KAAK,CAAC;QACX,KAAK,MAAM,CAAC;QACZ,KAAK,QAAQ,CAAC;QACd,KAAK,aAAa;YAChB,OAAO,IAAI,CAAC;QACd,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACjB,qEAAqE;YACrE,OAAO,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACnE,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,24 @@
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 Ruby AST.
13
+ *
14
+ * Ruby has no visibility marker on top-level constants - every top-level
15
+ * `class`, `module`, and `def` is part of the file's surface. We capture all
16
+ * three at the top level only (nested defs are part of an enclosing class's
17
+ * surface, not separate file-level exports).
18
+ *
19
+ * Names: leading underscore is treated as a private convention (`_helper`),
20
+ * but we still record it - Ruby doesn't enforce it, and other modules can
21
+ * still call it.
22
+ */
23
+ export declare function extractRubyExports(root: TsNode): AstExport[];
24
+ export {};
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Extract package-level "exports" from a Ruby AST.
3
+ *
4
+ * Ruby has no visibility marker on top-level constants - every top-level
5
+ * `class`, `module`, and `def` is part of the file's surface. We capture all
6
+ * three at the top level only (nested defs are part of an enclosing class's
7
+ * surface, not separate file-level exports).
8
+ *
9
+ * Names: leading underscore is treated as a private convention (`_helper`),
10
+ * but we still record it - Ruby doesn't enforce it, and other modules can
11
+ * still call it.
12
+ */
13
+ export function extractRubyExports(root) {
14
+ const out = [];
15
+ for (const child of root.namedChildren) {
16
+ visitTopLevel(child, out);
17
+ }
18
+ return out;
19
+ }
20
+ function visitTopLevel(node, out) {
21
+ switch (node.type) {
22
+ case 'class':
23
+ addNamed(node, 'class', out, 'constant');
24
+ return;
25
+ case 'module':
26
+ addNamed(node, 'type', out, 'constant');
27
+ return;
28
+ case 'method':
29
+ addNamed(node, 'function', out, 'identifier');
30
+ return;
31
+ default:
32
+ return;
33
+ }
34
+ }
35
+ function addNamed(node, kind, out, nameType) {
36
+ const nameNode = node.childForFieldName?.('name') ?? findChild(node, nameType) ?? findChild(node, 'identifier');
37
+ if (!nameNode)
38
+ return;
39
+ out.push({
40
+ name: nameNode.text,
41
+ kind,
42
+ typeOnly: false,
43
+ line: node.startPosition.row + 1,
44
+ });
45
+ }
46
+ function findChild(node, type) {
47
+ for (const c of node.namedChildren) {
48
+ if (c.type === type)
49
+ return c;
50
+ }
51
+ return null;
52
+ }
53
+ //# sourceMappingURL=rubyExports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rubyExports.js","sourceRoot":"","sources":["../../../src/core/languages/rubyExports.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;GAWG;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,OAAO;YACV,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;YACzC,OAAO;QACT,KAAK,QAAQ;YACX,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;YACxC,OAAO;QACT,KAAK,QAAQ;YACX,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC;YAC9C,OAAO;QACT;YACE,OAAO;IACX,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CACf,IAAY,EACZ,IAAgB,EAChB,GAAgB,EAChB,QAAmC;IAEnC,MAAM,QAAQ,GACZ,IAAI,CAAC,iBAAiB,EAAE,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,SAAS,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IACjG,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,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,22 @@
1
+ import type { FunctionInfo } from '../ast.js';
2
+ interface TsNode {
3
+ type: string;
4
+ text: string;
5
+ startPosition: {
6
+ row: number;
7
+ column: number;
8
+ };
9
+ endPosition: {
10
+ row: number;
11
+ column: number;
12
+ };
13
+ namedChildren: TsNode[];
14
+ childForFieldName?: (name: string) => TsNode | null;
15
+ }
16
+ /**
17
+ * Per-method McCabe CC for Ruby. Walks `method` (def) and `singleton_method`
18
+ * (def self.foo) nodes inside class/module bodies. Methods are named
19
+ * `Class.method` (or `Module.method`).
20
+ */
21
+ export declare function extractRubyFunctions(root: TsNode): FunctionInfo[];
22
+ export {};
@@ -0,0 +1,91 @@
1
+ const RUBY_DECISION_NODES = new Set([
2
+ 'if',
3
+ 'elsif',
4
+ 'unless',
5
+ 'while',
6
+ 'until',
7
+ 'for',
8
+ 'when',
9
+ 'rescue',
10
+ 'conditional',
11
+ ]);
12
+ /**
13
+ * Per-method McCabe CC for Ruby. Walks `method` (def) and `singleton_method`
14
+ * (def self.foo) nodes inside class/module bodies. Methods are named
15
+ * `Class.method` (or `Module.method`).
16
+ */
17
+ export function extractRubyFunctions(root) {
18
+ const out = [];
19
+ walk(root, null, out);
20
+ return out;
21
+ }
22
+ function walk(node, className, out) {
23
+ if (node.type === 'class' || node.type === 'module') {
24
+ const nameNode = node.childForFieldName ? node.childForFieldName('name') : findChild(node, 'constant');
25
+ const cls = nameNode?.text ?? null;
26
+ const body = node.childForFieldName ? node.childForFieldName('body') : null;
27
+ if (body) {
28
+ for (const child of body.namedChildren)
29
+ walk(child, cls, out);
30
+ }
31
+ else {
32
+ for (const child of node.namedChildren)
33
+ walk(child, cls, out);
34
+ }
35
+ return;
36
+ }
37
+ if (node.type === 'method' || node.type === 'singleton_method') {
38
+ const nameNode = node.childForFieldName ? node.childForFieldName('name') : findChild(node, 'identifier');
39
+ const baseName = nameNode?.text ?? '<anonymous>';
40
+ const fnName = className ? `${className}.${baseName}` : baseName;
41
+ const line = node.startPosition.row + 1;
42
+ const endLine = node.endPosition.row + 1;
43
+ const cc = countDecisions(node);
44
+ out.push({ name: fnName, line, endLine, cyclomaticComplexity: cc });
45
+ return;
46
+ }
47
+ for (const child of node.namedChildren)
48
+ walk(child, className, out);
49
+ }
50
+ function countDecisions(fnNode) {
51
+ let count = 0;
52
+ const body = fnNode.childForFieldName ? fnNode.childForFieldName('body') : null;
53
+ if (!body)
54
+ return 1;
55
+ walkSkipNested(body, (n) => {
56
+ if (RUBY_DECISION_NODES.has(n.type)) {
57
+ count++;
58
+ return;
59
+ }
60
+ if (n.type === 'binary') {
61
+ if (/(\s|^)(\|\||&&)(\s|$)/.test(n.text) || /\b(and|or)\b/.test(n.text))
62
+ count++;
63
+ }
64
+ });
65
+ return count + 1;
66
+ }
67
+ function walkSkipNested(node, visit) {
68
+ visit(node);
69
+ for (const child of node.namedChildren) {
70
+ if (child.type === 'method' ||
71
+ child.type === 'singleton_method' ||
72
+ child.type === 'class' ||
73
+ child.type === 'module' ||
74
+ child.type === 'lambda' ||
75
+ child.type === 'block') {
76
+ // Skip blocks too: in Ruby, `each { |x| ... }` is a block; treating it
77
+ // as opaque keeps method CC tight to the method's own logic. (This is
78
+ // a tradeoff - some style guides include block branches. We pick the
79
+ // more conservative reading.)
80
+ continue;
81
+ }
82
+ walkSkipNested(child, visit);
83
+ }
84
+ }
85
+ function findChild(node, type) {
86
+ for (const c of node.namedChildren)
87
+ if (c.type === type)
88
+ return c;
89
+ return null;
90
+ }
91
+ //# sourceMappingURL=rubyFunctions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rubyFunctions.js","sourceRoot":"","sources":["../../../src/core/languages/rubyFunctions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
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
+ childForFieldName?(name: string): TsNode | null;
10
+ }
11
+ export declare function extractRubyImports(root: TsNode): AstImport[];
12
+ export {};
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Extract `require` / `require_relative` / `load` / `autoload` calls from a
3
+ * Ruby AST.
4
+ *
5
+ * Ruby has no `import` keyword - these are method calls. Tree-sitter-ruby
6
+ * represents them as `call` nodes whose method identifier is `require` /
7
+ * `require_relative` / etc., with a string argument carrying the path.
8
+ *
9
+ * Captured shapes:
10
+ * require 'json'
11
+ * require_relative 'helpers/util'
12
+ * load 'config.rb'
13
+ * autoload :Sym, 'lib/sym'
14
+ *
15
+ * Only top-level (module-level) calls are collected. Inner-scope requires
16
+ * are valid Ruby but rare and noisy for graph purposes.
17
+ */
18
+ const REQUIRE_NAMES = new Set(['require', 'require_relative', 'load', 'autoload']);
19
+ export function extractRubyImports(root) {
20
+ const out = [];
21
+ visit(root, out);
22
+ return out;
23
+ }
24
+ function visit(node, out) {
25
+ for (const child of node.namedChildren) {
26
+ if (child.type === 'call') {
27
+ handleCall(child, out);
28
+ }
29
+ else if (child.type === 'body_statement' || child.type === 'program') {
30
+ visit(child, out);
31
+ }
32
+ // Don't descend into method/class/module bodies - only top-level requires.
33
+ }
34
+ }
35
+ function handleCall(node, out) {
36
+ // The `method` field holds the identifier we're calling.
37
+ const methodNode = node.childForFieldName?.('method') ?? findChild(node, 'identifier');
38
+ if (!methodNode)
39
+ return;
40
+ if (!REQUIRE_NAMES.has(methodNode.text))
41
+ return;
42
+ const args = node.childForFieldName?.('arguments') ?? findChild(node, 'argument_list');
43
+ if (!args)
44
+ return;
45
+ const line = node.startPosition.row + 1;
46
+ // For autoload(:Symbol, 'path'), the path is the SECOND argument.
47
+ // For require/require_relative/load, the path is the FIRST and only.
48
+ const isAutoload = methodNode.text === 'autoload';
49
+ const targetIndex = isAutoload ? 1 : 0;
50
+ let i = 0;
51
+ for (const arg of args.namedChildren) {
52
+ if (arg.type === 'string' && i === targetIndex) {
53
+ const inner = findChild(arg, 'string_content');
54
+ if (inner) {
55
+ out.push({
56
+ source: inner.text,
57
+ kind: methodNode.text === 'require_relative' ? 'static' : 'static',
58
+ specifiers: [],
59
+ typeOnly: false,
60
+ line,
61
+ });
62
+ }
63
+ return;
64
+ }
65
+ i++;
66
+ }
67
+ }
68
+ function findChild(node, type) {
69
+ for (const c of node.namedChildren) {
70
+ if (c.type === type)
71
+ return c;
72
+ }
73
+ return null;
74
+ }
75
+ //# sourceMappingURL=rubyImports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rubyImports.js","sourceRoot":"","sources":["../../../src/core/languages/rubyImports.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,kBAAkB,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;AAEnF,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,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,MAAM,EAAE,CAAC;YAC1B,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACzB,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACvE,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACpB,CAAC;QACD,2EAA2E;IAC7E,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,IAAY,EAAE,GAAgB;IAChD,yDAAyD;IACzD,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IACvF,IAAI,CAAC,UAAU;QAAE,OAAO;IACxB,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO;IAEhD,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,WAAW,CAAC,IAAI,SAAS,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;IACvF,IAAI,CAAC,IAAI;QAAE,OAAO;IAElB,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC,CAAC;IACxC,kEAAkE;IAClE,qEAAqE;IACrE,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,KAAK,UAAU,CAAC;IAClD,MAAM,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEvC,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACrC,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,WAAW,EAAE,CAAC;YAC/C,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;YAC/C,IAAI,KAAK,EAAE,CAAC;gBACV,GAAG,CAAC,IAAI,CAAC;oBACP,MAAM,EAAE,KAAK,CAAC,IAAI;oBAClB,IAAI,EAAE,UAAU,CAAC,IAAI,KAAK,kBAAkB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ;oBAClE,UAAU,EAAE,EAAE;oBACd,QAAQ,EAAE,KAAK;oBACf,IAAI;iBACL,CAAC,CAAC;YACL,CAAC;YACD,OAAO;QACT,CAAC;QACD,CAAC,EAAE,CAAC;IACN,CAAC;AACH,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,20 @@
1
+ import type { FileEntry } from '../../types.js';
2
+ export interface RubyProjectInfo {
3
+ /** Absolute paths to source roots - directories under which require_relative paths resolve. */
4
+ sourceRoots: string[];
5
+ /** What kind of Ruby project this looks like. Informational only. */
6
+ projectKind: 'gem' | 'rails' | 'plain' | 'unknown';
7
+ }
8
+ /**
9
+ * Detect a Ruby project layout.
10
+ *
11
+ * - **Gem**: presence of `*.gemspec` or `Gemfile`. Source root is `lib/`.
12
+ * - **Rails**: presence of `config/application.rb`. Source roots include
13
+ * `app/`, `lib/`, `config/`.
14
+ * - **Plain**: any directory with `.rb` files but no manifest. Source root is
15
+ * the repo root.
16
+ *
17
+ * Returns `null` only when no `.rb` files exist and no manifest is present -
18
+ * i.e. this is not a Ruby project.
19
+ */
20
+ export declare function detectRubyProject(rootPath: string, files: FileEntry[]): Promise<RubyProjectInfo | null>;
@@ -0,0 +1,55 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ /**
4
+ * Detect a Ruby project layout.
5
+ *
6
+ * - **Gem**: presence of `*.gemspec` or `Gemfile`. Source root is `lib/`.
7
+ * - **Rails**: presence of `config/application.rb`. Source roots include
8
+ * `app/`, `lib/`, `config/`.
9
+ * - **Plain**: any directory with `.rb` files but no manifest. Source root is
10
+ * the repo root.
11
+ *
12
+ * Returns `null` only when no `.rb` files exist and no manifest is present -
13
+ * i.e. this is not a Ruby project.
14
+ */
15
+ export async function detectRubyProject(rootPath, files) {
16
+ const rbFiles = files.filter((f) => f.relativePath.endsWith('.rb'));
17
+ const hasGemfile = await fileExists(path.join(rootPath, 'Gemfile'));
18
+ const hasGemspec = files.some((f) => f.relativePath.endsWith('.gemspec'));
19
+ const hasRailsConfig = await fileExists(path.join(rootPath, 'config/application.rb'));
20
+ if (!hasGemfile && !hasGemspec && !hasRailsConfig && rbFiles.length === 0)
21
+ return null;
22
+ let projectKind;
23
+ const roots = new Set();
24
+ if (hasRailsConfig) {
25
+ projectKind = 'rails';
26
+ for (const r of ['app', 'lib', 'config']) {
27
+ if (await fileExists(path.join(rootPath, r)))
28
+ roots.add(r);
29
+ }
30
+ }
31
+ else if (hasGemfile || hasGemspec) {
32
+ projectKind = 'gem';
33
+ if (await fileExists(path.join(rootPath, 'lib')))
34
+ roots.add('lib');
35
+ }
36
+ else {
37
+ projectKind = 'plain';
38
+ }
39
+ if (roots.size === 0)
40
+ roots.add('.');
41
+ return {
42
+ sourceRoots: [...roots].map((r) => path.join(rootPath, r)),
43
+ projectKind,
44
+ };
45
+ }
46
+ async function fileExists(p) {
47
+ try {
48
+ await fs.access(p);
49
+ return true;
50
+ }
51
+ catch {
52
+ return false;
53
+ }
54
+ }
55
+ //# sourceMappingURL=rubyManifests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rubyManifests.js","sourceRoot":"","sources":["../../../src/core/languages/rubyManifests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAU7B;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,QAAgB,EAChB,KAAkB;IAElB,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IACpE,MAAM,UAAU,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;IACpE,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IAC1E,MAAM,cAAc,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,uBAAuB,CAAC,CAAC,CAAC;IAEtF,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,IAAI,CAAC,cAAc,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvF,IAAI,WAA2C,CAAC;IAChD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAEhC,IAAI,cAAc,EAAE,CAAC;QACnB,WAAW,GAAG,OAAO,CAAC;QACtB,KAAK,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC;YACzC,IAAI,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;gBAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;SAAM,IAAI,UAAU,IAAI,UAAU,EAAE,CAAC;QACpC,WAAW,GAAG,KAAK,CAAC;QACpB,IAAI,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACrE,CAAC;SAAM,CAAC;QACN,WAAW,GAAG,OAAO,CAAC;IACxB,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC;QAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAErC,OAAO;QACL,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC1D,WAAW;KACZ,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,CAAS;IACjC,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}