| name | fallow |
| description | Codebase intelligence via fallow CLI — dead code, duplication, complexity, refactoring targets.
Load when investigating unused code, planning refactors, or reviewing fallow output.
|
Fallow
Fallow is a static analysis tool for TypeScript/JavaScript installed as a root devDependency.
It finds unused code, duplication, complexity hotspots, and refactoring targets.
Run it via pnpm fallow scripts — never npx.
Quick reference
pnpm fallow
pnpm fallow:audit
pnpm fallow:health
pnpm fallow:dead-code
pnpm fallow:dupes
JSON output for programmatic use
Always invoke via pnpm --silent fallow … --format json --quiet 2>/dev/null and append || true.
The --silent is load-bearing: fallow exits 1 when issues are found (normal), and without --silent pnpm appends [ELIFECYCLE] Command failed with exit code 1. to stdout after the JSON — json.load then fails with "Extra data", and 2>/dev/null does not strip it.
Only exit code 2 is a real error.
pnpm --silent fallow dead-code --format json --quiet 2>/dev/null || true
pnpm --silent fallow health --score --targets --format json --quiet 2>/dev/null || true
Useful flags
| Flag | Purpose |
|---|
--unused-exports | Filter to only unused exports |
--unused-files | Filter to only unused files |
--changed-since main | Only files changed since a ref |
--workspace <name> | Scope to one package |
--group-by package | Group findings by workspace package |
--score | Show health score (0–100) |
--hotspots | Riskiest files by churn × complexity |
--targets | Ranked refactoring recommendations |
--mode semantic | Duplication: catch renamed-variable clones |
Configuration
Config lives at .fallowrc.json in the repo root.
Entry points for pi.extensions are declared manually since fallow does not know that convention.
Rules use "error" (fail CI), "warn" (report only), or "off" (skip).
Suppressing findings
export const keepThis = 1;
export type KeepThisType = string;
The kind token must be the exact singular issue kind (unused-class-member, not unused-class-members) and the only text after the directive — fallow parses every space-separated token as a kind, so trailing prose (-- because …) produces "stale suppression" noise.
Put rationale on the line above the directive.
Use /** @public */ or /** @expected-unused */ JSDoc tags for library API exports.
Auto-fix cycle
Always dry-run first:
pnpm fallow fix --dry-run
pnpm fallow fix --yes
pnpm fallow dead-code
Key gotchas
- Fallow uses syntactic analysis only (no TypeScript compiler) — fully dynamic
import(variable) is not resolved.
- Re-export chains through barrel files are resolved correctly.
--changed-since is additive — only new issues in changed files.
- Never run
fallow watch — it is interactive and never exits.
- The human-readable
health --targets output omits the "Refactoring targets" section entirely when there are zero targets — to confirm a file dropped off the list, use --format json and check the targets array is empty rather than grepping the text output.
- Class-member liveness is keyed off
implements clauses: a member reached only through a structural type the class does not explicitly implements reads as dead once the last implements is removed.
Prefer re-declaring the genuine contract (implements ThatInterface) over a suppression.
If the consumer is wired via an object-literal property (which fallow cannot trace), prefer moving the read into a traced closure body at the composition root (e.g. getX: () => owner.member) over a suppression; suppress only when neither is practical.