projscan 0.11.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.
Files changed (203) hide show
  1. package/README.md +45 -26
  2. package/dist/analyzers/unusedDependencyCheck.js +69 -17
  3. package/dist/analyzers/unusedDependencyCheck.js.map +1 -1
  4. package/dist/cli/_shared.d.ts +16 -0
  5. package/dist/cli/_shared.js +210 -0
  6. package/dist/cli/_shared.js.map +1 -0
  7. package/dist/cli/commands/analyze.d.ts +1 -0
  8. package/dist/cli/commands/analyze.js +87 -0
  9. package/dist/cli/commands/analyze.js.map +1 -0
  10. package/dist/cli/commands/audit.d.ts +1 -0
  11. package/dist/cli/commands/audit.js +47 -0
  12. package/dist/cli/commands/audit.js.map +1 -0
  13. package/dist/cli/commands/badge.d.ts +1 -0
  14. package/dist/cli/commands/badge.js +45 -0
  15. package/dist/cli/commands/badge.js.map +1 -0
  16. package/dist/cli/commands/ci.d.ts +1 -0
  17. package/dist/cli/commands/ci.js +57 -0
  18. package/dist/cli/commands/ci.js.map +1 -0
  19. package/dist/cli/commands/coupling.d.ts +1 -0
  20. package/dist/cli/commands/coupling.js +83 -0
  21. package/dist/cli/commands/coupling.js.map +1 -0
  22. package/dist/cli/commands/coverage.d.ts +1 -0
  23. package/dist/cli/commands/coverage.js +63 -0
  24. package/dist/cli/commands/coverage.js.map +1 -0
  25. package/dist/cli/commands/dependencies.d.ts +1 -0
  26. package/dist/cli/commands/dependencies.js +45 -0
  27. package/dist/cli/commands/dependencies.js.map +1 -0
  28. package/dist/cli/commands/diagram.d.ts +1 -0
  29. package/dist/cli/commands/diagram.js +45 -0
  30. package/dist/cli/commands/diagram.js.map +1 -0
  31. package/dist/cli/commands/diff.d.ts +1 -0
  32. package/dist/cli/commands/diff.js +70 -0
  33. package/dist/cli/commands/diff.js.map +1 -0
  34. package/dist/cli/commands/doctor.d.ts +1 -0
  35. package/dist/cli/commands/doctor.js +62 -0
  36. package/dist/cli/commands/doctor.js.map +1 -0
  37. package/dist/cli/commands/explain.d.ts +1 -0
  38. package/dist/cli/commands/explain.js +42 -0
  39. package/dist/cli/commands/explain.js.map +1 -0
  40. package/dist/cli/commands/file.d.ts +1 -0
  41. package/dist/cli/commands/file.js +45 -0
  42. package/dist/cli/commands/file.js.map +1 -0
  43. package/dist/cli/commands/fix.d.ts +1 -0
  44. package/dist/cli/commands/fix.js +70 -0
  45. package/dist/cli/commands/fix.js.map +1 -0
  46. package/dist/cli/commands/help.d.ts +1 -0
  47. package/dist/cli/commands/help.js +11 -0
  48. package/dist/cli/commands/help.js.map +1 -0
  49. package/dist/cli/commands/hotspots.d.ts +1 -0
  50. package/dist/cli/commands/hotspots.js +74 -0
  51. package/dist/cli/commands/hotspots.js.map +1 -0
  52. package/dist/cli/commands/mcp.d.ts +1 -0
  53. package/dist/cli/commands/mcp.js +21 -0
  54. package/dist/cli/commands/mcp.js.map +1 -0
  55. package/dist/cli/commands/outdated.d.ts +1 -0
  56. package/dist/cli/commands/outdated.js +51 -0
  57. package/dist/cli/commands/outdated.js.map +1 -0
  58. package/dist/cli/commands/prDiff.d.ts +1 -0
  59. package/dist/cli/commands/prDiff.js +59 -0
  60. package/dist/cli/commands/prDiff.js.map +1 -0
  61. package/dist/cli/commands/search.d.ts +1 -0
  62. package/dist/cli/commands/search.js +233 -0
  63. package/dist/cli/commands/search.js.map +1 -0
  64. package/dist/cli/commands/structure.d.ts +1 -0
  65. package/dist/cli/commands/structure.js +58 -0
  66. package/dist/cli/commands/structure.js.map +1 -0
  67. package/dist/cli/commands/upgrade.d.ts +1 -0
  68. package/dist/cli/commands/upgrade.js +44 -0
  69. package/dist/cli/commands/upgrade.js.map +1 -0
  70. package/dist/cli/commands/workspaces.d.ts +1 -0
  71. package/dist/cli/commands/workspaces.js +35 -0
  72. package/dist/cli/commands/workspaces.js.map +1 -0
  73. package/dist/cli/index.js +45 -1416
  74. package/dist/cli/index.js.map +1 -1
  75. package/dist/core/couplingAnalyzer.d.ts +1 -1
  76. package/dist/core/couplingAnalyzer.js +3 -3
  77. package/dist/core/hotspotAnalyzer.js +2 -2
  78. package/dist/core/languages/LanguageAdapter.d.ts +1 -1
  79. package/dist/core/languages/goAdapter.js +7 -5
  80. package/dist/core/languages/goAdapter.js.map +1 -1
  81. package/dist/core/languages/goCallSites.d.ts +20 -0
  82. package/dist/core/languages/goCallSites.js +42 -0
  83. package/dist/core/languages/goCallSites.js.map +1 -0
  84. package/dist/core/languages/goCyclomatic.d.ts +1 -1
  85. package/dist/core/languages/goCyclomatic.js +2 -2
  86. package/dist/core/languages/goExports.d.ts +1 -1
  87. package/dist/core/languages/goExports.js +1 -1
  88. package/dist/core/languages/goManifests.d.ts +1 -1
  89. package/dist/core/languages/goManifests.js +2 -2
  90. package/dist/core/languages/javaAdapter.d.ts +2 -0
  91. package/dist/core/languages/javaAdapter.js +148 -0
  92. package/dist/core/languages/javaAdapter.js.map +1 -0
  93. package/dist/core/languages/javaCallSites.d.ts +16 -0
  94. package/dist/core/languages/javaCallSites.js +45 -0
  95. package/dist/core/languages/javaCallSites.js.map +1 -0
  96. package/dist/core/languages/javaCyclomatic.d.ts +21 -0
  97. package/dist/core/languages/javaCyclomatic.js +49 -0
  98. package/dist/core/languages/javaCyclomatic.js.map +1 -0
  99. package/dist/core/languages/javaExports.d.ts +25 -0
  100. package/dist/core/languages/javaExports.js +80 -0
  101. package/dist/core/languages/javaExports.js.map +1 -0
  102. package/dist/core/languages/javaImports.d.ts +25 -0
  103. package/dist/core/languages/javaImports.js +49 -0
  104. package/dist/core/languages/javaImports.js.map +1 -0
  105. package/dist/core/languages/javaManifests.d.ts +25 -0
  106. package/dist/core/languages/javaManifests.js +86 -0
  107. package/dist/core/languages/javaManifests.js.map +1 -0
  108. package/dist/core/languages/pythonAdapter.js +3 -1
  109. package/dist/core/languages/pythonAdapter.js.map +1 -1
  110. package/dist/core/languages/pythonCallSites.d.ts +19 -0
  111. package/dist/core/languages/pythonCallSites.js +40 -0
  112. package/dist/core/languages/pythonCallSites.js.map +1 -0
  113. package/dist/core/languages/registry.js +3 -1
  114. package/dist/core/languages/registry.js.map +1 -1
  115. package/dist/core/languages/rubyAdapter.d.ts +2 -0
  116. package/dist/core/languages/rubyAdapter.js +131 -0
  117. package/dist/core/languages/rubyAdapter.js.map +1 -0
  118. package/dist/core/languages/rubyCallSites.d.ts +16 -0
  119. package/dist/core/languages/rubyCallSites.js +34 -0
  120. package/dist/core/languages/rubyCallSites.js.map +1 -0
  121. package/dist/core/languages/rubyCyclomatic.d.ts +19 -0
  122. package/dist/core/languages/rubyCyclomatic.js +47 -0
  123. package/dist/core/languages/rubyCyclomatic.js.map +1 -0
  124. package/dist/core/languages/rubyExports.d.ts +24 -0
  125. package/dist/core/languages/rubyExports.js +53 -0
  126. package/dist/core/languages/rubyExports.js.map +1 -0
  127. package/dist/core/languages/rubyImports.d.ts +12 -0
  128. package/dist/core/languages/rubyImports.js +75 -0
  129. package/dist/core/languages/rubyImports.js.map +1 -0
  130. package/dist/core/languages/rubyManifests.d.ts +20 -0
  131. package/dist/core/languages/rubyManifests.js +55 -0
  132. package/dist/core/languages/rubyManifests.js.map +1 -0
  133. package/dist/core/languages/treeSitterLoader.js +3 -1
  134. package/dist/core/languages/treeSitterLoader.js.map +1 -1
  135. package/dist/core/monorepo.js +5 -5
  136. package/dist/core/outdatedDetector.d.ts +13 -2
  137. package/dist/core/outdatedDetector.js +86 -16
  138. package/dist/core/outdatedDetector.js.map +1 -1
  139. package/dist/core/prDiff.d.ts +1 -1
  140. package/dist/core/prDiff.js +2 -2
  141. package/dist/grammars/tree-sitter-java.wasm +0 -0
  142. package/dist/grammars/tree-sitter-ruby.wasm +0 -0
  143. package/dist/mcp/server.js +0 -22
  144. package/dist/mcp/server.js.map +1 -1
  145. package/dist/mcp/tools/_shared.d.ts +24 -0
  146. package/dist/mcp/tools/_shared.js +82 -0
  147. package/dist/mcp/tools/_shared.js.map +1 -0
  148. package/dist/mcp/tools/analyze.d.ts +2 -0
  149. package/dist/mcp/tools/analyze.js +55 -0
  150. package/dist/mcp/tools/analyze.js.map +1 -0
  151. package/dist/mcp/tools/audit.d.ts +2 -0
  152. package/dist/mcp/tools/audit.js +32 -0
  153. package/dist/mcp/tools/audit.js.map +1 -0
  154. package/dist/mcp/tools/coupling.d.ts +2 -0
  155. package/dist/mcp/tools/coupling.js +67 -0
  156. package/dist/mcp/tools/coupling.js.map +1 -0
  157. package/dist/mcp/tools/coverage.d.ts +2 -0
  158. package/dist/mcp/tools/coverage.js +53 -0
  159. package/dist/mcp/tools/coverage.js.map +1 -0
  160. package/dist/mcp/tools/dependencies.d.ts +2 -0
  161. package/dist/mcp/tools/dependencies.js +16 -0
  162. package/dist/mcp/tools/dependencies.js.map +1 -0
  163. package/dist/mcp/tools/doctor.d.ts +2 -0
  164. package/dist/mcp/tools/doctor.js +30 -0
  165. package/dist/mcp/tools/doctor.js.map +1 -0
  166. package/dist/mcp/tools/explain.d.ts +2 -0
  167. package/dist/mcp/tools/explain.js +30 -0
  168. package/dist/mcp/tools/explain.js.map +1 -0
  169. package/dist/mcp/tools/file.d.ts +2 -0
  170. package/dist/mcp/tools/file.js +22 -0
  171. package/dist/mcp/tools/file.js.map +1 -0
  172. package/dist/mcp/tools/graph.d.ts +2 -0
  173. package/dist/mcp/tools/graph.js +69 -0
  174. package/dist/mcp/tools/graph.js.map +1 -0
  175. package/dist/mcp/tools/hotspots.d.ts +2 -0
  176. package/dist/mcp/tools/hotspots.js +64 -0
  177. package/dist/mcp/tools/hotspots.js.map +1 -0
  178. package/dist/mcp/tools/outdated.d.ts +2 -0
  179. package/dist/mcp/tools/outdated.js +36 -0
  180. package/dist/mcp/tools/outdated.js.map +1 -0
  181. package/dist/mcp/tools/prDiff.d.ts +2 -0
  182. package/dist/mcp/tools/prDiff.js +38 -0
  183. package/dist/mcp/tools/prDiff.js.map +1 -0
  184. package/dist/mcp/tools/search.d.ts +2 -0
  185. package/dist/mcp/tools/search.js +167 -0
  186. package/dist/mcp/tools/search.js.map +1 -0
  187. package/dist/mcp/tools/structure.d.ts +2 -0
  188. package/dist/mcp/tools/structure.js +34 -0
  189. package/dist/mcp/tools/structure.js.map +1 -0
  190. package/dist/mcp/tools/upgrade.d.ts +2 -0
  191. package/dist/mcp/tools/upgrade.js +38 -0
  192. package/dist/mcp/tools/upgrade.js.map +1 -0
  193. package/dist/mcp/tools/workspaces.d.ts +2 -0
  194. package/dist/mcp/tools/workspaces.js +13 -0
  195. package/dist/mcp/tools/workspaces.js.map +1 -0
  196. package/dist/mcp/tools.d.ts +12 -6
  197. package/dist/mcp/tools.js +40 -854
  198. package/dist/mcp/tools.js.map +1 -1
  199. package/dist/tool-manifest.json +358 -0
  200. package/dist/types.d.ts +8 -6
  201. package/dist/utils/config.js +0 -10
  202. package/dist/utils/config.js.map +1 -1
  203. package/package.json +6 -3
