MCPcopy Create free account
hub / github.com/PatrickSys/codebase-context / findCycles

Method findCycles

src/utils/usage-tracker.ts:744–802  ·  view source on GitHub ↗

* Find all circular dependencies in the graph using DFS with recursion stack. * Returns unique cycles (avoids duplicates like A->B->A and B->A->B). * * @param scope Optional path prefix to limit analysis (e.g., 'src/features')

(scope?: string)

Source from the content-addressed store, hash-verified

742 * @param scope Optional path prefix to limit analysis (e.g., 'src/features')
743 */
744 findCycles(scope?: string): CyclePath[] {
745 const visited = new Set<string>();
746 const recursionStack = new Set<string>();
747 const cycles: CyclePath[] = [];
748 const cycleSignatures = new Set<string>(); // To avoid duplicates
749
750 // Get all files to check
751 let filesToCheck = Array.from(this.imports.keys());
752 if (scope) {
753 filesToCheck = filesToCheck.filter((f) => f.startsWith(scope));
754 }
755
756 const dfs = (node: string, path: string[]): void => {
757 visited.add(node);
758 recursionStack.add(node);
759 path.push(node);
760
761 const neighbors = this.imports.get(node) || new Set<string>();
762 for (const neighbor of neighbors) {
763 // Apply scope filter to neighbors too
764 if (scope && !neighbor.startsWith(scope)) continue;
765
766 if (recursionStack.has(neighbor)) {
767 // Found a cycle! Extract just the cycle portion
768 const cycleStart = path.indexOf(neighbor);
769 if (cycleStart !== -1) {
770 const cyclePath = [...path.slice(cycleStart), neighbor];
771
772 // Create a normalized signature to avoid duplicate cycles
773 // Sort by the minimum element to create a canonical form
774 const normalized = [...cyclePath.slice(0, -1)].sort();
775 const signature = normalized.join('|');
776
777 if (!cycleSignatures.has(signature)) {
778 cycleSignatures.add(signature);
779 cycles.push({
780 files: cyclePath,
781 length: cyclePath.length - 1 // -1 because last element = first element
782 });
783 }
784 }
785 } else if (!visited.has(neighbor)) {
786 dfs(neighbor, path);
787 }
788 }
789
790 path.pop();
791 recursionStack.delete(node);
792 };
793
794 for (const file of filesToCheck) {
795 if (!visited.has(file)) {
796 dfs(file, []);
797 }
798 }
799
800 // Sort by cycle length (shorter cycles are often more problematic)
801 return cycles.sort((a, b) => a.length - b.length);

Callers 3

handleFunction · 0.80
handleFunction · 0.80
collectFileMetricsFunction · 0.80

Calls 1

hasMethod · 0.80

Tested by

no test coverage detected