Skip to content
→ All work

Case study 36 / 37

RepoGraph

An interactive dependency map for TypeScript and JavaScript codebases: import cycles, dead exports, API surface and click-to-highlight blast radius in one HTML file.

Status
In development
Period
2026
Domain
Tools
Language
TypeScript
Last push
26 OCT 2026
License
MIT
Source of claims
README.md, CHANGELOG.md, docs/PRODUCTION_READINESS.md, fixtures/demo-app.report.txt, .github/workflows/ci.yml
repograph force-directed dependency graph of its own repository

01/The problem

Dependency tools either print one report (cycles, unused deps or unused exports) or draw a pretty graph with no analysis behind it. Neither answers “what breaks if I change this file?”

02/The system

An analysis engine built on the TypeScript compiler API's parser, a CLI, and a React viewer with a hand-written canvas force layout. It finds cycles with iterative Tarjan SCC, flags exports nothing imports while respecting re-exports and namespace imports, and writes the whole result as one self-contained HTML file.

03/Implementation

  1. 01Resolves every internal import, re-export, require() and dynamic import() to a real scanned file, with a deduplicated list of external specifiers per file.
  2. 02Circular dependencies as strongly connected components (size > 1) found with an iterative Tarjan's algorithm.
  3. 03Dead-export detection that tracks default, named, type-only, namespace imports and export * separately, and exempts declared entry points.
  4. 04Blast radius: every transitive dependent grouped by hop distance, plus which of the file's exports those dependents actually consume.
  5. 05html (one self-contained file), serve, report (exit 1 on cycles), blast and analyze (JSON) commands.

04/Engineering

Syntax only, no type checker

The parser never builds a ts.Program, so it is one fast pass per file and works on plain .js code with no tsconfig.json.

Conservative dead-code rules

A file targeted by import * as ns or export * has none of its exports flagged, trading a few missed results for fewer false positives.

Safe local serving

serve binds 127.0.0.1, rejects non-loopback Host headers to block DNS rebinding, and the scanner skips symlinked directories and outside-root symlinked files.

05/Interface

repograph's own module graph: 56 files, 87 edges, 1 cycle and 69 dead exports, with cycle and dead-code nodes highlighted.
Selecting packages/core/src/types.ts shows its API surface and a 14-file blast radius, highlighted on the graph.
The Cycles filter isolates the store/actions.ts <-> store/state.ts cycle in the bundled demo-app fixture, with its unused export flagged.

Real screenshots of the running software. Captions state the data shown.

06/Tech stack

  • TypeScript
  • TypeScript compiler API
  • React
  • Vite
  • Canvas
  • Vitest
  • npm workspaces

07/Result

Verified outcomes

  • CI on Ubuntu, Windows and macOS (Node 20 and 22) asserts the CLI's report against the bundled fixture byte for byte.

Known limitations

  • No tsconfig paths or bundler alias resolution; no cross-package resolution in monorepos.
  • Not published to npm; the npm package of the same name is an unrelated project.

08/Links

Related work

Next case study

Waterfall Lens →