depmod-ui
v0.4.3
Published
Visualise the dependency graph of a JavaScript / TypeScript project from your terminal.
Maintainers
Readme
depmod
See the shape of your codebase. A terminal-launched, browser-rendered dependency graph explorer for TypeScript and JavaScript projects.
What is depmod?
depmod is a small CLI that reads your TypeScript or JavaScript project, builds an import graph, and opens an interactive dashboard in your browser. It works on single apps, monorepos, and most setups that ts-morph can parse. Nothing is uploaded anywhere; parsing and the UI stay on your machine.
(Not the Linux depmod command, and not Depeche Mode, though I do love them :D)
Try the live demo — the dashboard exploring depmod's own dependency graph, updated on every release.
It is meant for the moment you land in a repo and need answers quickly:
- Who sits in the middle of the graph? Martin coupling (
Ca,Ce, instability) - If I change this file, what breaks? Blast radius (reverse reachability on the import graph)
- What does this module actually pull in? Focus mode around a node
- Where are the import cycles? Tarjan SCCs, with isolation per cycle
- What looks unused? Unreferenced files, type-only imports, thin stubs
- What is declared in package.json but never imported? Per-workspace static check
Quickstart
Install
Global (recommended if you reach for it often):
npm install -g depmod-ui
# or: pnpm add -g depmod-ui
# or: yarn global add depmod-uiOne-off (no install):
npx depmod-uiRun
After a global install, depmod-ui is on your PATH. With npx, prefix commands the same way.
# Current directory
depmod-ui
# Any project path
depmod-ui /path/to/project
# Re-parse when files change
depmod-ui . --watchBy default the dashboard listens on http://127.0.0.1:45455. If that port is busy, depmod-ui tries the next one so you can run several projects side by side.
Features
| | |
| --- | --- |
| Two canvases | 2D Cytoscape (WebGL) for large graphs; 3D force-directed view for exploration. Switch with one click. |
| Classification | Modules tagged as page, api, hook, component, lib, test, or config. Filter, dim, solo, or hide by class. |
| Path mask | Comma-separated globs on the canvas, e.g. **/*.tsx,!**/*.test.* |
| Inspector | LOC, size, Ca / Ce, instability, exports, dependents, dependencies, rough bundle hint, cycle membership |
| Blast radius | Press B on a selection to highlight everything that depends on it |
| Focus mode | Press F to keep only an N-hop neighbourhood (even across hidden classes) |
| Cycle isolation | List cycles and isolate one to trace the loop |
| Dead-code hints | No importers, no exports, type-only usage, near-empty files |
| Unused-deps report | Compares package.json to real imports; respects monorepo paths |
| Code viewer | Press C or open source in a Monaco panel |
| Live reload | With --watch, saves trigger a re-parse over SSE; filters and selection stick |
| Layout cache | First layout can take a moment; later loads reuse cached positions per graph version |
Commands
| Command | Purpose |
| --- | --- |
| depmod-ui [path] | Parse, serve, open browser (default) |
| depmod-ui serve [path] | Same as default |
| depmod-ui analyze <path> | Write graph.json and metrics.json to disk |
| depmod-ui check <path> --fail-on <rules> | Exit non-zero when rules fail (CI-friendly) |
Useful flags
depmod-ui .
depmod-ui /repo --watch
depmod-ui /repo --port 51000
depmod-ui /repo --no-open
depmod-ui /repo --include "src/**"
depmod-ui /repo --exclude "**/*.test.*"
depmod-ui /repo --no-gitignore
depmod-ui /repo --exclude-tests
depmod-ui /repo --no-cache
depmod-ui check .
depmod-ui check . --fail-on cycles,unused-deps
depmod-ui check . --fail-on instability:>0.7Development
Requirements
- Node 20.18 or newer (
.nvmrcpins 24.14) - pnpm 10+ (
corepack enableis enough)
Setup
nvm use
corepack enable
pnpm install
pnpm build
pnpm depmod-ui .Daily loop
pnpm -r test
pnpm typecheck
pnpm lint
pnpm format
pnpm --filter web devGitHub Pages static demo
Build the static export locally (same pipeline as release deploy):
pnpm build:pages
npx serve apps/web/out -p 3000
# open http://localhost:3000/depmod/Workspace layout
| Path | Role | Published? |
| --- | --- | --- |
| packages/types | Zod Graph schema shared by CLI and web | private |
| packages/parser | Static analysis, metrics, cycles, dead-code, unused-deps | private |
| packages/cli | depmod-ui command; bundles parser and dashboard | npm: depmod-ui |
| apps/web | Next.js dashboard (Cytoscape, three.js, inspector) | bundled in the CLI tarball |
| bench | Benchmarks on OSS repos | private |
How it works
┌────────────────────────┐ ┌─────────────────────────┐ ┌─────────────────────┐
│ depmod-ui │ │ Next.js server │ │ browser │
│ (CLI process) │ │ (spawned by CLI) │ │ │
│ │ │ │ │ │
│ 1. parse (ts-morph) │────▶│ /api/graph reads │────▶│ Cytoscape / 3D │
│ 2. write session file │ │ session JSON │ │ Inspector, etc. │
│ 3. spawn server │ │ /api/events (watch) │────▶│ live updates │
└────────────────────────┘ └─────────────────────────┘ └─────────────────────┘The parser is a pure function: (rootDir) -> Graph. The web app does not re-parse; it only reads what the CLI wrote. Same input should match between the terminal summary and the dashboard.
A few implementation details:
- Path aliases: each workspace
tsconfigpathsmap is applied when resolving imports (workspace-aliases.ts). - Externals: imports resolved to
node_modulestypes are tracked for the unused-deps report, not dropped. - Focus / blast: out-of-scope nodes use
display: none, not opacity, so the view matches the question you asked. - Incremental cache: per-file slices in
.depmod-cache; bumpPARSER_VERSIONin the parser to invalidate.
More design notes: docs/specs/.
Benchmark snapshot
Cold analyze() on real OSS repos (parser 0.3.0, incremental cache off). Wall-clock parse time; full metrics in bench/results/.
| Target | Tier | Files | Edges | Cycles | LOC | Parse |
| --- | --- | ---: | ---: | ---: | ---: | ---: |
| vercel-commerce | primary | 65 | 120 | 1 | 3,896 | 407 ms |
| shadcn-taxonomy | medium | 127 | 247 | 0 | 7,730 | 350 ms |
| create-t3-turbo | medium | 74 | 62 | 1 | 3,370 | 331 ms |
| unkey | medium | 1,921 | 2,914 | 10 | 180,304 | 2.7 s |
| react-email | medium | 1,089 | 1,785 | 7 | 200,826 | 1.6 s |
| documenso | stress | 1,855 | 3,540 | 25 | 233,359 | 2.9 s |
| dub | stress | 3,913 | 10,897 | 19 | 435,857 | 4.7 s |
| cal-web | stress | 988 | 1,287 | 9 | 126,331 | 1.6 s |
| cal.com | stretch | 4,999 | 9,157 | 24 | 546,207 | 7.1 s |
| music.eduardlupu.com | stretch | 41 | 77 | 0 | 7,677 | 1.3 s |
cal-web analyzes only apps/web from the Cal.com monorepo (shared clone). Re-run with pnpm bench or smoke-test with pnpm bench:quick; see bench/README.md.
Contributing
See CONTRIBUTING.md for setup, changesets, and the release flow. Before a PR:
pnpm typecheck,pnpm -r test, andpnpm lintshould pass.- Add or update tests next to the code you change.
- User-visible CLI changes need a changeset (
pnpm changeset).
For larger ideas, open an issue first.
License
MIT © Eduard Lupu
