Rules control the severity of each issue type, making it easy to adopt fallow incrementally in CI.
Start with all rules as warn, then promote to error one type at a time as the codebase cleans up.
| Level | Behavior |
|---|---|
error | Report and fail CI (non-zero exit code) |
warn | Report but exit 0 |
off | Don't detect or report |
Most issue types default to error when not configured. The exceptions are private-type-leaks, coverage-gaps, prop-drilling, thin-wrapper, duplicate-prop-shape, and require-suppression-reason (default off), plus stale-suppressions and policy-violation (default warn).
{
"rules": {
"unused-files": "error",
"unused-exports": "warn",
"unused-types": "off",
"private-type-leaks": "off",
"unused-dependencies": "error",
"unused-dev-dependencies": "warn",
"unused-optional-dependencies": "warn",
"unused-enum-members": "warn",
"unused-class-members": "off",
"unresolved-imports": "error",
"unlisted-dependencies": "error",
"duplicate-exports": "warn",
"circular-dependencies": "error",
"re-export-cycle": "warn",
"boundary-violation": "error",
"type-only-dependencies": "error",
"test-only-dependencies": "warn",
"dev-dependencies-in-production": "warn",
"coverage-gaps": "off",
"stale-suppressions": "warn"
}
}| Rule | Description |
|---|---|
unused-files | Files not reachable from any entry point |
unused-exports | Exported symbols never imported |
unused-types | Type aliases and interfaces never referenced |
private-type-leaks | Opt-in API hygiene check for exported signatures that reference same-file private types (default off) |
unused-dependencies | Packages in dependencies never used |
unused-dev-dependencies | Packages in devDependencies never used |
unused-optional-dependencies | Packages in optionalDependencies never used |
unused-enum-members | Enum values never referenced |
unused-class-members | Class members never referenced |
unused-store-members | Pinia store members (state / getter / action) never accessed by any consumer; runs only when pinia / @pinia/nuxt is a dependency (default warn) |
unused-catalog-entries | Entries in pnpm-workspace.yaml's catalog: / catalogs: maps that no workspace package references via the catalog: protocol (default warn) |
empty-catalog-groups | Named catalogs.<name>: groups in pnpm-workspace.yaml that contain no package entries (default warn; top-level catalog: placeholders are ignored) |
unresolved-catalog-references | package.json references to catalog: / catalog:<name> whose catalog does not declare the package (default error; pnpm install would fail) |
unused-dependency-overrides | pnpm-workspace.yaml#overrides / package.json#pnpm.overrides entries whose target package is not declared by any workspace package and is not present in pnpm-lock.yaml (default warn; when the lockfile is missing the check degrades to manifest-only and the hint field flags those conservative cases) |
misconfigured-dependency-overrides | pnpm.overrides entries whose key is unparsable or value is missing (default error; pnpm install would fail) |
unresolved-imports | Imports that can't be resolved |
unlisted-dependencies | Imported packages not in package.json |
duplicate-exports | Same symbol exported from multiple files |
circular-dependencies | Modules that import each other, directly or transitively |
re-export-cycle | Barrel files re-exporting from each other in a loop (kind: "multi-node"), or a single barrel re-exporting from itself (kind: "self-loop"). Distinct from circular-dependencies (which describes runtime import loops); re-export cycles are structurally a no-op and almost always bugs. Default warn. Per-file overrides.rules.re-export-cycle is a no-op because the cycle spans multiple files; use the project-wide rules block or // fallow-ignore-file re-export-cycle on any member. Aliases: re-export-cycles, reexport-cycle, reexport-cycles all accepted. |
boundary-violation | Imports that cross user-defined architecture zone boundaries |
policy-violation | Calls, imports, or catalogue-derived effects banned by a rule pack (the rulePacks config key). Default warn so a new pack never hard-fails CI on its first run; a per-rule severity in the pack overrides this master per finding, and the exit-code gate reads the effective per-finding severity. off is a kill switch for the whole evaluator. |
type-only-dependencies | Production dependencies only imported via import type |
test-only-dependencies | Production dependencies only imported by test files |
dev-dependencies-in-production | Packages in devDependencies imported by production code with a runtime import; they should be promoted to dependencies (default warn) |
coverage-gaps | Runtime files and exports not reached by any test dependency path (default off) |
stale-suppressions | fallow-ignore comments or @expected-unused JSDoc tags that no longer match any issue (default warn) |
require-suppression-reason | Opt-in suppression hygiene check: when enabled, every fallow-ignore-* comment and @expected-unused tag must carry a trailing -- <reason>; a suppression without one reports as missing-suppression-reason (default off) |
circular-dependencies and circular-dependency are interchangeable. Both forms are accepted in rules, overrides[].rules, and inline directives, so the singular/plural slug doesn't matter.
Adopting fallow in an existing codebase works best gradually:
warn so you see issues without blocking CI.unresolved-imports and unlisted-dependencies to error. These are almost never false positives.unused-files to error. Unreachable files are safe to remove.error once the count is manageable.unused-types, unused-enum-members, and unused-class-members as you clean up each category. Enable private-type-leaks only for packages where public API or declaration hygiene matters.Use baselines to avoid blocking CI on pre-existing issues while enforcing rules on new code.
Use --fail-on-issues in CI to temporarily treat all warnings as errors without changing the config file. Good for pre-merge gates on critical branches.
fallow dead-code --fail-on-issues
| Code | Meaning |
|---|---|
0 | No error-severity issues |
1 | Error-severity issues found |
2 | Fatal error (invalid config, parse failure) |