# core/converters/graph.converter.ts

> 53 lines of code and 19 definitions.

Tree: GovLab Docs
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/docs#file-docs-core-converters-graph-converter-ts
Source text: https://banes-lab.com/source/docs/core/converters/graph.converter.ts.txt

Listed in [core/converters](https://banes-lab.com/api/source/docs/core/converters.md), after [core/converters/document.converter.ts](https://banes-lab.com/source/docs/core/converters/document.converter.ts.md) and before [core/converters/layer.converter.ts](https://banes-lab.com/source/docs/core/converters/layer.converter.ts.md).

## Definitions

- `dedupeEdges` (lexical_declaration, line 20)
- `depGraphOf` (lexical_declaration, line 51, exported)
- `collapseByLabel` (lexical_declaration, line 33, exported)
- `canonicalizeNodes` (lexical_declaration, line 4)
- `canon` (lexical_declaration, line 5)
- `idMap` (lexical_declaration, line 6)
- `existing` (lexical_declaration, line 10)
- `byKey` (lexical_declaration, line 21)
- `from` (lexical_declaration, line 23)
- `to` (lexical_declaration, line 24)
- `key` (lexical_declaration, line 25)
- `{ idMap, nodes }` (lexical_declaration, line 34, exported)
- `shortName` (lexical_declaration, line 38)
- `slash` (lexical_declaration, line 39)
- `presentEdges` (lexical_declaration, line 43)
- `present` (lexical_declaration, line 52, exported)
- `edges` (lexical_declaration, line 53, exported)
- `connected` (lexical_declaration, line 54, exported)
- `nodes` (lexical_declaration, line 55, exported)

## Contained in

- [core/converters](https://banes-lab.com/anatomy/docs/folder-docs-core-converters.md)

## Used by

- [core/plugins/diagram.package.plugin.ts](https://banes-lab.com/source/docs/core/plugins/diagram.package.plugin.ts.md)

## Linked from

- [core/converters](https://banes-lab.com/anatomy/docs/folder-docs-core-converters.md)
- [core/plugins](https://banes-lab.com/anatomy/docs/folder-docs-core-plugins.md)

## Source

```typescript
import type { CanonicalNodes, CodeEdge, CodeGraph, CodeNode } from "#types/graph.types";
import type { GraphEdge, GraphModel, GraphNode, ModuleDep } from "#types/diagram.types";

const canonicalizeNodes = function canonicalizeNodes(graph: CodeGraph): CanonicalNodes {
    const canon = new Map<string, string>();
    const idMap = new Map<string, string>();
    const nodes: CodeNode[] = [];
    for (const node of graph.nodes.toSorted((left, right) => left.id.localeCompare(right.id))) {
        const key = `${node.kind} ${node.label}`;
        const existing = canon.get(key);
        idMap.set(node.id, existing ?? node.id);
        if (existing === undefined) {
            canon.set(key, node.id);
            nodes.push(node);
        }
    }
    return { idMap, nodes };
};

const dedupeEdges = function dedupeEdges(edges: readonly CodeEdge[], idMap: ReadonlyMap<string, string>): CodeEdge[] {
    const byKey = new Map<string, CodeEdge>();
    for (const edge of edges) {
        const from = idMap.get(edge.from) ?? edge.from;
        const to = idMap.get(edge.to) ?? edge.to;
        const key = `${from} ${to} ${edge.kind} ${edge.label ?? ""}`;
        if (from !== to && !byKey.has(key)) {
            byKey.set(key, { from, kind: edge.kind, label: edge.label, to });
        }
    }
    return [...byKey.values()];
};

export const collapseByLabel = function collapseByLabel(graph: CodeGraph): CodeGraph {
    const { idMap, nodes } = canonicalizeNodes(graph);
    return { edges: dedupeEdges(graph.edges, idMap), nodes };
};

const shortName = function shortName(name: string): string {
    const slash = name.lastIndexOf("/");
    return slash === -1 ? name : name.slice(slash + 1);
};

const presentEdges = function presentEdges(modules: readonly ModuleDep[], present: ReadonlySet<string>): GraphEdge[] {
    return modules.flatMap((module) =>
        module.deps
            .filter((dep) => present.has(dep))
            .map((dep) => ({ from: module.name, to: dep, weight: "dependency" as const })),
    );
};

export const depGraphOf = function depGraphOf(modules: readonly ModuleDep[], keepIsolated = false): GraphModel {
    const present = new Set(modules.map((module) => module.name));
    const edges = presentEdges(modules, present);
    const connected = new Set(edges.flatMap((edge) => [edge.from, edge.to]));
    const nodes: GraphNode[] = modules
        .filter((module) => keepIsolated || connected.has(module.name))
        .map((module) => ({ id: module.name, label: shortName(module.name) }));
    return { direction: "TD", edges, kind: "graph", nodes };
};
```
