Skip to content

Graph Construction

Arham-Qureshi edited this page Jul 21, 2026 · 1 revision

Graph Construction

After parsing, the extracted dependencies and entities are assembled into a dependency graph using graphology.

Pipeline

Parsed Files ──→ buildGraph() ──→ enrichNodes() ──→ exportGraphToJson()
                      │                │                   │
                      ▼                ▼                   ▼
                 Directed MGraph    Communities +       graph.json
                 (multi-graph)      Colors + Sizes

buildGraph() — Phase 1: Add Nodes

Each parsed file produces up to three types of nodes:

File Nodes

Every source file becomes a primary graph node:

graph.addNode(filePath, {
  label: basename(filePath),
  dependencies: parsedData.dependencies,
  entities: parsedData.entities,
  language: detectedLanguage
})

Entity Sub-Nodes

Classes, functions, and methods become secondary nodes connected to their parent file:

File: src/handler.js
  ├── ::Handler           (class) ── contains ──▶ src/handler.js
  ├── ::validateToken     (function)
  ├── ::handleRequest     (method)
  └── ::processData       (method)

Entity nodes use :: notation in their key (e.g., src/handler.js::Handler). They're connected to the parent file via contains edges (rendered as dashed lines in the visualizer).

External Package Nodes

When a dependency can't be resolved to a local file, an external node is created:

graph.addNode('express', {
  external: true,
  npm: true,       // detected via package.json
  label: 'express',
  community: 'dependencies'
})

Dependency resolution:

  • Relative paths (./, ../) — resolve relative to the importing file's directory
  • Bare imports (express, lodash) — check if any local file matches; if not, create an external node
  • npm detection — reads package.json to identify which bare imports are npm packages

buildGraph() — Phase 2: Add Dependency Edges

Each dependency becomes a directed edge between nodes:

graph.addEdge(sourceFile, resolvedTarget, {
  relationship: 'imports'
})

enrichNodes() — Louvain Community Detection

After building the raw graph, enrichNodes() applies community detection:

Step 1: Build Undirected Subgraph

File-only nodes (excluding entities and external packages) are extracted into an undirected subgraph. Multi-edges become single edges.

Step 2: Run Louvain

The Louvain algorithm maximizes modularity — it groups files that are densely connected by dependency edges into communities.

import louvain from 'graphology-communities-louvain'
const communities = louvain(subgraph)

Step 3: Name Communities

Each community is named by its most frequent directory:

function nameCommunities(communityFileMap, commonRoot) {
  // For each community, count files per directory
  // Pick the directory with the most files
  // Disambiguate duplicates with #1, #2 suffixes
}

If two communities share the same dominant directory (e.g., both are src/utils), they get disambiguated as src/utils #1 and src/utils #2.

Step 4: Assign Colors

Community Type Color
File communities 12-color palette (#4E79A7, #F28E2B, #E15759, ...)
External packages #2d6a4f (green), community: dependencies
Entity nodes #6a2d6a (purple), inherit parent's community name

Step 5: Set Visual Attributes

node.size = clamp(degree, 5, 15)     // Proportional to connection count
node.x = Math.random() * 100          // Initial positions for ForceAtlas2
node.y = Math.random() * 100
node.language = detectedLanguage      // From file extension
node.label = basename(filePath)       // Default display name

Cycle Detection

detectCycles() runs DFS on file-only nodes:

  1. Track visited nodes and current DFS path
  2. When a back-edge is found, record the cycle
  3. Rotate to canonical form for deduplication
  4. Cap at 200 cycles

See the detect command page for details.

Edge Types

Type Meaning Visual
imports File A depends on file B Solid arrow
contains A file contains a class/function Dashed arrow

Node Type Summary

Type Key Pattern Attributes Size
File src/file.js label, community, color, language, dependencies 5–15
Entity src/file.js::ClassName label, kind (class/function/method), community, color 3
External express label, external: true, npm: boolean 5–15

Clone this wiki locally