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

nervegraph

v0.4.0

Published

Obsidian Graph View, but for source code — scan a JS/TS, Vue/Svelte/Astro, HTML/CSS, Python, Ruby, Rust, Go, PHP, Java/Kotlin, C/C++ or Shopify Liquid project into a dependency graph and browse it interactively.

Readme

nervegraph

Obsidian Graph View, but for source code. One command scans the project, another opens an interactive relationship map in the browser.

Liquid · JavaScript · TypeScript · Vue/Svelte/Astro · HTML · CSS · Python · Ruby · Rust · Go · PHP · Java/Kotlin · C/C++ · Markdown · Shell · Terraform

npx nervegraph scan     # → .nervegraph/graph.json
npx nervegraph view     # → http://localhost:4210

nervegraph viewer — an interactive dependency map with a detail panel showing which files use the selected file

nervegraph scanning its own repo. Selecting src/graph/build.ts shows its 4 dependents and 8 dependencies, each with the exact import line.


Install

Nothing to install — npx runs it straight from npm:

npx nervegraph scan
npx nervegraph view

Or install it properly:

npm install -g nervegraph     # global, gives you the `nerve` binary
nerve scan
nerve view
npm install -D nervegraph     # per-project
npx nerve scan

Requires Node ≥ 22.6.


Usage

nerve scan  [path] [flags]    # writes .nervegraph/graph.json
nerve view         [flags]    # serves the interactive map
nerve query <file> [flags]    # what uses this file, and what it uses
nerve check        [flags]    # fail the build when the graph breaks a budget
nerve diff <a> [b] [flags]    # what changed structurally between two graphs
nerve export       [flags]    # the graph as mermaid or dot text
nerve mcp          [flags]    # serve the graph to an agent over MCP (stdio)

nerve scan

| Flag | Default | What it does | |---|---|---| | --profile <name> | auto | shopify, js, poly, or auto detection | | --watch | off | re-scan on change; an open viewer reloads itself | | --out <dir> | .nervegraph | Where graph.json is written | | --ignore <glob> | — | Extra ignore pattern; repeatable | | --no-gitignore | off | Don't read .gitignore | | --classes | off | Also collect CSS/HTML class names (noisy) | | --pretty | off | Indent graph.json — bigger, but diffable by hand | | --json | off | Machine-readable summary on stdout | | --quiet | off | Suppress the report |

.gitignore and .nervegraphignore are respected by default, and node_modules, dist, build, vendor, coverage and minified files are always skipped.

nerve view

| Flag | Default | What it does | |---|---|---| | --port <n> | 4210 | Port to serve on (probes upward if taken) | | --graph <path> | .nervegraph/graph.json | Which graph to open | | --no-open | off | Don't launch a browser |

In the viewer: click a node for its dependents and dependencies, / or Ctrl+K to search, and use the sidebar to filter by language, relation type, or to isolate orphans, high-risk files and broken links. ? shows the rest — colour and size modes, the issues panel (i), shift-click to trace the path between two files, and the focus controls.

nerve query

| Flag | Default | What it does | |---|---|---| | --json | off | The card as JSON and nothing else — pipe-safe | | --depth <n> | — | Also list files within n hops | | --orphans | off | List every file nothing references | | --missing | off | List referenced files that aren't on disk | | --unresolved | off | List references static analysis couldn't follow | | --risk <level> | — | List files at low, medium or high risk | | --dead-exports | off | List exports nothing imports | | --duplicates | off | List groups of byte-identical files | | --cycles | off | List dependency cycles | | --packages | off | Fold the graph up to one node per package (monorepos) | | --graph <path> | .nervegraph/graph.json | Which graph to read |

$ nerve query src/graph/types.ts
src/graph/types.ts
  typescript javascript · 122 loc · risk high
  34 dependents (50 transitive) · 0 dependencies

nerve check

The same artifact as a gate. Nothing fails unless you ask for it, except a reference to a file that isn't on disk:

nerve scan --quiet && nerve check --max-cycles 0 --no-duplicates
# exit code 7 when a budget is broken, 0 when every budget is met

| Flag | What it means | |---|---| | --max-cycles <n> | At most n dependency cycles | | --max-unresolved <n> | At most n references static analysis couldn't follow | | --max-orphans <n> | At most n files nothing references | | --allow-missing | Tolerate references to files that aren't on disk | | --no-dead-exports | Fail if any export is unused | | --no-duplicates | Fail if any two files are byte-identical | | --rules <file> | Layer rules; .nervegraph/rules.json is picked up automatically |

Layer rules are yours to write — the tool only checks what you declared:

[
  { "name": "ui must not reach the database", "from": "src/ui/**", "deny": "src/db/**" },
  { "from": "src/core/**", "allow": "src/core/**" }
]

nerve diff and nerve export

nerve diff old-graph.json                  # against .nervegraph/graph.json
nerve export --format mermaid --focus src/graph/build.ts --depth 1
nerve export --format dot --out graph.dot

diff names new and removed files and edges, cycles introduced or resolved, files that became orphans, and files whose risk went up — the structural half of a code review. export writes the map as text, so a diagram can live in a README or an ADR and be diffed like code.

Typical run

cd some-project
npx nervegraph scan          # 98 files · 114 edges
npx nervegraph view          # → http://localhost:4210

For AI agents

graph.json is plain, sorted, deterministic JSON, so an agent (Claude Code, Cursor) can read the dependency map instead of grepping the whole repo:

nerve query snippets/product-card.liquid --json

usedBy lists what breaks if the file changes, transitiveDependents is the full blast radius, and stale: true means the file changed since the scan — run nerve scan first. The whole map is also readable directly:

