Repository navigation
chore(deps): update dependency fallow to v3.36.0 - #955
renovate[bot] wants to merge 1 commit into
Conversation
|
Important Review skippedBot user detected. To trigger a single review, invoke the
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
commit: |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #955 +/- ##
=======================================
Coverage 94.77% 94.77%
=======================================
Files 10 10
Lines 746 746
Branches 235 235
=======================================
Hits 707 707
Misses 36 36
Partials 3 3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
712e1e2 to
33b7b10
Compare
33b7b10 to
e065afc
Compare
e065afc to
48821ec
Compare
Note
This PR body was truncated due to platform limits.
This PR contains the following updates:
3.33.1→3.36.0Release Notes
fallow-rs/fallow (fallow)
v3.36.0Compare Source
Added
Built-in dotenv CLI plugin support.
fallow migratenow maps thedotenvplugin key. Scripts invokingdotenvxcredit@dotenvx/dotenvx,and
dotenvx run -- <command>credits the wrapped command, asvarlock run --already does. Thanks@VariableVince for the patch in
#3771.
(Closes #3762)
Experimental tsrs backend for type-aware analysis. Set
FALLOW_TYPE_AWARE_BACKEND=tsrsto run the semantic pass on tsrs, the Rust port of the TypeScript 7 checker, instead of TypeScript-Go. Install the pinnedtsrsversion next tofallow-type-aware.fallow type-aware statusand_meta.type_awarereport the backend that ran. TypeScript-Go stays the default.health.cyclomaticPolicy: "literal-membership"counts a literalmembership chain as one predicate. A chain such as
kind === "a" || kind === "b" || kind === "c"then adds no cyclomaticincrements for its
||operators. Only strict equality checks of oneidentifier against string, number, bigint, boolean, or
nullliteralsqualify. The policy applies to the
maxCyclomaticgate and to every CRAPscore. Findings report the policy value as
cyclomaticand the standardvalue as
standard_cyclomatic, the summary names the policy, and--complexity-breakdownmarks the chain operators withliteral_membership. The default staysstandard. The health score,complexity density, and hotspots keep the standard count. The parse cache
version changes because the cached breakdown has the new flag.
(Closes #3781)
Fallow reports config entries that match something but change nothing in the dead-code run. Three new kinds in
workspace_diagnostics[].kindcarry these entries, each with the config entry inpattern:entry-pattern-redundant: anentrypattern whose matched files are all runtime entry points from another source, for examplepackage.jsonfields and scripts, a plugin, an infrastructure config ordynamicallyLoaded.ignore-dependencies-entry-redundant: anignoreDependenciesentry that hid no dependency finding. A glob that matches no declared dependency keeps theignore-dependencies-glob-unmatchedkind and is not reported twice.ignore-patterns-entry-redundant: anignorePatternsglob that excluded no file that a built-in ignore,.gitignoreor the file type filter does not already skip. A glob for build output has an effect only after a build, and the message says so.The entries reach JSON, SARIF
toolConfigurationNotifications[], the stderr note in human, compact, CodeClimate andgithub-annotations, and the LSP information diagnostic on the config entry. Markdown,github-summary, the PR comments and the review summary add a separateRedundant config entriessection afterUnmatched config patterns. A--productionrun reports none of the three kinds, because that run checks fewer files and dependency sections. No exit code changes, and noschema_versionchanges, becauseworkspace_diagnostics[].kindis an open set.(Closes #3824)
Changed
Every platform npm package now ships a
LICENSEfile and a full README.This covers the
@fallow-cli/<platform>CLI binaries, the@fallow-cli/fallow-similar-code-<platform>sidecar binaries, the@fallow-cli/fallow-node-<platform>addons and@fallow-cli/fallow-node.The README names the platform and the package to install instead, and says
how the binary is verified. The
fallow-similar-codeplatform packages nowalso have a description.
A file that nests too deep to parse has its own workspace diagnostic. Fallow does not parse a file that nests brackets past the parser cap, or that needs more stack than fallow reserves. Such a file was reported as
source-parse-degraded, with a message about parser errors, although the file can be valid code. It is now reported assource-nesting-too-deep, with the cap inlimit. The message says thatfallow fixholds back its actions while the file is reachable, and thatignoreFindingstakes a generated file out of the run. The file still counts for theparse-errorgate, and its item ingate_outcomes["parse-error"].filescarries the new optionalreason: "nesting-too-deep". The human report, the GitHub Action and the GitLab template name that reason instead of "1 parser error".workspace_diagnostics[].kindis an open set, so no schema version changes, but a consumer that filters onsource-parse-degradedmust also matchsource-nesting-too-deepto keep seeing these files.Fallow now parses with Oxc 0.155 and converts coverage with
oxc_coverage_instrument0.17.0.A short TypeScript input that made the parser use seconds of time and
gigabytes of memory (oxc-project/oxc#27637) now stops at its syntax error at
once. Analysis output for real projects does not change. A string with a
lone surrogate now keeps it as
\uXXXX, so the extraction, graph andduplication caches get a new version and the first run after the upgrade
is cold. Oxc 0.155 needs Rust 1.97, so the minimum Rust version of the
Fallow crates goes from 1.96 to 1.97.
The agent gate prints a short block message. When the gate that
fallow agent installandfallow hooks install --target agentwriteblocks a
git commitorgit push, stderr now holds one line for eachfinding that caused the block, the verdict, and the path of the full JSON
report. Under the default
gate = "new-only"the list holds only theintroduced findings, so inherited findings no longer fill the context of
the agent. The full report goes to
.fallow/gate-report.jsonin theaudited tree. Set
FALLOW_GATE_REPORTto write it to another path, orFALLOW_GATE_OUTPUT=jsonto print the full report on stderr as before.The exit codes do not change. Run
fallow agent installagain to update aninstalled gate. Thanks @ankoor7 for the report.
(Closes #3725)
Fixed
fallow dead-code --type-awareno longer confirms a used class member orexport as unused when a shorthand name reads it. The type-aware check now
finds a class member that a shorthand destructure reads, for example
const { run } = service,({ run }: Service) => run()or({ run } = service), and a renamed assignment destructure, for example({ run: local } = service). It also finds an export that a shorthand objectproperty reads, for example
{ value }, and an export that a destructure ofa namespace import reads, for example
const { value } = ns. Before thisfix, a used member that only a parameter destructure or an assignment
destructure read could get a guarded
remove-class-memberfix. For anarray or loop assignment destructure, and for a nested assignment
destructure with a default value, the check now abstains with
dynamic-member-access.fallow healthcounts runtime code in modules that export only types.Top-level calls, assignments,
await, and runtime declarations keep a modulein coverage gaps. Pure type and ambient declarations remain excluded. The
extraction cache version is bumped so warm caches pick up the new facts.
Thanks @xiaofeilovecoding for the
patch in #3578.
(Closes #3549)
unused-class-memberscredits members that a parameter destructure or a destructuring assignment reads. Without--type-aware,({ m }: Service) => m()and({ m } = service)did not creditService.m, so Fallow reported a used member as unused. Fallow now credits these reads in the same way asconst { m } = service. The parameter form works for arrow functions, function declarations and methods, with renamed keys, defaults and nested type literals such as({ svc: { m } }: { svc: Service }). A rest element still marks the whole class as used.The GitHub Action no longer fails
command: auditon a default shallow checkout. On apull_requestevent, a depth-1 checkout of the merge commit has no merge base with the PR base, sofallow audit --changed-sinceexited 2 with "no merge base". The Action now fetches the smallest history that gives a merge base: the base commit, then the parents of the merge commit, then a bounded deepen. A failed fetch does not fail the job.fetch-depth: 0stays the recommended setup.The napi-rs plugin finds a generated loader in a subdirectory. A build with
napi build --cwd bindingwrites the loader tobinding/index.cjs, but thenapiconfig stays in the package manifest. Fallow read only the package directory, so it reported each platform package of the loader as an unlisted dependency. Fallow now also reads the direct subdirectories of the package, exceptnode_modules,target,dist,build,out, hidden directories, and directories with more than 32 script files. The WASI workers andbrowser.jsnext to such a loader count as generated too.CI scripts named outside a
runvalue are no longer unused files. Fallow now reads three more GitHub Actions forms. A step that runs only${{ matrix.<path> }}runs the values of that key path in the matrix of its job. Anactions/github-scriptstep loads the files after${process.env.GITHUB_WORKSPACE}/or${{ github.workspace }}/, and the files ofrequire('./path'). A stepenventry whose whole value is${{ github.action_path }}or another workspace anchor makes"$NAME/script.mjs"resolve like that anchor.fallow dead-codeno longer reports the dependencies of a gitignored workspace member. A pnpm workspace can declare a directory that CI fills with a clone of an upstream repository, for exampleviteorrolldown. Source discovery does not read the files of a gitignored member, so the run cannot prove that its dependencies are unused. The member stays a workspace for imports and catalog references.A
workspace:<name>@<range>dependency alias resolves to the aliased workspace package. With"vite": "workspace:@scope/core@*", an import ofvite/packresolves through theexportsmap of@scope/core. Before, a workspace member with the namevitetook the import, and the import was unresolved when that member had no./packexport.unused-class-memberscredits members that a nested destructure reads. Without--type-aware,({ k: { m } } = { k: service }),({ k: { m } } = holder)and({ k: { m } = service } = {})did not creditService.m, so Fallow reported a used member as unused. Theconstforms had the same gap. Fallow now follows a nested pattern into an object literal property, a default value and a static member path. The member path form readsholder.k.min the same way as the member expression, so it credits a member only when Fallow knows the class ofholder.k.fallow dead-code --type-awareno longer confirms an export as unused whena namespace object can read it. The type-aware check now credits an export
that a static read of a namespace object reads, for example
({ value } = ns),const { "value": local } = ns,function run({ value } = ns),(ns as typeof ns).value,outer.inner.valueafterexport * as inner, andm.valueafterimport m = require("./values"). When the check cannot prove which exportsa namespace use reads, for example
console.log(ns),const copy = ns,export { ns },const { ...rest } = ns,ns[key]ormodule.exports = require("./values"), it now abstains withdynamic-behaviorfor every export of that module, also the exports that abarrel re-exports. A destructure that can read a nested namespace, for
example
const { inner } = outer, abstains for the exports of the nestedmodule. A dynamic import, for example
import("./barrel"), now alsoabstains for the exports that the barrel re-exports. Before this fix, such an
export could get a
confirmed-no-static-referencesdecision.fallow dead-code --type-awareabstains for a class member that a computeddestructure key can read.
const { [key]: run } = serviceand({ [key]: run } = service)now givedynamic-member-access, asservice[key]already did. A string key, for exampleconst { "run": local } = service, now credits the member.tsconfig
pathsaliases that point intonode_modulesno longer become unlisted dependencies. Apathsentry such as"@components/*": ["./node_modules/some-theme/src/components/*"]made Fallow report@components/sharedas an unlisted dependency, because Fallow credited the specifier text and not the package that the alias resolves into. Fallow now treats each specifier that apathspattern of the nearest tsconfig matches as an alias, as TypeScript does, and credits the package of the resolved file. This is also true when a real package has the same name as the pattern. Apathsentry onto a declaration file, such as"react": ["./node_modules/@types/react"], still creditsreact. Without an install, an alias into a package directory that does not exist credits that package and is not an unresolved import.A file with deeply nested brackets no longer aborts the run. A source with thousands of nested
(,[,{or template substitutions overflowed the parser stack, and the stack overflow killed the wholefallowprocess. Fallow now measures the nesting before it parses a file. A file past the limit shows as a file that failed to parse,fallow dupesfinds no clones in it, and the analysis of the other files continues. Brackets inside strings, comments and template text do not count. The extraction and duplication cache versions change, so the first run after the upgrade parses every file again. OSS-Fuzz found this.unused-class-membersno longer reports a used member as unused when one of these destructure forms is its only read. This applies with and without--type-aware. In each example,Service.mis now used:for...ofdestructure, such asfor (const { k: { m } } of [{ k: service }]),for ({ m } of [service]),for (const { m } of services)withservices: Service[]orfor (const { m } of [...services]).function run({ m } = service)orfunction run({ k: { m } } = { k: service }).typeof, such as({ k: { m } }: typeof holder)or({ k: { m } }: { k: typeof service }).{ get k() { return service } }or{ ...holder }.unused-class-membersno longer credits a class through a local shadow. Without--type-aware, a module instanceconst service = new Service()gave credit toService.mforservice.m,service['m'],const { m } = serviceand({ m }: typeof service)inside a function that declares its ownservice. Fallow then missed an unused member. Fallow now ignores this credit when the nestedserviceprovably holds no class instance: an object literal of functions and primitives, a primitive or function value, or a parameter whose type annotation is a primitive, literal or function type. A type literal or a named type gives no proof, because a class instance can satisfy it. A class instance that the nested scope declares keeps its credit.(Closes #3798)
unused-class-membersno longer reports a used member as unused when a destructure of an imported instance or of a class field is its only read. This applies with and without--type-aware. In each example,Service.mis now used:const { m } = serviceafterimport { service } from './service'.const { m } = this.servicein a class withconstructor(private service: Service).typeofforms of these destructures, such as({ m } = service),run({ m } = this.service)and({ m }: typeof service) => m().unused-exportsno longer reports the exports of a namespace import as unused when a destructure with a computed key reads it. A computed key, such asconst { [name]: value } = ns, can read any export, so all exports ofnsare now used.The language server and the MCP
decision_surfacetool no longer abort on nesting that the CLI survives. Both ran analysis on threads with a 2 MiB stack, so a deeply nested file overflowed that stack and stopped the process. The language server now gives its analysis threads the 16 MiB stack of the CLI workers. Its Tokio runtime uses two worker threads, because analysis runs on the blocking threads and the Rayon pool.decision_surfaceandrun_decision_surfacenow parse on the analysis worker pool, as the other tools do. Their thread count now follows thethreadsoption.Deep nesting without brackets no longer aborts a run. TypeScript generics, JSX elements,
class extends, ternaries, arrows, assignment chains and long member or call chains could overflow the stack, and the whole process stopped. Each parse of a source file now runs on a stack that is sized from the length of the file. A file up to about 15 KB still parses on the analysis worker. A larger file parses on its own thread with a stack of that size. This applies to every analysis that parses a file: dead code, duplication, health, similar code, mutation testing and the config file plugins.Deeply nested arrow functions no longer make extraction quadratic. Each function hashed its whole source text for runtime coverage, so a chain of nested arrows took time that grew with the square of its length. 160000 levels took about 17 seconds. A function with more than 64 levels of nested functions below it now gets no
source_hash. Real code stays far below that, so every real function keeps the same hash. The coverage inventory also computes columns in linear time for nested functions on one line, and it no longer sends the hash of empty input for a span that it cannot read.A very large file no longer parses on a stack that is too small. The parse stack cap is now 64 GiB on 64-bit targets, which a source over 64 MB reaches in a release build. A file that needs more stack is reported as a failed parse, like a file with too deeply nested brackets. Before, such a file got a capped stack and could stop the run.
A deeply nested stylesheet no longer risks a stack overflow in the CSS parser. CSS Modules class extraction,
fallow health --cssanalytics and color parsing now check the bracket depth before they call the CSS parser, and they run the parse on a stack sized for the stylesheet. A stylesheet past the limit falls back to the plain class scan, or is left out of the CSS analytics. The depth check now reads a stylesheet with CSS rules: a//insideurl(//host/a.png), a CSS escape or an unquotedurl()body no longer hides the rest of a minified file. The extraction cache version changes, so the first run after the upgrade parses every file again. OSS-Fuzz found this.Bare
fallownow applies thecoverage-gapsrule, asfallow healthdoes. Before, the combined run did not compute coverage gaps, so acoverage-gapsrule aterrornever failed it. Now an enabled rule shows the gaps in the health section, and a rule aterrormakes the human run exit 1. The JSONgate_outcomesobject gets ahealth-coverage-gapsentry. Like the other findings rules of the combined run, the machine formats exit 1 on it only with--fail-on-issuesor--ci. Thanks @Smrtnyk for the report. (Closes #3823)A suppression that only
--productionneeds is no longer reported stalein a run without
--production. Production mode excludes test files. Anexport that only a test file imports is unused there, so its
fallow-ignore-next-line unused-exportcomment is necessary. A run without--productioncredits the test import and does not use the comment. Thatrun now keeps the comment out of
stale-suppressionwhen every reference tothe 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-exportandunused-type. Thanks@Smrtnyk for the report.
(Closes #3825)
Vite projects no longer report Sass files that Vite loads without an import as unused.
Three cases caused false findings. An
@useincss.preprocessorOptions.scss.additionalDatanow credits the file that Sassloads when the specifier has no extension, leaves out the
_of a partial, ornames a directory with an
_indexorindexfile (#3819). An@usein a Sassfile now resolves through a Vite
resolve.aliasentry, with the Sass partialand directory-index rules. Before, Fallow reported the import as unresolved
and the target partial as unused (#3820). The Sass settings file that
vite-plugin-vuetifyreads fromstyles.configFileis now an entry. Fallowreads the path relative to the Vite
root(#3821). Thanks@Smrtnyk for the three reports.
(Closes #3819, #3820, #3821)
A private class method now takes coverage from its own
#namerecord.Before,
fallow health --coveragealways reported a private method(
#name() {}) asestimated, also when the coverage data had a record forthat method. Fallow now uses a record that has the same
#namenear themethod. A record at the exact position of the method is used first. Near
the method, the record must be the only one with that name. Fallow does not
give a private method the coverage of the function that holds it or of a
public method. istanbul-lib-instrument writes no record for a private
method, so its reports do not change.
Thanks @Jerc92 for the report.
(Refs #3816)
A script that an Nx
run-commandstarget runs is no longer reported as an unused file whenproject.jsoncontains a comment.Fallow now reads
project.jsonas JSONC, as Nx does. Line comments, blockcomments and trailing commas no longer hide the target commands. Thanks to
@Pajn for the report and the clear reproduction.
(Closes #3704)
fallow audit --gate new-onlyno longer reports an unchanged anonymous function as introduced when only its CRAP score changes. When a file has several anonymous functions, audit matches each head finding to a base finding. Before this fix, a CRAP change from estimated to measured coverage stopped the unchanged function from matching its own base finding. The line-order fallback then paired it with a different, smaller function, and audit reported it as introduced. Audit now also matches on the cyclomatic and cognitive values without the CRAP score before it uses line order. The matched pair still compares the CRAP score. Thanks @Jerc92 for the report and the reproduction.(Closes #3815)
A workspace import now keeps its credit when the package
exportspoint intodist/src/and the build output exists. A package with tsconfigrootDir: "./"andoutDir: "./dist"emitssrc/index.tsasdist/src/index.js. Fallow now uses therootDirandoutDirof the package tsconfig to map that output back tosrc/index.ts. Before, the imported exports were reported as unused after a build. Thanks @Smrtnyk for the report. (Closes #3817)A warm cache now gives the same result as
--no-cacheafter the build output of a workspace package appears or goes away. The graph cache key now includes whether each static entry path in the project and workspacepackage.jsonfiles exists on disk. Thanks @Smrtnyk for the report. (Closes #3818)fallow health --productionno longer marks tested functions as untested in the CRAP estimate.Production mode keeps test files out of the analysis. The static coverage
estimate used the same file set, so it found no tests. Each function with a
cyclomatic complexity of 6 or more then failed the default
maxCrapof 30.Coverage gaps also reported tested files and exports as untested. Fallow now
reads test reachability from a graph of the full project, and findings still
come only from production files. A production run now gives the same CRAP
scores and coverage gaps as a full run for the files that it analyzes. A
production health run builds this second graph, so it takes more time.
Thanks @Smrtnyk for the report and the
reproduction. (Closes #3822)
The
--fail-on-issueshelp text now says where the flag changes the exit code.Bare
fallowin a json, sarif or codeclimate format andfallow dupesexit0 on issues unless
--fail-on-issuesor--ciis set. The human, compactand markdown formats of a bare run, and the standalone analysis commands,
already exit 1 on error-severity issues. The exit codes do not change.
Thanks @Smrtnyk for the report.
(Refs #3826)
fallow health --production --runtime-coverageno longer reports tested functions asnot_covered.The fix for #3822 gave the CRAP estimate and the coverage gaps the test files
in production mode. The runtime coverage evidence still used the production
graph, which has no test files, so
evidence.test_coveragewasnot_coveredfor each function. The runtime coverage evidence now readstest reachability from the same full-project graph. A production health run
builds this graph one time and uses it for file scores and for runtime
coverage. A production run with runtime coverage and without file scores
(for example
--complexitywithmaxCrap: 0) now builds this graph too, soit takes more time.
An executed private class method now gets its measured coverage from native V8 coverage.
Before,
fallow health --coverage <NODE_V8_COVERAGE dir>reported a privatemethod (
#name() {}) asestimatedand gave it a CRAP finding, also whenNode ran every path of the method. Its public twin with the same body was
measured and had no finding. The V8 converter now writes a record for each
private method (
#name,get #name,set #name) with its own call count,and Fallow uses that record. A private method that did not run stays at 0%.
Thanks @Jerc92 for the report.
(Closes #3816)
A script binary now credits the npm alias that installs it. A
dependency such as
"@typescript/native": "npm:typescript@~7.0.2"providesthe binaries of
typescript, so atscscript no longer reports the aliasas unused. When the same manifest also declares the aliased package under
its own name, that declaration provides the binary, and an alias that
nothing else uses is still reported. A plain declaration in another
workspace does not change the result. Thanks
@waldoluisribeiro for the report. (Closes #3844)
SCSS Modules no longer report Sass module members as unused classes.
A namespaced function call such as
math.div(100px, 10)orutils.bar(),and a namespaced mixin such as
@include utils.baz, gave a falseunused-exportfordiv,barandbaz. The class scan now skips a.memberthat follows a namespace and comes before(, and the mixin nameafter
@include. In.module.sassfiles, the scan also skips the mixinname in the
+utils.bazinclude shorthand. A compound selector such asbutton.primarystillexports
primary. The extraction cache version changes, so the first runafter the upgrade parses each file again.
(Closes #3839)
CSS Modules no longer report view transition class names as unused
classes. In
::view-transition-new(.demo)and the other::view-transition-*()pseudo-elements,.demois a view transition classand not an element class. The SCSS class scan exported it, and fallow then
gave a false
unused-exportfordemo. The class scan now skips theargument of these pseudo-elements. The extraction cache version changes, so
the first run after the upgrade parses each file again.
(Closes #3840)
The agent gate audits only the worktree for a push that cannot send the
session branch. A strict
git pushfrom a linked worktree of the sessionrepository also audited the session tree, because linked worktrees share
refs. Untracked build output in the session thus blocked every push from a
clean worktree. The gate now audits only the worktree when git shows that
the push cannot send the session branch: the options are only
-u,-q,--set-upstream,--force-with-leaseand--force-if-includes, eachrefspec source is the checked-out branch of the worktree and not the
session branch, and a push without a refspec has
push.defaultunset orsimple,current,upstreamornothingand no remotepushormirrorsetting. Any other push from a linked worktree, and any git errorduring the check, keeps the session audit. The strict push form now also
accepts
--force-if-includes. Runfallow agent installagain to update aninstalled gate.
A CODEOWNERS file with thousands of wildcards no longer stalls ownership lookups.
All CODEOWNERS rules match in one automaton, and its lookup time grows with
the number of wildcards (
*,?,[,{). A 4 KB file with one*a*a*a…pattern took seconds for each lookup. Fallow now rejects apattern with more than 32 wildcards, or a file with more than 4096, with an
error that names the limit. Large public CODEOWNERS files use fewer than
400. OSS-Fuzz found this.
A CODEOWNERS pattern with an escaped space now parses.
GitLab writes a space in a path as
\, for example/docs/Security\ Release.md @owner. Before, Fallow split the line at thatspace, and
--group-by ownerand--group-by sectionfailed with"dangling ''". GitLab's own CODEOWNERS file failed in this way. On Windows, a
backslash in a pattern is now an escape, as on other platforms, and no
longer a path separator. Thus
\and\#now work on Windows too.Performance
Very long member chains extract much faster. Extraction built the dotted name of a member chain again at each link, and each build copied the name once per link. A file with a chain of 4000 links (
a.b.b.b...) took several seconds to extract. Fallow now builds each name in one pass, so the same file takes a fraction of a second. Real code does not have chains this long, but generated or hostile input can. The extraction output does not change.Public API entry resolution no longer resolves every module path again for each missed package entry.
A package entry that exists on disk but is not in the module graph, such as
an extensionless
binscript, made Fallow resolve the real path of eachmodule in the package. Fallow did this again for each candidate path that it
probed. Fallow now builds the canonical module index one time for each
package and resolves each parent directory one time. On a large project,
this removes several seconds from
fallow dead-codeand from the--type-awarerefinement. The results do not change.A very long member chain no longer makes extraction slow and large. For a chain such as
a.b.b.bwith thousands of links, each link recorded the full name of its receiver, so the time and the cached output grew with the square of the chain length. Fallow now records a receiver name only up to 64 links. A link deeper than that records no member access. A fluent builder chain such asnew Builder().a().b()now records each step name once, so a long chain keeps the credit for all of its steps, and a chain that repeats a method stays small. The extraction cache version changes, so the first run after the upgrade parses every file again. OSS-Fuzz found this.Long member,
await,+, JSX tag and fluent call chains are now linearto analyze. A file with a chain of many thousand links took minutes, or
used gigabytes of memory, because several path builders copied the whole
path at each link or scanned a growing list at each step. For example,
160000 nested
awaittook 33 seconds and now take 0.04 seconds, and afluent chain of 4000 calls with different method names used 700 MB. A
fluent chain now gives one fact for the chain instead of one fact for each
call. A taint source path or a JSX tag receiver longer than 64 links is not
recorded, the same limit that member access names use. The cache version
changes, so the first run after the upgrade parses all files again.
Long heritage names and long dotted component tags in Astro and MDX files
are now linear to analyze. Each level of
interface X extends a.b.c...or<a.b.c... />recorded the full name of its receiver, so the time and theoutput grew with the square of the name length. For example, an interface
heritage name of 20000 links took 110 seconds and now takes 0.08 seconds. A
receiver longer than 64 links is not recorded, the same limit that member
access names use. The names of
class C extends a.b.c...andimplements A.B.C...are now built once, instead of a copy of the name ateach link. The cache version changes, so the first run after the upgrade
parses all files again.
v3.35.0Compare Source
Added
fallow health --cssreads server-side template files as class references. Before, a class that only a Shopify.liquidsection applied was reported as anunreferenced-css-class. Fallow now reads files with the extensionsliquid,njk,hbs,handlebars,twig, andblade.php, and any class-like word in them counts as used. Templates are more lenient than Markdown: the whole text of a template counts, so a common word such ashiddenortitlein a template also credits that class, while a Markdown file counts only when it has dynamic class syntax. A class prefix before a{{echo, as ingrid--{{ columns }}, also counts.{{is a shared interpolation marker, so it also applies to.vue,.html,.mdand.astrosources with dynamic class syntax, and these can report fewer classes too. The new top-levelmarkupExtensionskey adds other template extensions, for example["tpl", "erb"]. A value that ends in a script, stylesheet, data or asset extension, such asmodule.cssord.ts, is a config error. The walk honors.gitignore,ignorePatternsand--max-file-size. These files do not join source discovery, so they never become unused files. Thanks @james2doyle for the report. (Closes #3917)The npm package now ships
rule-pack-schema.json. A rule pack can point$schemaat./node_modules/fallow/rule-pack-schema.jsonand get a schema that matches the installed Fallow version. The package also exports the file asfallow/rule-pack-schema.json. Thanks @hckhanh for the report and @tahodev for the change. (Closes #3851)An interrupted
fallow mutationrun keeps its finished results, and--resumecontinues it. Each run writes the killed and the retry-confirmed survived mutants to.fallow/mutation/journal.jsonlwhen each result is final, also with--no-cache.fallow mutation --resumereuses these results and runs only the other mutants. A journal of other inputs exits 2 withFALLOW_MUTATION_RESUME_MISMATCHand names each input that differs. A resumed mutant hasresumed: true, and the report has aresumeobject. A complete run removes the journal, and a partial run keeps it. (Closes #3783)Changed
A bare machine run that exits 0 on a failing gate now says so on stderr. Bare
fallowin--format json,sarif,codeclimate, the GitHub formats and the comment and review formats exits 0 on findings, whilefallow dead-code --format jsonexits 1 on the same findings. Before, nothing showed why. Now such a run prints one stderr line, also under--quiet, that names each failing findings gate and the flag that enforces it:[W] Exit code 0: gate error-severity-findings (2 at or above error) failed, but bare fallow does not enforce it in --format json. To exit 1 on it, pass --fail-on-issues.The exit code and stdout do not change. Thanks @Smrtnyk for the report. (Closes #3826)The exit-code contract no longer says that exit 0 means a clean run.
fallow schema(exit_codes), the fallow skill,AGENTS.mdand the npm README said that 0 means no error-severity findings. For barefallowin a machine format, that was false. They now say that 0 means no enforced gate failed, that 1 means findings and not a tool failure, and that a bare machine run gates on findings only with--fail-on-issues. To see a failing gate that did not set the exit code, readgate_outcomes.fallow mutationgives no mutant for a pure condition whose only effect is a logging call. Anifstatement such asif (count > 0) { console.info("Items", count); }gave mutants for its test that could only change which message was logged. The collector already skips eachconsole.*anddebug(...)call, and it now also skips such anifstatement. The test must be pure, and each branch must hold only logging calls with pure arguments. Anelse ifchain must meet the same conditions. The callee must be the globalconsoleordebug: a file that declares or imports one of these names keeps the conditions that call it. A condition with a call, an assignment or another statement in a branch keeps its mutants. (Closes #3719)Fixed
thin-wrapperandduplicate-prop-shapeset toerrornow fail the gates. These rules count in issue totals, the dead-code and audit gates, and CI reports when their severity iserror. Warnings stay advisory, also with--fail-on-issues. Per-file overrides and saved baselines use the component that owns each finding. Thanks @luciocabrera for the report. (Closes #3867)A CommonJS
postcss.config.cjsno longer reports itsmodule.exportskeys as unused exports. Since 3.35.0, Fallow credited only thedefaultexport of a PostCSS config. A tool that loads a CommonJS file readsmodule.exportsas its default export. When a plugin credits onlydefaultfor a CommonJS module, Fallow now credits every key ofmodule.exports. This applies to the CommonJS config files of all plugins that read the default export. UserignoreExportsrules do not change. Thanks @waldoluisribeiro for the report. (Closes #3876)fallow securityresolves a string enum member in the same module. It now reads the member like aconststring. A template literal such as`<meta name="${MetaName.BUILD_NUMBER}">`that goes to an HTML sink is no longer reported as non-literal HTML. Enums and constants that you import from another module stay unresolved, because sink extraction reads one module at a time. The extraction cache version changes, so the first run after the update parses all files again. Thanks @Smrtnyk for the report. (Closes #3854)Component export queries no longer stop type-aware refinement for the whole run. Vue, Astro, Svelte and MDX query files receive an
unsupported-syntaxabstention before the semantic backend opens them. Supported TypeScript and JavaScript queries continue to resolve in the same batch. (Closes #3763)fallow dead-codefollowschild_process.forktargets built from the module directory. A file thatfork(path.join(path.dirname(fileURLToPath(import.meta.url)), "child.ts"))starts is no longer an unused file. Fallow also readspath.resolve,__dirname,import.meta.dirnameand a constant that holds the module directory. A target with a computed segment stays unresolved. Thanks @VariableVince for the report. (Closes #3919)Config values in a same-file constant are now read. A config can keep a
part of its value in a top-level constant, for example
const UNIT = { test: { setupFiles: [...] } }withprojects: [UNIT], orsetupFiles: SETUPandtest: TEST. Fallow read only inline values, so itreported the setup files of such a Vitest project as unused files. The config
parser now uses the value of the constant in each property value and array
element that names it. This applies to all plugins that read config files. A
constant that the file declares more than one time or writes to stays
unresolved. Thanks @Smrtnyk for the report.
(Closes #3852)
fallow dead-codeno longer reports a class member as unused when an array pattern or a held getter is its only read. Without--type-aware,unused-class-membersmissed two destructure forms. The first is an array pattern around an object pattern, such asconst [{ m }] = [service]orfor (const [{ m }] of [[service]]). The second is a getter on an object literal that a variable holds, such asconst src = { get k() { return service } }with a latersrc.k.mread or{ k: { m } } = srcdestructure. Each form now credits the member. An array element after a spread, or a getter with more than one statement, gives no credit. The extraction cache version is 386. (Closes #3799)Package scripts and CI steps that run a local shell helper now credit the commands of that helper. For
bash scripts/build.shor./scripts/release.sh, Fallow reads the helper without running it. A binary that the helper runs makes its package used, and a file that the helper runs withtsxornodebecomes an entry. The helper path resolves in the package directory or in theworking-directoryof the CI step, and it must stay inside the project. Fallow follows nested helpers and stops at a cycle. It follows acdto a literal directory or to the helper directory (cd "$(dirname "$0")/.."). After anothercd, the remaining commands of the helper credit packages but do not add entries. Thanks @VariableVince for the report in #3918. (Closes #3886)fallow dead-codenow credits a store member that a helper reads through a store alias declared in a function body.unused-store-memberresolvedtype X = ReturnType<typeof useXStore>only at the top level of a module or through an import. An alias in a function body or a block now credits the store member for a parameter in the same scope. An inner type of the same name for another type still shadows the alias. A local non-store type alias also shadows a module-level store alias of the same name, so a read through the local type credits no store member. A parameter type resolves in the scope outside the function body, the same as in TypeScript. The extraction cache version is 386. (Closes #3550)Deeply nested defaults in an object pattern no longer make extraction hang. In a declaration such as
const { k: { k: { m } = s } = s } = s, each default walked its left pattern again against the fallback. The work doubled at each level of nesting, so a short file could stop the analysis. A step budget now bounds the walk of one declaration.fallow dead-codenow credits Expo config plugins that come from a local array binding. An app config can hold itspluginslist in a top-levelconstorlet, such asconst plugins = [...]; export default { plugins }. Fallow now reads that list, its spreads of array literals or other stable bindings, and entries or tuples that a binding holds. Apush,unshiftorsplicecall on the list adds its arguments, also inside a top-level block orifstatement. Anexpoobject with a type assertion, such asexpo: { ... } satisfies ExpoConfig, is read too. Fallow credits nothing from a binding that the config reassigns, from a name that the config declares more than once, or from a dynamic value. (Closes #3883)--production --type-awareno longer credits an export that only a test file uses. A type-aware reference from a file outside the analysis scope, such as a test file that production mode excludes, no longer removes a dead-code finding. Thanks @Smrtnyk for the report. (Closes #3869)Audit cleanup preserves unrelated Git worktree registrations. Removing legacy
Fallow snapshots keeps user worktrees registered when their directories are
temporarily unavailable.
(Closes #3884)
Astro extraction avoids repeated scans of malformed script and style tags. Quoted attributes retain their original source offsets and Unicode tag matching while incomplete tags no longer cause quadratic region scanning.
CODEOWNERS lookups avoid excessive memory use and slow matching with long patterns. Pattern batches bound regex fallback costs while preserving last-match precedence and GitLab section behavior.
Vue and Angular template extraction avoids repeated scans of incomplete tags. Recovery from an incomplete template tag or component interpolation no longer scans the same suffix again. Recovered references and source offsets stay the same.
MDX extraction avoids repeated parser runs on unfinished imports and exports. A multiline import or export that does not end no longer makes the parser try again for each line, and blank continuation lines are skipped.
Type-aware refinement on the
tsrsbackend no longer fails on public APIs with many non-generic interfaces. The semantic sidecar asked the checker for the type arguments of every type in the public API walk. Ontsrs, each call on a type that is not a type reference panicked and wrote the panic to stderr. With enough such interfaces, stderr went past its 16384-byte limit and Fallow dropped the type-aware result. The sidecar now asks for type arguments only on type references, sotsrsgives the same result astypescript-go. Thanks to @Smrtnyk for the report and the reproduction. (Closes #3872)Programmatic and MCP audits no longer show their base snapshot in
git worktree list.The API, the MCP
audittool, and the decision surface now remove the Gitregistration of the base snapshot before the base analysis starts, as
fallow auditdoes. They also remove the snapshots that a stopped processleft behind. A snapshot stays when its owner process can still run, when
Fallow cannot inspect that process, or when the snapshot is younger than one
minute.
(Closes #3885)
fallow dead-codeno longer reports the default export of a local OxlintjsPluginsfile. Oxlint loads the default export of each local plugin file. WithincludeEntryExports, fallow reported this export as unused. Fallow now credits the default export, as it does for the Oxlint config file. Other exports of the plugin file stay reportable. Thanks to @Smrtnyk for the report.(Closes #3871)
fallow dead-codeno longer reportstsrsandfallow-type-awareas unused devDependencies. Fallow loads these type-aware backend packages at run time, so no script or import names them. The tooling catalogue now credits both packages by name. Thanks @Smrtnyk for the report. (Closes #3873)fallow dead-codecredits the packages in tsconfigcontentMappers. TypeScript 7.1 loads eachcontentMappers[].packageto map files such as.vueor.astrointo the program. Fallow did not read this field, so it reported the mapper package as an unused dependency. The TypeScript plugin now credits the package name. Relative and absolute paths credit no dependency. Thanks to @Smrtnyk for the report.(Closes #3870)
fallow securityfollows per-path overrides for its dead-code note. A sink in a file where an override setsunused-filesorunused-exportstooffno longer shows the "also reported as unused-file" or "unused-export" note, and no longer gets the delete or remove action. Thanks to @Smrtnyk for the report.(Closes #3853)
fallow securityno longer reports module-relative paths as path-traversal candidates. A path that starts fromimport.meta.url,import.meta.dirname,import.meta.filename,__dirname,__filenameorprocess.cwd()and adds only literal segments cannot carry caller input. Examples arepath.resolve(fileURLToPath(new URL(".", import.meta.url)), "..")andfs.readFileSync(path.join(__dirname, "template.html")). A module-scopeconstthat holds such a path is also trusted. The rule applies to every sink argument, sores.sendFile(path.join(__dirname, "index.html"))is also clear.fileURLToPath(value)andnew URL(value, import.meta.url)stay candidates whenvalueis not known.(Closes #3711)
fallow securitynow checks everypath.joinandpath.resolvesegment. Before, the path-traversal matcher checked only the first argument, sopath.resolve(".", input)andpath.join(".", input)gave no candidate. The matcher now checks each argument and reports one candidate for each call. Apath.relativecontainment guard on the result still clears the call.(Closes #3712)
fallow health --csscredits classes that plain.jsand.tsDOM code applies. Before, a class was reported asunreferenced-css-classwhen only a.js,.ts,.mjsor.cjsfile applied it. Fallow now reads the values ofel.className = ...,el.classList.add / remove / toggle / replace / contains(...),el.setAttribute('class', ...)andclass:orclassName:object properties. A template value such as`panel-${kind}`credits itspanel-prefix. Other strings in these files still do not credit a class. A.jsfile that holds JSX is now scanned the same as a.jsxfile. Thanks to @osazemeu for the report and the clear reproduction.(Closes #3865)
fallow mutationnow runs Vitest projects in ahappy-domorjsdomenvironment when the cache is outside the project. A client test environment loads the setup file of the mutation runtime through Vite. Vite refused that file whenFALLOW_CACHE_DIRwas outside the project, so the project failed its baseline withCannot find module '/@fs/.../vitest-setup.mjs'. The Vitest adapter now adds the companion directory toserver.fs.allowof the root server and of each inline project.(Closes #3793)
fallow dead-codecredits a member access on the declared return type of an instance method. A call such asmanager.current()?.onDown?.(), wherecurrent(): Tool | undefined, now creditsTool.onDown. The implementerSelectTool.onDownis no longer reported as an unused class member. A class return type, such asmanager.widget().paint()withwidget(): Widget, now creditsWidget.paint. Static, private, and method-generic return types give no credit. Thanks to @Smrtnyk for the report.(Closes #3868)
fallow dead-codeno longer reports an aliased asset import as an unlisted dependency when the alias is in a referenced tsconfig. A solution-style roottsconfig.jsoncan reference a config whoseincludestarts with./, for example `"./app/javascript/*Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.