-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathcycles.js
More file actions
82 lines (73 loc) · 2.53 KB
/
Copy pathcycles.js
File metadata and controls
82 lines (73 loc) · 2.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import { tarjan } from './graph/algorithms/tarjan.js';
import { buildDependencyGraph } from './graph/builders/dependency.js';
import { CodeGraph } from './graph/model.js';
import { loadNative } from './native.js';
/**
* Detect circular dependencies in the codebase using Tarjan's SCC algorithm.
* Dispatches to native Rust implementation when available, falls back to JS.
* @param {object} db - Open SQLite database
* @param {object} opts - { fileLevel: true, noTests: false }
* @returns {string[][]} Array of cycles, each cycle is an array of file paths
*/
export function findCycles(db, opts = {}) {
const fileLevel = opts.fileLevel !== false;
const noTests = opts.noTests || false;
const graph = buildDependencyGraph(db, { fileLevel, noTests });
// Build a label map: DB string ID → human-readable key
// File-level: file path; Function-level: name|file composite (for native Rust compat)
const idToLabel = new Map();
for (const [id, attrs] of graph.nodes()) {
if (fileLevel) {
idToLabel.set(id, attrs.file);
} else {
idToLabel.set(id, `${attrs.label}|${attrs.file}`);
}
}
// Build edge array with human-readable keys (for native engine)
const edges = graph.toEdgeArray().map((e) => ({
source: idToLabel.get(e.source),
target: idToLabel.get(e.target),
}));
// Try native Rust implementation
const native = loadNative();
if (native) {
return native.detectCycles(edges);
}
// Fallback: JS Tarjan via graph subsystem
// Re-key graph with human-readable labels for consistent output
const labelGraph = new CodeGraph();
for (const { source, target } of edges) {
labelGraph.addEdge(source, target);
}
return tarjan(labelGraph);
}
/**
* Pure-JS Tarjan's SCC implementation.
* Kept for backward compatibility — accepts raw {source, target}[] edges.
*/
export function findCyclesJS(edges) {
const graph = new CodeGraph();
for (const { source, target } of edges) {
graph.addEdge(source, target);
}
return tarjan(graph);
}
/**
* Format cycles for human-readable output.
*/
export function formatCycles(cycles) {
if (cycles.length === 0) {
return 'No circular dependencies detected.';
}
const lines = [`Found ${cycles.length} circular dependency cycle(s):\n`];
for (let i = 0; i < cycles.length; i++) {
const cycle = cycles[i];
lines.push(` Cycle ${i + 1} (${cycle.length} files):`);
for (const file of cycle) {
lines.push(` -> ${file}`);
}
lines.push(` -> ${cycle[0]} (back to start)`);
lines.push('');
}
return lines.join('\n');
}