find_unused_exports_by_tsmorph
Find exported symbols unreferenced outside their declaring file. Identifies dead code candidates across TypeScript projects using ts-morph static analysis.
Instructions
[ts-morph] List exports that have no references outside their declaring file across the project. Read-only.
When to use
Hunting for dead code candidates after a refactor or migration.
Auditing a module's surface area: which exports does nobody actually consume?
Pre-deletion safety check before manually removing exports — combine with
find_references_by_tsmorphto double-confirm.
When NOT to use
You want a single symbol's references — use
find_references_by_tsmorph.Single-file unused locals —
tsc --noUnusedLocalsis faster.
Detection scope
Reports:
export function/class/const/let/var/enum/interface/type ...(inline export keyword)export default function/class ...andexport default <Identifier>export = <Identifier>(CommonJS)
Detection algorithm
For each candidate identifier, findReferencesAsNodes() is run and the following references are excluded before deciding "unused":
References inside the SAME file as the declaration (internal use does not count).
References inside any
ExportDeclaration(pure re-export sites likeexport { x } from "./y"orexport *). This means a symbol re-exported only via a barrel — with nothing actually consuming the barrel — IS reported as unused.References in
node_modules.
If 0 references remain, the export is reported.
Known limitations (this tool returns CANDIDATES, not verdicts)
Static analysis cannot see:
Dynamic
require()/import()resolved from runtime strings.File-system / convention based routing (Next.js
page.tsx, Remix routes, etc.). Pass these asentryPoints.Symbols looked up via reflection or string keys.
Pure local re-exports (
export { x }withoutfrom) wherexis declared by a separateconst x = ...in the same file — this form is not enumerated.Mixed function + namespace declarations may be partially missed.
Default exports are high false-positive
export default <Identifier> / export = <Identifier> (shown with the [default] tag) are prone to FALSE POSITIVES: findReferencesAsNodes runs on the local identifier and often fails to connect to import Foo from "./mod" default-import sites. A default export reported here with textHits well above 0 is almost certainly actually used. Treat [default] candidates as low confidence and always confirm with find_references_by_tsmorph.
Always verify a candidate with find_references_by_tsmorph before deletion.
Options
tsconfigPath: absolute path totsconfig.json.entryPoints: list of absolute file paths whose exports should be skipped (treat as public API). Reference sites IN these files still count as "used" automatically.excludeFilePatterns: substrings; any file whose absolute pathincludes()a pattern is not scanned. Use this for test files (e.g.".test."), generated dirs, etc.maxResults: cap on number of reported entries. Default 100. When reached, scanning stops andtruncatedbecomes true — narrow scope with the filters above and retry.
Output modes (responseFormat)
"list"(default): one line per candidate (format below)."summary": aggregate counts for the WHOLE project — total, delete-safety split (deletable vs unexport-only), default-export count, and breakdowns by kind and by directory. On large repos the per-line list easily blows past the response size limit, so start with"summary"to see where dead code clusters, then narrow withentryPoints/excludeFilePatternsand switch to"list"for exact locations. (summaryscans the whole project regardless ofmaxResults.)
Result format (list mode)
A bullet list of candidates with file:line:column, symbol name, declaration kind, a [default] tag for default exports, textHits=N, and sameFileRefs=N.
sameFileRefs — decides delete vs. unexport (read this first)
Every reported export is, by definition, unreferenced OUTSIDE its declaring file. sameFileRefs tells you whether it is still used INSIDE that file (declaration itself and re-export sites excluded), which determines the safe action:
sameFileRefs=0: not used anywhere, including its own file → truly dead, safe to delete the whole declaration (combine withtextHits=0for highest confidence).sameFileRefs=1+: used within its own file → only theexportkeyword is unnecessary. Removeexport, but KEEP the declaration — deleting it breaks the in-file references.
Deleting every reported declaration blindly will break the build: the majority are often sameFileRefs=1+ (over-exported but internally used).
textHits — text-occurrence triage hint
textHits is the number of word-boundary occurrences of the export's name in OTHER source files (declaring file excluded — so it says nothing about same-file usage; use sameFileRefs for that):
textHits=0: no OTHER file mentions the name. Does NOT by itself mean deletable — still checksameFileRefs.textHits=1+: the name appears as a string literal, JSX tag, dynamicimport().then(m => m.X), or comment. Verify withfind_references_by_tsmorphbefore deleting. Short names (e.g.a,id) match incidentally — discount accordingly.
Trailing line reports Scanned files: N and Truncated: bool.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| tsconfigPath | Yes | Absolute path to the project's tsconfig.json. | |
| entryPoints | No | Absolute file paths to treat as public API. Exports declared here are skipped. | |
| excludeFilePatterns | No | Substrings; files whose absolute path includes any of these are not scanned. | |
| maxResults | No | Cap on reported entries (list mode). Default 100. Ignored intent in "summary" mode, which scans the whole project. | |
| responseFormat | No | "list" (default): one line per candidate. "summary": aggregate counts (delete-safety / kind / directory) for the WHOLE project — use this first on large repos to avoid huge output, then narrow with entryPoints/excludeFilePatterns and switch to "list". | list |
| expandNamespaceImports | No | Default true. Inject synthetic named imports into files containing `import * as ns from "./mod"` so that exports of the target module register as 'used' even when consumed only via `{ ...ns }` spread or other escaping patterns. Set to false if you want raw findReferences semantics. |