Skip to content

Dead Exports ​

devtoolz dead-exports finds named exports nothing in the project imports.

bash
devtoolz dead-exports [paths...] [options]

The command only reads code — it never deletes or rewrites anything, there's no --fix. The point isn't to clean up automatically, it's to show what's actually worth a look before you remove it by hand.

Real output — a small package with an entry point src/index.ts re-exporting formatPrice and applyDiscount, where roundToNearestNickel is declared right next to formatPrice but never re-exported or used anywhere:

🧰 devtoolz — chores, automated

Scanned 4 files.

1 dead export found:
  src/pricing.ts:5  roundToNearestNickel

formatPrice doesn't show up — it's re-exported from the public entry point and, separately, actually used in src/cart.ts. applyDiscount doesn't show up either — it's re-exported from the entry point, and by default that's enough to not count as dead, even though nothing inside the repo calls it.

What counts as "dead" ​

An export is left out of the report if any of the following holds:

  • there's at least one import for it somewhere in the scanned files;
  • it's genuinely reachable from the package's public entry point — not just declared directly in the entry file, but re-exported through an export { x } from './y'/export * from './y' chain of any depth. A named re-export clears only the specific re-exported symbol, not the whole file it comes from — a second, un-re-exported function declared right next to it can still turn out to be dead;
  • the module is imported wholesale (import * as ns from './x') — then everything ./x exports counts as used;
  • it's a type imported via import type — that also counts as usage.

Public entry points are auto-detected from package.json's exports/main/module/bin, plus src/index.ts/src/index.tsx if it exists, even without a package.json. --entry adds extra files to that list.

Workspace ​

A pnpm/npm/yarn workspace is auto-detected (from pnpm-workspace.yaml or workspaces in the root package.json) — a sibling package in the same workspace importing your export doesn't read as dead, even if nothing inside the current package references it. --no-workspace turns this off and checks only the scanned code itself.

Options ​

--strict ​

Also check public entry points, not just internal files — something re-exported outward but genuinely unused even outside the repo (external consumers are unknown to the tool, so this is a deliberate tradeoff, not a guarantee). On the same example above:

🧰 devtoolz

Scanned 4 files.

2 dead exports found:
  src/discounts.ts:1  applyDiscount
  src/pricing.ts:5    roundToNearestNickel

--entry <path> ​

An extra public-entry file (repeatable) — exports from it are exempt, same as auto-detected ones.

--no-workspace ​

Don't auto-detect a pnpm/npm/yarn workspace for cross-package resolution.

--cwd <path> ​

Root paths are resolved against.

--ext <list> ​

Comma-separated extensions to include. Default: .ts,.tsx,.js,.jsx,.cjs,.mjs.

--ignore <glob> ​

Extra ignore pattern (repeatable) on top of the built-in defaults. Build-tool config files (*.config.ts/*.config.js/*.config.mjs/*.config.cjs) are always excluded from the walk by default — their default export is usually picked up by the matching tool via the file name, not through an import anywhere in the repo, and would otherwise read as dead in nearly every project.

--no-respect-gitignore ​

Don't also honor the project's .gitignore.

Example:

bash
devtoolz dead-exports src                       # a regular check
devtoolz dead-exports src --strict               # entry points too
devtoolz dead-exports src --entry src/testing.ts  # one more entry point, e.g. testing utilities
devtoolz dead-exports . --no-workspace            # ignore sibling workspace packages