Skip to content

v2.76.0: --diff-file scopes every finding, schema-derive ladder lands, fallow_core deprecated

Choose a tag to compare

@BartWaardenburg BartWaardenburg released this 19 May 12:59
· 625 commits to main since this release
v2.76.0
4f2b533

Highlights

  • --diff-file now filters every finding, not just the runtime-coverage section. A single flag scopes unused-export, complexity hotspot, clone family, boundary violation, and runtime-coverage findings to lines inside an added hunk, so the JSON total_issues already matches what your PR introduces. CI pipelines previously running a downstream jq filter (filter-changed.jq) can drop it; the --diff-stdin alias accepts gh pr diff | fallow audit --diff-file -. Closes #424.

  • fallow fix knows about pnpm-catalog comment blocks. A new fix.catalog.deletePrecedingComments knob (auto default, plus always / never) deletes a contiguous YAML comment block when removing the catalog entry it documents, with two escape hatches: a # fallow-keep marker on any line and an auto-policy banner heuristic for # === React 18 pins ===-style headers. The JSON action gains an entry_line field alongside line so annotators can pick either anchor unambiguously. Closes #360.

  • fallow fix creates .fallowrc.json when no fallow config exists. Previously the duplicate-export add-to-config fix told you to run fallow init first, then re-run fallow fix --yes. Now the fixer materialises the same framework-aware scaffolding fallow init would emit, layers your ignoreExports rules on top, and skips when invoked from a monorepo subpackage (with a targeted error pointing at the workspace root). --dry-run previews the write as a unified diff in both human and JSON modes. Closes #332.

  • Architecture boundaries: opt-in allowTypeOnly lets type-only imports cross zones. { from: "featureB", allowTypeOnly: ["featureA"] } allows import type {...}, inline import { type Foo } (when every named specifier carries the type qualifier), and export type { Foo } from "..." across zones without a runtime dependency. Mixed-specifier imports still fire. Strictly additive: omitting the field preserves pre-feature behavior. Closes #365. Thanks @DrJonki.

  • Angular component complexity rollup. fallow health --complexity (and audit, combined) now emits a synthetic <component> finding per Angular component whose class function findings AND template both cleared the cyclomatic / cognitive thresholds. The rollup sums worst_class_method + template and ships a component_rollup payload so the breakdown stays visible. Component-inherited CRAP provenance also lands on synthetic <template> findings via the inverse templateUrl edge, with coverage_source: "estimated_component_inherited" and inherited_from on the wire. Closes #234. Refs #186 tier 1.

  • autoDiscover parent zones now auto-allow their discovered children. Top-level barrels (src/features/index.ts) can re-export feature modules without false-positive cross-zone violations. The Bulletproof preset gains patterns: ["src/features/**"] on the features zone so non-barrel top-level files (src/features/types.ts) classify under the parent zone and still obey the features rule. Closes #372.

  • fallow list --boundaries --format json surfaces the logical group name for autoDiscover zones. Config UIs and Sankey renderers no longer need to reconstruct the grouping from the features/<child> naming convention; a parallel logical_groups[] array carries name, children, auto_discover, status, source_zone_index, file_count, plus byte-accurate config-patch metadata. Closes #373.

Schema-derive ladder shipped

Issue #384 closes in this release. Every object-shaped --format json envelope (AuditOutput, CheckOutput, CombinedOutput, DupesOutput, HealthOutput, ExplainOutput, CoverageSetupOutput, CoverageAnalyzeOutput, ReviewEnvelopeOutput, ReviewReconcileOutput, ListBoundariesOutput) is now a variant of a typed FallowOutput enum derived from Rust source via schemars. The document-root oneOf block in docs/output-schema.json is regenerated instead of hand-maintained, and every legacy inject_*_actions JSON post-pass in report/json.rs is retired in favour of typed *Finding envelope wrappers that flatten the bare payload and carry actions: T[] natively. Wire shape is byte-identical (verified against vue-core, vite, preact, zod, next.js); the codegen-derived TS contracts in editors/vscode/src/generated/output-contract.d.ts and npm/fallow/types/output-contract.d.ts expose FallowOutput as a discriminated union, plus backwards-compat aliases for the bare names that json-schema-to-typescript dedupes away.

fallow_core deprecated for external consumers

