Inline suppression
Keep fallow quiet about a false positive, a library export, or an intentional exception, without turning off a rule. Use fallow-ignore comments, JSDoc visibility tags (@public, @internal, @beta, @alpha), and @expected-unused.
Inline suppression removes one finding from the report and keeps the rule on for the rest of the codebase. Use it for a false positive or an intentional exception. You add a comment or a JSDoc tag next to the code.
For broad patterns, use config-level rules. Keep inline suppression for single exceptions. For library exports that other projects use, add JSDoc visibility tags (@public, @internal, @beta, @alpha) instead of suppression comments.
JSDoc visibility tags
Fallow never reports an export with a JSDoc visibility tag as unused. It recognizes @public, @internal, @beta, and @alpha. Use these tags for library APIs that external projects use, and for exports that you keep for a specific audience. These exports have no references in your repo, but they are part of your public API.
/** @public */
export function createClient() {
// Not imported anywhere in this repo, but consumed by users of the library
}
/**
* Configuration options for the client.
* @public
*/
export interface ClientOptions {
baseUrl: string;
timeout?: number;
}
/** @internal */
export function resetState() {
// Used by sibling packages in the monorepo, not public API
}
/** @beta */
export function experimentalFeature() {
// Available to early adopters, not yet stable
}
/** @alpha */
export function unstableApi() {
// Early-stage API, subject to breaking changes
}
/** @api public */
export type UserId = string; // TSDoc @api convention also supported
The tags work with all export types: named, default, classes, interfaces, enums, type aliases, and multi-specifier exports (export { foo, bar }).
On a decorated class, put the JSDoc block before the first decorator, as TypeScript does. The block then applies to the export. This is also true for @deprecated.
/** @public */
@Component({ selector: "app-card" })
export class CardComponent {}
A JSDoc block between a decorator and export does not apply to the export. A block before an earlier, unrelated statement does not apply either.
Fallow recognizes only /** */ JSDoc block comments. Line comments (// @public) and regular block comments (/* @public */) have no effect.
@expected-unused JSDoc tag
To mark an export as intentionally unused, add /** @expected-unused */. Fallow checks this tag for staleness, which it does not do for visibility tags (@public, @internal). If another module imports the export later, fallow reports the tag as a stale suppression.
/** @expected-unused */
export const deprecatedHelper = () => {
// Intentionally kept but not used anywhere
};
Use @expected-unused when you want to:
- Suppress an unused export finding.
- Get a finding when the export is used again (through the
stale-suppressionsrule).
The stale-suppressions rule (default: warn) sets the severity of stale @expected-unused tags. For details, see dead code explained.
Comment syntax
// Suppress all issues on the next line
// fallow-ignore-next-line
export const keepThis = 1;
// Suppress a specific issue type
// fallow-ignore-next-line unused-export
export const keepThisToo = 2;
// Suppress all issues in the entire file
// fallow-ignore-file
// Suppress a specific issue type file-wide
// fallow-ignore-file unused-export
Suppressing complexity findings
To remove one function from the complexity report of fallow health, suppress complexity on it. The comment suppresses the cyclomatic and the cognitive metric together.
// fallow-ignore-next-line complexity
function* parseCsv(text) {
// intentionally complex parser, not worth refactoring
// ...
}
function processRow(row) {
// this function is NOT suppressed, still appears in health output
// ...
}
A file-level suppression removes all functions in the file from the complexity findings. File health scores do not change:
// fallow-ignore-file complexity
Reference
| Comment | Scope |
|---|---|
// fallow-ignore-next-line | All issues on the next line |
// fallow-ignore-next-line <type> | One issue type on the next line |
// fallow-ignore-next-line <type>, <type> | Multiple issue types on the next line (separated by commas or spaces) |
// fallow-ignore-file | All issues in the file |
// fallow-ignore-file <type> | One issue type in the file |
Multi-kind markers and typos
A marker can list more than one token. A token may not match a known issue kind, for example because of a typo or because a newer fallow release renamed the kind. The recognized tokens then still apply, and fallow reports each unknown token as a stale-suppression finding.
When a known kind is within edit distance 2, the finding includes a "Did you mean ...?" hint. An upgrade across a rename therefore never breaks the whole marker without a message.
// Recognized + unknown on the same marker:
// fallow-ignore-next-line unused-export, complexity-typo
export const foo = 1;
// `unused-export` still suppresses `foo`; `complexity-typo` surfaces as a stale
// suppression so you can fix the typo or drop the unknown token.
In JSON output, the additive origin.kind_known field on stale_suppressions[].origin tells an unknown-kind token apart from a known token that is stale. The field is present and false only for an unknown kind. It is absent when fallow recognizes the kind.
Documenting suppressions with a reason
A reason tells the next reader why a suppression exists. Add -- <reason> at the end of any suppression comment or @expected-unused tag:
// fallow-ignore-next-line unused-export -- public compatibility export
export const legacyHelper = () => {};
// fallow-ignore-file -- generated route map
/** @expected-unused -- kept for the v3 migration */
export const oldApi = () => {};
By default, the reason is optional. Fallow records it in the suppression hygiene output. To require a reason on every suppression, enable the opt-in require-suppression-reason rule (default off):
{ "rules": { "require-suppression-reason": "warn" } }
With the rule at warn (or error to fail CI), fallow reports each fallow-ignore-* comment or @expected-unused tag without a -- <reason> as a missing-suppression-reason finding. You can then find the suppressions that have no reason and add one. For the rule reference, see Rules & Severity.
Issue type tokens
Use these tokens in suppression comments. The Scope column shows where each token works: line level (// fallow-ignore-next-line), file level (// fallow-ignore-file), or both.
| Token | Issue | Scope |
|---|---|---|
unused-file | Unused file | file |
unused-export | Unused export | both |
unused-type | Unused type export | both |
private-type-leak | Exported signature that references a private type | both |
unused-enum-member | Unused enum member | both |
unused-class-member | Unused class member | both |
unused-store-member | Unused Pinia store member | both |
unresolved-import | Unresolved import | both |
duplicate-export | Duplicate export. A line-level marker has no effect, and fallow reports it as a stale suppression | file |
code-duplication | Code duplication | both |
circular-dependency | Circular dependency | both |
re-export-cycle | Re-export cycle. Put the marker on any file in the cycle | file |
package-cycle | One import or re-export statement between workspace packages. Each statement counts on its own, also when two statements in one file import the same module. The package cycle goes away when every import on one hop is suppressed | both |
boundary-violation | Boundary violation. Covers all boundary findings: import direction, zone coverage, and forbidden calls. Fallow also accepts the aliases boundary-call-violation, boundary-call-violations, and boundary-calls | both |
policy-violation:<pack>/<rule-id> | One rule-pack policy violation. Bare policy-violation covers every rule-pack rule on the line or in the file | both |
invalid-client-export | Server-only or route-config export from a "use client" file | both |
mixed-client-server-barrel | Barrel that re-exports client and server-only modules | both |
misplaced-directive | "use client" or "use server" directive that is not in the leading position | both |
route-collision | Next.js App Router route files that resolve to the same URL | file |
dynamic-segment-name-conflict | Sibling Next.js dynamic segments with different slug names | file |
unused-server-action | Unused Next.js Server Action | both |
unprovided-inject | inject() or getContext() key that nothing provides | both |
unrendered-component | Component that no code renders | both |
unused-component-prop | Unused component prop | both |
unused-component-emit | Unused Vue component emit | both |
unused-component-input | Unused Angular component input | both |
unused-component-output | Unused Angular component output | both |
unused-svelte-event | Unused Svelte event | both |
unused-load-data-key | Unused SvelteKit load() data key | both |
prop-drilling | Prop drilling chain | both |
thin-wrapper | Thin wrapper component | both |
duplicate-prop-shape | Duplicate prop shape | both |
complexity | Function above the complexity threshold | both |
coverage-gaps | File that no test reaches through the import graph | file |
css-token-drift, css-duplicate-block, css-selector-complexity, css-dead-surface, css-broken-reference | Styling findings | both |
feature-flag | Detected feature flag pattern | both |
security-sink | Security sink candidate | both |
security-client-server-leak | Client-server secret leak candidate | file |
unused-catalog-entry | Unused pnpm catalog entry. Works only as a # fallow-ignore-next-line unused-catalog-entry YAML comment on the line directly above the entry in pnpm-workspace.yaml | line |
Dependency findings (unused-dependencies, unused-dev-dependencies, unused-optional-dependencies, unlisted-dependencies, type-only-dependencies, test-only-dependencies, dev-dependencies-in-production) belong to package.json. A comment in a source file does not suppress them. Use ignoreDependencies for a package, or set the rule severity. For catalog and override findings, use ignoreCatalogReferences and ignoreDependencyOverrides.
Tokens grouped by command
fallow dead-code (dead code, dependency hygiene, and framework findings):
unused-file, unused-export, unused-type, private-type-leak, unused-enum-member, unused-class-member, unused-store-member, unresolved-import, duplicate-export, circular-dependency, re-export-cycle, package-cycle, boundary-violation, policy-violation, policy-violation:<pack>/<rule-id>, the client and server directive tokens, the Next.js route tokens, the component tokens, prop-drilling, thin-wrapper, duplicate-prop-shape, unused-catalog-entry
fallow health (complexity, coverage, and styling with --css):
complexity, coverage-gaps, the css-* tokens
fallow dupes (code duplication):
code-duplication
fallow flags (feature flag detection):
feature-flag
fallow security (security candidates):
security-sink, security-client-server-leak
You can use circular-dependency and circular-dependencies in every place that takes a rule slug: inline directives, config rules, and overrides[].rules. The singular form is the standard form for inline directives. The plural form is the standard form for the rules config. Both forms work in both places.
When to use suppression
For broad patterns, use the config (rules, ignoreExports). Use inline suppression for:
- Individual false positives
- Temporary suppression during migration
Do not suppress an issue that repeats across the codebase. If a pattern affects many files, configure it at the project level.
Config-level vs inline suppression
// src/components/Button.tsx
// fallow-ignore-next-line unused-export
export const Button = () => { /* ... */ };
// src/components/Input.tsx
// fallow-ignore-next-line unused-export
export const Input = () => { /* ... */ };
// src/components/Modal.tsx
// fallow-ignore-next-line unused-export
export const Modal = () => { /* ... */ };
// Repeated across dozens of component files...