npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ts-modularity

v0.1.0

Published

TypeScript dependency graph generator with interactive visualization

Downloads

227

Readme

ts-modularity

Scan a TypeScript codebase, build a dependency graph, and serve an interactive visualization — all in one command.

npx ts-modularity

No install required. Opens a browser at http://localhost:3008.


Features

  • Five views — Chord diagram, Force graph, DAG layout, Coupling matrix, Move Suggestions
  • Modularity score per cluster (0 = cohesive, 1 = coupled)
  • Move suggestions — per-file refactor candidates with confidence rating and ready-to-run git mv commands
  • Bidirectional cycle detection highlighted in the matrix view
  • tsconfig path aliases resolved automatically
  • Optional config file to define explicit module boundaries
  • Zero runtime dependencies

Usage

# Scan ./src, serve on port 3008
npx ts-modularity

# Custom source dir
npx ts-modularity --src ./packages/app/src

# Write files only, no server
npx ts-modularity --no-serve

# All options
npx ts-modularity --src ./src --out ./dep-graph-out --depth 1 --port 3008

Options

| Flag | Default | Description | |------|---------|-------------| | --src <dir> | . | Directory to scan for .ts / .tsx files | | --out <dir> | ./dep-graph-out | Output directory for graph.json and index.html | | --depth <n> | 1 | Cluster grouping depth when no config file is present | | --port <n> | 3008 | Preferred HTTP port (falls back to a random port if unavailable) | | --no-serve | — | Write output files and exit without starting the server | | --help | — | Print help |

Output files

| File | Description | |------|-------------| | dep-graph-out/graph.json | Raw graph data — clusters, edges, modularity metrics | | dep-graph-out/index.html | Self-contained interactive visualization |


Config file

Create dep-graph.config.json in your project root to define explicit module boundaries.

{
  "excludeOther": true,
  "modularityThreshold": 0.4,
  "modules": [
    "src/api/*",
    "src/app/*",
    "src/services/*",
    "src/store/*",
    "src/utils/*",
    "src/components"
  ]
}

Module rule syntax

Rules are relative to the project root (where dep-graph.config.json lives), not --src.

| Pattern | Behavior | |---------|----------| | "src/api/*" | Each direct child of src/api/ becomes its own cluster (api/checkout, api/user, …) | | "src/components" | Entire src/components/ is one cluster | | "src/store/auth" | Exactly src/store/auth/ is one cluster | | "packages/*" | Each package in a monorepo becomes its own cluster |

Rules are matched in order — first match wins. Files that match no rule:

  • excludeOther: false (default) — grouped into an <other> cluster
  • excludeOther: true — excluded from the graph entirely

Config fields

| Field | Type | Default | Description | |-------|------|---------|-------------| | modules | string[] | [] | Module boundary rules (see syntax above) | | excludeOther | boolean | false | Drop files that match no rule | | modularityThreshold | number | 0.4 | Informational — clusters above this score are flagged |


Understanding the metrics

Modularity score measures how self-contained a cluster is:

modularity = internal_edges / (internal_edges + external_edges)

| Score | Meaning | |-------|---------| | 1.0 | Fully modular — all dependencies are internal | | 0.5 | Equal internal and external coupling | | 0.0 | Fully coupled — no internal dependencies |

Color coding in the UI: green ≥ 0.67, amber ≥ 0.34, red < 0.34.


Views

Chord

Default view. Arcs represent clusters; ribbons show inter-cluster imports weighted by import count.

Force graph

Physics simulation. Node size scales with file count; the instability arc shows the modularity score. Drag nodes to explore.

DAG layout

Layered top-down layout grouped by modularity bucket. Orthogonal edge routing. Use the Sort dropdown to reorder by modularity, file count, in-deps, or name.

Coupling matrix

Heatmap where row A, column B = number of imports from cluster A into cluster B. Bidirectional cells (cycle risk) are highlighted in red.

Move suggestions

Lists files whose external coupling points strongly to a different cluster. Each suggestion includes:

  • Source and destination cluster
  • Confidence rating (high / medium / low) based on affinity score
  • A git mv command block you can copy and run directly

Adjust the threshold input and click Run to re-compute at any value. Files with score < 2 (too diffuse to have a dominant destination) are omitted.


Programmatic API

import { buildGraph, serve, computeSuggestions, loadModuleConfig, loadAliases } from 'ts-modularity';

const root = process.cwd();
const moduleConfig = loadModuleConfig(root);   // reads dep-graph.config.json
const aliases = loadAliases(root);             // reads tsconfig.json paths

const graph = buildGraph(root, './src', './out', 1, moduleConfig, aliases);

// graph.clusters   — ClusterNode[]
// graph.files      — FileNode[]
// graph.edges      — Edge[]
// graph.clusterEdges — ClusterEdge[]

await serve(graph, 'src', 3008);

// Compute move suggestions programmatically
const result = computeSuggestions(graph, 0.4);
// result.badClusters  — ClusterSuggestions[]
// result.gitMvCommands — string[]  (ready to run)
console.log(result.gitMvCommands.join('\n'));

Types

interface ClusterNode {
  id: string;
  label: string;
  files: number;
  outEdges: number;
  inEdges: number;
  internalEdges: number;
  modularity: number;   // 0–1
  coupling: number;
  cohesion: number;
}

interface FileNode {
  id: string;
  label: string;
  path: string;
  cluster: string;
  imports: number;
  importedBy: number;
}

interface MoveSuggestion {
  file: string;
  fromCluster: string;
  fromLabel: string;
  toCluster: string;
  toLabel: string;
  score: number;
  affinity: number;
  confidence: 'high' | 'medium' | 'low';
}

interface SuggestionsResult {
  threshold: number;
  badClusters: { cluster: ClusterNode; suggestions: MoveSuggestion[] }[];
  gitMvCommands: string[];
}

Full type definitions are exported from the package (dist/index.d.ts).


Requirements

  • Node.js 18+
  • TypeScript project with .ts / .tsx source files

License

MIT