@@ -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
@@ -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,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"}
@@ -23,7 +23,9 @@ function grammarDirs() {
23
23
  const nodeModulesWebTs = path.join(repoRoot, 'node_modules', 'web-tree-sitter');
24
24
  const nodeModulesPy = path.join(repoRoot, 'node_modules', 'tree-sitter-python');
25
25
  const nodeModulesGo = path.join(repoRoot, 'node_modules', 'tree-sitter-go');
26
- return [distDir, nodeModulesWebTs, nodeModulesPy, nodeModulesGo];
26
+ const nodeModulesJava = path.join(repoRoot, 'node_modules', 'tree-sitter-java');
27
+ const nodeModulesRuby = path.join(repoRoot, 'node_modules', 'tree-sitter-ruby');
28
+ return [distDir, nodeModulesWebTs, nodeModulesPy, nodeModulesGo, nodeModulesJava, nodeModulesRuby];
27
29
  }
28
30
  function findWasm(filename) {
29
31
  for (const dir of grammarDirs()) {
@@ -1 +1 @@
1
- {"version":3,"file":"treeSitterLoader.js","sourceRoot":"","sources":["../../../src/core/languages/treeSitterLoader.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"treeSitterLoader.js","sourceRoot":"","sources":["../../../src/core/languages/treeSitterLoader.ts"],"names":[],"mappings":"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"}
@@ -26,7 +26,7 @@ export async function detectWorkspaces(rootPath) {
26
26
  const patterns = readNpmYarnPatterns(rootPkg);
27
27
  if (patterns && patterns.length > 0) {
28
28
  const packages = await collectPackages(rootPath, patterns, rootPkg);
29
- // Distinguish yarn from npm if a yarn.lock is present affects nothing
29
+ // Distinguish yarn from npm if a yarn.lock is present - affects nothing
30
30
  // about discovery, just labelling.
31
31
  const kind = (await fileExists(path.join(rootPath, 'yarn.lock')))
32
32
  ? 'yarn'
@@ -34,7 +34,7 @@ export async function detectWorkspaces(rootPath) {
34
34
  return { kind, packages, source: 'package.json#workspaces' };
35
35
  }
36
36
  }
37
- // 3) Lerna its config has an explicit `packages` field with globs.
37
+ // 3) Lerna - its config has an explicit `packages` field with globs.
38
38
  const lernaPath = path.join(rootPath, 'lerna.json');
39
39
  if (await fileExists(lernaPath)) {
40
40
  const patterns = (await readJsonField(lernaPath, 'packages')) ?? ['packages/*'];
@@ -43,7 +43,7 @@ export async function detectWorkspaces(rootPath) {
43
43
  return { kind: 'lerna', packages, source: 'lerna.json#packages' };
44
44
  }
45
45
  }
46
- // 4) Nx workspaceLayout supplies appsDir/libsDir; project.json files
46
+ // 4) Nx - workspaceLayout supplies appsDir/libsDir; project.json files
47
47
  // anywhere identify packages. Older Nx variants used workspace.json (a
48
48
  // single file enumerating projects); we read its `projects` map too.
49
49
  const nxJsonPath = path.join(rootPath, 'nx.json');
@@ -55,7 +55,7 @@ export async function detectWorkspaces(rootPath) {
55
55
  return { kind: 'nx', packages: merged, source: 'nx.json + project.json scan' };
56
56
  }
57
57
  }
58
- // 5) Turbo turbo.json defines task pipelines, NOT workspace layout.
58
+ // 5) Turbo - turbo.json defines task pipelines, NOT workspace layout.
59
59
  // Turbo always rides on top of npm/yarn/pnpm workspaces (handled above).
60
60
  // If we get here with a turbo.json but no workspace declaration, treat it
61
61
  // as a marker and fall back to the packages/* convention so we still
@@ -113,7 +113,7 @@ async function readPnpmPackages(manifestPath) {
113
113
  patterns.push(m[1]);
114
114
  }
115
115
  else if (line.length > 0 && !line.startsWith(' ') && !line.startsWith('\t')) {
116
- // New top-level key packages section ended.
116
+ // New top-level key - packages section ended.
117
117
  inPackages = false;
118
118
  }
119
119
  }
@@ -1,9 +1,20 @@
1
- import type { OutdatedReport } from '../types.js';
1
+ import type { OutdatedReport, WorkspaceInfo } from '../types.js';
2
+ export interface OutdatedOptions {
3
+ /** When provided, scan each workspace package's package.json individually. */
4
+ workspaces?: WorkspaceInfo;
5
+ /** When provided alongside workspaces, restrict to a single package by name. */
6
+ workspaceFilter?: string;
7
+ }
2
8
  /**
3
9
  * Offline outdated check - compares the version declared in package.json
4
10
  * to the version installed under node_modules/<pkg>/package.json.
5
11
  *
12
+ * In a monorepo (caller supplies workspaces), each package's package.json is
13
+ * scanned. Each result entry carries the workspace it came from. Installed
14
+ * versions still resolve through the root node_modules (npm/yarn/pnpm hoisting
15
+ * makes that the right place to look in 95% of installs).
16
+ *
6
17
  * Does not hit the npm registry. `latest` is filled in only when a node_modules
7
18
  * install exists; the drift calculation uses installed vs declared.
8
19
  */
9
- export declare function detectOutdated(rootPath: string): Promise<OutdatedReport>;
20
+ export declare function detectOutdated(rootPath: string, options?: OutdatedOptions): Promise<OutdatedReport>;