(graph: Graph)
| 74 | * you can only move forward in the list, not backward. |
| 75 | */ |
| 76 | export function topologicalSort(graph: Graph) { |
| 77 | // We can't use a standard sorting algorithm because |
| 78 | // often, two packages won't have any dependency relationship |
| 79 | // between each other, meaning they are incomparable. |
| 80 | verifyGraph(graph); |
| 81 | const sorted: string[] = []; |
| 82 | |
| 83 | while (sorted.length < Object.keys(graph).length) { |
| 84 | // Find nodes not yet in 'sorted' that have edges |
| 85 | // only to nodes already in 'sorted' |
| 86 | const emptyNodes = Object.entries(graph) |
| 87 | .filter(([node, edges]) => { |
| 88 | if (sorted.includes(node)) { |
| 89 | return false; |
| 90 | } |
| 91 | for (const edge of edges) { |
| 92 | if (!sorted.includes(edge)) { |
| 93 | return false; |
| 94 | } |
| 95 | } |
| 96 | return true; |
| 97 | }) |
| 98 | .map(([node, edges]) => node); |
| 99 | |
| 100 | // If there are no such nodes, then the graph has a cycle. |
| 101 | if (emptyNodes.length === 0) { |
| 102 | throw new Error('Dependency graph has a cycle.'); |
| 103 | } |
| 104 | |
| 105 | for (let node of emptyNodes) { |
| 106 | sorted.push(node); |
| 107 | } |
| 108 | } |
| 109 | return sorted; |
| 110 | } |
| 111 | |
| 112 | /** |
| 113 | * Find all subnodes in the subgraph generated by taking the transitive |
no test coverage detected
searching dependent graphs…