Configuration
Fallow runs with no config. Add .fallowrc.json, .fallowrc.jsonc, or fallow.toml when you need to change entry points, ignore patterns, rules, or plugin settings for your project.
Fallow works with no configuration. Add a config file when the defaults do not fit your project, for example to add entry points, ignore generated files, or change rule severity. To create one, run:
fallow init # Creates .fallowrc.json
fallow init --toml # Creates fallow.toml
Config file formats
Fallow looks for config files in this order:
.fallowrc.json(JSONC, comments allowed).fallowrc.jsonc(same JSONC parser; editors detect the JSON-with-comments syntax from the extension)fallow.toml.fallow.toml
Fallow checks each directory up to the workspace root, and the first match wins. .fallowrc.json and .fallowrc.jsonc behave the same. The .jsonc extension only tells editors to expect comments.
If one directory has more than one of these files, fallow loads the file with the higher precedence. It prints a warning on stderr that names the ignored file. An old config from a migration therefore cannot take effect without a message.
Fallow parses both JSON forms as JSONC, and the published schema declares this (allowComments, allowTrailingCommas). Editors that use the JSON language service, such as VS Code and Zed, therefore accept comments and trailing commas in a config that references schema.json.
Full example
{
"$schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json",
"entry": ["src/workers/*.ts", "scripts/*.ts"],
"ignorePatterns": ["**/*.generated.ts", "**/*.d.ts"],
"ignoreDependencies": ["autoprefixer", "@types/node"],
"ignoreExports": [
{ "file": "src/public-api.ts", "exports": ["*"] }
],
"ignoreExportsUsedInFile": true,
"publicPackages": ["@myorg/sdk"],
"dynamicallyLoaded": ["src/plugins/**/*.ts"],
"rules": {
"unused-files": "error",
"unused-exports": "warn",
"unused-types": "off",
// Opt-in API hygiene check; default is "off"
"private-type-leaks": "warn",
"unresolved-imports": "error"
},
"duplicates": {
"mode": "mild",
"minTokens": 50,
"minLines": 5,
"threshold": 10
},
"similarCode": {
"threshold": 0.8,
"minLines": 3,
"ignore": ["src/generated/**"]
},
"health": {
"maxCyclomatic": 20,
"maxCognitive": 15,
"ignore": ["**/*.generated.ts"],
"thresholdOverrides": [
{
"files": ["src/legacy/**"],
"functions": ["parseLegacy"],
"maxCyclomatic": 40,
"maxCognitive": 35,
"reason": "Queued legacy parser cleanup"
}
]
},
"audit": {
"gate": "new-only"
},
"security": {
"requestReceivers": ["h", "httpReq"]
},
"boundaries": {
"preset": "bulletproof"
},
"production": false
}Config fields
entry
Extra entry point glob patterns. Fallow detects entry points from package.json (main, module, bin, exports) and from framework plugins. Add the files that fallow does not detect.
{
"entry": ["src/workers/*.ts", "scripts/*.ts", "src/cli.ts"]
}Entry point auto-detection
Without an entry config, fallow still finds entry points in your package.json fields. These files are the roots of the module graph. Fallow treats all code that they reach as "used."
Fallow reads these package.json fields:
| Field | Description |
|---|---|
main | CJS entry point |
module | ESM entry point |
types / typings | TypeScript declaration entry point |
source | Source entry point before the build (a common convention for dev tooling) |
browser | Browser entry point (a string, or an object with per-path overrides) |
bin | CLI binaries (a string for one binary, an object for multiple) |
exports | Modern entry point map (resolved recursively, including conditional exports and subpath patterns) |
Fallow resolves the exports field recursively. It follows nested conditions such as "import", "require", "types", and "default" to their target files. It also includes subpath exports (for example "./utils").
When these fields point at output directories (dist/, build/, out/, esm/, cjs/), fallow maps the paths back to the same files in src/ and tries the source extensions. Fallow ignores output directories by default.
Framework plugins add their own entry points to the package.json entry points. For example, Next.js adds pages/** and app/**/page.tsx, and Vitest adds **/*.test.ts. Run fallow list to see all detected entry points.
ignorePatterns
Glob patterns for files that fallow does not analyze at all.
{
"ignorePatterns": ["**/*.generated.ts", "**/*.d.ts", "legacy/**"]
}The built-in ignores apply at any depth: **/node_modules/**, **/dist/**, **/build/**, **/coverage/**, and the minified-bundle globs. Since 3.26.0, the build default is recursive like the others. Fallow therefore also skips a nested build/ directory with hand-written source.
A !-prefixed entry is an exception. It brings back files that a built-in default, one of your own patterns, or the hidden-directory skip removes:
{
"ignorePatterns": [
"legacy/**",
// lift the built-in **/coverage/** for this subtree only
"!src/policy/coverage/**",
// add the hidden directory .config to discovery
"!.config/**"
]
}- Fallow applies the built-in defaults first, then your own patterns, then the
!exceptions. - An exception lifts only the paths that it matches. With
!src/policy/coverage/**, a top-levelcoverage/output directory stays excluded. - In a hidden directory that only an exception opens, fallow keeps only the files that an exception matches.
- Paths under
node_modulesor.gitcannot be lifted. A!entry that names one of these segments is a config error (exit 2).
To see which built-in pattern removed files, and how many, run with --explain-skipped.
ignoreFindings
Glob patterns for files where fallow does not report dead-code findings. Fallow still discovers and parses matching files and keeps them in the module graph, so everything they import stays reachable. To remove files from the analysis fully, use ignorePatterns.
{
"ignoreFindings": [
"vitest.config.ts",
"src/generated/**",
// a leading ! keeps matching paths reportable
"!src/generated/keep.ts"
]
}Patterns are relative to the project root and use / on every platform, including Windows. Matching is case sensitive. * and ? match across /, so src/* covers the whole src subtree. Fallow rejects absolute paths, .. segments, and a bare "!" when it loads the config.
Order does not matter. A negation wins over any positive match, in any position in the array. An array with only negations hides every path except the paths that a negation matches.
Fallow hides findings before it applies rules, severities, and --fail-on-issues. A hidden finding therefore cannot fail a run and does not show in any output format.
Some findings belong to several files: circular dependencies, re-export cycles, package cycles (which belong to the files of their example imports), duplicate exports, prop drilling chains, and unlisted dependencies (which belong to their import sites). Fallow hides such a finding only when every owning file matches.
These findings stay visible even when their path matches:
- Dependency findings that belong to a manifest (unused, dev, optional, type-only, test-only, catalog, override).
- Architecture boundary violations and policy violations.
- Stale suppressions.
- Framework correctness findings, such as client and server directives and route collisions.
- Security candidates.
A pattern that matched no finding has no effect. The usual cause is a typo. Every output of fallow dead-code names such a pattern, see Unmatched config patterns.
ignoreDependencies
Package names that fallow always treats as used and as available.
Fallow does not report listed dependencies as unused or as unlisted.
Use it for packages that the runtime provides, such as bun:sqlite, and for other dependencies that are available without a declaration.
{
"ignoreDependencies": ["autoprefixer", "@types/node", "bun:sqlite"]
}An entry can also be a glob. An entry with *, ?, [ or { is a glob with the same syntax as ignorePatterns, and fallow matches it against the package name. An entry without these characters matches one package name exactly.
{
// Every package in the @acme scope, and every @types package.
"ignoreDependencies": ["@acme/*", "@types/*", "eslint-plugin-*"]
}@acme/*matches@acme/liband@acme/ui, but not@other/lib.- A glob applies to the same checks as an exact name: unused, unlisted, type-only, test-only and dev-in-production dependencies.
- An invalid glob, for example
@acme/[, stops the config load with anignoreDependencieserror.
A glob that matches no dependency that a package.json of the project declares has no effect. The usual cause is a typo in the scope. Every output of fallow dead-code names such a glob, see Unmatched config patterns.
ignoreCommandEntries
Command names whose file arguments do not become entry points.
Fallow reads the commands in package.json scripts, CI files, Dockerfiles, Procfiles, and fly.toml. A file that a command names, such as node scripts/seed.ts, normally becomes an entry point. Formatters and linters (ESLint, Prettier, Oxlint, Oxfmt, Biome, Stylelint, textlint, and similar tools) never make their targets entry points, so you do not have to list them. Use this field for another command that reads files as data:
{
"ignoreCommandEntries": ["my-codegen"]
}- A listed command is still a used dependency, and its
--configfile is still tracked. - A name matches the command after env, package-manager, and wrapper prefixes (
cross-env,dotenv -e .env --,npx,pnpm exec,pnpm --filter web exec,yarn run,varlock run --). The match is on the exact file name. - A script call such as
npm run gen -- src/input.tsornpm run gen src/input.tsmatches the command in the body of thegenscript. "*"matches every command. Use["*"]to turn off entry points from all commands, and declare the real entry points inentry. This also drops the modules that a linter loads through a flag, such aseslint -f ./tools/fmt.js. Add those modules toentrytoo.- The field does not change commands that a plugin reads from a tool config, such as Playwright
webServer.command.
ignoreCatalogReferences
Entries that suppress unresolved-catalog-reference findings. This finding is a catalog: dependency that points at a catalog that does not declare the package. The findings are in package.json, and JSON has no comments. // fallow-ignore-next-line cannot work there, so a config entry is the only way to suppress them.
{
"ignoreCatalogReferences": [
// Placeholder package; the `upcoming` catalog lands in a later change.
{
"package": "future-dep",
"catalog": "upcoming",
"consumer": "packages/placeholder/package.json"
},
// Staged migration: any catalog, every workspace package.
{ "package": "old-react", "consumer": "packages/**/package.json" }
]
}package is required. Fallow compares it to the dependency key as an exact, case-sensitive string, so "@scope/*" matches nothing. catalog and consumer are optional. When you leave one out, it matches any value.
Fallow compares catalog to the normalized catalog name. "react": "catalog:" and "react": "catalog:default" are both default, and "catalog:react17" is react17.
consumer is a glob for the path of the package.json that uses the catalog. The path is relative to the project root and uses / on every platform: package.json for the root manifest, packages/app/package.json for a workspace member. The glob must match the whole path, and * and ? match across /.
All fields of one entry must match. Any matching entry in the array suppresses the finding.
Each unresolved-catalog-reference finding in fallow dead-code --format json output has an add-to-config action with package, catalog, and consumer already filled in.
This field changes only unresolved-catalog-reference. unused-catalog-entries and empty-catalog-groups do not change. To turn off the rule for a path, use overrides with "unresolved-catalog-references": "off". To turn it off everywhere, set rules.unresolved-catalog-references to "off".
ignoreFindings does not apply here, because findings that belong to a manifest stay visible when their path matches. A consumer value with invalid glob syntax, an absolute path, or a .. segment makes the config load fail.
ignoreDependencyOverrides
Package-manager override entries that fallow does not report, for example a CVE pin that must stay. The field suppresses unused-dependency-override and misconfigured-dependency-override for matching entries in these places:
overrides(pnpm-workspace.yaml)pnpm.overrides- npm
overrides - bun
resolutions
These findings are in manifests, not in source files. // fallow-ignore-next-line cannot work there, so a config entry is the only way to suppress them.
{
"ignoreDependencyOverrides": [
// overrides: { "lodash": "^4.17.21" } in pnpm-workspace.yaml
// No source filter, so the entry matches in either file
{ "package": "lodash" },
// pnpm.overrides: { "@scope/legacy-pkg": "^1.0.0" } in package.json
{ "package": "@scope/legacy-pkg", "source": "package.json" },
// resolutions: { "**/trim-newlines": "^3.0.1" } in a bun package.json
{ "package": "trim-newlines", "source": "package.json" }
]
}Fallow compares package to the target package of the override as an exact, case-sensitive string. It is not a glob and not the raw key. Write react-dom for "react>react-dom" and @types/react for "@types/react@<18".
Fallow removes the ** path segments from bun resolutions keys, so "**/trim-newlines" matches as trim-newlines. Fallow does not remove a **/ prefix from an overrides key. When fallow cannot parse a key, it compares the key as written, so "react>" needs { "package": "react>" }.
source is the label of the file that declares the override: "pnpm-workspace.yaml" or "package.json". It is not a path. Leave it out to match both. Any other value loads without an error and never matches. pnpm.overrides, npm overrides, and bun resolutions all report "package.json", so source cannot tell them apart.
Fallow reads the resolutions of a bun project only when the root manifest declares no overrides key. When overrides exists, fallow ignores resolutions and never reports them, so an entry for them matches nothing.
Both fields must match. Any matching entry in the array suppresses the finding.
Both findings have an add-to-config action in JSON output, with package and source already filled in. An entry that matches nothing gives no warning. To turn off either finding fully, set rules.unused-dependency-overrides or rules.misconfigured-dependency-overrides to "off". ignoreFindings does not apply here, because findings that belong to a manifest stay visible when their path matches.
ignoreUnresolvedImports
Glob patterns that fallow matches against the import specifier as written, not against a file path. Fallow never reports a matching specifier as unresolved-import. Use it for imports that fallow cannot resolve on disk:
- Code that a later build step generates.
- A package with an
exportsmap that points at build artifacts that are not in the checkout. - A bundler alias that fallow does not model.
{
"ignoreUnresolvedImports": [
"@example/icons", // the bare specifier
"@example/icons/**", // its subpaths, not the bare name
"../generated/**" // parent-relative codegen output
]
}Matching is case-sensitive, and a pattern must match the whole specifier. * and ? match across /, but a trailing /** requires the separator. To match the package and its subpaths, list the bare package name and the subpath pattern. Fallow removes a leading ./ from each pattern and tries each specifier with and without its own ./. "./generated/x.js" and "generated/x.js" therefore cover the same imports.
A .. segment is allowed here, because these patterns match specifiers, not paths. The file-path glob fields do not allow it. There is no negation syntax and no ordering. Fallow suppresses a specifier if any pattern matches. A pattern with invalid glob syntax makes the config load fail, with a message that names the field and the pattern.
The field covers static imports, dynamic imports, and re-export sources, for value imports and type-only imports. Resolution and the module graph do not change, and neither does the dependency usage count. unlisted-dependency therefore does not change. For that finding, use ignoreDependencies, which matches package names as exact strings.
Each unresolved-import finding in JSON output has an add-to-config action for this field with the reported specifier. When you move an import here from an inline // fallow-ignore-next-line unresolved-import comment, delete the comment. The config check runs first, so fallow then reports the comment as stale-suppression.
ignoreExports
Entries that tell fallow to ignore specific exports. Use them for public API packages. Use them also for UI libraries with namespace barrels (shadcn, Radix, bits-ui), where many components/ui/<name>/index.ts files export the same short names on purpose.
ignoreExports suppresses unused-export findings and duplicate-export grouping for matching files. Use exports: ["*"] to exclude every export from the matched files. Use a list of names to exclude only those names.
{
"ignoreExports": [
{ "file": "src/index.ts", "exports": ["*"] },
{ "file": "src/utils/*.ts", "exports": ["default"] },
// shadcn / Radix / bits-ui barrels
{ "file": "src/lib/components/ui/**", "exports": ["*"] }
]
}A per-file overrides.rules.duplicate-exports = "off" has no effect, because the rule spans multiple files. A duplicate-export finding groups N locations in N files. When fallow detects this override, it prints a warning at load time and points you to ignoreExports.
ignoreExportsUsedInFile
Suppress unused-export findings when the file that declares the export also uses it. Fallow still reports exports with no references at all. This is the same as the ignoreExportsUsedInFile option of knip.
{
"ignoreExportsUsedInFile": true
}For knip compatibility, fallow also accepts an object form:
{
"ignoreExportsUsedInFile": { "type": true, "interface": true }
}Fallow reports type aliases and interfaces under the same unused-types issue. { "type": true }, { "interface": true }, and { "type": true, "interface": true } therefore behave the same: fallow suppresses any type export that the same file uses. The two fields exist only for knip-config compatibility.
A reference in the export specifier itself (export { foo }, export { foo as bar }, export default foo) does not count as a same-file use. Fallow still reports such an export when no other file and no other expression in the file references the binding.
includeEntryExports
Report unused exports in entry files (package.json main and exports, framework pages, and so on). By default, fallow marks these exports as used. This option finds typos in framework-convention exports, such as meatdata instead of metadata.
{
"includeEntryExports": true
}The global CLI flag --include-entry-exports does the same. When both are set, the CLI flag takes precedence.
typeAware
Use the TypeScript type checker for more exact results. With this option, fallow gets checker evidence for exact symbol use, class contracts, cross-file private type leaks, affected tests, and public-signature coupling. The option is off by default, so the normal analysis stays fast and does not need the TypeScript companion.
{
"typeAware": {
"enabled": true,
"projects": ["tsconfig.json"],
"require": "best-effort"
}
}projects is optional and takes tsconfig paths relative to the project root.
With an empty list, fallow discovers the projects automatically. Start with
best-effort. It keeps conservative findings when the checker evidence is
partial or not available. Choose complete only when an incomplete checker pass
must fail the run.
This option does not replace tsc or Oxlint. For installation, performance,
safety, and library-consumer boundaries, see
Type-aware TypeScript analysis.
security
Configure the catalogue of fallow security. security.categories selects the candidate categories that run. security.requestReceivers adds the names of your own HTTP request objects to the built-in receiver allowlist (req, request, ctx, context, event).
requestReceivers only adds names. It does not replace the built-in names. Fallow trims the values and matches them case-insensitively for *.query, *.params, and *.body source reads. The field has no effect on *.searchParams.
{
"security": {
"requestReceivers": ["h", "httpReq"],
"categories": {
"include": ["command-injection"],
"exclude": ["dynamic-regex"]
}
}
}autoImports
Resolve framework auto-imports (Nuxt components) as real edges in the module graph. Frameworks such as Nuxt make components available to templates by file name, with no import statement: <Card001 /> resolves to components/Card001.vue.
{
"autoImports": true
}Fallow always creates the edges for these tag references, so includeEntryExports counts a default export that a <Card /> tag uses. The rules below do not change this. A template tag or a composable call marks its file as used, whatever your nuxt.config says.
autoImports: true also removes the convention entry patterns. A convention file is then no longer an entry point, and fallow reports it as unused-file when nothing references it.
Fallow reads two settings from nuxt.config.ts or nuxt.config.js and handles each one separately:
components:controls the component patterns.imports:controls the composable, util,shared/utils, andshared/typespatterns.
Fallow removes the patterns of a setting when the config does not have the key, or when the value scans no more than the Nuxt defaults:
components: false,components: null,components: [],components: true, orcomponents: { dirs: [] }.components: truegives the same directories as a missing key. Other keys next to an emptydirsdo not change the result.imports: {},imports: { dirs: [] }, orimports: { scan: false }with noimports.dirsor an empty array. The first two are the Nuxt defaults:imports.dirsstarts empty, and Nuxt registers the convention directories itself.
Fallow reads every other shape as a custom layout. That setting keeps its entry patterns:
components: {}, the array form (components: [{ path: '~/ui' }]), and an object with no emptydirs. A non-emptydirs, or only aprefixorpathPrefix, keeps the patterns. Any other value is also custom.imports: { scan: true }, a non-emptyimports.dirs, andimports: { scan: false }next to a non-emptyimports.dirs, which Nuxt scans either way.imports: { autoImport: false }alone. It turns off the injection, not the scan, and the documented replacement (import { useThing } from '#imports') resolves to no file.- A config with a top-level
extends, a spread or a repeated key in either object, or a config object that fallow cannot resolve statically. - A top-level property that fallow cannot read statically: a spread such as
{ ...base, devtools: {} }, a computed key such as['imports'], an accessor, or a shorthand method. That property can configure either setting, so both settings keep their patterns for that root.
Only a top-level property counts when the config object is readable. A nested key, such as a routeRules path that ends in components or imports, does not keep a fallback on its own.
Two nested locations still count for a setting, because Nuxt reads them the same way as the top level:
$production,$development,$test, and each$env.<name>override, when it carries a nestedcomponentsorimportskey.- A
hooksentry forcomponentsorimports, in the flat form ('components:dirs': fn) and in the nested form (components: { dirs() {} }), because Nuxt joins nested hook names with:.
An override object or a hooks object that fallow cannot read statically falls back to a text match for the components and imports keys, inside that object. An unrelated hook or override does not change the static reading.
For each setting that fallow reads as custom, it adds a plugin-effect-not-modeled entry to workspace_diagnostics[], one entry per config file and setting. The entry has plugin, key, and reason, with the config file in path. The entry does not make the analysis less complete, and fallow prints no warning for it. For the diagnostic kinds, see Debugging.
Fallow reads the config of each root separately: the project root and every workspace root, plus each local layer of that root. The config of the root, and of its local layers, that contains a pattern decides that pattern. One custom nuxt.config in a monorepo does not keep the patterns of another app.
Component names follow Nuxt. Nuxt names a component directly under components/global/ or components/islands/ after its own directory. components/global/Foo.vue is then <Foo>, and components/base/global/Card.vue stays <BaseGlobalCard>. Fallow removes the .client, .server, .global, and .island suffixes.
A name that you import or re-export by hand from #components or #imports marks its convention file as used, like a template tag. A member access on a namespace import, such as C.Card on import * as C from '#components', marks components/Card.vue as used. A file with export * from '#components' or export * from '#imports' passes on the names that its importers take from it. When fallow cannot read which names a file takes, for example a spread of the namespace, every name of that module counts as used. A plugin-effect-not-modeled diagnostic then names the file, with key set to the module. #layers/<name>/ resolves to a local layer by its $meta.name.
An auto-import name marks as used only the files of the app that renders or calls it, and of the layers that this app extends. In a monorepo, a <Card /> in one app does not keep the unused Card.vue of another app. See Nuxt auto-import scope.
Some component files stay entry points with autoImports on, because fallow cannot see how they render:
components/global/files and*.global.vuefiles. A string reference such asresolveComponent('Name')can render a global component.components/content/files of the project and of each local layer, when@nuxt/contentis inmodules(as a string or as a['@nuxt/content', { ... }]entry), or when a package layer such asdocusregisters it. Markdown content renders these components, and fallow does not read Markdown.
An OG image template that a defineOgImage('Name') or defineOgImageComponent('Name') call names with a static string counts as used. See Nuxt auto-import scope.
The default is false. Fallow tracks Pinia store auto-imports separately.
ignoreDecorators
Decorator names that do not mean reflective use. By default, fallow skips every class member with a decorator. Fallow checks a member that has only listed decorators like a member with no decorators. A member with any decorator that is not in the list stays skipped, so framework defaults (NestJS, Angular, TypeORM) do not change.
Use this for utility decorators that do not mean runtime reflection. Examples are @step("label") from Playwright, internal labeling decorators such as @measure, @log, and @retry, and custom decorators that wrap a method for logging or tracing.
{
"ignoreDecorators": ["@step"]
}The behavior is conservative. A method with both @step and @Inject stays skipped, because @Inject is not in the list. Fallow applies the standard usage check only to methods where every decorator is in the list.
Matching
- Bare entries (
"step"or"decorators") match the leftmost segment of the decorator path. One bare"decorators"entry covers every@decorators.*decorator. Use this when an internal namespace has many utility decorators that are all non-reflective. - Dotted entries (
"decorators.log") match the full dotted path exactly. A sibling such as@decorators.auditstays skipped, unless you also list"decorators.audit"or use the bare"decorators"form. "@step"and"step"are the same. Fallow removes a leading@before matching.
Unmatched-entry warning
When an entry matches no decorator in the analyzed codebase, fallow prints one warning at the end of the run. usedClassMembers does the same for unmatched patterns. Remove unmatched entries, because they have no effect.
The default is an empty list, which keeps the behavior that skips all decorated members. Existing NestJS, Angular, and TypeORM projects see no change. The first run after you enable this option can report new unused-class-members findings on members that fallow skipped before.
unusedComponentProps
Options for the unused-component-props rule. Some props stay in the public API but are not used inside the component. To stop fallow from reporting them, set ignorePattern to a regex. Fallow skips each prop where the local destructure binding name matches.
This supports the convention that a leading underscore marks an accepted but intentionally unused value. TypeScript noUnusedParameters and the varsIgnorePattern and argsIgnorePattern options of ESLint @typescript-eslint/no-unused-vars use the same convention:
{
"rules": { "unused-component-props": "warn" },
"unusedComponentProps": {
"ignorePattern": "^_"
}
}<script lang="ts">
interface Props { stage: string; variant?: string }
// `_stage` is exempted by `^_`; `variant` is still reported if unused.
let { stage: _stage, variant }: Props = $props();
</script>This applies to Vue, Svelte, Astro, React, and Preact props. The default behavior does not change: with no ignorePattern, fallow reports every unused prop.
Matching
- Fallow matches the regex against the local destructure binding name (
_stageinlet { stage: _stage } = $props()). It does not match the public prop name that the finding reports (stage). When a prop has no alias, the local name is the public name. - Matching is unanchored (like
RegExp.testin ESLint)._with no anchor matches any prop that contains an underscore (on_click,aria_label). To match a leading underscore, use^_. - An invalid regex makes the config load fail with a clear error.
When the pattern skips at least one prop, fallow prints a one-line note with the count. If the note does not show, your pattern matches nothing, which is often a typo.
The Angular rules unused-component-input and unused-component-output do not change, because their declarations are class fields, not destructured locals.
usedClassMembers
Names of class methods and properties that fallow never reports as unused. The names add to the built-in Angular and React lifecycle allowlist, for methods that third-party libraries call.
Use this when a library calls methods on your class at runtime through an interface or contract. Common examples are ag-Grid (agInit, refresh), TypeORM migrations (up, down), and Web Components (connectedCallback, disconnectedCallback, attributeChangedCallback).
Each entry is one of these:
- A plain member name, which applies to all classes.
- A scoped object, which matches only classes with the configured
extendsorimplementsidentifier in their heritage clause.
Strings can be exact names or glob patterns:
{
"usedClassMembers": [
"agInit",
{ "implements": "ICellRendererAngularComp", "members": ["refresh"] },
{ "extends": "BaseCommand", "members": ["execute"] },
{ "implements": "CanActivate", "members": ["canActivate"] },
{ "extends": "GrammarBaseListener", "members": ["enter*", "exit*"] }
]
}A name such as agInit is unique enough to suppress for all classes. A common name such as refresh or execute would hide real findings in unrelated classes, so scope it with implements or extends. A scoped rule needs at least one of extends or implements. Fallow rejects an object rule with no heritage field ({ "members": [...] }) at load time.
Heritage matching is syntactic. Fallow compares the identifier in the extends Foo or implements IBar clause of the source to the string in the rule. For a renamed import (import { IBar as IBaz }), use the identifier that the class declaration shows.
Glob patterns
Fallow treats a member string that contains * or ? as a glob pattern. Exact strings keep their meaning.
"*"matches every member of a matching class."enter*"matches any member with a name that starts withenter."*Handler"matches any member with a name that ends withHandler."on*Event"combines a prefix and a suffix.
Use glob patterns for parser-generator listeners (ANTLR), generated bridges, and abstract framework base classes that call members by a name prefix.
When a glob pattern matches zero members in the codebase, fallow prints a WARN at the end of the run, so you can remove the entry. Exact-string entries give no warning, because they are common boilerplate that can match in some configurations and not in others.
The allowlist applies only to class methods and properties. Fallow still checks enum members with the same names.
For an allowlist that applies only when a library is installed, use a plugin file with the usedClassMembers field. For the plugin format, see Custom plugins.
publicPackages
Workspace packages with a public API. Fallow never reports exports, exported enum members, or exported class members from these packages as unused, also when no code in the repo imports them. Use it for library packages that other projects use.
{
"publicPackages": ["@myorg/sdk", "@myorg/types"]
}dynamicallyLoaded
Glob patterns for files that your code loads at runtime, for example with import(), lazy routes, or plugin systems. Fallow treats these files as entry points and does not report them as unused.
{
"dynamicallyLoaded": ["src/plugins/**/*.ts", "src/routes/**/page.tsx"]
}duplicates
Configuration for fallow dupes:
| Field | Type | Default | Description |
|---|---|---|---|
mode | "strict" | "mild" | "weak" | "semantic" | "mild" | Detection mode. strict and mild match exact tokens. weak ignores string literal values. semantic ignores identifiers and all literal values for structural (Type-2) detection. |
near | boolean | false | Also report near-miss clones: functions that differ by small structural edits. |
minTokens | number | 50 | Minimum token count for a code block to count as a clone |
minLines | number | 5 | Minimum line count for a clone group |
minOccurrences | number | 2 | Minimum number of occurrences before fallow reports a clone group. Must be at least 2. |
threshold | number | 0 | Maximum duplication percentage. Above it, the run fails (exit code 1). 0 turns off the limit. |
enabled | boolean | true | Turn duplication detection on or off |
ignore | string[] | [] | Glob patterns to exclude from duplication analysis. Fallow adds the built-in defaults (see ignoreDefaults) unless ignoreDefaults is false. |
ignoredClones | string[] | [] | Clone groups that you reviewed and want to hide. Each key uses <fingerprint>:<instance_count>, for example dup:6f12ab34:2. When the tokens or the occurrence count change, fallow reports the group again. |
ignoreDefaults | boolean | true | Add the built-in ignores for generated output (**/.next/**, **/.nuxt/**, **/.svelte-kit/**, **/.turbo/**, **/.parcel-cache/**, **/.vite/**, **/.cache/**, **/out/**, **/storybook-static/**) to ignore. Set to false to use only your ignore list. |
skipLocal | boolean | false | Report only duplicates across directories. Skip clones inside one file |
crossLanguage | boolean | false | Find clones between .ts and .js files. Fallow removes TypeScript type annotations before it compares |
ignoreImports | boolean | true | Exclude module wiring (imports and re-exports) from clone detection. By default, fallow excludes ES imports, re-export declarations, and top-level static require() binding declarations. Set to false to count them again. Fallow still counts runtime code, local exports, side-effect or nested require() calls, dynamic require arguments, and mixed declarations. |
ignoreSymlinks | boolean | false | Omit clone instances whose path is a symlink, or lies under a symlinked directory, inside the project root. Set to true to report only duplication between real files. Fallow does not report a clone group with fewer than two remaining instances. The file and line totals and the threshold percentage then count only real files. With false, JSON output marks these instances with is_symlink: true. See Symlinked files. |
normalization | object | none | Fine-grained normalization overrides: ignoreIdentifiers, ignoreStringValues, ignoreNumericValues |
minCorpusSizeForShingleFilter | number | 1024 | Performance setting. Above this number of tokenized files, focused duplication runs (--changed-since or audit) prefilter unchanged files with k-token shingles. Below it, fallow skips the prefilter, because the shingle index costs more time than it saves. |
minCorpusSizeForTokenCache | number | 5000 | Performance setting. Above this number of source files, fallow uses the persistent duplication token cache under <root>/.fallow/cache/dupes-tokens-vN/. Below it, loading and saving the cache costs more time than it saves, so the cache stays off, also without --no-cache. |
{
"duplicates": {
"mode": "mild",
"near": true,
"minTokens": 50,
"minLines": 5,
"minOccurrences": 2,
"threshold": 5,
"ignore": ["**/*.generated.ts"],
"ignoredClones": ["dup:6f12ab34:2"],
"skipLocal": true,
"ignoreImports": true
}
}similarCode
Project settings for the separate fallow similar-code workflow:
| Field | Type | Default | Description |
|---|---|---|---|
threshold | number from 0 to 1 | 0.8 | Model-specific cutoff for unverified candidates. It is not a probability and not a gate. |
minLines | positive number | 3 | Exclude functions with fewer source lines than this number. |
ignore | string[] | [] | Extra globs, relative to the root, that fallow excludes from similar-code extraction only. |
{
"similarCode": {
"threshold": 0.8,
"minLines": 3,
"ignore": ["src/generated/**"]
}
}The config cannot select the provider, model, executable, download, or credential. The model setup always needs a separate, explicit action from you. For the discovery, inspect, and verdict flow, see Semantic similar code.
health
Configuration for fallow health:
| Field | Type | Default | Description |
|---|---|---|---|
maxCyclomatic | number | 20 | Maximum cyclomatic complexity. Fallow reports functions above it |
maxCognitive | number | 15 | Maximum cognitive complexity. Fallow reports functions above it |
maxCrap | number | 30 | Maximum CRAP score. Fallow reports functions above it |
maxUnitSize | number | 60 | Maximum function length in lines of code. Fallow reports longer functions as a "large function". Raise it for generated or test files, where a describe() block can span hundreds of lines. The complexity checks stay on. |
crapRefactorBand | number | 5 | Band below maxCyclomatic. In this band, a CRAP-only finding also gets a secondary refactor-function action when the cognitive complexity is at least half of maxCognitive |
ignore | string[] | [] | Glob patterns for files to exclude from complexity analysis |
coverage | string | none | Path to Istanbul coverage data (coverage-final.json) or raw V8 coverage (a NODE_V8_COVERAGE directory, or one V8 coverage JSON file) for exact per-function CRAP scores. A relative path resolves against the project root. The --coverage flag and FALLOW_COVERAGE take precedence |
coverageRoot | string | none | Absolute prefix that fallow removes from the file paths in the coverage data. Use it when CI or Docker generated the coverage in a different checkout root. The --coverage-root flag and FALLOW_COVERAGE_ROOT take precedence |
ownership | object | see description | Settings for --ownership output: botPatterns (globs for bot and service-account authors, which replace the defaults) and emailMode (raw, handle, or anonymized; default handle). See fallow health |
thresholdOverrides | array | [] | Per-file threshold exceptions. Each entry has files globs, optional exact functions, one or more of maxCyclomatic, maxCognitive, maxCrap, maxUnitSize, and an optional reason. Leave functions empty to relax every function in the matching files, including a describe() wrapper and its it() blocks. JSON health output shows the override state in threshold_overrides. It has one row per threshold dimension (complexity for the cyclomatic, cognitive, and unit-size limits; crap for maxCrap), with the status active, stale, insufficient, or no_match. Rows from one entry share override_index, so group on it to count configured entries. |
suggestInlineSuppression | boolean | true | Add suppress-line action hints to health findings in JSON output. Fallow always leaves them out when --baseline or --save-baseline is active. Set to false to turn off inline suppression hints for the whole project, for example when you do not want CI to suggest inline suppressions. |
{
"health": {
"maxCyclomatic": 20,
"maxCognitive": 15,
"maxCrap": 30,
"crapRefactorBand": 5,
"ignore": ["**/*.generated.ts", "src/legacy/**"],
"thresholdOverrides": [
{
"files": ["src/legacy/**"],
"functions": ["parseLegacy"],
"maxCyclomatic": 40,
"maxCognitive": 35,
"reason": "Queued legacy parser cleanup"
},
{
"files": ["**/*.test.*", "**/*.spec.*"],
"maxUnitSize": 500,
"reason": "Test suites: describe/it blocks are long by design"
}
],
"suggestInlineSuppression": true
}
}audit
Configuration for fallow audit, the command that reviews changed files:
| Field | Type | Default | Description |
|---|---|---|---|
gate | "new-only" | "all" | "new-only" | Which findings affect the audit verdict. new-only fails only on issues that the current changeset introduces, and shows inherited findings as context. all gates every finding in changed files and skips the extra base-snapshot attribution pass. |
css | boolean | true | Include styling analytics in fallow audit. Set to false to match --no-css. |
cssDeep | boolean | true | Include project-wide styling reachability checks in fallow audit. Set to false to match --no-css-deep. Set to true to force the same behavior as --css-deep. |
cacheMaxAgeDays | number | 30 | Maximum age of a base-snapshot worktree cache that fallow audit keeps and reuses, in days since the last reuse or since creation. The next fallow audit removes older entries when it starts. 0 turns off the cleanup. The FALLOW_AUDIT_CACHE_MAX_AGE_DAYS env var overrides it per run and takes precedence when both are set. |
deadCodeBaseline | string | none | Dead-code baseline file (from fallow dead-code --save-baseline). Issues in the baseline do not count toward the verdict. The --dead-code-baseline flag takes precedence |
healthBaseline | string | none | Health baseline file (from fallow health --save-baseline). The --health-baseline flag takes precedence |
dupesBaseline | string | none | Duplication baseline file (from fallow dupes --save-baseline). The --dupes-baseline flag takes precedence |
typeAware | boolean | inherits typeAware.enabled | Turn type-aware analysis on or off for fallow audit only. The --type-aware and --no-type-aware flags and FALLOW_TYPE_AWARE take precedence |
{
"audit": {
"gate": "new-only",
"css": true,
"cssDeep": true,
"cacheMaxAgeDays": 14
}
}[audit]
gate = "all"
css = true
cssDeep = true
cacheMaxAgeDays = 14fix
Configure the behavior of fallow fix.
| Field | Type | Default | Description |
|---|---|---|---|
catalog.deletePrecedingComments | "auto" | "always" | "never" | "auto" | What happens to a YAML comment block directly above a removed pnpm catalog entry. |
{
"fix": {
"catalog": {
"deletePrecedingComments": "auto"
}
}
}auto removes a leading comment block when it clearly belongs to the deleted catalog entry. That is the case when the block directly follows the parent catalog: or catalogs.<name>: header, or follows a blank line. Use always to remove every adjacent leading comment block. Use never to keep leading comments for manual review.
Two mechanisms keep your comments, whatever the setting:
-
Add
# fallow-keepto any line in a comment block to keep the whole block, also underalways. This works like thefallow-ignore-next-lineandfallow-ignore-fileinline suppression comments:catalog: # fallow-keep: audit trail, CVE-2024-XXXX react: ^18.2.0 -
Under
auto, fallow keeps section banners. A banner is a comment where the text after#and optional whitespace starts with three or more of the same character:=,-,*,_,~,+, or#:catalog: # === React 18 production pins === react: ^18.2.0Under
always, the banner rule does not apply. To keep banners withalways, use# fallow-keep.
The JSON action in fixes[N] has two line fields:
line: the first deleted line (1-based). This is the leading comment whenautooralwaysremoves one.entry_line: the original line of the catalog entry (1-based).
CI annotators and dedup caches that key on the entry position should use entry_line, which stays stable. Tools that point at the actual file edit should use line.
production
Enable production mode to analyze only the code that ships to users. The field takes the legacy boolean form, which applies to every analysis. It also takes a per-analysis object. For example, you can run health on production code only, and dead-code and duplication on the full tree:
{
"production": true
}{
"production": {
"deadCode": false,
"health": true,
"dupes": false
}
}The CLI flags --production, --production-dead-code, --production-health, and --production-dupes (bare combined runs and fallow audit) override the config. The matching env vars FALLOW_PRODUCTION, FALLOW_PRODUCTION_DEAD_CODE, FALLOW_PRODUCTION_HEALTH, and FALLOW_PRODUCTION_DUPES follow the same precedence order. A per-analysis env var takes precedence over the global env var. For the full order, see global flags.
workspaces
Extra workspace patterns, added to the workspaces that fallow detects automatically (package.json workspaces, pnpm-workspace.yaml, a root Deno config, and tsconfig.json references). See Workspaces:
{
"workspaces": {
"patterns": ["tools/*", "shared/*"]
}
}extends
Inherit from one or more base config files. Use it to share one config across projects, or to keep a base config with per-project overrides.
{
"extends": ["./configs/base.json", "./configs/strict.json"]
}extends = ["./configs/base.json", "./configs/strict.toml"]The extends field accepts these source types:
| Source | Example | Description |
|---|---|---|
| Relative path | "./configs/base.json" | Local file, relative to the config file |
| npm package | "npm:@my-org/fallow-config" | Reads the main entry of the package from node_modules |
| HTTPS URL | "https://example.com/fallow-base.json" | Remote file, fetched over HTTPS |
{
"extends": [
"./configs/local-overrides.json",
"npm:@my-org/fallow-config",
"https://example.com/shared/fallow-base.json"
]
}How extends works:
- Deep merge: fallow deep-merges object fields (such as
rules). The child config overrides the base, and the child inherits the fields that it does not set. - Array replacement: array fields (such as
entryandignorePatterns) replace the base value. Fallow does not concatenate them. If the child setsentry, it overrides the baseentry. - Cross-format support: a JSON config can extend a TOML config, and a TOML config can extend a JSON config.
- Circular detection: fallow detects circular extends chains and reports an error.
- Max depth: an extends chain can have 10 levels at most, to prevent accidental deep nesting.
- String shorthand: you can pass one path as a string instead of an array:
"extends": "./base.json".
Fallow resolves paths relative to the config file that contains the extends field.
URL extends
URL sources must use https://. Fallow rejects plain HTTP. It fetches the config on every run, with no cache.
- Timeout: 5 seconds by default. Change it with
FALLOW_EXTENDS_TIMEOUT_SECS. - Body limit: 1 MB maximum response size.
- Chaining: a config from a URL can extend other URLs or
npm:packages. It cannot extend relative paths, because there is no file system location to resolve them against.
Fallow fetches URL extends on every run. If the remote server is slow or not available, your analysis fails. For important shared configs, use a versioned URL or an npm: package.
minimumVersion
The lowest fallow version that this config needs, as MAJOR.MINOR.PATCH. It gives a clear upgrade message instead of an "unknown field" error on older fallow versions.
An unknown config key fails the run with exit 2. This catches mistakes such as ignorePaths instead of ignorePatterns. It also means that every new config field needs a coordinated upgrade.
When a team commits a field from a newer version, every runner on an older version breaks. The failure comes on the commit that adds the field, not on an upgrade. The error names the field, so the first guess is a typo.
With minimumVersion, the older runner shows a clear message instead.
{
"minimumVersion": "3.21.0",
"entry": ["src/main.ts"]
}A fallow binary below the minimum version stops before it reports any unknown field:
fallow.json requires fallow 3.21.0 or newer, running 3.19.0.
Upgrade fallow, or remove minimumVersion and the fields that need it.
How minimumVersion works:
- Inherited through
extends: a monorepo base config sets it once, and every config that extends the base inherits the minimum version. - Checked before unknown keys: the version message replaces the misleading field-name error. It does not only come first.
- No other effect: at or above the minimum version, an unknown key still fails, because there it is a real typo.
- Optional: unset by default. Without it, nothing changes.
- Validated: a value that is not
MAJOR.MINOR.PATCH(for examplev3.21) is a config error.
Set it in the same change that adds a field from a newer version. Do not set it in advance. A minimum version higher than the config needs blocks runners for no reason.
failOnParseError
Fail the run with exit code 1 when fallow cannot parse a source file cleanly. Boolean, default false. It arms the parse-error gate on fallow dead-code, fallow health, fallow audit, and the bare fallow run. The --fail-on-parse-error flag arms the same gate for one run.
{
"failOnParseError": true
}The gate is off by default, because the parser also rejects valid syntax that is newer than the parser. An older fallow version rejects this key as unknown, so set minimumVersion to the first release that has the key. See Parse error gate for the output and the CI behavior.
overrides
Apply different rules to specific file patterns. For example, relax rules in test files, or make them stricter in critical paths.
{
"overrides": [
{
"files": ["*.test.ts", "*.spec.ts"],
"rules": {
"unused-exports": "off"
}
},
{
"files": ["src/core/**/*.ts"],
"rules": {
"unused-exports": "error",
"unused-types": "error"
}
}
]
}[[overrides]]
files = ["*.test.ts", "*.spec.ts"]
[overrides.rules]
unused-exports = "off"
[[overrides]]
files = ["src/core/**/*.ts"]
[overrides.rules]
unused-exports = "error"
unused-types = "error"Each override object has these fields:
files: array of glob patterns, matched against paths relative to the project.rules: rule severities for the files that match the patterns.
When multiple overrides match the same file, the later override takes precedence.
sealed
Mark a config as sealed to make sure that no config from a parent directory can change it through extends. When sealed: true:
extendspaths must be relative to the file.extendspaths must resolve inside the directory of the config (no../paths).- Fallow rejects
npm:andhttps:extends with a clear error.
{
"sealed": true,
"extends": ["./base.json"]
}sealed = true
extends = ["./base.json"]Use cases:
- Library publishers who ship a
.fallowrc.jsonin an npm package can make sure that the config is self-contained. - Monorepo sub-packages (for example a shared component library that multiple apps use) that do not inherit from the monorepo root config on purpose.
sealed: true does not change config discovery. Fallow already stops at the nearest config in the directory tree (first match wins). This option only limits what extends can reference.
circularDependencies
Options for the circular-dependencies rule. The only option is ignoreLazyImports (default false).
{
"circularDependencies": { "ignoreLazyImports": true }
}With ignoreLazyImports: true, an import edge that loads its target on demand or on another thread does not take part in cycle detection. These edges are lazy:
- an
import()inside a function - a template
import() - a lazy
import.meta.glob - a worker URL
- a webpack inline request through a worker loader, such as
worker-loader!./work.js(see inline loader requests)
These edges stay in the cycle graph: a top-level await import(), require(), an eager import.meta.glob, and an edge that also has a static import. Fallow removes the lazy edges before it counts the cycles of a group, so lazy cycles do not fill the cycle limit and hide a static cycle.
A top-level await import() in a Vue <script setup> block, a Svelte instance script, or an Astro frontmatter stays lazy, because that code runs for each component instance.
boundaries
Define architecture boundary zones and import rules. Use a built-in preset, or define custom zones.
{
"boundaries": {
"preset": "bulletproof"
}
}Available presets: layered, hexagonal, feature-sliced, bulletproof. For full control, define custom zones and rules.
For presets, custom zones, examples, and output formats, see Architecture boundaries.
rulePacks
Rule packs let you ban specific calls, imports, and effects across your project. A rule pack is a standalone JSON or JSONC file with banned-call, banned-import, and banned-effect rules. Fallow loads packs as data only and never runs project code. Paths are relative to the project root. Fallow reports matches as policy-violation findings with the ID <pack>/<rule-id>.
{
"rulePacks": ["./rule-packs/team-policy.jsonc"]
}A pack file declares version: 1, a unique name, and a non-empty rules array. Pack names and rule IDs must use ASCII letters, digits, ., _, or -, so that scoped suppression comments can name them without ambiguity.
banned-callrules match callee paths per segment, through imports.child_process.*covers named, namespace, and default imports fromchild_processandnode:child_process.banned-importrules match import specifiers as written, per segment.momentcoversmoment/locale/nl, nevermoment-timezone.- Optional
filesandexcludeglobs limit where a rule applies. "ignoreTypeOnly": trueskips type-only imports.- An optional per-rule
severityoverrides the mainrules."policy-violation"severity (defaultwarn).
To suppress one rule, use // fallow-ignore-next-line policy-violation:<pack>/<rule-id> or the file-level form. Bare policy-violation still suppresses all rule-pack findings.
For editor autocomplete, run fallow rule-pack-schema to print the pack JSON Schema, or point $schema at https://raw.githubusercontent.com/fallow-rs/fallow/main/rule-pack-schema.json. An invalid or missing pack makes the config load fail with exit code 2. Fallow does not continue with a pack that enforces nothing.
Keep pack files in a committed directory such as rule-packs/. .fallow/ is the gitignored cache directory, so packs there are missing from the checkouts of your teammates.
For details on the issue type, see Policy violations.
rules
Severity per issue type: error (fails CI with exit code 1), warn (reports but exits 0), or off (no detection). Each rule has its own default. See Rules for the default of each rule.
{
"rules": {
"unused-files": "warn",
"unused-exports": "error",
"circular-dependency": "off",
"css-token-drift": "warn",
"css-duplicate-block": "warn",
"css-selector-complexity": "warn",
"css-dead-surface": "warn",
"css-broken-reference": "warn"
}
}The complexity-cyclomatic, complexity-cognitive, and complexity-crap rules decide if a complexity finding fails fallow health and fallow audit. See Complexity rules.
Styling audit findings use the same severity map. The styling rules are css-token-drift, css-duplicate-block, css-selector-complexity, css-dead-surface, and css-broken-reference. They default to warn, so they show in the output but do not change the verdict, unless you set them to error or off.
Rules and severity has the full list of issue types, with the default severities. It includes the pnpm catalog rules such as unused-catalog-entries and empty-catalog-groups. For per-glob severities, use the overrides field.
flags
Customize feature flag detection. Fallow detects the SDKs of LaunchDarkly, Statsig, Unleash, GrowthBook, Split, PostHog, Vercel Flags, ConfigCat, Flagsmith, Optimizely, and Eppo by default. With this field you can:
- Add SDK call patterns.
- Declare environment variable prefixes.
- Enable opt-in heuristic detection.
| Field | Type | Default | Description |
|---|---|---|---|
sdkPatterns | SdkPattern[] | [] | Extra SDK call patterns to detect as feature flags, added to the built-ins. Each entry has function (function name to match), nameArg (zero-based index of the argument with the flag name, default 0), and provider (optional label in the output). |
envPrefixes | string[] | [] | Environment variable prefixes that mark feature flags, added to the built-ins. Fallow reports only process.env.* and import.meta.env.* accesses that match these prefixes. |
configObjectHeuristics | boolean | false | Enable heuristic detection. Fallow reports property accesses on objects with "feature", "flag", or "toggle" in the name as low-confidence flags. Opt-in, because it gives more false positives. |
vendorKeyPrefix | string | - | Prefix to remove from each key of a fallow flags --flag-state vendor export before the key is compared with the flag names in the code. For example, "web." makes web.new-checkout match new-checkout. |
{
"flags": {
"sdkPatterns": [
{ "function": "useFlag", "nameArg": 0, "provider": "LaunchDarkly" },
{ "function": "client.getFeatureValue", "nameArg": 0 }
],
"envPrefixes": ["FEATURE_", "NEXT_PUBLIC_ENABLE_"],
"configObjectHeuristics": false
}
}fallow flags is the command that uses this configuration.
resolve
Module resolver configuration. It controls how fallow matches the exports and imports maps of package.json.
| Field | Type | Default | Description |
|---|---|---|---|
conditions | string[] | [] | Extra export and import condition names that fallow uses during resolution. They add to the built-in set: development, import, require, default, types, node (plus react-native and browser when the React Native or Expo plugin is active). Your conditions have a higher priority than the built-in set. |
Use this for community conditions such as worker, edge-light, and deno, or for any custom condition that your bundler uses. The built-in set already has development. A package with a development branch therefore resolves to source with no config. This is common in monorepos, where development points at source and import points at compiled output.
{
"resolve": {
"conditions": ["worker", "edge-light"]
}
}With this config, a package.json like:
{
"exports": {
"./api": {
"worker": "./src/api.worker.ts",
"import": "./dist/api.js"
}
}
}resolves ./api to src/api.worker.ts instead of dist/api.js.
For the full list of established condition names, see the Node.js community conditions reference.
codeowners
Path to a CODEOWNERS file for --group-by owner. When unset, fallow looks for CODEOWNERS, .github/CODEOWNERS, .gitlab/CODEOWNERS, and docs/CODEOWNERS. Set this field when your file is in a different location.
{
"codeowners": ".github/CODEOWNERS"
}framework
Inline framework plugin definitions. Define custom entry points, ignored files, or test patterns without a separate plugin file.
{
"framework": [
{
"name": "my-framework",
"entry": ["src/server.ts"],
"ignore": ["**/*.internal.ts"]
}
]
}plugins
Paths to external plugin files or directories. Fallow also finds *.toml, *.json, and *.jsonc files in .fallow/plugins/, and fallow-plugin-*.{toml,json,jsonc} in the project root, automatically.
{
"plugins": ["./my-plugins/custom.toml", "./plugins/"]
}cache
Settings for the incremental cache. Use dir to move the extraction cache. Use maxSizeMb to limit its serialized size. Above the limit, fallow removes the least recently used entries when it saves.
{
"cache": {
"dir": "/tmp/fallow-cache",
"maxSizeMb": 64
}
}The default cache file is .fallow/cache.bin under the project root. When cache.dir is set, fallow stores the cache file in that directory. When both are set, the env var FALLOW_CACHE_DIR overrides cache.dir.
The default limit is 256 MB. The limit applies to the serialized extraction cache. When a save goes past 80% of the limit, fallow removes the oldest entries until the cache is at 60%. When both are set, the env var FALLOW_CACHE_MAX_SIZE overrides cache.maxSizeMb. Both values are in whole megabytes.
Fallow rebuilds the cache automatically when config that affects extraction
changes: the active plugin names and the inline framework definition names.
Fields that affect only detection, such as entry, ignorePatterns, and
severity overrides, do not invalidate the extraction cache.
--no-cache turns off the cache fully. This section then has no effect.
regression
Regression baseline in the config. It stores the issue counts of a known-good state, so CI can fail when the counts go up. --save-regression-baseline (with no path argument) writes it, and --fail-on-regression reads it.
The count keys use camelCase, for example totalIssues and unusedExports. Fallow ignores snake_case keys such as unused_exports and treats a missing count as 0.
{
"regression": {
"baseline": {
"totalIssues": 68,
"unusedFiles": 11,
"unusedExports": 48,
"unusedTypes": 5,
"unusedDependencies": 3,
"circularDependencies": 1
}
}
}Unmatched config patterns
An ignoreDependencies glob that matches no declared dependency, or an ignoreFindings pattern that matches no finding, has no effect. The usual cause is a typo. fallow dead-code, fallow audit and the combined fallow run report such an entry in every output format. The presentation is different per format, but each format names the same entries.
| Output | Where the entry shows |
|---|---|
| JSON | workspace_diagnostics[], with kind set to ignore-dependencies-glob-unmatched or ignore-findings-pattern-unmatched, and a pattern field. fallow audit puts the list under dead_code |
| SARIF | invocations[0].toolConfigurationNotifications[] of the dead-code run, with descriptor.id set to the kind, and properties.setting and properties.pattern |
Markdown, github-summary, pr-comment-github, pr-comment-gitlab | An Unmatched config patterns section after the findings |
review-github, review-gitlab | An Unmatched config patterns section in the summary body of the review envelope, before the markers |
Human, compact, CodeClimate, github-annotations | A note on stderr, so stdout does not change. --quiet removes the note, also on fallow report --from |
| Editor (LSP) | An information diagnostic on the entry in the config file. The code is the kind, and the message is the message of the JSON entry. For an extends chain, the diagnostic goes on the file that declares the list. A pattern that the server cannot find in a local file (for example from a remote extends config) goes to the output log, once for each changed set |
fallow report --from shows the entries at the same place as the live run of the same format. The GitHub Action and the GitLab template post the job summary and the PR or MR comment, so the entries reach the pull request. They also write one warning with the entries to the job log. The integrations run fallow with --quiet, and they post a review only when it has new inline comments, so the log warning also covers a setup that posts only the review.
The MCP dead-code tool and the programmatic API carry the same workspace_diagnostics[] entries.
{
"path": ".",
"kind": "ignore-dependencies-glob-unmatched",
"pattern": "@acm/*",
"message": "ignoreDependencies glob '@acm/*' matched no declared dependency in this run, so it has no effect. A glob matches package names, such as @scope/*. Fix the glob, or remove it from the config."
}
An entry is advisory. It does not change the exit code, and degrades_analysis is not set.
Fallow reports an ignoreDependencies glob only when the run shows dependency findings. These runs do not report it:
- A run with an issue filter that does not include
--unused-depsor--unlisted-deps, for example--unused-files. - A run with
--file, because such a run drops dependency findings. - A run where every dependency rule is
off.
Fallow compares an ignoreFindings pattern with every finding of the analysis, before issue filters and scope filters. A filtered run therefore does not make a pattern look unused. A run with no findings at all reports no ignoreFindings pattern.
JSON Schema
Add the $schema field to get autocomplete and validation in your editor:
{
"$schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json"
}
To generate the schema locally, run:
fallow config-schema > schema.json