nerve scan --quiet && cat .nervegraph/graph.json

Keep it fresh while working: nerve scan --watch in one terminal, nerve view in another — the map reloads itself on every re-scan.

Agents that speak MCP can skip the shell entirely:

{ "mcpServers": { "nervegraph": { "command": "npx", "args": ["nervegraph", "mcp"] } } }

It exposes two tools — nervegraph_query (one file's dependents, dependencies, risk and blast radius) and nervegraph_list (orphans, missing files, high-risk files, unused exports, duplicates, cycles). The graph is re-read on every call, so nerve scan --watch keeps the agent current.


Languages

| Language | Extensions | What becomes an edge | |---|---|---| | Liquid | .liquid | render, include, section, sections, content_for, asset_url, t | | JavaScript / TypeScript | .js .mjs .cjs .jsx .ts .tsx .mts .cts | import, export … from, import(), require() | | HTML | .html .htm | <script src>, <link href>, <img>/<source>/<iframe>/… src | | CSS | .css .scss .sass .less | @import, @use, url() | | Python | .py .pyi | import x, from x import y, relative from .x import y | | Ruby | .rb .rake .gemspec | require, require_relative | | Rust | .rs | mod x; (the declarations that pull files into the crate) | | Vue / Svelte / Astro | .vue .svelte .astro | the <script> imports, <style> @use/url(), and template src attributes | | Go | .go | import "…" inside the repo's own module (from go.mod) | | PHP | .php .phtml | require/include, and use through composer's PSR-4 map | | Java / Kotlin | .java .kt .kts | import a.b.C under the Maven/Gradle source root | | C / C++ | .c .h .cc .cpp .hpp … | #include "x.h" (angle-bracket includes are external) | | Markdown | .md .mdx | links and images pointing at project files | | Shell | .sh .bash .zsh | source x.sh / . x.sh | | Terraform | .tf .tfvars | module { source = "./…" } | | Shopify JSON | templates/*.json, sections/*.json | sections.*.type | | Web components | any markup + JS | <product-form>customElements.define('product-form', …) |

Maturity

Breadth without measured accuracy is how a dependency graph quietly starts lying, so each language carries a label rather than an implied promise:

| Level | Languages | What it means | |---|---|---| | verified | Liquid, JavaScript / TypeScript, HTML, CSS, Shopify JSON | Exercised against real projects; scanner and resolver both covered by tests | | experimental | Python, Ruby, Rust, Go, PHP, Java / Kotlin, C / C++, Vue / Svelte / Astro, Markdown, Shell, Terraform | Covered by unit tests on fixtures, not yet measured on a large real codebase — expect gaps rather than wrong edges |

A language moves to verified when its unresolved share has been measured on a real repository, not before.

Third-party references stay out of the graph: import requests, require 'json' and a CDN <script src> are reported as external, not as broken links. A reference that must be a project file — a relative Python import, a require_relative, a mod declaration — and isn't there becomes a missing node, which is the point: that's a broken link in production.

Known gaps

Deliberate, and cheaper to state than to hide:

  • Rust use paths are not followed. mod declarations already define the file graph; use is item-level and would need full name resolution for zero extra nodes.
  • Rails autoloading is invisible. Zeitwerk resolves Userapp/models/user.rb with no require, so a Rails app's graph is thinner than its real coupling.
  • Python docstrings can produce a phantom import if a line inside one starts with import x.
  • ERB <%= %> inside an HTML attribute is skipped, because the > in %> ends the tag match. .html.erb isn't a scanned extension.
  • A Go import resolves to one file of the package, not to all of them — the package's own <name>.go if it exists, otherwise its first source. Nodes are files, and a package is a directory.
  • Java's same-package references are invisible, because they need no import; only cross-package coupling shows up.
  • import a.b.* and use App\{A, B} are skipped rather than expanded to an arbitrary member of the package.
  • Aliases are resolved from a single root config. tsconfig.json extends chains and per-package tsconfigs in a monorepo are not followed; workspace package names are.

Why

Open someone else's 60,000-line theme and understand its architecture in 10 seconds. See who uses a file before deleting it. See what breaks before editing a snippet.

A useful side effect: graph.json is machine-readable, so an AI agent (Claude Code, Cursor) can read the map instead of grepping the whole repo.


Key decisions (locked in)

| Question | Decision | Why | |--------|---------|-----| | Package name | nervegraph, binary nerve | Checked for availability on npm before publishing | | Runtime | Node ≥ 22.6, TypeScript, ESM | Needs --experimental-strip-types to run TS sources directly | | Argument parser | node:utilparseArgs | Stdlib, commander not needed | | HTTP server | node:http | 3 static files, express not needed | | Liquid / HTML | Regex over tags, not a full AST | Reference tags are trivially regular | | JS | es-module-lexer + regex for require | A full Babel AST is overkill for v0.1 | | CSS | postcss | Correctly extracts @import and selectors | | Python, Ruby, Rust, Go, PHP, Java/Kotlin, C/C++, Markdown, Shell, Terraform | Line-oriented regex, zero dependencies | Import syntax in all of them is regular; a per-language parser would be one more dependency each for the same edges | | Vue / Svelte / Astro | Split the file, reuse the HTML, JS and style passes | A SFC is three languages in one file, and all three scanners already exist | | Visualization | force-graph (vasturiano), canvas | Drag, zoom, highlight, 5k+ nodes out of the box | | Frontend framework | None. Vanilla JS + one HTML file | The detail panel is 150 lines of DOM | | Storage | A single graph.json | SQLite at 100k+ nodes, not before |