Per ADR-008, the top-level entry points (analyze, analyze_with_usages, analyze_with_trace, analyze_retaining_modules, analyze_with_parse_result, analyze_project), the fallow_core::analyze::* detector helpers, and the feature-flag helpers (collect_feature_flags, correlate_with_dead_code) now carry #[deprecated(since = "2.76.0")] annotations pointing external consumers at fallow_cli::programmatic. Workspace path-dependency callers continue to compile via #[expect(deprecated)]. The next minor release (target 2.77.0, no earlier than 2026-Q3) will flip publish = false on fallow-core; this release is the one-cycle warning window. See docs/fallow-core-migration.md for the function-by-function map. Closes #418.

Bug fixes

  • typeof import('./path').X inside .d.ts ambient declarations now traces the target file. unplugin-auto-import's auto-imports.d.ts and unplugin-vue-components' components.d.ts no longer surface their composables and components as unused-files. Declaration files are also auto-promoted to entry points so module-augmentation interfaces like GlobalComponents skip unused-export checks (pass --include-entry-exports to opt back in). Closes #396, #397. Thanks @wouterkroes.

  • new URL('./', import.meta.url) no longer flags ./ as an unresolved import. Directory-only specifiers (./, ../, paths ending in /) are now skipped because they construct a directory URL, not a file URL. File-pointing specifiers (./worker.js, ./assets/foo.svg) are unaffected. Closes #399. Thanks @wouterkroes.

  • Standalone export default <template>...</template> in .gts files now extracts imports and the default export. The v2.75.0 multi-template fix only treated single-byte expression delimiters as expression position, so the canonical template-only-component shape fell through and oxc dropped every import in the file. Closes #379.

  • Abstract base-class methods called through a generic-constrained this.<field> are credited. A BaseService<TClient extends BaseClient> with constructor(client: TClient) and a body calling this.client.fetchLatest(id) now resolves to BaseClient.fetchLatest through the existing bound-member-access pipeline. Closes #388. Thanks @vethman.

  • Fluent-builder chains credit intermediate setters. EventBuilder.createWithDefaults().setProcessId("x").setSubject("y").build() no longer flags every setter as unused; a new propagate_fluent_chain_accesses pass walks each chain step against the resolved class export and credits is_self_returning methods reached from an is_instance_returning_static root. Closes #387. Thanks @vethman.

  • Class methods reached via a typed getter chain inside a Playwright fixture teardown are credited. Typed getter declarations now register as instance bindings, so a fixture pattern like get processEventsService() { return container.resolve(ProcessEventsService); } followed by fixtures.processEventsService.clearLast() resolves through the bound-member pipeline. Closes #386. Thanks @vethman.

  • PR comments no longer render empty when the only finding is a project-level dependency / catalog / override issue. --format pr-comment-github and --format pr-comment-gitlab previously applied FALLOW_DIFF_FILTER to every finding, including those anchored at fixed package.json / pnpm-workspace.yaml lines. Project-level rules now bypass the diff filter in the PR-comment path so the body always explains every CI-failure reason. Closes #381. Thanks @cloud-walker.

  • Runtime coverage mapping is tightened across V8 offsets, source maps, and cloud coverage. Five-fix bundle: V8 coverage offsets map as source positions, partial source-map remaps are kept, owner segments are preserved in source-map project ids, cloud coverage requires a line match before attributing a hit, and scoped source-map repo names (@scope/name) are accepted. PR #376. Thanks @M-Hassan-Raza.

  • Duplication JSON output now emits actions: [] on every nested CloneGroup inside clone_families[].groups[] and on the combined dupes block. Closes #393.

  • docs/output-schema.json regen no longer silently drops struct fields named after JSON Schema keywords. ContributorEntry.format was listed in required but missing from properties because the schema normaliser stripped the format key recursively. Closes #394.

Documentation

  • TypeScript bare-name aliases ship in the npm-published fallow/types contract. The 17 dead-code bare aliases (UnusedExport, UnusedDependency, BoundaryViolation, etc.) plus 7 dupes/health aliases now live in the generated output-contract.d.ts, so import type { UnusedExport } from "fallow/types" type-checks after upgrading. Closes #415.

  • fallow explain for the three complexity rules now covers synthetic <template> and <component> Angular findings. Prose names both shapes, enumerates the template constructs that contribute to cyclomatic and cognitive, and explains the rollup math; flows through SARIF fullDescription and the MCP fallow_explain tool. Closes #404.

  • auto_fixable is documented as per-finding, not per action type. The field_definitions block in _meta calls out per-instance evaluation explicitly and enumerates the four current per-instance flips. Closes #361.

Install

npx fallow                  # one-off
pnpm add -D fallow          # project
cargo install fallow-cli    # Rust users

Full Changelog: v2.75.0...v2.76.0