Fallow's default analysis is syntactic: no TypeScript compiler, no type
resolution. The optional --type-aware pass adds
bounded checker evidence for Fallow-owned project questions. It does not turn
Fallow into a compiler or general typed linter.
The limitations below apply to specific patterns. Standard TypeScript projects with isolatedModules: true are unaffected by most of them.
By default, Fallow parses source code as syntax trees using Oxc and uses
oxc_semantic for scope-aware binding analysis. It does not invoke the
TypeScript compiler or resolve type information. This keeps the default path
fast. Add --type-aware for exact symbol identity,
cross-file private type leaks, targeted tests, and public type coupling.
Impact: Type-only imports (import type { Foo }) are handled correctly. Value imports used only as type annotations (e.g., import { Foo } followed by const x: Foo) are also tracked correctly via oxc_semantic reference counting. Conditional type exports or dead code behind generic interfaces would require actual type resolution to detect.
Workaround: Set isolatedModules: true in tsconfig.json. esbuild, swc, and Vite already require this, so most modern projects have it. With isolatedModules, every import is either explicitly import type or a real value import, removing ambiguity.
{
"compilerOptions": {
"isolatedModules": true
}
}In Svelte components, export let declarations serve double duty: they define component props and regular exports. Fallow can't distinguish between props (used by consumers via the template) and utility exports that may genuinely be unused.
Impact: Some unused exports in .svelte files may not be reported.
Workaround: No configuration workaround exists. Review Svelte component exports manually. Extract utility functions into separate .ts files where fallow can track them accurately.
This limitation is about export let prop detection only. Fallow does track template-visible imports since v2.15.0: component tags, directives (use:tooltip, v-focus-trap), expressions, $store subscriptions in Svelte, and v-on/v-bind object syntax in Vue are all correctly credited as used. See File types for details.
CSS and SCSS files are parsed with lightweight regex extraction rather than a full CSS parser. This covers the most common patterns but not the entire CSS specification.
What's tracked:
@import and @use statements@forward rules@apply and @tailwind directives (as Tailwind dependency usage)@plugin directives (Tailwind v4 plugin sources, package or relative)What's not tracked:
composes: (CSS Modules composition):global() and :local() pseudo-selectorsWorkaround: If fallow reports false positives for CSS files that use composes: or :global(), suppress them with ignorePatterns or inline comments.
{
"ignorePatterns": ["**/legacy-styles/**/*.css"]
}Fallow tracks class member usage syntactically by looking for direct references in source code. In NestJS and other DI frameworks, abstract class methods may be invoked at runtime through the DI container without appearing as direct references.
Impact: Abstract class methods and properties consumed via dependency injection may be reported as unused.
Workaround: Disable unused-class-members for DI-heavy projects:
{
"rules": {
"unused-class-members": "off"
}
}If your package exports types for external consumers (e.g., ButtonProps, Config), fallow correctly reports them as unused within your project, because they are. No internal file imports them.
Impact: Correct findings that are noisy for library authors.
Workaround: Use ignoreExports to mark public API files:
{
"ignoreExports": [
{ "file": "src/index.ts", "exports": ["*"] },
{ "file": "src/types.ts", "exports": ["*"] }
]
}Fallow's built-in plugins parse config files (e.g., vite.config.ts, jest.config.js) using Oxc's AST parser. This handles static objects, arrays, and defineConfig() wrappers, but it can't evaluate arbitrary JavaScript.
What's handled:
defineConfig() / module.exports wrappersWhat's not handled:
process.env.NODE_ENV === 'production' ? ... : ...)require() with variablesWorkaround: Add undetected entry points manually in your fallow config:
{
"entry": ["src/dynamic-entry.ts", "scripts/*.ts"]
}React Native auto-linked native packages (installed via npx pod-install or auto-linking) aren't detected as used by fallow. These packages may not have any explicit import in JavaScript code.
Impact: Auto-linked packages like react-native-screens or react-native-gesture-handler may be reported as unused dependencies.
Workaround: Add auto-linked packages to ignoreDependencies:
{
"ignoreDependencies": [
"react-native-screens",
"react-native-gesture-handler",
"react-native-safe-area-context"
]
}Discovery skips dot-prefixed directories. Five conventional names are
traversed by default (.storybook, .vitepress, .well-known,
.changeset, .github), and an active framework plugin can contribute the
hidden directory its framework owns. Everything else is dropped before its
contents are parsed, which keeps machine-written trees such as .next,
.turbo, and .venv out of analysis without any configuration.
Impact: First-party source under a hidden directory, for example agent
hooks in .claude/hooks/ or plugins in .omp/extensions/, is not analyzed.
Its imports and exports are invisible, so it neither produces findings of its
own nor credits the modules it imports. The run reports the skip rather than
dropping it silently: a skipped-source-dotdir entry in
workspace_diagnostics[] and one aggregated note on stderr name each hidden
directory that holds source files your project has not otherwise excluded.
Workaround: No config field adds a directory to traversal. Analyze the directory on its own by pointing fallow at it:
fallow dead-code --root .claudeIf the directory holds tool or agent scratch state you never want analyzed,
add it to ignorePatterns to silence the advisory:
{
"ignorePatterns": [".claude/**"]
}Contents already excluded from the run stay silent on their own, so a hidden
directory covered by .gitignore (including a global gitignore, which
commonly covers agent tooling) never produces the advisory.
The fallow LSP server reports column positions as byte offsets rather than character offsets. For ASCII-only source files this makes no difference. For files with multi-byte characters (emoji, CJK, accented characters), the column number may be off by a few positions.
Impact: Diagnostic squiggles in VS Code may be slightly misaligned on lines containing non-ASCII characters.
Workaround: This is a cosmetic issue only. The line number and file path are always correct, so navigation still works. A fix is planned for a future release.