Skip to content
Fallow home
All docs pages

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 kind discriminator on typed roots
  • schema_version
  • summary
  • relative paths
  • injected actions arrays
  • a _meta block with docs links, when explain: 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".

FunctionCLI equivalentReturns
detectDeadCode(options?)fallow dead-code --format jsonDeadCodeReport
detectCircularDependencies(options?)fallow dead-code --circular-depsDeadCodeReport filtered to the circular_dependencies category
detectBoundaryViolations(options?)fallow dead-code --boundary-violationsDeadCodeReport filtered to the boundary_violations category
detectDuplication(options?)fallow dupes --format jsonDuplicationReport
detectSimilarCode(options?)fallow similar-code --format jsonSimilarCodeReport with unverified candidates
detectFeatureFlags(options?)fallow flags --format jsonFeatureFlagsReport
computeComplexity(options?)fallow health --format jsonHealthReport
computeHealth(options?)fallow health --format jsonAlias for computeComplexity

Shared options

Every options object extends AnalysisOptions:

FieldTypeDefaultPurpose
rootstringprocess.cwd()Project root directory
configPathstringauto-discoverPath to .fallowrc.json / .fallowrc.jsonc / fallow.toml / .fallow.toml
allowRemoteExtendsbooleanfalseLet trusted config files extend HTTPS URLs (same as --allow-remote-extends)
noCachebooleanfalseSkip the extraction cache
threadsnumberCPU countNumber of threads. Must be > 0
diffFilestringunsetPath to a unified diff file for line-level scoping (same as --diff-file). Diffs from stdin work only in the CLI.
productionbooleanfalseProduction mode (excludes test/build files)
changedSincestringunsetScope analysis to files changed since a git ref
workspacestring[]unsetRestrict to the named workspace packages (monorepo)
changedWorkspacesstringunsetRestrict to workspaces with files changed since a git ref. Cannot be used with workspace.
explainbooleanfalseAdd 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.

FieldTypeNotes
unusedFiles / unusedExports / unusedDeps / unusedTypesboolean
privateTypeLeaksbooleanOpt-in. Private type leaks show only when you set this filter.
unusedEnumMembers / unusedClassMembersboolean
unresolvedImports / unlistedDepsboolean
duplicateExports / circularDeps / boundaryViolations / staleSuppressionsboolean
filesstring[]Restrict to specific files (like fallow dead-code path/to/file.ts)
includeEntryExportsbooleanReport unused exports on entry files
findingIdsstring[]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:

FieldTypeDefault
mode"strict" | "mild" | "weak" | "semantic""mild"
nearbooleanfalse
minTokensnumber50
minLinesnumber5
minOccurrencesnumber2
thresholdnumber0 (no limit)
skipLocalbooleanfalse
crossLanguagebooleanfalse
ignoreImportsbooleantrue
ignoreSymlinksbooleanunset (the project config decides)
topnumberunset

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:

FieldTypeDefault
thresholdnumber from 0 to 1config or 0.8
minLinespositive numberconfig or 3
toppositive numberunset
filesstring[]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:

FieldTypeDefault
topnumberunset

Complexity / health options

ComplexityOptions matches the fallow health flags:

FieldTypeNotes
maxCyclomatic, maxCognitivenumber (0 to 65535)Per-function thresholds
maxCrapnumberCRAP score ceiling
topnumberLimit the number of ranked entries
sort"cyclomatic" | "cognitive" | "lines" | "severity"Finding sort order (default cyclomatic)
complexity, fileScores, coverageGaps, hotspots, targets, scorebooleanTurn on single report sections
complexityBreakdownbooleanAdd the contributions[] breakdown of decision points to each complexity finding
cssbooleanAdd structural CSS analytics (same as --css)
ownership, ownershipEmailsboolean + "raw" | "handle" | "anonymized" | "hash"Ownership-aware hotspots
effort"low" | "medium" | "high"Filter refactoring targets
since, minCommitsstring, numberGit history window for hotspots
coverage, coverageRootstringIstanbul 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 bindingsUse 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 parsingYou want zero runtime dependencies (native binary via npx fallow)
You call many analyses in sequence and do not want to initialize again each timeYou want human-readable CLI output, progress bars, or interactive flags
You need typed access to the result envelope in TypeScriptYou 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, and coverage setup.
  • detectSimilarCode is read-only and never does the first model download. For that, run fallow similar-code setup --local through the CLI after explicit approval.
  • There is no baseline or regression support yet. For those workflows, use the CLI flags --baseline and --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.

See also