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

envscope

v0.2.0

Published

Analyze a repo (single-package or monorepo) and determine which environment variables each app actually requires through its dependency graph.

Downloads

304

Readme

envscope

Which environment variables does each app actually require — traced through its real dependency graph?

envscope analyzes a repository (a monorepo or a plain single-package repo) and, for every app, computes the set of environment variables it depends on at runtime — including variables used deep inside shared internal packages. It uses AST parsing (the TypeScript compiler via ts-morph), never regex, and attributes each variable to an app only if it is reachable through that app's dependency graph.

App: apps/web

Required env vars (9):

  - API_URL
      packages/api-client/src/client.ts:2 (process.env)
  - STRIPE_SECRET
      packages/payments/src/stripe.ts:2 (process.env)
  - AUTH_SECRET
      apps/web/src/auth/session.ts:4 (process.env)
  ...

Why

In a monorepo, env vars are used deep inside shared packages, indirectly required by apps, undocumented, and duplicated. A global .env scan can't answer "what does this app need?" — because it doesn't know app boundaries. envscope does: it builds the import graph and only counts a variable for an app if a file using it is actually reachable from that app.

Install / run

# one-off, no install:
npx envscope analyze --cwd path/to/repo

# or install the CLI globally:
npm i -g envscope
envscope analyze --cwd path/to/repo

# as a project dev dependency:
npm i -D envscope        # pnpm add -D envscope / yarn add -D envscope

From a clone (development):

pnpm install
pnpm build           # compiles to dist/, exposes the `envscope` bin

# or run straight from source during dev:
pnpm exec tsx src/cli.ts analyze --cwd path/to/repo

Commands

envscope                      # analyze every app (default)
envscope analyze              # same as above
envscope app apps/web         # analyze a single app (by relPath or package name)
envscope find STRIPE_SECRET   # reverse lookup: which apps need it, and the chain

Options

| Flag | Description | | --- | --- | | --json | Machine-readable JSON output | | --md, --markdown | Markdown report (great for > ENV_REPORT.md) | | --cwd <dir> | Repo root to analyze (default: current dir) | | --schema <file> | Use a specific .env schema/example for diffing | | --no-color | Disable ANSI colors | | -h, --help / -v, --version | |

Reverse lookup

$ envscope find STRIPE_SECRET
STRIPE_SECRET is used in:
  - apps/api → packages/payments → stripe.ts (packages/payments/src/stripe.ts:2)
  - apps/web → packages/payments → stripe.ts (packages/payments/src/stripe.ts:2)

The chain is the collapsed import path from an app entry to the file that reads the variable.

What gets detected (AST, not regex)

| Pattern | Example | | --- | --- | | process.env.X | const k = process.env.STRIPE_SECRET | | process.env["X"] | process.env["API_TIMEOUT"] | | Destructuring | const { STRIPE_WEBHOOK_SECRET } = process.env | | import.meta.env.X | import.meta.env.VITE_THEME (Vite/SvelteKit/Astro) | | Typed env wrapper | env.SESSION_TTL — only when env is imported (t3-env style), to avoid false positives |

Missing / unused / shared detection

If an app (or the repo root) has a .env.schema, .env.example, .env.sample, .env.template, or .env.defaults, envscope diffs it against real usage:

  • used but not defined — referenced in code but absent from the schema (likely undefined at runtime)
  • ⚠️ defined but unused — present in the schema but never referenced (drift / dead config)
  • 🔁 shared across apps — the same variable required by more than one app

How it works

discover workspace ─▶ collect source files ─▶ AST scan each file ─▶ build import graph
                                                     │                      │
                                          env usages per file      file ──imports──▶ file
                                                     │                      │
                                                     ▼                      ▼
                              per-app aggregation over the set of files REACHABLE
                              from that app's own files through the import graph
  1. Workspace discovery (src/workspace.ts) — reads pnpm-workspace.yaml, package.json workspaces (npm/yarn), or lerna.json; falls back to the apps/* + packages/* folder convention; and falls back again to treating a single-package repo as one app. Apps are classified by directory convention, refined by "nobody depends on it ⇒ it's an app".
  2. AST scanning (src/scanner.ts) — one ts-morph traversal per file extracts both env usages and module specifiers (static imports, export … from, dynamic import(), require()).
  3. Dependency graph (src/graph.ts) — a deterministic resolver (no tsconfig needed) handles relative imports and workspace-package imports (@scope/pkg, subpaths, exports/main/module/source entries, and ./x.js → ./x.ts). It builds a file-level import graph, does reachability BFS per app, and traces chains for find.
  4. Aggregation + schema (src/analyze.ts, src/schema.ts) — dedupes, maps each variable to its file locations, computes per-app/shared/missing/unused.

The key invariant: a variable belongs to an app only if a file that uses it is reachable from that app's own files. That's why apps/web (which imports @sample/ui) gets VITE_THEME, while apps/api (which doesn't) never sees it — and DATABASE_URL is attributed to apps/api alone.

Programmatic API

import { analyzeRepo, findEnvVar } from "envscope";

const result = analyzeRepo(process.cwd());
for (const app of result.analysis.apps) {
  console.log(app.app.relDir, app.envVars.map((v) => v.name));
}
console.log(findEnvVar(result, "STRIPE_SECRET"));

Try the bundled examples

pnpm demo             # analyze example/sample-monorepo
pnpm demo:find        # find STRIPE_SECRET in the sample monorepo
pnpm test             # the test suite asserts the attribution above

example/sample-monorepo is a pnpm-style workspace (apps/web, apps/api, shared packages/*). example/single-app is a plain repo, proving the same engine works without any workspace config.

Project structure

src/
  cli.ts        # arg parsing + command dispatch
  index.ts      # programmatic API
  workspace.ts  # monorepo / single-repo discovery + app classification
  scanner.ts    # ts-morph AST: env usages + import specifiers
  graph.ts      # module resolver, import graph, reachability, find-chains
  analyze.ts    # pipeline: collect → scan → graph → aggregate → schema → shared
  schema.ts     # .env schema loading + missing/unused diff
  output.ts     # human / JSON / markdown renderers
  paths.ts      # cross-platform path normalization
  types.ts      # shared types
example/
  sample-monorepo/   # pnpm workspace demo
  single-app/        # single-package demo
test/
  analyze.test.ts    # end-to-end attribution tests

Roadmap (architected for, not yet built)

  • env drift report across apps (same var, different schema defaults)
  • --write-example to generate a per-app .env.example from detected usage
  • visual dependency graph export (DOT / Mermaid)
  • --fail-on used-but-undefined for CI gating

License

MIT