nadeesha/ts-prune

Find unused exports in a typescript project. 🛀

TypeScript

2,064

184 commits

updated Oct 6, 2026

See the code

README

ts-prune

CI npm version Monthly downloads Node.js requirement License: MIT

Find potentially unused exports in your TypeScript project. ts-prune reads your tsconfig.json and reports exports with no detected use elsewhere in the project. It reports findings without changing your files.

Requirements

Node.js 22.18 or newer and a TypeScript project with a tsconfig.json. JavaScript files are supported when allowJs is enabled in that configuration.

Quick start

Install with your package manager:

npm install --save-dev ts-prune
# or
yarn add --dev ts-prune
# or
pnpm add --save-dev ts-prune

Run from your project root:

npx ts-prune

For a different TypeScript configuration:

npx ts-prune --project tsconfig.build.json

Example

Given src/math.ts:

export const add = (a: number, b: number) => a + b;
export const subtract = (a: number, b: number) => a - b;
export const multiply = (a: number, b: number) => a * b;

And src/app.ts:

import { add } from './math';
console.log(add(2, 3));

ts-prune reports:

src/math.ts:2 - subtract
src/math.ts:3 - multiply

Each line is file:line - exportName. Exports referenced within their own file are marked (used in module); use --unusedInModule to hide them. Re-exports may show undefined as the line number when the declaration is in another file.

CLI options

OptionDescriptionExample
-p, --project [path]TypeScript configuration; defaults to tsconfig.jsonts-prune -p tsconfig.build.json
-i, --ignore [regexp]Hide matching output linests-prune -i 'test|spec'
-s, --skip [regexp]Exclude matching files from analysis and from references counted as usests-prune -s '\.test\.ts$'
-e, --errorExit with status 1 if any findings remain after filteringts-prune -e
-u, --unusedInModuleHide exports marked (used in module)ts-prune -u
-h, --helpShow helpts-prune -h

--ignore filters the report; --skip changes the analysis. For example, skipping tests allows exports used only by tests to appear as unused. Patterns are JavaScript regular expressions, not globs.

Without --error, findings do not cause a nonzero exit status. Configuration and execution errors still fail the command.

Configuration

Create .ts-prunerc.json:

{
  "project": "tsconfig.build.json",
  "ignore": "components/(Button|Input)",
  "skip": "\\.test\\.|test/",
  "unusedInModule": false
}

Or use the ts-prune property in package.json:

{
  "ts-prune": {
    "project": "tsconfig.build.json",
    "error": true
  }
}

Configuration discovery also supports .ts-prunerc (JSON or YAML), .ts-prunerc.yaml, .ts-prunerc.yml, and JS, CJS, or TS configuration files such as .ts-prunerc.cjs:

module.exports = {
  project: 'tsconfig.build.json',
  unusedInModule: true,
};

Use export default in TypeScript configuration files. Discovery starts in the working directory and searches parent directories until it reaches the home directory or filesystem root. Within a directory, the ts-prune property in package.json takes precedence over the standard configuration files. Project paths are resolved from the working directory.

Options are applied in this order: defaults, discovered configuration, explicitly supplied CLI options. In configuration files, use booleans for error and unusedInModule; false disables them. Legacy string values remain accepted, but the string "false" does not disable a flag.

Ignoring an export

Place // ts-prune-ignore-next immediately before its declaration:

// ts-prune-ignore-next
export interface PublicApi {
  name: string;
}

Files explicitly listed in the selected tsconfig's files array are treated as public entrypoints: their exports are excluded from the report. Files selected through include are analyzed normally.

Using ts-prune in CI

Add a script to package.json:

{
  "scripts": {
    "deadcode": "ts-prune --error"
  }
}

Run npm run deadcode in CI to fail when findings remain. Add --unusedInModule if you only want exports without detected local uses.

Limitations

Review findings before deleting code:

  • Frameworks and build tools may consume exports through conventions or reflection that ts-prune cannot detect.
  • Imports with computed paths cannot reliably be resolved. Resolved literal dynamic imports, such as import('./module'), conservatively mark all exports in that module as used.
  • Namespace uses that cannot be tracked, side-effect imports, and wildcard re-exports can keep unused exports out of the report.
  • Uses outside the configured project are not visible. Mark public entrypoints or ignore exports consumed elsewhere.

