All files / src/layout node-classifier.ts

69.44% Statements 25/36
46.87% Branches 15/32
100% Functions 2/2
75% Lines 24/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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                  24x 24x 294x                                         24x     24x 24x 294x 294x   294x 294x         24x 24x 24x   24x 24x 273x   273x       273x   273x           273x 214x   59x       24x       24x    
import type { LGraph, LLink, ClassifiedNodes } from "./types";
import { debugLog } from "../debug";
 
/**
 * Iterate over links in a graph (handles Map, Record, and Array formats)
 */
function* iterateLinks(
  links: Map<number, LLink> | Record<number, LLink> | LLink[]
): Generator<LLink> {
  if (links instanceof Map) {
    for (const link of links.values()) {
      Eif (link) yield link;
    }
  E} else if (Array.isArray(links)) {
    for (const link of links) {
      if (link) yield link;
    }
  } else {
    for (const link of Object.values(links)) {
      if (link) yield link;
    }
  }
}
 
/**
 * Classify nodes into connected, disconnected, and reroutes
 *
 * - connected: nodes with at least one link
 * - disconnected: nodes with no links (annotations, bookmarks, etc.)
 * - reroutes: nodes with type "Reroute" (handled separately for chain collapse)
 */
export function classifyNodes(graph: LGraph): ClassifiedNodes {
  const hasLinks = new Set<number>();
 
  // Mark all nodes that appear in any link
  Eif (graph.links) {
    for (const link of iterateLinks(graph.links)) {
      Eif (typeof link.origin_id === "number") {
        hasLinks.add(link.origin_id);
      }
      Eif (typeof link.target_id === "number") {
        hasLinks.add(link.target_id);
      }
    }
  }
 
  const connected = new Set<number>();
  const disconnected = new Set<number>();
  const reroutes = new Set<number>();
 
  const nodes = graph._nodes ?? [];
  for (const node of nodes) {
    Iif (!node || node.id === undefined) continue;
 
    const nodeType = node.type ?? "";
 
    // Check if this is a reroute node
    // Common reroute types: "Reroute", "Reroute (rgthree)", etc.
    const isReroute = nodeType === "Reroute" || nodeType.startsWith("Reroute ");
 
    Iif (isReroute) {
      reroutes.add(node.id);
      // Reroutes are also tracked as connected if they have links
      if (hasLinks.has(node.id)) {
        connected.add(node.id);
      }
    } else if (hasLinks.has(node.id)) {
      connected.add(node.id);
    } else {
      disconnected.add(node.id);
    }
  }
 
  debugLog(
    `Classified: ${connected.size} connected, ${disconnected.size} disconnected, ${reroutes.size} reroutes`
  );
 
  return { connected, disconnected, reroutes };
}