adam-java-analytics
Version:
Java code analyzer for detecting package dependencies and circular references
70 lines (57 loc) • 1.8 kB
text/typescript
import { Dependency, Package } from "./analyzer"
/**
* Detects circular dependencies in the project
*/
export function detectCircularDependencies(packages: Package[], dependencies: Dependency[]): string[][] {
const graph: Record<string, string[]> = {}
// Build adjacency list
packages.forEach((pkg) => {
graph[pkg.name] = []
})
dependencies.forEach((dep) => {
if (graph[dep.source]) {
graph[dep.source].push(dep.target)
}
})
const cycles: string[][] = []
const visited = new Set<string>()
const recursionStack = new Set<string>()
function dfs(node: string, path: string[] = []): void {
if (recursionStack.has(node)) {
// Found a cycle
const cycleStart = path.indexOf(node)
if (cycleStart !== -1) {
const cycle = path.slice(cycleStart).concat(node)
// Check if this cycle or its rotation is already in cycles
const isNewCycle = !cycles.some((existingCycle) => {
if (existingCycle.length !== cycle.length) return false
// Check all possible rotations
for (let i = 0; i < existingCycle.length; i++) {
const rotated = [...existingCycle.slice(i), ...existingCycle.slice(0, i)]
if (JSON.stringify(rotated) === JSON.stringify(cycle)) return true
}
return false
})
if (isNewCycle) {
cycles.push(cycle)
}
}
return
}
if (visited.has(node)) return
visited.add(node)
recursionStack.add(node)
path.push(node)
for (const neighbor of graph[node] || []) {
dfs(neighbor, [...path])
}
recursionStack.delete(node)
}
// Run DFS from each node
for (const node of Object.keys(graph)) {
if (!visited.has(node)) {
dfs(node)
}
}
return cycles
}