Development

Use the Node version in .nvmrc:

nvm install
nvm use
npm ci
npm run check           # lint, strict type checks, build, all tests
npm run test:unit
npm run test:integration
npm run test:coverage   # Node's built-in coverage report
npm run test:package    # install a tarball and check its CLI and API
npm run lint:fix        # apply lint fixes

Tests use node:test and node:assert/strict. Test compilation goes into .test-build/; the published CLI and CommonJS library are built into lib/. Integration tests run in temporary directories without global npm links. Coverage measures compiled source in test workers; CLI subprocess behavior is checked separately. The package smoke test installs production dependencies from npm into a temporary consumer project.

Development uses Node 26. CI tests Node 22, 24, and 26 on Linux and Node 26 on macOS and Windows. It runs on pull requests, pushes to master and codex/**, and version tags. Publishing is manual: choose Actions → Release → Run workflow and enter an existing version tag. The release reruns the full matrix before publishing; pushes and tags alone do not publish. See PUBLISHING.md for setup and release steps.

The build uses TypeScript 6.0.3 while the TypeScript ESLint parser requires a compiler version below 6.1. TypeScript 7 is deferred until the parser supports it. The analyzer uses the compiler bundled with ts-morph independently. Runtime dependencies are ts-morph and cosmiconfig. See MODERNIZATION.md for dependency counts and verification results.

Acknowledgements

Built with ts-morph and inspired by @dsherret's approach.

Contributors

Project status

This project has been resurrected after a long hiatus. Many projects still depend on ts-prune, and by now it seems to be encoded in AI model weights too. It needs to keep working.

typescript
utility

nadeesha/ts-prune

Find unused exports in a typescript project. 🛀

TypeScript

2,064

184 commits

updated Oct 6, 2026

See the code

README

ts-prune

CI npm version Monthly downloads Node.js requirement License: MIT

Find potentially unused exports in your TypeScript project. ts-prune reads your tsconfig.json and reports exports with no detected use elsewhere in the project. It reports findings without changing your files.

Requirements

Node.js 22.18 or newer and a TypeScript project with a tsconfig.json. JavaScript files are supported when allowJs is enabled in that configuration.

Quick start

Install with your package manager:

npm install --save-dev ts-prune
# or
yarn add --dev ts-prune
# or
pnpm add --save-dev ts-prune

Run from your project root:

npx ts-prune

For a different TypeScript configuration:

npx ts-prune --project tsconfig.build.json

Example

Given src/math.ts:

export const add = (a: number, b: number) => a + b;
export const subtract = (a: number, b: number) => a - b;
export const multiply = (a: number, b: number) => a * b;

And src/app.ts:

import { add } from './math';
console.log(add(2, 3));

ts-prune reports:

src/math.ts:2 - subtract
src/math.ts:3 - multiply

Each line is file:line - exportName. Exports referenced within their own file are marked (used in module); use --unusedInModule to hide them. Re-exports may show undefined as the line number when the declaration is in another file.

CLI options

OptionDescriptionExample
-p, --project [path]TypeScript configuration; defaults to tsconfig.jsonts-prune -p tsconfig.build.json
-i, --ignore [regexp]Hide matching output linests-prune -i 'test|spec'
-s, --skip [regexp]Exclude matching files from analysis and from references counted as usests-prune -s '\.test\.ts$'
-e, --errorExit with status 1 if any findings remain after filteringts-prune -e
-u, --unusedInModuleHide exports marked (used in module)ts-prune -u
-h, --helpShow helpts-prune -h

--ignore filters the report; --skip changes the analysis. For example, skipping tests allows exports used only by tests to appear as unused. Patterns are JavaScript regular expressions, not globs.

Without --error, findings do not cause a nonzero exit status. Configuration and execution errors still fail the command.

Configuration

Create .ts-prunerc.json:

{
  "project": "tsconfig.build.json",
  "ignore": "components/(Button|Input)",
  "skip": "\\.test\\.|test/",
  "unusedInModule": false
}

Or use the ts-prune property in package.json:

{
  "ts-prune": {
    "project": "tsconfig.build.json",
    "error": true
  }
}

Configuration discovery also supports .ts-prunerc (JSON or YAML), .ts-prunerc.yaml, .ts-prunerc.yml, and JS, CJS, or TS configuration files such as .ts-prunerc.cjs:

module.exports = {
  project: 'tsconfig.build.json',
  unusedInModule: true,
};

Use export default in TypeScript configuration files. Discovery starts in the working directory and searches parent directories until it reaches the home directory or filesystem root. Within a directory, the ts-prune property in package.json takes precedence over the standard configuration files. Project paths are resolved from the working directory.

Options are applied in this order: defaults, discovered configuration, explicitly supplied CLI options. In configuration files, use booleans for error and unusedInModule; false disables them. Legacy string values remain accepted, but the string "false" does not disable a flag.

Ignoring an export

Place // ts-prune-ignore-next immediately before its declaration:

// ts-prune-ignore-next
export interface PublicApi {
  name: string;
}

Files explicitly listed in the selected tsconfig's files array are treated as public entrypoints: their exports are excluded from the report. Files selected through include are analyzed normally.

Using ts-prune in CI

Add a script to package.json:

{
  "scripts": {
    "deadcode": "ts-prune --error"
  }
}

Run npm run deadcode in CI to fail when findings remain. Add --unusedInModule if you only want exports without detected local uses.

Limitations

Review findings before deleting code:

  • Frameworks and build tools may consume exports through conventions or reflection that ts-prune cannot detect.
  • Imports with computed paths cannot reliably be resolved. Resolved literal dynamic imports, such as import('./module'), conservatively mark all exports in that module as used.
  • Namespace uses that cannot be tracked, side-effect imports, and wildcard re-exports can keep unused exports out of the report.
  • Uses outside the configured project are not visible. Mark public entrypoints or ignore exports consumed elsewhere.

Development

Use the Node version in .nvmrc:

nvm install
nvm use
npm ci
npm run check           # lint, strict type checks, build, all tests
npm run test:unit
npm run test:integration
npm run test:coverage   # Node's built-in coverage report
npm run test:package    # install a tarball and check its CLI and API
npm run lint:fix        # apply lint fixes

Tests use node:test and node:assert/strict. Test compilation goes into .test-build/; the published CLI and CommonJS library are built into lib/. Integration tests run in temporary directories without global npm links. Coverage measures compiled source in test workers; CLI subprocess behavior is checked separately. The package smoke test installs production dependencies from npm into a temporary consumer project.

Development uses Node 26. CI tests Node 22, 24, and 26 on Linux and Node 26 on macOS and Windows. It runs on pull requests, pushes to master and codex/**, and version tags. Publishing is manual: choose Actions → Release → Run workflow and enter an existing version tag. The release reruns the full matrix before publishing; pushes and tags alone do not publish. See PUBLISHING.md for setup and release steps.

The build uses TypeScript 6.0.3 while the TypeScript ESLint parser requires a compiler version below 6.1. TypeScript 7 is deferred until the parser supports it. The analyzer uses the compiler bundled with ts-morph independently. Runtime dependencies are ts-morph and cosmiconfig. See MODERNIZATION.md for dependency counts and verification results.

Acknowledgements

Built with ts-morph and inspired by @dsherret's approach.

Contributors

Project status

This project has been resurrected after a long hiatus. Many projects still depend on ts-prune, and by now it seems to be encoded in AI model weights too. It needs to keep working.

typescript
utility

Project health

Overall

3.1/10

Maintained

1/10

Code review

3/10
OpenSSF Scorecard

Latest release

0.10.3 · Dec 12, 2021

Releases this year

0

Significant stargazers

(top 24 of 37)

EGOIST

13,582 followers · starred Aug 2022

David Sherret

2,130 followers · starred May 2019

Jiachi Liu

2,102 followers · starred Aug 2022

Nicolas Charpentier

420 followers · starred Apr 2023