@optave/codegraph 3.1.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/grammars/tree-sitter-go.wasm +0 -0
- package/package.json +8 -9
- package/src/ast-analysis/rules/csharp.js +201 -0
- package/src/ast-analysis/rules/go.js +182 -0
- package/src/ast-analysis/rules/index.js +82 -0
- package/src/ast-analysis/rules/java.js +175 -0
- package/src/ast-analysis/rules/javascript.js +246 -0
- package/src/ast-analysis/rules/php.js +219 -0
- package/src/ast-analysis/rules/python.js +196 -0
- package/src/ast-analysis/rules/ruby.js +204 -0
- package/src/ast-analysis/rules/rust.js +173 -0
- package/src/ast-analysis/shared.js +223 -0
- package/src/ast.js +15 -28
- package/src/audit.js +4 -5
- package/src/boundaries.js +1 -1
- package/src/branch-compare.js +84 -79
- package/src/builder.js +0 -5
- package/src/cfg.js +106 -338
- package/src/check.js +3 -3
- package/src/cli.js +99 -179
- package/src/cochange.js +1 -1
- package/src/communities.js +13 -16
- package/src/complexity.js +196 -1239
- package/src/cycles.js +1 -1
- package/src/dataflow.js +269 -694
- package/src/db/connection.js +88 -0
- package/src/db/migrations.js +312 -0
- package/src/db/query-builder.js +280 -0
- package/src/db/repository.js +134 -0
- package/src/db.js +19 -399
- package/src/embedder.js +145 -141
- package/src/export.js +1 -1
- package/src/flow.js +161 -162
- package/src/index.js +34 -1
- package/src/kinds.js +49 -0
- package/src/manifesto.js +3 -8
- package/src/mcp.js +37 -20
- package/src/owners.js +132 -132
- package/src/queries-cli.js +866 -0
- package/src/queries.js +1323 -2267
- package/src/result-formatter.js +21 -0
- package/src/sequence.js +177 -182
- package/src/structure.js +200 -199
- package/src/test-filter.js +7 -0
- package/src/triage.js +120 -162
- package/src/viewer.js +1 -1
package/src/owners.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { findDbPath, openReadonlyOrFail } from './db.js';
|
|
4
|
-
import {
|
|
4
|
+
import { outputResult } from './result-formatter.js';
|
|
5
|
+
import { isTestFile } from './test-filter.js';
|
|
5
6
|
|
|
6
7
|
// ─── CODEOWNERS Parsing ──────────────────────────────────────────────
|
|
7
8
|
|
|
@@ -163,142 +164,144 @@ export function ownersForFiles(filePaths, repoRoot) {
|
|
|
163
164
|
*/
|
|
164
165
|
export function ownersData(customDbPath, opts = {}) {
|
|
165
166
|
const db = openReadonlyOrFail(customDbPath);
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
167
|
+
try {
|
|
168
|
+
const dbPath = findDbPath(customDbPath);
|
|
169
|
+
const repoRoot = path.resolve(path.dirname(dbPath), '..');
|
|
170
|
+
|
|
171
|
+
const parsed = parseCodeowners(repoRoot);
|
|
172
|
+
if (!parsed) {
|
|
173
|
+
return {
|
|
174
|
+
codeownersFile: null,
|
|
175
|
+
files: [],
|
|
176
|
+
symbols: [],
|
|
177
|
+
boundaries: [],
|
|
178
|
+
summary: {
|
|
179
|
+
totalFiles: 0,
|
|
180
|
+
ownedFiles: 0,
|
|
181
|
+
unownedFiles: 0,
|
|
182
|
+
coveragePercent: 0,
|
|
183
|
+
ownerCount: 0,
|
|
184
|
+
byOwner: [],
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
}
|
|
187
188
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
189
|
+
// Get all distinct files from nodes
|
|
190
|
+
let allFiles = db
|
|
191
|
+
.prepare('SELECT DISTINCT file FROM nodes')
|
|
192
|
+
.all()
|
|
193
|
+
.map((r) => r.file);
|
|
193
194
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
195
|
+
if (opts.noTests) allFiles = allFiles.filter((f) => !isTestFile(f));
|
|
196
|
+
if (opts.file) {
|
|
197
|
+
const filter = opts.file;
|
|
198
|
+
allFiles = allFiles.filter((f) => f.includes(filter));
|
|
199
|
+
}
|
|
199
200
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
201
|
+
// Map files to owners
|
|
202
|
+
const fileOwners = allFiles.map((file) => ({
|
|
203
|
+
file,
|
|
204
|
+
owners: matchOwners(file, parsed.rules),
|
|
205
|
+
}));
|
|
206
|
+
|
|
207
|
+
// Build owner-to-files index
|
|
208
|
+
const ownerIndex = new Map();
|
|
209
|
+
let ownedCount = 0;
|
|
210
|
+
for (const fo of fileOwners) {
|
|
211
|
+
if (fo.owners.length > 0) ownedCount++;
|
|
212
|
+
for (const o of fo.owners) {
|
|
213
|
+
if (!ownerIndex.has(o)) ownerIndex.set(o, []);
|
|
214
|
+
ownerIndex.get(o).push(fo.file);
|
|
215
|
+
}
|
|
214
216
|
}
|
|
215
|
-
}
|
|
216
217
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
218
|
+
// Filter files if --owner specified
|
|
219
|
+
let filteredFiles = fileOwners;
|
|
220
|
+
if (opts.owner) {
|
|
221
|
+
filteredFiles = fileOwners.filter((fo) => fo.owners.includes(opts.owner));
|
|
222
|
+
}
|
|
222
223
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
224
|
+
// Get symbols for filtered files
|
|
225
|
+
const fileSet = new Set(filteredFiles.map((fo) => fo.file));
|
|
226
|
+
let symbols = db
|
|
227
|
+
.prepare('SELECT name, kind, file, line FROM nodes')
|
|
228
|
+
.all()
|
|
229
|
+
.filter((n) => fileSet.has(n.file));
|
|
230
|
+
|
|
231
|
+
if (opts.noTests) symbols = symbols.filter((s) => !isTestFile(s.file));
|
|
232
|
+
if (opts.kind) symbols = symbols.filter((s) => s.kind === opts.kind);
|
|
233
|
+
|
|
234
|
+
const symbolsWithOwners = symbols.map((s) => ({
|
|
235
|
+
...s,
|
|
236
|
+
owners: matchOwners(s.file, parsed.rules),
|
|
237
|
+
}));
|
|
238
|
+
|
|
239
|
+
// Boundary analysis — cross-owner call edges
|
|
240
|
+
const boundaries = [];
|
|
241
|
+
if (opts.boundary) {
|
|
242
|
+
const edges = db
|
|
243
|
+
.prepare(
|
|
244
|
+
`SELECT e.id, e.kind AS edgeKind,
|
|
245
|
+
s.name AS srcName, s.kind AS srcKind, s.file AS srcFile, s.line AS srcLine,
|
|
246
|
+
t.name AS tgtName, t.kind AS tgtKind, t.file AS tgtFile, t.line AS tgtLine
|
|
247
|
+
FROM edges e
|
|
248
|
+
JOIN nodes s ON e.source_id = s.id
|
|
249
|
+
JOIN nodes t ON e.target_id = t.id
|
|
250
|
+
WHERE e.kind = 'calls'`,
|
|
251
|
+
)
|
|
252
|
+
.all();
|
|
253
|
+
|
|
254
|
+
for (const e of edges) {
|
|
255
|
+
if (opts.noTests && (isTestFile(e.srcFile) || isTestFile(e.tgtFile))) continue;
|
|
256
|
+
const srcOwners = matchOwners(e.srcFile, parsed.rules);
|
|
257
|
+
const tgtOwners = matchOwners(e.tgtFile, parsed.rules);
|
|
258
|
+
// Cross-boundary: different owner sets
|
|
259
|
+
const srcKey = srcOwners.sort().join(',');
|
|
260
|
+
const tgtKey = tgtOwners.sort().join(',');
|
|
261
|
+
if (srcKey !== tgtKey) {
|
|
262
|
+
boundaries.push({
|
|
263
|
+
from: {
|
|
264
|
+
name: e.srcName,
|
|
265
|
+
kind: e.srcKind,
|
|
266
|
+
file: e.srcFile,
|
|
267
|
+
line: e.srcLine,
|
|
268
|
+
owners: srcOwners,
|
|
269
|
+
},
|
|
270
|
+
to: {
|
|
271
|
+
name: e.tgtName,
|
|
272
|
+
kind: e.tgtKind,
|
|
273
|
+
file: e.tgtFile,
|
|
274
|
+
line: e.tgtLine,
|
|
275
|
+
owners: tgtOwners,
|
|
276
|
+
},
|
|
277
|
+
edgeKind: e.edgeKind,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
278
280
|
}
|
|
279
281
|
}
|
|
280
|
-
}
|
|
281
282
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
}
|
|
283
|
+
// Summary
|
|
284
|
+
const byOwner = [...ownerIndex.entries()]
|
|
285
|
+
.map(([owner, files]) => ({ owner, fileCount: files.length }))
|
|
286
|
+
.sort((a, b) => b.fileCount - a.fileCount);
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
codeownersFile: parsed.path,
|
|
290
|
+
files: filteredFiles,
|
|
291
|
+
symbols: symbolsWithOwners,
|
|
292
|
+
boundaries,
|
|
293
|
+
summary: {
|
|
294
|
+
totalFiles: allFiles.length,
|
|
295
|
+
ownedFiles: ownedCount,
|
|
296
|
+
unownedFiles: allFiles.length - ownedCount,
|
|
297
|
+
coveragePercent: allFiles.length > 0 ? Math.round((ownedCount / allFiles.length) * 100) : 0,
|
|
298
|
+
ownerCount: ownerIndex.size,
|
|
299
|
+
byOwner,
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
} finally {
|
|
303
|
+
db.close();
|
|
304
|
+
}
|
|
302
305
|
}
|
|
303
306
|
|
|
304
307
|
// ─── CLI Display ─────────────────────────────────────────────────────
|
|
@@ -310,10 +313,7 @@ export function ownersData(customDbPath, opts = {}) {
|
|
|
310
313
|
*/
|
|
311
314
|
export function owners(customDbPath, opts = {}) {
|
|
312
315
|
const data = ownersData(customDbPath, opts);
|
|
313
|
-
if (opts
|
|
314
|
-
console.log(JSON.stringify(data, null, 2));
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
316
|
+
if (outputResult(data, null, opts)) return;
|
|
317
317
|
|
|
318
318
|
if (!data.codeownersFile) {
|
|
319
319
|
console.log('No CODEOWNERS file found.');
|