Skip to content

v3.36.0: redundant config entries, literal-membership complexity, dotenv plugin

Choose a tag to compare

@BartWaardenburg BartWaardenburg released this 25 Oct 11:47
· 148 commits to main since this release
Immutable release. Only release title and notes can be modified.
v3.36.0
3c6e45b

Features

  • Fallow reports config entries that match something but change nothing in the dead-code run. Three new workspace_diagnostics[].kind values name the config entry in pattern. entry-pattern-redundant marks an entry pattern whose matched files are all entry points from another source, such as package.json, a plugin or dynamicallyLoaded. ignore-dependencies-entry-redundant marks an ignoreDependencies entry that hid no finding. ignore-patterns-entry-redundant marks an ignorePatterns glob whose files a built-in ignore, .gitignore or the file type filter already skips. The entries show up in JSON, SARIF, the human and CI formats, the PR comments and the LSP. A --production run reports none of them. The exit codes and schema_version do not change. Thanks @Smrtnyk for the request. (#3824)

  • health.cyclomaticPolicy: "literal-membership" counts a literal membership chain as one predicate. With this policy, kind === "a" || kind === "b" || kind === "c" adds no cyclomatic increments for its || operators. Only strict equality checks of one identifier against string, number, bigint, boolean or null literals qualify. The policy applies to the maxCyclomatic gate and to every CRAP score. Findings report the policy value as cyclomatic and the standard value as standard_cyclomatic. The summary names the policy, and --complexity-breakdown marks the chain operators with literal_membership. The default stays standard, and the health score, complexity density and hotspots keep the standard count. Thanks @lebedev-nikita for the request. (#3781)

  • Fallow has a built-in dotenv plugin. Scripts that call dotenvx credit @dotenvx/dotenvx, and dotenvx run -- <command> credits the wrapped command. fallow migrate maps the dotenv plugin key. Thanks @VariableVince for the patch in #3771. (#3762)

  • Type-aware analysis has an experimental tsrs backend. Set FALLOW_TYPE_AWARE_BACKEND=tsrs to run the semantic pass on tsrs, the Rust port of the TypeScript 7 checker. Install the pinned tsrs version next to fallow-type-aware. fallow type-aware status and _meta.type_aware report the backend that ran. TypeScript-Go stays the default.

Changed

  • A file that nests too deep to parse has its own workspace diagnostic. Fallow reported such a file as source-parse-degraded, with a message about parser errors, although the file can be valid code. It is now source-nesting-too-deep, with the cap in limit. The file still counts for the parse-error gate, and its item in gate_outcomes["parse-error"].files has reason: "nesting-too-deep". A script or tool that filters on source-parse-degraded must also match source-nesting-too-deep to keep seeing these files.

  • The agent gate prints a short block message. When the gate blocks a git commit or git push, stderr now holds one line for each finding that caused the block, the verdict, and the path of the full JSON report. Under the default gate = "new-only", the list holds only the introduced findings. The full report goes to .fallow/gate-report.json in the audited tree. Set FALLOW_GATE_REPORT to write it to another path, or FALLOW_GATE_OUTPUT=json to print the full report on stderr as before. The exit codes do not change. Run fallow agent install again to update an installed gate. Thanks @ankoor7 for the report. (#3725)

  • Every platform npm package now ships a LICENSE file and a full README. The README names the platform, the package to install instead, and how to verify the binary.

  • A short TypeScript input no longer uses seconds of parse time. Such an input now stops at its syntax error at once, without gigabytes of memory. Analysis output for real projects does not change. The extraction, graph and duplication caches get a new version, so the first run after the upgrade is cold. The minimum Rust version of the Fallow crates goes from 1.96 to 1.97.

Bug fixes

  • unused-class-members credits parameter, nested, loop and instance destructures. These forms now credit Service.m without --type-aware:

    • a parameter destructure or a destructuring assignment, such as ({ m }: Service) => m() and ({ m } = service);
    • a nested destructure, such as ({ k: { m } } = holder).

    These forms now credit Service.m with and without --type-aware:

    • a for...of destructure, a parameter destructure with a default value, and a parameter annotated with typeof;
    • a destructure of an imported instance or of a class field, such as const { m } = this.service.

    A local that provably holds no class instance no longer gives credit through a shadowed module instance. (#3798)

  • fallow dead-code --type-aware no longer confirms a used member or export as unused when a shorthand name or a namespace object reads it. The check now finds shorthand destructures, shorthand object properties, and static reads of a namespace object. When the check cannot prove which exports a namespace use reads, for example console.log(ns) or ns[key], it abstains with dynamic-behavior for every export of that module. This includes the exports that a barrel re-exports. For a computed destructure key of a class instance, the check abstains with dynamic-member-access.

  • unused-exports marks every export of a namespace import as used when a destructure with a computed key reads the namespace. A computed key, such as const { [name]: value } = ns, can read any export, so all exports of ns are now used.

  • Deep nesting no longer aborts a run. A file with thousands of nested brackets, or deep generics, JSX, ternaries and long chains, could overflow the stack and stop the process. Fallow now measures the bracket depth before it parses, and it parses each file on a stack sized from its length. A file past the limit shows as a file that failed to parse, and the analysis of the other files continues. The language server and the MCP decision_surface tool now use the same large analysis stack as the CLI. The thread count of decision_surface now follows the threads option. Deeply nested stylesheets get the same protection in the CSS parser.

  • Vite projects no longer report Sass files that Vite loads without an import as unused. An @use in css.preprocessorOptions.scss.additionalData now follows the Sass partial and index rules. A Sass @use resolves through a Vite resolve.alias entry. The file that vite-plugin-vuetify reads from styles.configFile is an entry point. Thanks @Smrtnyk for the three reports. (#3819, #3820, #3821)

  • SCSS Modules no longer report Sass module members or view transition classes as unused. A namespaced call such as math.div(100px, 10), a namespaced mixin such as @include utils.baz, and the argument of ::view-transition-new(.demo) are no longer exported classes. Thanks @rothsandro for the reports. (#3839, #3840)

  • fallow health --production no longer marks tested functions as untested. The CRAP estimate, coverage gaps and runtime coverage evidence now read test reachability from a graph of the full project. Findings still come only from production files. A production health run builds this second graph, so it takes more time. Thanks @Smrtnyk for the report and the reproduction. (#3822)

  • A private class method now takes coverage from its own record. fallow health --coverage used to report a private method (#name() {}) as estimated. The V8 converter now writes a record for each private method, and Fallow uses a record with the same #name near the method. The istanbul-lib-instrument package writes no record for a private method, so its reports do not change. Thanks @Jerc92 for the report. (#3816)

  • fallow audit --gate new-only no longer reports an unchanged anonymous function as introduced when only its CRAP score changes. Audit now also matches on the cyclomatic and cognitive values before it uses line order. Thanks @Jerc92 for the report and the reproduction. (#3815)

  • Bare fallow now applies the coverage-gaps rule, as fallow health does. A rule at error now makes the human run exit 1, and gate_outcomes gets a health-coverage-gaps entry. The machine formats exit 1 on it only with --fail-on-issues or --ci. Thanks @Smrtnyk for the report. (#3823)

  • A suppression that only --production needs is no longer stale in a run without --production. A fallow-ignore-next-line unused-export comment stays out of stale-suppression when every reference to the export comes from a file that production mode excludes. The comment stays stale when a package entry that production mode keeps re-exports the export. This applies to unused-export and unused-type. Thanks @Smrtnyk for the report. (#3825)

  • Workspace imports keep their credit after a build. When package exports point into dist/src/, Fallow maps the output back to source through the rootDir and outDir of the package tsconfig. A warm cache now gives the same result as --no-cache after the build output appears or goes away. Thanks @Smrtnyk for the reports. (#3817, #3818)

  • Fallow credits more alias forms and skips the dependencies of gitignored workspace members. These cases no longer give unused, unlisted or unresolved findings:

    • an npm alias that installs a script binary, such as "@typescript/native": "npm:typescript@~7.0.2" for a tsc script;
    • a tsconfig paths alias that points into node_modules, which now credits the package of the resolved file;
    • a workspace:<name>@<range> alias, which now resolves to the aliased workspace package;
    • the dependencies of a gitignored workspace member, which Fallow no longer reports.

    Thanks @waldoluisribeiro for the npm alias report. (#3844)

  • A script that an Nx run-commands target runs is no longer an unused file when project.json has a comment. Fallow now reads project.json as JSONC. Thanks @Pajn for the report and the reproduction. (#3704)

  • fallow health counts runtime code in modules that export only types. Top-level calls, assignments, await and runtime declarations keep such a module in coverage gaps. Thanks @xiaofeilovecoding for the patch in #3578. (#3549)

  • The GitHub Action no longer fails command: audit on a default shallow checkout. On a pull_request event, the Action now fetches the smallest history that gives a merge base. fetch-depth: 0 stays the recommended setup.

  • CI scripts named outside a run value are no longer unused files. Fallow now reads ${{ matrix.<path> }} steps, actions/github-script file loads, and step env entries that hold a workspace anchor.

  • The napi-rs plugin finds a generated loader in a subdirectory. A build with napi build --cwd binding writes the loader to binding/index.cjs, and Fallow no longer reports the platform packages of the loader as unlisted dependencies.

  • Fallow parses escaped spaces in CODEOWNERS and limits the wildcards. A pattern with an escaped space, such as /docs/Security\ Release.md, now parses. On Windows, a backslash in a pattern is now an escape, as on other platforms, and no longer a path separator. A file with thousands of wildcards no longer stalls ownership lookups. Fallow rejects a pattern with more than 32 wildcards, or a file with more than 4096, with an error that names the limit.

  • The agent gate audits only the worktree for a push that cannot send the session branch. Untracked build output in the session no longer blocks a strict push from a clean linked worktree. Run fallow agent install again to update an installed gate.

  • The --fail-on-issues help text now says where the flag changes the exit code. The exit codes do not change. Thanks @Smrtnyk for the report. (#3826)

Performance

  • Long chains are now linear to analyze. Long member, await, +, JSX tag, fluent call and heritage chains used to take time or memory that grew with the square of their length. For example, 160000 nested await took 33 seconds and now take 0.04 seconds. Fallow records a receiver name only up to 64 links, which real code does not reach.

  • Deeply nested arrow functions no longer make extraction quadratic. Before, a chain of 160000 nested arrows took about 17 seconds. A function with more than 64 levels of nested functions below it now gets no runtime coverage source_hash. Real code stays far below that, so every real function keeps the same hash.

  • Public API entry resolution builds the module index one time for each package. On a large project, this removes several seconds from fallow dead-code and from the --type-aware refinement. The results do not change.

Upgrade notes

  • Several cache versions change, so the first run after the upgrade parses every file again.
  • A script or tool that filters workspace_diagnostics on source-parse-degraded must also match source-nesting-too-deep.
  • Run fallow agent install again to get the new agent gate message and the worktree push fix.
  • On Windows, a backslash in a CODEOWNERS pattern is now an escape, not a path separator.

Full Changelog: v3.35.0...v3.36.0