Node.js bindings
Run fallow analyses from your own Node.js code and get typed results. The NAPI bindings need no CLI subprocess or JSON parsing and return the same JSON envelopes as the CLI.
With @fallow-cli/fallow-node, you call the main fallow analyses as async Node.js functions. You do not start a CLI subprocess or parse JSON. The functions use the same analysis layer as the CLI and return the same JSON report envelopes as --format json. The optional detectSimilarCode function verifies and starts its local model companion, which must be the exact matching version.
Use the bindings to embed fallow in code that would otherwise call the CLI:
- another tool
- a Node.js server
- an editor extension
- a custom script
Install
npm install @fallow-cli/fallow-node
The package is a native NAPI-RS addon. It has platform-specific optionalDependencies for these platforms:
- macOS (arm64, x64)
- Linux (gnu and musl on arm64 and x64)
- Windows (x64, arm64)
npm installs the matching binary.
The package requires Node 22 or newer.
Basic usage
import {
detectDeadCode,
detectDuplication,
detectSimilarCode,
computeHealth,
} from '@fallow-cli/fallow-node';
const deadCode = await detectDeadCode({
root: process.cwd(),
explain: true,
});
console.log(deadCode.summary.total_issues);
console.log(deadCode.unused_exports);
const similar = await detectSimilarCode({
root: process.cwd(),
threshold: 0.8,
top: 20,
});
console.log(similar.candidates);
Each function returns a promise. The promise resolves to the same JSON envelope that the CLI prints with --format json. The envelope has:
- a top-level
kinddiscriminator on typed roots schema_versionsummary- relative paths
- injected
actionsarrays - a
_metablock with docs links, whenexplain: true
API
All eight functions are async and take an optional options object. The functions ignore unknown fields. Enum-like fields take lowercase CLI-style literals, such as "mild", "cyclomatic", "handle", and "low".
| Function | CLI equivalent | Returns |
|---|---|---|
detectDeadCode(options?) | fallow dead-code --format json | DeadCodeReport |
detectCircularDependencies(options?) | fallow dead-code --circular-deps | DeadCodeReport filtered to the circular_dependencies category |
detectBoundaryViolations(options?) | fallow dead-code --boundary-violations | DeadCodeReport filtered to the boundary_violations category |
detectDuplication(options?) | fallow dupes --format json | DuplicationReport |
detectSimilarCode(options?) | fallow similar-code --format json | SimilarCodeReport with unverified candidates |
detectFeatureFlags(options?) | fallow flags --format json | FeatureFlagsReport |
computeComplexity(options?) | fallow health --format json | HealthReport |
computeHealth(options?) | fallow health --format json | Alias for computeComplexity |
Shared options
Every options object extends AnalysisOptions:
| Field | Type | Default | Purpose |
|---|---|---|---|
root | string | process.cwd() | Project root directory |
configPath | string | auto-discover | Path to .fallowrc.json / .fallowrc.jsonc / fallow.toml / .fallow.toml |
allowRemoteExtends | boolean | false | Let trusted config files extend HTTPS URLs (same as --allow-remote-extends) |
noCache | boolean | false | Skip the extraction cache |
threads | number | CPU count | Number of threads. Must be > 0 |
diffFile | string | unset | Path to a unified diff file for line-level scoping (same as --diff-file). Diffs from stdin work only in the CLI. |
production | boolean | false | Production mode (excludes test/build files) |
changedSince | string | unset | Scope analysis to files changed since a git ref |
workspace | string[] | unset | Restrict to the named workspace packages (monorepo) |
changedWorkspaces | string | unset | Restrict to workspaces with files changed since a git ref. Cannot be used with workspace. |
explain | boolean | false | Add a _meta block with metric and rule documentation |
Dead-code options
detectDeadCode, detectCircularDependencies, and detectBoundaryViolations take the same options type, DeadCodeOptions. When you set one or more filter booleans below, the report has only those issue types. When you set none, the report has every category, as the CLI does by default.
| Field | Type | Notes |
|---|---|---|
unusedFiles / unusedExports / unusedDeps / unusedTypes | boolean | |
privateTypeLeaks | boolean | Opt-in. Private type leaks show only when you set this filter. |
unusedEnumMembers / unusedClassMembers | boolean | |
unresolvedImports / unlistedDeps | boolean | |
duplicateExports / circularDeps / boundaryViolations / staleSuppressions | boolean | |
files | string[] | Restrict to specific files (like fallow dead-code path/to/file.ts) |
includeEntryExports | boolean | Report unused exports on entry files |
findingIds | string[] | Report only the findings with these finding_id values, like fallow dead-code --finding-id. The report adds finding_id_query. See finding_id_query. Only detectDeadCode accepts it. detectCircularDependencies and detectBoundaryViolations reject it with the error code FALLOW_UNSUPPORTED_OPTION. |
typeAware | { enabled?, projects?, require? } | Type-aware TypeScript analysis: enabled (boolean), projects (string[] of tsconfig paths), and require ("best-effort" | "complete") |
Duplication options
DuplicationOptions adds these fields to AnalysisOptions:
| Field | Type | Default |
|---|---|---|
mode | "strict" | "mild" | "weak" | "semantic" | "mild" |
near | boolean | false |
minTokens | number | 50 |
minLines | number | 5 |
minOccurrences | number | 2 |
threshold | number | 0 (no limit) |
skipLocal | boolean | false |
crossLanguage | boolean | false |
ignoreImports | boolean | true |
ignoreSymlinks | boolean | unset (the project config decides) |
top | number | unset |
Each returned clone group has spread. Near-miss groups also have similarity. ignoreSymlinks: true omits clone instances whose path is a symlink or lies under a symlinked directory. Otherwise, such an instance has is_symlink: true. Duplication stats can have clone_groups_ignored and near_candidates_skipped. The report leaves out these fields when the value is zero.
Similar-code options
SimilarCodeOptions adds these fields for model-based search to
AnalysisOptions:
| Field | Type | Default |
|---|---|---|
threshold | number from 0 to 1 | config or 0.8 |
minLines | positive number | config or 3 |
top | positive number | unset |
files | string[] | unset |
Before it reads any source, the loader finds the fallow-similar-code package
of the exact matching version and verifies its native binary.
detectSimilarCode never downloads the model. To get the model, run
fallow similar-code setup --local once through the CLI, after explicit user
approval. The results are unverified. Read
Semantic similar code before you use a score to plan
a refactor.
Feature flag options
FeatureFlagsOptions adds one field to AnalysisOptions:
| Field | Type | Default |
|---|---|---|
top | number | unset |
Complexity / health options
ComplexityOptions matches the fallow health flags:
| Field | Type | Notes |
|---|---|---|
maxCyclomatic, maxCognitive | number (0 to 65535) | Per-function thresholds |
maxCrap | number | CRAP score ceiling |
top | number | Limit the number of ranked entries |
sort | "cyclomatic" | "cognitive" | "lines" | "severity" | Finding sort order (default cyclomatic) |
complexity, fileScores, coverageGaps, hotspots, targets, score | boolean | Turn on single report sections |
complexityBreakdown | boolean | Add the contributions[] breakdown of decision points to each complexity finding |
css | boolean | Add structural CSS analytics (same as --css) |
ownership, ownershipEmails | boolean + "raw" | "handle" | "anonymized" | "hash" | Ownership-aware hotspots |
effort | "low" | "medium" | "high" | Filter refactoring targets |
since, minCommits | string, number | Git history window for hotspots |
coverage, coverageRoot | string | Istanbul coverage file and its root |
typeAware | { enabled?, projects?, require? } | Type-aware TypeScript analysis, as in the dead-code options |
Structured errors
A rejected promise throws a FallowNodeError. This is a plain Error with extra fields, and it has the same structured errors as the CLI:
import type { FallowNodeError } from '@fallow-cli/fallow-node';
try {
await detectDeadCode({ root: '/does/not/exist' });
} catch (err) {
const e = err as FallowNodeError;
console.error(e.name); // "FallowNodeError"
console.error(e.message); // "analysis root does not exist: ..."
console.error(e.exitCode); // 2
console.error(e.code); // "FALLOW_INVALID_ROOT"
console.error(e.context); // "analysis.root"
console.error(e.help); // optional remediation hint
}
The exit codes are the same as in the CLI, for example 0 for success, 2 for a generic or validation error, and 7 for a network error (license and cloud operations).
When to use bindings vs the CLI
| Use the bindings | Use the CLI |
|---|---|
| You embed fallow in a long-running Node process (editor, devserver, CI runner) | You run fallow once from a shell script or GitHub Action |
| You already have a Node runtime and want to skip the cost of a subprocess and JSON parsing | You want zero runtime dependencies (native binary via npx fallow) |
| You call many analyses in sequence and do not want to initialize again each time | You want human-readable CLI output, progress bars, or interactive flags |
| You need typed access to the result envelope in TypeScript | You consume the analysis from Rust, Python, Go, or any non-Node language |
The bindings and the CLI run the same analysis engine and return the same data.
Limitations
- The bindings cover the one-shot analyses only. They do not include commands that write to the filesystem:
fix,init,hooks install,hooks uninstall,--save-baseline,license activate, andcoverage setup. detectSimilarCodeis read-only and never does the first model download. For that, runfallow similar-code setup --localthrough the CLI after explicit approval.- There is no baseline or regression support yet. For those workflows, use the CLI flags
--baselineand--save-baseline. - There is no watch mode. For incremental analysis, call the function again. The fallow per-file cache makes repeat runs fast.
- The API is new. Until it is stable, a minor version can have breaking changes.