UNPKG

knip

Version:

Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects

95 lines (94 loc) 2.94 kB
export function graphSequencer(graph, includedNodes = [...graph.keys()]) { const reverseGraph = new Map(); for (const key of graph.keys()) { reverseGraph.set(key, []); } const nodes = new Set(includedNodes); const visited = new Set(); const outDegree = new Map(); for (const [from, edges] of graph.entries()) { outDegree.set(from, 0); for (const to of edges) { if (nodes.has(from) && nodes.has(to)) { changeOutDegree(from, 1); reverseGraph.get(to).push(from); } } if (!nodes.has(from)) { visited.add(from); } } const chunks = []; const cycles = []; let safe = true; while (nodes.size) { const chunk = []; let minDegree = Number.MAX_SAFE_INTEGER; for (const node of nodes) { const degree = outDegree.get(node); if (degree === 0) { chunk.push(node); } minDegree = Math.min(minDegree, degree); } if (minDegree === 0) { chunk.forEach(removeNode); chunks.push(chunk); } else { const cycleNodes = []; for (const node of nodes) { const cycle = findCycle(node); if (cycle.length) { cycles.push(cycle); cycle.forEach(removeNode); cycleNodes.push(...cycle); if (cycle.length > 1) { safe = false; } } } chunks.push(cycleNodes); } } return { safe, chunks, cycles }; function changeOutDegree(node, value) { const degree = outDegree.get(node) ?? 0; outDegree.set(node, degree + value); } function removeNode(node) { for (const from of reverseGraph.get(node)) { changeOutDegree(from, -1); } visited.add(node); nodes.delete(node); } function findCycle(startNode) { const queue = [[startNode, [startNode]]]; const cycleVisited = new Set(); const cycles = []; while (queue.length) { const [id, cycle] = queue.shift(); const nodes = graph.get(id); if (!nodes) continue; for (const to of nodes) { if (to === startNode) { cycleVisited.add(to); cycles.push([...cycle]); continue; } if (visited.has(to) || cycleVisited.has(to)) { continue; } cycleVisited.add(to); queue.push([to, [...cycle, to]]); } } if (!cycles.length) { return []; } cycles.sort((a, b) => b.length - a.length); return cycles[0]; } }