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

@revelio-tools/revelio

v0.8.0

Published

Static analysis CLI for React codebases — builds a queryable dependency graph and exposes it via REST and MCP

Downloads

691

Readme

Revelio

Revelio is a static analysis tool for React codebases that answers one question every engineer asks before touching shared code: "what will break if I change this?"

It parses your JavaScript and TypeScript source, builds a queryable dependency graph of every component, hook, selector, action, and context in the project, and exposes that graph through a REST API, an MCP server for AI coding agents, and a VS Code extension — all from a single revelio build.

Think of it as a GPS for your codebase: before you change a selector, a component, or a saga, Revelio tells you exactly what will be affected, how confident it is, and where the analysis becomes uncertain.


Supported codebase types

Revelio works with any React codebase, but coverage depth depends on what state-management patterns you use:

| Pattern | Support | Notes | |---|---|---| | React (JSX/TSX render graph) | ✅ Full | Components, hooks, styled-components | | Redux / Saga / Reselect | ✅ Full | USES_SELECTOR, DISPATCHES, SAGA_* edges | | Custom hooks | ✅ Full | HOOK_CALLS edges | | dynamic() / React.lazy() | ✅ Full | RENDERS_LAZY edges | | Styled-components | ✅ Full | WRAPS + CALLS edges | | React Context | ✅ Full | CONTEXT_PROVIDES + CONTEXT_CONSUMES for components and helper hooks — both block-body (const x = useContext(C)) and expression-body (() => useContext(C)) | | React Query / SWR / RTK Query | ⚠️ Partial | Render graph + hook calls ✓; no query-specific edge types. Tested: cal.com Turborepo (5k symbols, 95% impact-critical resolution ✅) | | Zustand / Jotai / Recoil | ⚠️ Partial | Render graph + hook calls ✓; store subscriptions not modelled. Tested: Excalidraw (Zustand, ~2.5k symbols ✅) | | Apollo / GraphQL codegen | ✅ Full | Render graph + hook calls ✓; generated *Query/*Mutation hooks correctly flagged as orphans when uncalled. Tested: Saleor Dashboard ✅ | | React Server Components | ⚠️ Partial | Parsed correctly; no server/client boundary edge types yet | | Monorepo (packages/) | ✅ Full | Auto-derives package aliases from package.json names | | Flat single-package repo | ✅ Full | Set packageRoots: [] in config | | Turborepo | ✅ Full | Tested: cal.com (21 packages auto-resolved, no manual aliases needed) | | Nx | ✅ Full | Resolves via package.json-name auto-derivation — no Nx-specific config needed. Tested: Kibana (71.9k files, 79.8k symbols, 63 packages ✅) |

Bottom line: Revelio is designed for React codebases (JSX/TSX). RENDERS_PASSES, CALLS, and HOOK_CALLS edges are always captured. Redux/Saga-specific edges appear when those patterns are present. Non-React TypeScript projects (custom renderers, Node.js backends, CLI tools) will see high orphan rates and misclassifications — the tool is not designed for them. Tested on: Mattermost (React+Redux ✅), cal.com (React+React Query+Turborepo ✅), Excalidraw (Zustand ✅), React Admin (component library ✅), Saleor Dashboard (Apollo GraphQL ✅), Kibana (Nx monorepo, 71.9k files ✅), and a Remix-ecosystem server-library monorepo — TypeScript, no React app, no Redux — which validates graceful non-React handling (correctly high orphan rate; plain generators no longer misclassified as Redux sagas) rather than a React-specific feature.


Install

npm install -g @revelio-tools/revelio

Requires Node.js >= 20. The package includes a prebuilt better-sqlite3 native binary — no post-install compile step needed.

VS Code extension: Install Revelio for VS Code to get hover tooltips, a File Risk panel, and the D3 graph visualiser directly in your editor — no terminal required after the initial build.

Or clone and link locally:

git clone https://github.com/sipandey/revelio.git
cd revelio
npm install          # installs deps + places the native sqlite3 binary
npm run build        # compiles TypeScript → dist/
npm link             # makes 'revelio' available in your shell

Requirements: Node.js >= 20

Note: Do not use npm rebuild better-sqlite3 — the native binary is bundled in prebuilds/ and installed automatically by npm install. Running npm run setup manually re-runs the same step if needed.


Development commands

npm test             # run all tests (vitest)
npm run build        # compile TypeScript
npm run typecheck    # tsc --noEmit only
npm run setup        # reinstall better-sqlite3 native binary
npm run dev          # watch mode TypeScript compile

If vitest or revelio are not found as bare commands, use:

npx vitest run
npx revelio build --config revelio.config.json
# or after npm link:
revelio build --config revelio.config.json

Quick start

# 1. Create a config in your repo root
revelio init
#    Auto-detects packageRoots (from workspaces), aliases (tsconfig/jest/vite/
#    webpack), and sensible excludes — monorepo-aware, including when the React
#    app lives in a sub-dir (e.g. webapp/channels/). Review the generated file.

# 2. Index the codebase (run once, then incrementally via the watcher)
revelio build

# 3. Start the MCP server
revelio serve --stdio

The index is stored in revelio.db (SQLite). There's no startup banner on stdout — it's reserved exclusively for the MCP JSON-RPC protocol. The server is ready once stderr shows:

[revelio] Warming traversal cache...
[revelio] Traversal cache warm (0.1s)

The MCP server includes a file watcher that incrementally reindexes changed files (target: < 100 ms per file). Note: the incremental watcher updates base graph edges only — Redux, Saga, HOC, and Context enriched edges do not update on incremental reindex. After changing a reducer, selector, saga, or context file, run revelio build to get fully updated enriched graph data.

Optional enrichments

# Add git co-change coupling (CO_CHANGES edges — symbols that change together)
revelio build --cochange
revelio build:cochange          # on an existing graph, no full rebuild

# Annotate nodes with test-coverage data from a coverage-summary.json
revelio build --coverage ./coverage/coverage-summary.json
revelio build:coverage --input ./coverage/coverage-summary.json   # existing graph

Claude Code integration

Claude Code reads MCP server definitions from a .mcp.json file at your project root (project-scoped; share it with your team or gitignore it for local-only use). Add a revelio entry to the mcpServers object — alongside any other servers you already have configured, not in place of them:

{
  "mcpServers": {
    "some-other-server": {
      "command": "...",
      "args": ["..."]
    },
    "revelio": {
      "command": "revelio",
      "args": ["serve", "--stdio", "--config", "/path/to/your/repo/revelio.config.json"]
    }
  }
}

Only the revelio block is new — merge it into whatever mcpServers object you already have. --config should be an absolute path to your repo's revelio.config.json; Revelio resolves relative paths inside that file (e.g. the default output: "./revelio.db") against the config file's own directory, not whatever working directory the host happens to spawn the process from, so this works regardless of where Claude Code launches it.

Claude Code also supports registering servers without hand-editing JSON — see claude mcp add --help for the current CLI syntax. (See Quick start above for how to confirm the server started — there's no stdout banner under --stdio.)

Claude Code can then call:

  • search_nodes — discover symbol ids by name, kind, or package (start here)
  • get_signature — look up any exported symbol by id or name
  • get_dependents — find everything that depends on a symbol
  • get_dependencies — find everything a symbol depends on
  • get_prop_dependents — find which parent components pass a given prop to a component ("who passes prop P to component C")
  • impact_query — full blast-radius analysis with risk scoring

Every tool accepts a responseMode parameter (summary | standard | full) to control output size; MCP defaults to standard (REST/HTTP still defaults to full — the VS Code extension's behavior is unchanged). Pass responseMode: 'full' explicitly to get the old, fuller output back. impact_query also accepts maxAffected to cap how many affected[] entries come back (default 50 over MCP) without affecting riskScore or meta.totalAffected. See CHANGELOG.md for the full rationale.


Cursor integration

In .cursor/mcp.json, add a revelio entry to mcpServers alongside any other servers already configured there:

{
  "mcpServers": {
    "revelio": {
      "command": "npx",
      "args": ["revelio", "serve", "--stdio"]
    }
  }
}

REST API

Start with revelio serve --port 3001 to enable both MCP (over HTTP/SSE) and a REST API.

| Method | Endpoint | Description | |--------|----------|-------------| | GET | /api/v1/signature/:id | Symbol record by id | | GET | /api/v1/dependents/:id | What depends on this symbol (paginated) | | GET | /api/v1/dependencies/:id | What this symbol depends on (paginated) | | GET | /api/v1/prop-dependents/:id | Which parents pass a prop to this component (?propName=, paginated) | | POST | /api/v1/impact | Full impact query (same as MCP tool) | | GET | /api/v1/nodes | Browse/search all symbols (paginated) | | GET | /api/v1/stats | Graph statistics |

All list endpoints support ?limit=50&offset=0 (max 200 per page). /api/v1/nodes also supports ?kind=selector&package=@myorg/core&q=getItems&file=/abs/path/to/file.jsx (the file param returns every symbol defined in that exact file) and ?coverage=none|low|any (requires revelio build --coverage; none = 0%, low = <50%, any = has data). /api/v1/impact's body also accepts an optional typeChange: { field, before, after } for breaking-change analysis (same as the MCP tool).

Auth: Set auth.enabled: true in revelio.config.json and REVELIO_AUTH_TOKEN=<secret> in the environment. Pass Authorization: Bearer <token> on all requests.


CI integration (Jenkins)

# Write a snapshot — never prints to stdout
revelio build:ci --output revelio.snapshot.json

# Quality gate: fail if ghost node count exceeds threshold
revelio build:ci --fail-on-ghosts 200

# Blast-radius gate: fail if a key selector's risk score is too high  
revelio build:ci --fail-on-risk getAuthStatus:100

Exit codes:

  • 0 — success
  • 1 — build error
  • 2 — ghost gate exceeded
  • 3 — risk gate exceeded

Example Jenkinsfile stage:

stage('Revelio Analysis') {
  steps {
    sh 'npx revelio build:ci --output revelio.snapshot.json --fail-on-ghosts 500'
    archiveArtifacts artifacts: 'revelio.snapshot.json'
  }
}

Plugin API

Extend Revelio with custom symbol extractors. Plugins run after Phase 2 extraction and before graph construction.

// my-revelio-plugin.cjs
module.exports = {
  name: 'my-plugin',
  version: '1.0.0',
  async: false,   // synchronous plugins only — async not yet supported
  extract: (ast, filePath) => {
    // ast: Babel AST node (File)
    // filePath: absolute path to the file being parsed
    // Return an array of SymbolRecord objects to add to the graph
    if (!filePath.endsWith('.model.js')) return [];
    return [{
      id: `@myorg/models:${path.basename(filePath, '.model.js')}`,
      kind: 'function',
      name: path.basename(filePath, '.model.js'),
      filePath,
      packageName: '@myorg/models',
      signature: 'Model',
      exportedAs: [path.basename(filePath, '.model.js')],
      loc: { start: 0, end: 0 },
      confidence: 0.9,
    }];
  },
};

Register in revelio.config.json:

{
  "plugins": ["./my-revelio-plugin.cjs"]
}

Error isolation: If a plugin throws, Revelio logs the error, skips that plugin for that file, and continues the build. A crashing plugin never fails the build.


Configuration reference

revelio.config.json:

| Field | Type | Default | Description | |-------|------|---------|-------------| | targetRepo | string | required | Absolute path to your repo root | | packageRoots | string[] | ["packages/"] | Subdirectories containing packages. revelio init auto-detects these from workspace globs (incl. nested webapp/package.json). | | excludes | string[] | build output + test/story/mock helpers | Glob patterns to skip during discovery (defaults cover dist, build, node_modules, **/*.test.*, **/*.stories.*, **/e2e/**, **/__mocks__/**, …) | | output | string | "./revelio.db" | SQLite database output path. Relative paths resolve against this config file's own directory, not the caller's working directory. | | plugins | string[] | [] | Plugin module paths (relative or npm package) | | aliases | object | {} | Path alias map (mirrors tsconfig paths) | | sideEffectHocs | string[] | [] | Names of your own purely side-effectful HOCs (inject no props) to treat with the same confidence as built-in known HOCs (0.8, resolved) instead of the unknown-HOC default (0.3, opaque) | | auth.enabled | boolean | false | Enable Bearer token auth on HTTP transport | | auth.tokenEnv | string | "REVELIO_AUTH_TOKEN" | Env var name for the auth token | | corsOrigins | string[] | ["*"] | CORS allowed origins for HTTP transport | | cochange.enabled | boolean | false | Compute CO_CHANGES edges from git history (also enabled by --cochange) | | cochange.windowMonths | number | 6 | How far back into git history to look | | cochange.minRate | number | 0.3 | Minimum co-change rate to create an edge | | cochange.minOccurrences | number | 3 | Minimum shared commits to create an edge | | cochange.excludePaths | string[] | [] | Glob patterns (matched against repo-relative paths) to exclude from co-change analysis, e.g. test files |

CI quality gates (--fail-on-ghosts, --fail-on-risk) and the snapshot output path are CLI flags on revelio build:ci, not config file fields — see CI integration above. There is currently no ci.* section in revelio.config.json that the CLI reads.

Common patterns:

// Monorepo with sub-packages (e.g. packages/core, packages/ui)
{ "packageRoots": ["packages/"] }
// Package aliases are auto-derived from each package.json "name" field.
// Add explicit aliases for packages not in packageRoots, or for webpack
// root-relative bare imports like import('components/Foo'):
{ "aliases": { "components": "src/components", "actions": "src/actions" } }

Example full config:

{
  "targetRepo": "/path/to/your/repo",
  "packageRoots": ["packages/"],
  "excludes": ["**/dist/**", "**/__generated__/**", "**/node_modules/**"],
  "output": "./revelio.db",
  "plugins": [],
  "aliases": {
    "@myorg/core": "packages/core/src",
    "@myorg/ui": "packages/ui/src"
  },
  "auth": { "enabled": false, "tokenEnv": "REVELIO_AUTH_TOKEN" }
}

Performance

Measured on the test fixture codebase (25 files, 44 symbols, 46 edges):

| Operation | P50 | P99 | Target | |-----------|-----|-----|--------| | Cold index (25 files) | 51ms | 78ms | < 3s | | get_signature | < 1ms | < 1ms | < 20ms | | get_dependents | < 1ms | < 1ms | < 50ms | | get_dependencies | < 1ms | < 1ms | < 50ms | | impact_query (warm) | < 1ms | < 1ms | < 200ms | | impact_query (cold) | < 1ms | < 1ms | < 500ms | | Incremental reindex | 2ms | 3ms | < 100ms | | REST /api/v1/stats | < 1ms | < 1ms | < 30ms |

(from benchmarks.json, committed at every release — re-run with vitest run tests/benchmarks/)

Cold start note: The first impact_query call per process loads all edges into memory (bulk DB read). On a 10,000-node codebase this takes ~350ms. Subsequent calls are sub-10ms.

Real-world scale: Cold builds on public OSS repos — Remix (~1.4k symbols) in ~2.5s, and Kibana (71.9k files / 79.8k symbols / 63 packages) end-to-end in ~7 minutes. Impact queries stay sub-second once edges are warm, independent of repo size.


Limitations

Revelio is a static analysis tool — it can only see what the source code says at parse time.

Things it cannot detect:

  • Dynamic importsReact.lazy(() => import('./Component')) and dynamic(import('./Component')) are tracked as RENDERS_LAZY edges (confidence 0.7). Dynamic imports with computed paths (import(buildPath(x))) cannot be resolved statically — the target is opaque and no render edge is produced rather than a guessed one.
  • Runtime prop injection — props passed via React.cloneElement or context providers that pass functions are modelled at low confidence (0.3–0.5).
  • Circular dependency graphs — Revelio detects cycles in hook chains and stops traversal, marking them as resolution: 'cycle'.
  • Generated code — files in dist/, build/, __generated__/ should be excluded via excludes in your config.
  • Non-standard transpilation — Babel plugins not in Revelio's default set (all standard React plugins are included) may produce AST shapes the parser does not recognise.
  • TypeScript declaration merging — ambient module augmentation is not tracked.
  • Async plugin API — not yet supported. All plugins must be synchronous.

Licence

MIT