Documentation Index
Fetch the complete documentation index at: https://docs.fallow.tools/llms.txt
Use this file to discover all available pages before exploring further.
fallow dead-code performs dead code analysis on your TypeScript project. It builds a module graph from your and reports anything that isn’t reachable.
Finding dead code requires building a complete module graph. Fallow does this deterministically in milliseconds, giving agents, developers, and CI pipelines the same reliable results.
Issue types
Fallow detects 16 types of dead code:| Issue type | Description |
|---|---|
| Unused files | Files not reachable from any entry point |
| Unused exports | Exported symbols never imported elsewhere |
| Unused types | Type aliases and interfaces never referenced |
| Unused dependencies | Packages in dependencies never imported or used as script binaries |
| Unused devDependencies | Packages in devDependencies never imported or used as script binaries |
| Unused optionalDependencies | Packages in optionalDependencies never imported or used as script binaries |
| Unused enum members | Enum values never referenced |
| Unused class members | Class methods and properties never referenced outside their class, with inheritance tracking, decorator exclusion, and framework lifecycle allowlists |
| Unresolved imports | Import specifiers that cannot be resolved |
| Unlisted dependencies | Imported packages missing from package.json |
| Duplicate exports | Same symbol exported from multiple modules |
| Circular dependencies | Modules that import each other directly or transitively |
| Boundary violations | Imports that cross user-defined architecture zone boundaries |
| Type-only dependencies | Production dependencies only imported via import type (should be devDependencies) |
| Test-only dependencies | Production dependencies only imported by test files (should be devDependencies) |
| Stale suppressions | fallow-ignore comments or @expected-unused JSDoc tags that no longer match any issue |
$ fallow dead-code
Filtering by issue type
Report only specific issue types:Output formats
- Human (default)
- JSON
- SARIF
- Compact
- Markdown
- CodeClimate
Colored terminal output designed for readability.
Incremental analysis
Only check files changed since a git ref:$ fallow dead-code --changed-since main
Baseline comparison
Adopt fallow incrementally by saving a baseline of existing issues:Debugging
Trace why an export is or isn’t considered used:How it works
Fallow uses syntactic analysis with scope-aware binding resolution via Oxc. No TypeScript compiler, no type information. That’s what makes it fast. The graph-based approach guarantees completeness regardless of project size.Fallow works best with projects using
isolatedModules: true (required for esbuild, swc, and Vite). oxc_semantic scope analysis detects unused import bindings (imports where the bound name is never read), but legacy tsc-only projects without isolatedModules may still see edge cases with type-only imports.Script binary analysis
Fallow parsespackage.json scripts to detect CLI tool usage. This reduces false positives in unused dependency detection. When you have a script like "lint": "eslint src/", fallow recognizes that eslint is a binary provided by the eslint package and marks it as used.
How it works:
- Binary name to package name mapping: Script commands like
tsc,vitest, ornextare mapped back to their parent packages (typescript,vitest,next). These packages won’t be reported as unused even when they’re neverimport-ed in source code. --configarguments as entry points: When a script references a config file (e.g.,jest --config jest.e2e.config.ts), fallow treats that config file as an entry point. Config files won’t be flagged as unused.- File path arguments: Direct file references in scripts (e.g.,
node scripts/seed.js) are also recognized as entry points. - Env wrappers and package manager runners: Commands prefixed with
cross-env,npx,pnpx,yarn dlx, ornode -rare unwrapped to find the actual tool binary.
typescript, vite, vitest, and eslint as used dependencies, and vitest.config.ts as an entry point.
Infrastructure entry points
Fallow scans infrastructure config files for source file references and treats them as entry points. Worker processes, migration scripts, and other infrastructure-defined files won’t be reported as unused. Supported files:| File type | What fallow extracts |
|---|---|
Dockerfiles (Dockerfile, Dockerfile.*, *.Dockerfile) | RUN node, CMD, ENTRYPOINT, esbuild invocations |
| Procfiles | Process definitions (e.g., worker: node dist/worker.js) |
| fly.toml / fly.*.toml | release_command and process definitions |
CI pipelines (.gitlab-ci.yml, .github/workflows/*.yml) | npx and binary invocations in CI steps |
config/, docker/, deploy/) for these files.
Dynamic import resolution
Fallow resolves dynamic imports that use patterns rather than static strings. When you writeimport(\./locales/$.json`)`, the import target isn’t known at analysis time. Fallow converts these patterns into glob expressions and matches them against discovered files.
Supported patterns:
| Pattern | Example | Resolved as |
|---|---|---|
| Template literals | import(`./icons/${name}.svg`) | ./icons/*.svg |
| String concatenation | import("./routes/" + path) | ./routes/* |
import.meta.glob | import.meta.glob("./modules/*.ts") | ./modules/*.ts |
require.context | require.context("./themes", true, /\.css$/) | ./themes/**/*.css |
Dynamic imports with fully runtime-computed paths (e.g.,
import(userInput)) cannot be resolved statically. Use entry in your config to mark those directories as entry points.Re-export chain resolution
Fallow resolvesexport * chains through multiple levels of barrel files with cycle detection.
add in app.ts through src/index.ts and utils/index.ts back to utils/math.ts. The add export is correctly marked as used across the entire chain.
Resolution handles:
- Multi-level chains: Any depth of
export *re-exports is followed until the original declaration is found. - Cycle detection: Circular re-export chains (e.g.,
are-exports fromb,bre-exports froma) are detected and handled gracefully. - Mixed re-exports: Named re-exports (
export { foo } from './bar') and namespace re-exports (export * from './bar') are both tracked.
Namespace import narrowing
When a file usesimport * as ns from './module', fallow narrows which exports are actually consumed by scanning for member accesses (ns.foo, ns.bar) and destructuring patterns (const { foo, bar } = ns) in the importing file.
const mod = await import('./x')), and require (const mod = require('./x')).
Fallow also uses oxc_semantic scope analysis to detect imports where the binding is never read. An import { foo } from './utils' where foo is never referenced in the file does not count as a reference to the foo export. This improves unused-export detection precision.
Whole-object consumption patterns like
Object.values(ns), { ...ns }, for (const k in ns), and rest destructuring (const { a, ...rest } = ns) conservatively mark all exports as used. Fallow can’t determine which specific members are accessed in these cases.Class member detection
Fallow detects unused public class members (methods and properties) that are never referenced outside their defining class. Unlike simple text matching, fallow understands class inheritance, decorators, and framework conventions.- Inheritance: A method defined on a parent class and called via the parent type credits the child’s override as used
- Decorators: Any decorated member is excluded from detection. Decorators like
@Get(),@Column(),@Injectable()indicate runtime wiring - Framework lifecycle methods:
componentDidMount,ngOnInit,connectedCallback, and other framework lifecycle methods are never flagged - Whole-object patterns:
Object.values(instance),Object.keys(), spread operators, andfor..inloops conservatively mark all members as used
Class member detection works via syntactic analysis, without invoking the TypeScript compiler. This means fallow tracks member access through the import graph, not through type resolution.
CSS and SCSS tracking
Fallow tracks CSS and SCSS imports using dedicated AST-based extraction. SCSS@use, @forward, and partial imports (_prefix files) are resolved accurately. CSS Module class names are extracted as named exports and tracked through styles.className member accesses.
See CSS, SCSS, and Tailwind analysis for full details.
Entry-point partial unused exports
When a file is an entry point (matched by a plugin or theentry config), fallow traditionally marks all its exports as used. Starting in v2.15.0, fallow can detect partially unused exports in entry-point files: exports that exist in an entry file but are never imported by any other module in the project.
This is especially useful for framework convention files (Next.js pages, SvelteKit routes) where the framework consumes specific named exports (like default, loader, or getStaticProps) but the file may also export helper functions that nothing uses.
Entry-point files are still considered used (never reported as unused files), but individual exports within them that have zero references are now reported as unused exports.
Cross-reference with duplication
Runningfallow dead-code --include-dupes cross-references dead code findings with code duplication analysis. Clone instances in unused files, or overlapping with unused exports, are flagged as combined high-priority findings.
--include-dupes to prioritize cleanup: if a block of code is both duplicated and unused, removing it eliminates dead code and reduces duplication at the same time.
What the cross-reference finds:
- Clone instances in unused files: If a file is unreachable from entry points and contains duplicated code, the duplication finding is elevated.
- Clone instances overlapping unused exports: If an unused export contains code that is duplicated elsewhere, both findings are reported together.
Circular dependency benchmarks
Fallow detects dependency cycles during module graph construction at no extra cost. Standalone tools like madge and dpdm build their own graph from scratch.| Project | Files | fallow | madge | dpdm | vs madge | vs dpdm |
|---|---|---|---|---|---|---|
| zod | 174 | 17ms | 540ms | 190ms | 32x | 11x |
| preact | 244 | 19ms | 298ms | 132ms | 16x | 7x |
| fastify | 286 | 20ms | 165ms | 132ms | 8x | 7x |
| vue/core | 522 | 59ms | 175ms | 143ms | 3x | 2x |
| TanStack/query | 901 | 134ms | 168ms | 137ms | 1.3x | 1.0x |
3-32x faster than madge, 2-11x faster than dpdm on small-to-medium projects. Fallow uses 5-7x less memory by reusing the module graph already built for dead code analysis.
See also
CLI: dead-code
Full reference for the
fallow dead-code command and its flags.Rules & Severity
Control which issue types are errors, warnings, or disabled.
Auto-fix
Automatically remove the dead code fallow finds.