# 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.

> Page index of all docs: https://fallow.tools/llms.txt
> Docs MCP server (search and read the docs): https://fallow.tools/docs/mcp
> HTML version of this page: https://fallow.tools/docs/configuration/overview/

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:

```bash
fallow init          # Creates .fallowrc.json
fallow init --toml   # Creates fallow.toml
```

## Config file formats

Fallow looks for config files in this order:

1. `.fallowrc.json` (JSONC, comments allowed)
2. `.fallowrc.jsonc` (same JSONC parser; editors detect the JSON-with-comments syntax from the extension)
3. `fallow.toml`
4. `.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

**.fallowrc.json**

```jsonc
{
  "$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
}
```

**fallow.toml**

```toml
entry = ["src/workers/*.ts", "scripts/*.ts"]
ignorePatterns = ["**/*.generated.ts", "**/*.d.ts"]
ignoreDependencies = ["autoprefixer", "@types/node"]
ignoreExportsUsedInFile = true
publicPackages = ["@myorg/sdk"]
dynamicallyLoaded = ["src/plugins/**/*.ts"]
production = false

[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"]

[[health.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"
```

## Config fields

### entry {#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.

```jsonc
{
  "entry": ["src/workers/*.ts", "scripts/*.ts", "src/cli.ts"]
}
```

### Entry point auto-detection {#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. Fallow also maps a `lib/` path back to `src/`, but only when the `lib/` file is not in the analyzed files: it is not on disk, or `.gitignore` or `ignorePatterns` excludes it. A package with hand-written source in `lib/` keeps its `lib/` entry.

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 {#ignorepatterns}

Glob patterns for files that fallow does not analyze at all.

```jsonc
{
  "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 source folder named `build`, for example the build step of a CLI in `src/cli/build/`, is then not analyzed. Its imports do not count, so a dependency that only this folder uses can be reported as unused. The run names the skipped folder in `workspace_diagnostics` with the kind `excluded-by-default-ignore`, and `fallow list` shows it too. To analyze the folder, add an exception such as `"!src/**/build/**"`, as in the example below. Keep the built-in default for the build output itself.

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:

```jsonc
{
  "ignorePatterns": [
    "legacy/**",
    // lift the built-in **/coverage/** for this subtree only
    "!src/policy/coverage/**",
    // analyze source folders named build, not the build output
    "!src/**/build/**",
    // 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-level `coverage/` 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_modules` or `.git` cannot 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 {#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`.

```jsonc
{
  "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](https://fallow.tools/docs/configuration/overview/#unmatched-config-patterns).

### ignoreDependencies {#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.

```jsonc
{
  "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.

```jsonc
{
  // Every package in the @acme scope, and every @types package.
  "ignoreDependencies": ["@acme/*", "@types/*", "eslint-plugin-*"]
}
```

- `@acme/*` matches `@acme/lib` and `@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 an `ignoreDependencies` error.

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](https://fallow.tools/docs/configuration/overview/#unmatched-config-patterns).

### ignoreCommandEntries {#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:

```jsonc
{
  "ignoreCommandEntries": ["my-codegen"]
}
```

- A listed command is still a used dependency, and its `--config` file 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.ts` or `npm run gen src/input.ts` matches the command in the body of the `gen` script.
- `"*"` matches every command. Use `["*"]` to turn off entry points from all commands, and declare the real entry points in `entry`. This also drops the modules that a linter loads through a flag, such as `eslint -f ./tools/fmt.js`. Add those modules to `entry` too.
- The field does not change commands that a plugin reads from a tool config, such as Playwright `webServer.command`.

### ignoreCatalogReferences {#ignorecatalogreferences}

Entries that suppress `unresolved-catalog-reference` findings. This finding is a `catalog:` dependency or a `catalog:` pnpm override value that points at a catalog that does not declare the package. Most 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.

```jsonc
{
  "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. For a pnpm override, `package` is the target package of the override: write `child` for `"parent>child"`. `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. For a pnpm override, the consumer is the file that declares the override: `pnpm-workspace.yaml` or the root `package.json`. 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 {#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.

```jsonc
{
  "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 skips a pnpm source that the declared pnpm version does not read. For example, pnpm 11 does not read `pnpm.overrides`. See [override sources and the pnpm version](https://fallow.tools/docs/explanations/dead-code/#pnpm-override-sources). An entry for a skipped source matches nothing.

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 {#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 `exports` map that points at build artifacts that are not in the checkout.
- A bundler alias that fallow does not model.

```jsonc
{
  "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 {#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.

```jsonc
{
  "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 {#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`](https://knip.dev/reference/configuration#ignoreexportsusedinfile) option of knip.

```jsonc
{
  "ignoreExportsUsedInFile": true
}
```

For knip compatibility, fallow also accepts an object form:

```jsonc
{
  "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 {#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`.

```jsonc
{
  "includeEntryExports": true
}
```

The global CLI flag `--include-entry-exports` does the same. When both are set, the CLI flag takes precedence.

### showCascade {#showcascade}

Also report the unused exports, unused types, unused class members, and unused enum members of a file that fallow reports as an unused file. The default is `false`. When you delete an unused file, you also remove these findings, so the report hides them and counts them in `cascade_hidden`.

```jsonc
{
  "showCascade": true
}
```

The CLI flag `--show-cascade` does the same for one run. See [Findings in unused files](https://fallow.tools/docs/analysis/dead-code/#findings-in-unused-files).

### typeAware {#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.

```jsonc
{
  "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](https://fallow.tools/docs/analysis/type-aware/).

### security {#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`.

```jsonc
{
  "security": {
    "requestReceivers": ["h", "httpReq"],
    "categories": {
      "include": ["command-injection"],
      "exclude": ["dynamic-regex"]
    }
  }
}
```

### autoImports {#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`.

```jsonc
{
  "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`, and `shared/types` patterns.

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`, or `components: { dirs: [] }`. `components: true` gives the same directories as a missing key. Other keys next to an empty `dirs` do not change the result.
- `imports: {}`, `imports: { dirs: [] }`, or `imports: { scan: false }` with no `imports.dirs` or an empty array. The first two are the Nuxt defaults: `imports.dirs` starts 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 empty `dirs`. A non-empty `dirs`, or only a `prefix` or `pathPrefix`, keeps the patterns. Any other value is also custom.
- `imports: { scan: true }`, a non-empty `imports.dirs`, and `imports: { scan: false }` next to a non-empty `imports.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 nested `components` or `imports` key.
- A `hooks` entry for `components` or `imports`, 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](https://fallow.tools/docs/analysis/debugging/).

Fallow reads the config of each root separately: the project root and every workspace root, plus each [local layer](https://fallow.tools/docs/frameworks/built-in/#nuxt-local-layers) 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](https://fallow.tools/docs/frameworks/built-in/#nuxt-local-layers) 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](https://fallow.tools/docs/frameworks/built-in/#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.vue` files. A string reference such as `resolveComponent('Name')` can render a global component.
- `components/content/` files of the project and of each local layer, when `@nuxt/content` is in `modules` (as a string or as a `['@nuxt/content', { ... }]` entry), or when a [package layer](https://fallow.tools/docs/frameworks/built-in/#nuxt-local-layers) such as `docus` registers 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](https://fallow.tools/docs/frameworks/built-in/#nuxt-auto-import-scope).

The default is `false`. Fallow tracks Pinia store auto-imports separately.

### ignoreDecorators {#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.

```jsonc
{
  "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.audit` stays 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 {#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:

```jsonc
{
  "rules": { "unused-component-props": "warn" },
  "unusedComponentProps": {
    "ignorePattern": "^_"
  }
}
```

```svelte
<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** (`_stage` in `let { 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.test` in 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 {#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 `extends` or `implements` identifier in their heritage clause.

Strings can be exact names or glob patterns:

```jsonc
{
  "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 with `enter`.
- `"*Handler"` matches any member with a name that ends with `Handler`.
- `"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](https://fallow.tools/docs/frameworks/custom-plugins/).

### publicPackages {#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.

```jsonc
{
  "publicPackages": ["@myorg/sdk", "@myorg/types"]
}
```

### dynamicallyLoaded {#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.

```jsonc
{
  "dynamicallyLoaded": ["src/plugins/**/*.ts", "src/routes/**/page.tsx"]
}
```

### duplicates {#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](https://fallow.tools/docs/cli/dupes/#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`. |

```jsonc
{
  "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 {#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. |

```jsonc
{
  "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](https://fallow.tools/docs/analysis/similar-code/).

### health {#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`](https://fallow.tools/docs/configuration/environment/#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`](https://fallow.tools/docs/configuration/environment/#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](https://fallow.tools/docs/cli/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. |

```jsonc
{
  "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 {#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`](https://fallow.tools/docs/configuration/environment/#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 |

```jsonc
{
  "audit": {
    "gate": "new-only",
    "css": true,
    "cssDeep": true,
    "cacheMaxAgeDays": 14
  }
}
```

```toml
[audit]
gate = "all"
css = true
cssDeep = true
cacheMaxAgeDays = 14
```

### fix {#fix}

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. |

```jsonc
{
  "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-keep` to any line in a comment block to keep the whole block, also under `always`. This works like the `fallow-ignore-next-line` and `fallow-ignore-file` inline suppression comments:

  ```yaml
  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 `#`:

  ```yaml
  catalog:
    # === React 18 production pins ===
    react: ^18.2.0
  ```

  Under `always`, the banner rule does not apply. To keep banners with `always`, 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 when `auto` or `always` removes 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 {#production}

Enable [production mode](https://fallow.tools/docs/analysis/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:

```jsonc
{
  "production": true
}
```

```jsonc
{
  "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](https://fallow.tools/docs/cli/global-flags/#per-analysis-production-mode).

### workspaces {#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](https://fallow.tools/docs/configuration/workspaces/):

```jsonc
{
  "workspaces": {
    "patterns": ["tools/*", "shared/*"]
  }
}
```

Each package that these patterns find gets its own plugins, entry points, and dependency checks. `fallow list --workspaces`, `--workspace`, `--changed-workspaces`, and `--group-by package` also use these packages. See [Additional workspace patterns](https://fallow.tools/docs/configuration/workspaces/#additional-workspace-patterns).

### extends {#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.

```jsonc
{
  "extends": ["./configs/base.json", "./configs/strict.json"]
}
```

```toml
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 |

```jsonc
{
  "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 `entry` and `ignorePatterns`) replace the base value. Fallow does not concatenate them. If the child sets `entry`, it overrides the base `entry`.
- **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.

> **Warning:** 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 {#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.

```jsonc
{
  "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 example `v3.21`) is a config error.

> **Tip:** 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 {#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.

```jsonc
{
  "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](https://fallow.tools/docs/cli/global-flags/#parse-error-gate) for the output and the CI behavior.

### overrides {#overrides}

Apply different rules to specific file patterns. For example, relax rules in test files, or make them stricter in critical paths.

```jsonc
{
  "overrides": [
    {
      "files": ["*.test.ts", "*.spec.ts"],
      "rules": {
        "unused-exports": "off"
      }
    },
    {
      "files": ["src/core/**/*.ts"],
      "rules": {
        "unused-exports": "error",
        "unused-types": "error"
      }
    }
  ]
}
```

```toml
[[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 {#sealed}

Mark a config as **sealed** to make sure that no config from a parent directory can change it through `extends`. When `sealed: true`:

- `extends` paths must be relative to the file.
- `extends` paths must resolve **inside** the directory of the config (no `../` paths).
- Fallow rejects `npm:` and `https:` extends with a clear error.

```jsonc
{
  "sealed": true,
  "extends": ["./base.json"]
}
```

```toml
sealed = true
extends = ["./base.json"]
```

Use cases:

- **Library publishers** who ship a `.fallowrc.json` in 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 {#circulardependencies}

Options for the `circular-dependencies` rule. The only option is `ignoreLazyImports` (default `false`).

**.fallowrc.json**

```jsonc
{
  "circularDependencies": { "ignoreLazyImports": true }
}
```

**fallow.toml**

```toml
[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](https://fallow.tools/docs/frameworks/built-in/#webpack-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 {#boundaries}

Define architecture boundary zones and import rules. Use a built-in preset, or define custom zones.

```jsonc
{
  "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](https://fallow.tools/docs/analysis/boundaries/).

### rulePacks {#rulepacks}

Rule packs let you enforce project policies for calls, imports, exports, effects, and gdp-ts proof producers. A rule pack is a standalone JSON or JSONC file. 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>`.

```jsonc
{
  "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-call` rules match callee paths per segment, through imports. `child_process.*` covers named, namespace, and default imports from `child_process` and `node:child_process`.
- `banned-import` rules match import specifiers as written, per segment. `moment` covers `moment/locale/nl`, never `moment-timezone`.
- `banned-effect` rules match effect classes from the security catalogue, such as `network` and `database`.
- `banned-export` rules match exported names. A trailing `*` matches a name prefix.
- `gdp-proof-producer` rules restrict calls to `@gdp-ts/core.defineProof` to `allowedFiles` globs. Optional `proofKinds` labels restrict the rule to those literal proof kinds.
- Optional `files` and `exclude` globs limit where a rule applies.
- `"ignoreTypeOnly": true` skips type-only imports.
- An optional per-rule `severity` overrides the main `rules."policy-violation"` severity (default `warn`).

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.

#### gdp-ts proof producers

Use a global producer rule to allow proof factories only in your authorization modules. Add a rule for a specific proof kind to require its factory to live in one owner file:

**rule-packs/gdp.json**

```json
{
  "version": 1,
  "name": "gdp",
  "rules": [
    {
      "id": "trusted-producers",
      "kind": "gdp-proof-producer",
      "allowedFiles": ["src/auth/**"],
      "severity": "error"
    },
    {
      "id": "delete-project-owner",
      "kind": "gdp-proof-producer",
      "allowedFiles": ["src/auth/project.ts"],
      "proofKinds": ["CanDeleteProject"],
      "severity": "error"
    }
  ]
}
```

Add `"rulePacks": ["rule-packs/gdp.json"]` to your fallow config. Each matching rule reports independently. A call outside `src/auth/**` can violate both rules. A call in `src/auth/other.ts` violates only the owner rule.

`allowedFiles` must contain at least one project-root-relative glob. It grants producer permission. `files`, `exclude`, and `zones` limit where the rule checks code. Neither `allowedFiles` nor `proofKinds` applies to other rule kinds.

An absent or empty `proofKinds` checks every recognized factory call, including dynamic labels. A nonempty list matches exact static string labels; dynamic labels are skipped by that rule. Keep the global rule to restrict those calls too.

The rule checks every analyzed file, including files unreachable from entry points. Files excluded from discovery are outside its scope. It follows named and namespace imports, import aliases, and unambiguous project re-export chains to the gdp-ts factory. See [Policy violations](https://fallow.tools/docs/explanations/dead-code/#policy-violations) for supported forms and limits.

Inspect the configured permissions with `fallow rule-pack list --format json` or `fallow guard src/routes/project.ts --format json`. Use `fallow rule-pack test rule-packs/gdp.json --format json` to inspect matches while authoring the pack. This command exits successfully when it reports findings. For CI enforcement, run [`fallow architecture --policy`](https://fallow.tools/docs/cli/architecture/). An error-severity finding exits with code 1.

For details on the issue type, see [Policy violations](https://fallow.tools/docs/explanations/dead-code/#policy-violations).

### rules {#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](https://fallow.tools/docs/configuration/rules/#available-rule-names) for the default of each rule.

```jsonc
{
  "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](https://fallow.tools/docs/configuration/rules/#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](https://fallow.tools/docs/configuration/rules/) 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 {#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`. |

```jsonc
{
  "flags": {
    "sdkPatterns": [
      { "function": "useFlag", "nameArg": 0, "provider": "LaunchDarkly" },
      { "function": "client.getFeatureValue", "nameArg": 0 }
    ],
    "envPrefixes": ["FEATURE_", "NEXT_PUBLIC_ENABLE_"],
    "configObjectHeuristics": false
  }
}
```

[`fallow flags`](https://fallow.tools/docs/cli/flags/) is the command that uses this configuration.

### resolve {#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.

```jsonc
{
  "resolve": {
    "conditions": ["worker", "edge-light"]
  }
}
```

With this config, a `package.json` like:

```jsonc
{
  "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](https://nodejs.org/api/packages.html#community-conditions-definitions).

### codeowners {#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.

```jsonc
{
  "codeowners": ".github/CODEOWNERS"
}
```

### framework {#framework}

Inline framework plugin definitions. Define custom entry points, ignored files, or test patterns without a separate plugin file.

```jsonc
{
  "framework": [
    {
      "name": "my-framework",
      "entry": ["src/server.ts"],
      "ignore": ["**/*.internal.ts"]
    }
  ]
}
```

### plugins {#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.

```jsonc
{
  "plugins": ["./my-plugins/custom.toml", "./plugins/"]
}
```

### cache {#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.

```jsonc
{
  "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}

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`.

```jsonc
{
  "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.

```json
{
  "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-deps` or `--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:

```jsonc
{
  "$schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json"
}
```

To generate the schema locally, run:

```bash
fallow config-schema > schema.json
```

## See also

- [Rules & Severity](https://fallow.tools/docs/configuration/rules/): Control issue severity for incremental CI adoption.
- [Inline Suppression](https://fallow.tools/docs/configuration/suppression/): Suppress single findings in source code.
- [Custom Plugins](https://fallow.tools/docs/frameworks/custom-plugins/): Add entry point detection for your own frameworks.
