Repository navigation
chore(deps): update dependency fallow to v3.35.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
Note
This PR body was truncated due to platform limits.
This PR contains the following updates:
3.33.1→3.35.0Release Notes
fallow-rs/fallow (fallow)
v3.35.0Compare Source
Added
Each release now publishes a CycloneDX SBOM for every target. The files
are named
fallow-<target>.cdx.json..vex/openvex.jsonstates why fallowis not affected by each advisory that the repository ignores.
SECURITY.mdnow lists the supported versions and the code scanning policy.
A human
fallow mutationrun now shows phase progress on stderr. Thelines name the runner setup, the baseline run, the mutant runs with
done and total counts, the retries, and the final worktree check and
report. A long phase prints a line at a fixed interval, also in captured
non-TTY output, so a slow run does not look stalled.
--quietand--format jsonprint no progress lines, and stdout does not change. Theprogrammatic API has
run_mutation_with_progresswith theMutationProgressevents.(Closes #3599)
fallow mutation --runner-config <path>selects the Vitest config ofa run. A project that keeps its integration, UI or browser tests in a
separate Vitest config can now run mutants against those tests. The
mutation.runnerConfigconfig key sets the same file, and the flagwins. The file must exist inside the project root, and only the Vitest
runner accepts it. A static test file that the selected config does not
include stays visible as a
test-files-not-discoverederror.--configstill selects the Fallow config.
(Closes #3598)
fallow mutation --format jsonkeeps the history of a retried mutant.A retried mutant result has a new
attemptsarray with the first run andthe retry. Each entry has the status and the duration of that run, and the
killing test, the failure kind (
assertion,errorortimeout) or theruntime-error reason when the run has one. A
timeout -> survivedresultand a
survived -> killedresult are bothflaky, and the saved reportnow shows which history each one has. The
status,duration_msandretriedfields do not change.(Closes #3656)
fallow auditreports inherited complexity findings that got worse. Acomplexity finding that is in the base snapshot is now
worsenedwhen ameasured value is higher than at the base. The measured values are
cyclomatic, cognitive, CRAP (when both sides have a score) and the line
count of the function. The line count must grow by at least 5 lines. The
line count includes comments and blank lines. The minimum keeps a finding inherited after a short comment or a formatter
rewrap. A function that grows from 80 to 300 lines with the same complexity
values is now
worsened, not inherited.The JSON output adds these fields:
attributionon each complexity finding:introduced,worsenedorinherited.worsened_metricson each complexity finding with a grown value: themetric, the base value and the head value.
attribution.worsened: the active mode.attribution.complexity_worsened: the number of worsened findings.The human, compact and markdown output add a "Worse than base" section.
SARIF puts the values in the result property bag. CodeClimate descriptions
name them. The GitHub summary marks each finding as
worsened.The
--worsenedflag and theaudit.worsenedconfig key select the effect:warn(default): a worsened finding makes the verdict at leastwarn.The exit code stays 0.
error: a worsened finding counts like an introduced finding.off: a finding is worsened only when a metric that it exceeds grew.Growth only in other metrics stays
inherited.The GitHub Action takes a
worsenedinput. The GitLab template takesFALLOW_AUDIT_WORSENED. The MCPaudittool takes aworsenedparameter.Mutation JSON now shows the edit of each mutant. Each mutant in the
mutationreport and themutation-plandocument has achangeobject,for example
{"from": "??", "to": "&&"}. The values come from a fixedvocabulary of operator tokens, literals and placeholders such as
<expression>and<string>, so the report still holds no source text.(#3658)
fallow mutationnow mutates fluent chains in module-levelconstinitializers. The new
removed-chain-linkoperator removes one call linkof a chain, so
z.number().int().positive()gives a mutant without.int()and a mutant without.positive(). A validation schema that has noliteral or operator now has mutants. The operator is on by default and
--operatorsand themutation.operatorsconfig accept its name.(#3662)
The VS Code extension has a
fallow.audit.worsenedsetting. Thesetting selects how an inherited complexity finding that got worse affects
the audit verdict in the editor. The values are
warn,errorandoff,the same as
fallow audit --worsened. The defaultautousesaudit.worsenedfrom the project config. The extension sends the flag onlyto a CLI that supports it. With an older CLI, the audit runs without the
flag and the output channel shows a note.
External plugins can opt a config pattern out of the
defaultexportcredit. List the pattern in
configDefaultExportOptOutwhen the toolreads named exports, or when the pattern also matches ordinary source
files.
A dead-code baseline now records whether
node_modulesexisted. A--save-baselinerun writesanalysis_identity.node_modulesaspresentor
missing. A later run in the other install state adds abaseline-install-state-mismatchentry toworkspace_diagnosticsandprints a warning, also under
--quiet. Findings that need installedpackages can differ between the two states, so the run can report baseline
findings as new. Save the baseline again once to get this check. A baseline
without the key loads as before.
Fallow reads the entry files of a JavaScript GitHub Action. When
action.ymloraction.yamlsetsruns.usingto anoderuntime, theruns.pre,runs.mainandruns.postfiles are entry points. A path indist/maps to its source file with the same rules as apackage.jsonmainfield. A composite or Docker action adds no entry points.Fallow now detects Egg.js and tegg projects. The new
eggpluginactivates on
egg,@eggjs/coreor@eggjs/tegg, or on anegg,eggPluginoreggModulekey inpackage.json. It marks the files thatthe Egg loader loads by convention as entry points:
app.js,agent.js,the router, controllers, services, middleware, extensions, schedules and
config files, also in nested example and test fixture apps. It marks every
file of a tegg module as an entry point. It credits the packages that
config/plugin.*names withpackage, theegg.frameworkpackage and thedefault
eggframework to thepackage.jsonthat owns the app.Fallow now reads Vite+ projects. A
vite-plusdependency, or a rootvite.config.*that importsvite-plus, enables the newvite-plusplugin. The
pack.entryvalues are entry points, and thelint.jsPlugins,lint.overrides[].jsPluginsandlint.extendspackages are used. Avite.config.*that lists@e18e/eslint-plugininlint.jsPluginsnolonger reports it as an unused dev dependency. The
vite-plusdependencyalso enables the Vitest plugin, so the test and bench files are entry
points without a Vitest dependency.
Vite+ commands credit their dependencies. The
vp,vprandvpxbinaries credit
vite-plus.vp lintandvp fmtfile targets are notentry points,
vp test -c <file>reads the file as a Vitest config,vp exec <binary>credits the binary, andvp run <task>calls thescript. The commands of the
stagedandrun.tasksblocks and of the.vite-hooks/scripts are read likepackage.jsonscripts and hooks.A
vi.mockcall throughvite-plus/testnow counts as a Vitest mock.New workspace diagnostic
workspace-pattern-dir-missing. A positiveworkspace pattern whose directory does not exist (for example
viteandvite/packages/*for a repository that CI clones into the workspace) nowgets a diagnostic with the pattern and the declaring file. The packages in
that directory can use catalog entries, so the run does not report
unused-catalog-entrieswhile such a pattern exists. One such pattern turnsoff this check for all catalogs, also when the directory does not exist yet
(for example
packages/*in a template). The diagnostic is a stderrwarning with
degrades_analysis: true. To get the check back, create thedirectory before the run or remove the pattern. A directory that exists but
holds no package does not turn off the check. Unresolved catalog references
and empty catalog groups are still reported.
New workspace diagnostic
nested-project-root. A directory with its ownpackage.jsonandpnpm-workspace.yamlthat no root workspace patterndeclares is an independent pnpm project. Fallow now analyzes it as a
workspace and reports this information-only diagnostic.
Changed
The
fallowskill shows the review loop that fallow checks. One shortexample runs
fallow audit, reads the walkthrough guide, writes a judgment,and validates it with
--walkthrough-file. It saves the guide and thejudgment in the temporary folder, because a new file in the project tree
makes the judgment stale.
fallow audit --gate new-onlyno longer fails on a CRAP score fromestimated coverage alone. Without a coverage map, Fallow estimates CRAP
from static test reachability. This estimate does not measure the change. A
complexity finding that exceeds only the CRAP threshold with estimated
coverage now has
effective_severity: warn. It stays in the report, and theverdict is at least
warn, with exit code 0. SARIF reports it at levelwarning.A finding that also exceeds the cyclomatic or cognitive threshold still
gates. CRAP from measured coverage (
--coverage,health.coverage,FALLOW_COVERAGEor runtime coverage) still gates.--gate alldoes notchange.
fallow audit --gate new-onlyreports growth of an already exceededcomplexity metric as
worsened, notintroduced. A function thatexceeded a threshold on the base and grows a little in the PR no longer
fails the audit by default. With the default
audit.worsened: warn, theverdict is
warnand the exit code is 0. The finding staysintroducedwhen the growth moves it to a higher severity tier (
moderate,high,critical), or when it now exceeds a metric that it did not exceed on thebase and that metric grew. The exceeded flags and the tier of the base
finding come from the base run, so a threshold override in the same PR does
not make an old finding introduced.
To keep the earlier gate, set
audit.worsened: "error"or pass--worsened error. Withaudit.worsened: off, growth of an exceeded metricnow gives a
warnverdict asworsened, and the run does not fail. Growthonly in metrics that the finding does not exceed stays
inherited, asbefore. The JSON fields do not change. Cached base snapshots of earlier
versions are not reused.
fallow auditno longer fails on a complexity finding in a test file.A long
it(...)ordescribe(...)callback often exceeds the cognitivethreshold. Such a finding now has
effective_severity: warnand the newfield
gate_exemption: "test-file". It stays in the report with itsattribution, and the verdict is at least
warn, with exit code 0. Thisapplies to
--gate new-onlyand--gate all. SARIF reports the finding atlevel
warningwithgate_exemptionin the property bag. The human,markdown and compact views list these findings under "Test files, not
gated".
The shared test-path rule decides what a test file is:
*.test.*,*.spec.*,*.e2e.*, files below__tests__/,test/,tests/ore2e/,mocks and fixtures.
fallow healthdoes not change: it still reports thesefindings at the severity of their
complexity-*rule.The GitHub Action requests the branded token only when it posts. The
Fallow branded tokenstep now runs only on apull_requestevent withcomment: trueorreview-comments: true. Other runs do not request anOIDC token. The
branded-tokeninput now defaults toauto.autopostsas the Fallow app when the workflow grants
id-token: write, and it fallsback to
github-tokenwithout a log line when the permission is missing.An explicit
branded-token: truekeeps one short fallback line. No lineasks the workflow for more permissions.
falsebehaves as before.Fixed
A
<in template text no longer hides a later component invocation inthe component contract scan. Before this fix, the caller scan for Vue,
Svelte, Angular and Ember templates read each
<as the start of a tag.In
<p>{{ a < b }}<Button size="lg" /></p>, the scan read< b }}<Button size="lg" />as one tag, and theButtoninvocation waslost. The scan now reads a
<as a tag start only when a letter,/,!or
?follows it. It also skips text expressions as a unit:{{ }}inVue, Angular and Ember, and
{ }in Svelte. A Handlebars comment, such as{{!-- don't --}}, is skipped as plain text, so an apostrophe in it cannothide a later tag. The cache versions change, so a warm cache does not keep
the old result.
A JSON or JSONC config file that ends with a
//line comment and nofinal newline is now read. Before this fix, the comment hid the end of
the config, so the parse failed and fallow read nothing from the file.
This affected
.oxlintrc.jsoncand the JSON configs of the ESLint,Prettier, TypeScript, Jest and Babel plugins. For example, a
tsconfig.jsonwith a final comment lost itsextendspackage, and an.oxlintrc.jsonclost itsjsPluginspackages, which were then reportedas unused dependencies.
The
fallow-setupskill now detects all Oxlint and Oxfmt config filenames. The tooling detection table listed only
.oxlintrc.jsonand.oxfmtrc.json. An agent that followed the skill could miss an existinglinter or formatter and propose a second one. The table now also lists
.oxlintrc.jsonc,oxlint.config.{ts,mts},.oxfmtrc.jsoncandoxfmt.config.*.Glob expansion on disk works when the project path contains
[,*or?. PluginfileExistsdetection,fallow plugin-checkentry seeds andpackage.jsonorpnpm-workspace.yamlworkspace globs put the projectroot into the glob without an escape. Under a root such as
~/work/app [1], these globs then matched nothing. A plugin did notactivate, and a workspace package was not found, so its files were
reported as unused. Tooling config detection
already escaped the root. All four now use one function that escapes the
root and keeps every match inside it.
An older fallow no longer drops data from an Impact store that a newer
fallow wrote. A project-local fallow could rewrite the shared store through
its own older model, and fields it did not know were lost. Every write path
now leaves such a store unchanged.
fallow impact enable,fallow impact disable, andfallow init --declineexit with code 2 and name the storeschema and the fallow version of the newest record. Audit and combined runs do not
record into it. Reads stay available.
(Closes #3597)
A mutation run in which the runner does not list a static test file is
now
partial. The tests of such a file never run, but the run reportedcompletion.status: "complete". An empty survivor list then lookedconclusive. The completion now has the new reason
test-files-not-discovered, the same name as theerrorsentry thatnames the files. The exit code does not change.
(Closes #3596)
fallow mutationno longer makes an equivalent mutant for acaselabel whose body is the same as the
defaultbody. The original labeland the empty-string or swapped-boolean label select the same body for
each input, so no test can kill the mutant. The rule applies only when all
labels are literals, both bodies end in an unconditional exit, and no other
label has the original or the mutated value.
(Closes #3639)
fallow mutationno longer makes an equivalent!=mutant for the testof a counted
forloop. For aletcounter that starts at an integerliteral, moves one step to an integer literal bound, and has no other write,
i < Nandi != Nselect the same iterations. No test can kill thatmutant. The
<=and constant mutants of the test do not change.(Closes #3649)
fallow mutationno longer makes equivalent mutants for a zerocomparison on the length of a known array. An array length is never
negative, so
items.length > 0anditems.length != 0give the sameresult. No test can kill that mutant. The rule also covers
<= 0(the==swap),< 0(the constantfalse),>= 0(the constanttrue) andthe reversed forms. The receiver must be an array literal, or a name
that resolves to a
constwith an array value, a binding with an arraytype, or a rest parameter. A global with the same name and an object with
a
lengthproperty keep each mutant.(Closes #3651)
fallow mutationnow runs the tests of static importers for a modulethat a test also imports at run time. When one test file imports a module
statically and another test loads it through a dynamic
import(), a mutantin module-level code ran only the dynamic test. The assertion of the static
importer never ran, and the mutant survived. The mutant is now a static
mutant and runs the tests of both files.
(Closes #3652)
fallow mutationnow instruments a file that a Vitest test loads witha native
require. A test that loads a CommonJS file throughcreateRequire(import.meta.url)ran the original file. The report thenlisted the file in
unloaded_pathsand gave each of its mutantsno_coverage. The Vitest setup file now serves the instrumented file tosuch a load on Node 22.15 and later. On an older Node, the file stays in
unloaded_paths, and a staticimportis the workaround.(Closes #3653)
Warnings on a pipe or in a log file no longer carry color codes. The
tracing warnings on stderr were colored for every writer. An agent or a CI
log then got escape codes such as
\x1b[33maround eachWARNline. Colornow stays on only for a terminal, and
NO_COLORturns it off.CLICOLOR_FORCEturns it on, the same as for the report.The setup hint of a bare run names the current setup commands. The hint
now names
fallow initfor a config andfallow agent install --dry-runfor the agent guide, skills, MCP server, and commit gate. The JSON
setupnext step now has the same offer: its command is
fallow agent install --dry-run, notfallow schema.fallow initnames the next step. After it writes the config,inittells you to run
fallowfor a first report. When the config root is notthe current directory, the command includes
--root.--quietdrops thisline.
fallow agent install --dry-runnames the command that applies the plan.A dry run that plans writes puts
agent-install-applyfirst innext_actions[], also in the humanNext:list. The command repeats theflags that change the plan, without
--dry-run. A harness that detectionfound is repeated as
--harness, so a run in another shell writes thesame plan.
The review focus no longer calls one importer "high fan-in". The reason
now reads
fan-in (1 importer), the same form for every count.fallow dead-codeno longer reports unlisted dependencies below a nestedpackage.jsonwithout dependency fields.A nested manifest with only metadata, for example a tegg module with
{ "name": "x", "eggModule": {} }, now inherits the dependencydeclarations of its ancestor manifests. Version 3.34.1 used such a manifest
as the only owner of the files below it. A manifest with an explicit empty
field, such as
"dependencies": {}, stays the owner of its files.The
defaultexport ofpostcss.config.*is no longer reported as anunused export under
includeEntryExports. PostCSS reads that export, asVite reads the default export of
vite.config.*, which fallow alreadycredited. Thanks @osazemeu for the patch in
#3694.
A Vitest
test.includeno longer makes bench files unused. The benchfiles are entry points again when a config sets
test.include. Fallow nowreads
test.benchmark.include, and the default bench pattern is the Vitestdefault
**/*.{bench,benchmark}.?(c|m)[jt]s?(x). Before, the default patterndid not match
.benchmark.files or the.mtsand.ctsextensions.A Vitest config below the project root no longer replaces the test entry
points of the whole project. Vitest finds a
vitest.configorvite.configfile (.ts,.js,.mts,.mjs,.ctsor.cjs) in thedirectory that it runs in. The
test.includeglobs of such a file belowthe root, for example a fixture config, now apply below its own directory.
Only a config in the project root or the workspace root replaces the default
test patterns. The globs of a config that a package script loads with
--configstay relative to the package root. The configs of one projectnow add up, so no config removes the patterns of another config. A
vite.configfile next to avitest.configfile gives no includes, unlessthe
vitest.configfile names it. After the upgrade,fallow dead-codecanreport files that only a fixture config marked as used before. You can
remove an
entryworkaround for test files that Vitest finds.A Vitest reporter path is now a used file, not a dependency. A
reportersentry such as'./scripts/ci-reporter.ts'marks that file asused. Before, Fallow read the path as a package name and reported the file
as unused. Fallow also reads a reporter in a spread array, such as
...(process.env.CI ? ['./scripts/ci-reporter.ts'] : []). The config readerof the other plugins now also reads spread arrays and both branches of a
condition.
fallow mutationnow prunes mutants in functions that write through an ORM or a query builder. The side-effect prune took its database sinks from the security catalogue, which matches only injection shapes. A safe write such asdb.insert(records).values({ value })matched no row, so the plan kept its mutants and a run could write to a real database. The plan now also treats member calls with a database write method name (insert,insertOne,update,delete,del,create,upsert,save,$transactionand similar) as sinks. A tagged template or a call with SQL text that starts with a write verb is a sink too, such assql`insert into ...`forpostgresand@vercel/postgres, orlite.prepare("INSERT ...").run(value)forbetter-sqlite3. A prepared statement run, such asinsertStmt.run(value)afterconst insertStmt = db.prepare("INSERT ...")ordb.prepare(INSERT_SQL).run(value), is a sink too. This includes a class field (also astaticor a#privatefield), an object literal property or an assignment that holds the statement, such asthis.insert.run(value)orstatements.insert.run(value). This applies also when the module imports the client from a project module. A preparedSELECTstatement is not a sink. In a module that loads a database client package itself,runandexeccalls count too, butexecon a regular expression does not. A route registration such asapp.delete("/items/:id", handler)is not a sink. This applies only in a module that loads a database client package, such asdrizzle-orm,@prisma/client,kysely,knex,mongodborpg, or that loads a project module that loads one. An ES import, a type-only import, arequire()call and animport()call each count.--allow-side-effectskeeps these mutants. Projects with ORM writes can see moreprunedmutants and a different score.(Closes #3641)
The napi-rs plugin works in a source repository. The plugin now
activates on the
napifield and itstargets. It does not needoptionalDependencies, whichnapi prepublishadds only at publish time.In a loader with the
auto-generated by NAPI-RSheader, imports of<package>-<platform>packages and@napi-rs/wasm-runtimeare no longerunlisted dependencies. The generated
./<binary>.wasi.cjsand./<binary>.wasi-browser.jsloaders are no longer unresolved imports.A generated
browser.jsthat only re-exports<package>-wasm32-wasinolonger reports that package as unlisted.
wasi-worker*.mjsfiles arealways used. Projects can remove the
ignorePatternsandignoreDependenciesentries for these files.A resolve specifier from a conditional const credits each package.
const c = native ? 'pkg' : '@scope/pkg-wasm'followed byimport.meta.resolve(`${c}/package.json`)orrequire.resolve(c)nowcredits both packages. The
?:,||and??operators are supported wheneach branch is a string literal. The extraction cache version changes, so
the first run after the upgrade parses each file again.
css-broken-referenceno longer suggests a "did you mean" fix for a defined class. Three cases caused false findings. In a Tailwind project, a utility such asw-fullwas reported as a typo of an authored class such aswl-full. Fallow now skips tokens with the shape of a Tailwind utility when a stylesheet loads Tailwind or the rootpackage.jsondeclares it. As a result, in a Tailwind project an authored class that starts with a utility root (for exampletext-orlist-) gets no typo suggestion. A class that a component defines only through:globalin an Astro, Vue or Svelte<style>block, or in a CSS module, now counts as defined. This includes:global(.x), the Svelte 5 block form:global { .x {} }and the selector formdiv :global .x {}. A<style>block in an.htmlfile now contributes its classes. Thanks to @zaphir for the report.(Closes #3588)
The
defaultexport of a tool config file is no longer reported as anunused export under
includeEntryExports. The tool loads its config fileand reads the default export. This rule applies to every config pattern of
an active plugin, built-in or external, for example
tailwind.config.js,jest.config.js,playwright.config.tsand a configfile in a workspace package. Named exports of a config file stay
reportable. Some patterns do not get the credit: Gatsby
gatsby-node,gatsby-browserandgatsby-ssr(Gatsby reads named exports), Vercelvercel.*(Vercel reads the namedconfigexport), and GraphQL Codegencodegen*and Serverlessserverless.*, because these patterns also matchordinary source files. Output without
includeEntryExportsdoes not change. Thanks to@osazemeu for the report.
(Closes #3691)
The generated napi-rs loader no longer causes duplicate exports. The
napi-rs plugin now treats a loader with the
auto-generated by NAPI-RSheader and the generated
browser.jsas support entry points. Ahand-written wrapper that exports a class with the name of a binding is no
longer a duplicate export, and an unused binding is no longer an unused
export. A
requireof a native addon./<binary>.<platform>.nodeis nolonger an unresolved import. Projects can remove the
ignorePatternsentryfor the generated
index.js.Fallow now reads
test.root,test.dirand the Viterootof a Vitestconfig. The
test.includeglobs are relative totest.dirbelow theVitest root, and
setupFiles,globalSetupand reporter paths are relativeto the Vitest root. Before, Fallow read all of them relative to the config
directory or the project root, so it reported test files and setup files as
unused.
Vitest project files with another name now add their test files. A
project file such as
packages/a/vitest.unit.config.tsintest.projectsis the root of its project. Fallow now reads its
test.includeand itssetup files relative to its own directory. A project without
test.includeruns the Vitest default include. Before, Fallow reported thetest files of such a project as unused. An inline project in
test.projectsalso gets the includes, the excludes, therootand thedirthat it merges from its base config.The default Vitest test patterns match
.mts,.cts,.mjsand.cjsfiles. Files such as
src/a.test.mtsandtools/b.spec.ctsare testentry points without a config, as in Vitest.
A
fallow mutationrun with--time-budgetnow keeps time to confirmits survivors. Before, the first pass could use the full budget. Then no
survivor or timeout got its retry, the result cache stored none of them,
and a repeat run with the same budget ran the same mutants again. Now the
first pass stops new mutant runs early by the estimated retry time of the
survivors and timeouts that it found. The confirmed results go into the
cache, and a repeat run continues with the next mutants. A survivor that
gets no retry stays unconfirmed and is not stored.
(Closes #3665)
More tool config files keep their exports under
includeEntryExports.Fallow no longer reports the
defaultexport of these config files asunused: Capacitor
capacitor.config.*, Sanitysanity.config.*andsanity.cli.*, Nitro, SVGO, SVGR (svgr.config.*and.svgrrc.js),Kysely, OpenAPI TS, Remix, i18next
i18next.config.*, VitePress.vitepress/config.*, CSpellcspell.config.*,@sanity/pkg-utilspackage.config.*, Electron Forgeforge.config.*and electron-builderelectron-builder.{js,cjs,ts}. A rootcodegen*file of GraphQL Codegenand a root
serverless.*file of the Serverless Framework and osls alsokeep the
defaultexport. A helper module such assrc/lib/serverless.tsstays reportable. cucumber-js reads each named export of
cucumber.jsas aprofile, and
@vercel/configreads the named exports ofvercel.ts, sothese exports also count as used. Output without
includeEntryExportsdoes not change.
fallow dead-code --include-entry-exportsno longer reports the exportthat a Serverless handler names. A handler such as
src/users.mainmakessrc/users.tsan entry point, and the framework calls itsmainexport.The Serverless Framework and osls plugins now mark that export as used.
Other exports of the handler file still report. Output without
--include-entry-exportsdoes not change.fallow mutationno longer reports a false survivor when a test reads acached value. The per-test hits of the dry run name only the tests that
execute the mutated code. A test that reads a value that an earlier test of
its file cached gets no hit, so no run of the mutant included it. The retry
of a survivor now runs each test of the files of its covering tests. A kill
by a test that the first run did not execute gives
killed, notflaky,when the same tests pass in a control run with no active mutant. The first
run of each mutant stays narrow.
covering_testsstill lists the teststhat reached the mutant in the dry run, so
killed_bycan name a testthat is not in that list. With
--time-budget, the time that the firstpass keeps free for a survivor retry grows with the tests of its files.
(Closes #3670)
fallow mutationruns a mapped test file in a dot directory with thenode-testandbun-testrunners. The glob discovery of these runnersdid not go into a directory such as
.github/, so a test file there didnot run, and its mutants were
no_coverage. Fallow now sends the statictest files to the runner. The runner adds each file that matches its
default patterns. A file that the patterns do not match still makes the
run
partialwithtest-files-not-discovered.(Closes #3678)
fallow mutationno longer makes an equivalenttruemutant for anoptional lookup before
.filter(Boolean). In["base", align ? alignment[align] : ""].filter(Boolean), the constanttrueforaligngivesundefinedinstead of""for a falsyalign,and
filter(Boolean)drops both. No test can kill that mutant. Thefalsemutant stays. The object must be a localconstobject literalthat is not exported, has no falsy key such as
undefinedor0, and isonly read. A template literal, a
.joinwithout.filter(Boolean)and atruthy alternate keep both mutants.
(Closes #3667)
A nested
serverless.tsorserverless.jsmodule is no longer read as aServerless config. The Serverless Framework and osls read
serverless.*from the service directory only. Fallow now reads a JS or TS config only at
the project root, at a workspace package root, or in a directory with its
own
package.json. A module such assrc/lib/serverless.tsis an ordinarysource file again: its
pluginsget no dependency credit and its handlersare not entry points. A nested
serverless.ymlis still a service config.Fallow now treats
sanity.config.tsxandsanity.config.jsxas Sanityentry points. Sanity Studio loads its config from
sanity.config.{ts,tsx,js,jsx}, but fallow knew only the.tsand.jsnames. Modules that only a
.tsxor.jsxconfig file imports, such as astudio logo or a custom structure, are no longer reported as unused files,
and their exports are no longer reported as unused. Under
includeEntryExports, thedefaultexport of these config files counts asused. This changes the output for projects with a
.tsxor.jsxSanityconfig file in all modes.
A
<workspace-package>/package.jsonimport resolves withoutnode_modules.Fallow reported the import as unresolved and the workspace dependency as
unused when the package was not installed. The import now credits the
package, the same as an installed workspace package. An
exportsmapwithout a
./package.jsonkey still blocks the import.require.resolveon a function-scopedcreateRequire(import.meta.url)credits the package. Fallow treated the local
requireas a shadow andreported the dependency as unused. A
requirefromcreateRequire(import.meta.url)orcreateRequire(__filename)now counts inevery scope.
require('..')andrequire('.')resolve to package source. A relativeimport of a package root whose
mainpoints at missing build output now usesthe source entry, the same as
require('../').ignoreFindingsnow covers the parse errors of the files it hides. Asyntax-error test fixture that
ignoreFindingscovers no longer reportssource-parse-degraded, no longer prints a parse warning, and no longerfails the
parse-errorgate. Its partial parse also no longer marks otherfindings with the
incomplete-import-graphcaveat, and the pattern nolonger reads as a pattern that matched no finding.
--quietnow hides the analysis warnings on stderr. A run with--quiet,--ciorFALLOW_QUIETno longer prints warnings that repeat aworkspace_diagnosticsentry, such as parse degradations, the tsconfigchain warning, re-export cycles and skipped
package.jsonentry points.The JSON output does not change. Set
RUST_LOG=warnto show them again.Config warnings, such as an unknown rule name, still print.
fallow mutationnow keeps the error of a test that fails in a controlrun. A control run runs the covering tests with no active mutant. When a
test fails there, the
baseline_failuresmessage now adds the error of thetest after the reason: the assertion on the first line, then the stack
frames of the project. The
FALLOW_MUTATION_BASELINE_FAILEDerror andthe
Error:line of the human footer also show the assertion. Before, themessage gave the reason only. Paths are relative to the project, and the
error is about 4 KiB at most. The verdict does not change: the run still
stops with exit code 2.
(Closes #3690)
Workspace package binaries now credit their package without
node_modules. Fallow reads thebinfield of each workspace packagethat a manifest declares. A script that calls such a binary marks the
declaring package as used.
More binaries map to their package.
napiandnapi-rawnow credit@napi-rs/cli,taplocredits@taplo/cli,oxnodecredits@oxc-node/cli, andutandutxcreditutoo.GitHub Actions steps resolve file arguments in their
working-directory. Fallow reads the step value and thedefaults.run.working-directoryvalue of the workflow and of the job.Paths that start with
$GITHUB_WORKSPACE/or${{ github.workspace }}/resolve from the project root, and so do GitLab
$CI_PROJECT_DIR/paths.A relative path in a directory outside the project, or in a directory with
a run-time value, makes no entry point.
Fallow now reads composite actions. It reads the run steps in
.github/actions/*/action.yml,.github/actions/*/*/action.ymland theroot
action.ymllike workflow steps.${{ github.action_path }}/pathsresolve from the action directory.
VitePress: the
viteblock of the VitePress config credits itspackages. Packages in
vite.optimizeDeps,vite.ssr.externalandvite.ssr.noExternalare no longer unused dependencies, andvite.resolve.aliasentries resolve imports.Vite: a nested
optimizeDepsentry credits its parent package. An entrysuch as
'mermaid > @braintree/sanitize-url'now creditsmermaid.fallow mutationnow stops a Vitest inline project at the first failedtest. The Vitest companion set fail-fast only on the root config. A
worker reads the config of its project, and an inline project in
test.projectskeeps thebailvalue that Vitest copied at start. Amutant run in an inline project then ran each selected test after the
first failure. The companion now sets
bailon each project for each run,and a control run still runs the full selection.
(Closes #3701)
fallow mutationwithnode:testnow serves the mutant to a Node childprocess of a test. A test that ran a CLI with
execFileSyncofprocess.execPathloaded the original source in that child. So an exactCLI assertion could not kill the mutant, and the report showed a false
survivor. The test process now adds the overlay preload to
NODE_OPTIONS.Each Node child process (
spawn,exec,execFileorfork) then loadsthe instrumented files with the active mutant. The hits of a child process
do not count as coverage. A child that is not Node, or a child with an
environment without
NODE_OPTIONS, still loads the original files.(Closes #3680)
--traceno longer calls an export unused when a framework pluginmarks it as used. The trace read only import references. The
defaultexport of a Next.js page or the export that a Serverless handler names
traced as
is_used: false, although the analysis did not report it.--traceand--trace-filenow list these credits in a newplugin_creditsfield with the plugin and its file pattern. For areachable file, the export then traces as used, and the reason names the
plugin. The human output shows the same credit.
A peer of a package that a script runs or that a plugin credits is no
longer reported as an unused dependency. Before this fix, only the
packages that a file imports started the peer-dependency closure. For
example, a
napi buildscript uses@napi-rs/cli, but its peer@emnapi/runtimewas reported as an unused devDependency. The closure nowalso starts from the packages that a script runs, that a plugin config
names, and the plugin tooling with evidence of use.
fallow dead-code --trace-dependencynames the same host inpeer_of.A module that imports
Xunder an alias and exports its ownXis nolonger a duplicate export of the source module. Such a module is an
adapter, for example
import { configureAuth as core } from "../ci/auth"followed by
export function configureAuth(). It derives its export fromthe source, as a re-export does. When a third module exports the same name
independently, the finding stays between the source and that module. Two
modules that import
Xfrom each other under aliases are still reported.fallow audit --gate new-onlyno longer reads a missing base CRAP score asgrowth. A matched complexity finding was introduced when the head had a
CRAP score and the base finding had none. The two runs then had different
coverage input, and the code did not grow. Such a finding is now
worsenedor
inherited. A CRAP score from estimated coverage also no longer makes amatched finding introduced; it can make it
worsened.A
package.jsonwith no name and no dependencies is not an undeclaredworkspace. A file such as
bench/package.jsonwith only{"type":"module"}sets the module type of the scripts next to it. It nolonger gets
undeclared-workspace.A nested independent pnpm project owns its files and dependencies. Its
package.jsonnow activates framework plugins and declares the dependenciesof its files, without a
workspaces.patternsentry in the fallow config. Itsdirectories are not reported as undeclared workspaces, and its
catalog:references are not checked against the root catalog.
pnpm overrides take precedence over a
catalog:reference. pnpm appliesan override before it resolves
catalog:. Apackage.jsonreference to apackage that an override key with no version selector replaces (
vite, or<consumer>>vite) is no longer an unresolved catalog reference. A key with aversion selector (
vite@^5) does not replace the reference, because pnpmfails the install for it.
audit --gate new-onlydoes not gate a finding class that the base couldnot check. The base snapshot holds committed files only, so a gitignored
workspace member (for example a
vite/clone that CI makes) is missing inthe base. The base then does not report unused catalog entries, and the head
reports them. These head findings are now inherited, not introduced, so they
no longer change the verdict. A new unused catalog entry in such a run is
also inherited. The audit base snapshot cache moves to version 16.
Performance
fallow mutationno longer repeats the setup of unrelated test files foreach static mutant with Vitest
isolate: false. Without fresh modulesfor each run, each static mutant runs in a new runner process. That process
did a dry run of each test file of the run. It now dry-runs only the test
files that cover the mutant. The safety check of the new process stays: a
covering test that does not pass in its dry run is still a baseline failure.
(Closes #3650)
--type-awareclass-member refinement is faster on large projects. Since 3.18.0, the symbol-use scan sent extra checker requests and resolved file paths for every identifier in every file, also when the identifier could not match a query. The scan now checks the name first and keeps one alias answer for each symbol. On the TypeScript repository, the scan takes 4 to 12 s instead of 35 to 70 s on the same machine, with the same decisions.--type-awareasks the checker only about identifiers that can reach a query. The symbol-use scan sent every identifier of every TypeScript file to the checker. It now sends only the names of the queried symbols and the names that imports and exports of the project declare. JavaScript files keep the full scan, becauserequirebindings also create aliases. The decisions stay the same.Security
The Docker image now pins its base images and corepack.
Dockerfileand
Dockerfile.devpin each base image by its multi-platform digest andinstall corepack from a committed lockfile with an integrity hash.
Dependabot keeps the digests and the corepack version current.
Fallow and the similar-code sidecar link rustls-webpki 0.103.16 and
webpki-roots 1.0.10. rustls-webpki 0.103.15 has the advisory
RUSTSEC-2026-0382. webpki-roots 1.0.6 and 1.0.9 have the advisory
RUSTSEC-2026-0381. Both advisories are about certificate chains with
distrusted intermediate certificates.
The Docker image now runs on Node.js 26.12.1.
DockerfileandDockerfile.devpin thenode:26-bookworm-slimdigest of the Node.js26.12.1 security release. The earlier digest had Node.js 26.12.0.
v3.34.1Compare Source
Fixed
The Oxlint plugin now reads
.oxlintrc.jsoncandoxlint.config.mts.Oxlint finds these config files on its own, but fallow did not read them.
Packages in their
jsPluginslists, at the top level and inoverrides,showed as unused dependencies, and
oxlint.config.mtsshowed as an unusedfile. Fallow now reads every config file name that Oxlint finds. Comments
and trailing commas in
.oxlintrc.jsoncparse correctly. Thanks@gwynnnplaine for the report and the
analysis. (Closes #3525)
A coverage map with a negative hit count now loads. Vitest
coverage-v8can write a negative branch count, for example
[5, -1], for the implicitelse of an
if. Fallow rejected the whole map:fallow healthandfallow auditstopped with exit 2 when--coverageorFALLOW_COVERAGEnamed the map. Fallow now reads a negative count in
s,f,borb_Tas 0, which is a miss. An auto-detected map that fails to load now gives a
note that says Fallow ignored it, and not a note that says Fallow used it.
Thanks @norvalbv for the report and the
reproduction. (Closes #3564)
A
<inside a nested Vue<template>no longer hides the references ofthe template. A
<in an interpolation of a nested template, such as<template v-if="W">{{ 1 < 2 }}</template>, made fallow read the<as thestart of a tag. Each name that the template used was then lost, and a named
import that only the template used was reported as an unused export.
Fallow now skips
{{ }}interpolations when it looks for the closing</template>. A<that is not followed by a letter,/,!or?isread as text, as in HTML. The cache versions change, so a warm cache does
not keep the old result. Thanks @BrocksiNet
for the report and the minimal reproduction.
(Closes #3543)
fallow health --type-aware --type-couplingno longer fails--type-aware-require completeon projects with more than 40 couplingedges. The coupling report is one report for the whole project. It now
carries up to 10,000 evidence items, so a complete analysis is reported as
complete. Symbol, impact, and API-surface results keep the limit of 40items. Above the new limit, the report stays
partialwith theevidence-limitreason. Thanks @samaluk forthe report and the experiment.
(Closes #3563)
Security
An entry such as
../*or an absolute path inpackage.jsonworkspaces,pnpm-workspace.yamlpackages,deno.jsonworkspace, or theworkspaces.patternsconfig listed directories outside the root. Fallowadded no package from them, but a directory without a
package.jsongot aglob-matched-no-package-jsondiagnostic with its outside path. Fallow nowskips such an entry before it reads the disk. A new
workspace-glob-outside-rootdiagnostic names the entry and the file thatdeclares it. The run does not exit with an error, because npm, pnpm, and
yarn accept a
../workspace entry. A..that stays inside the root,as in
packages/../libs/*, still works. The findings do not change.v3.34.0Compare Source
Added
fallow mutationmeasures how well the tests detect code changes. Thecommand makes small changes (mutants) in the changed code and runs the
tests of the project against each one. A mutant that no test fails
on survives. The report gives the mutation score over the tested mutants,
the survivors by file with their covering tests, and the share of mutants
that a test reaches.
--planselects and ranks the mutants and runs no code.fallow mutation inspect <mutant_id>shows one mutant against the currentfile and runs no code. It reads the report that the last run saved in the
Fallow cache, or the file that
--reportnames. From a subdirectory, itfinds the report of a run at the project root. A changed file gives a
stalestatus.audit.--alland file paths widen or narrow it.host load alone does not make a mutant count as detected. The retry of
a timeout waits twice as long. A changed detection result is
flakyand leaves the score.
retried: trueon a mutant marks a status from both runs.--setup-timeoutsets how long each runner process can take to start,finish its dry run or reload (default
2m).--allow-dirtyis set. Mutantsnear file system, process, network or database calls are pruned unless
--allow-side-effectsis set. Fallow does not write to project files, andthe report lists any path that the tests changed.
-o <file>writes thereport after the run, and the worktree check leaves that file out.
--min-scoreormutation.minScoremakes alower score exit with 1.
--verdicts <file>marks equivalent mutants,which leave the score.
--max-mutants. The dry run decides their coverage: a pending mutant thatno test reaches in the dry run is
no_coverage. A capped run thereforereports the coverage of the full run.
*.test-d.ts,*.spec-d.ts, or a TypeScript file belowtest-d/) is not a test file of the plan. No runner executes it, so itno longer gives a false baseline failure or decides the runner.
baseline failure. The mutant runs with its other covering tests and is a
runtime error only when no covering test is left. A survivor of such a
run survives: the check of the test count uses the tests of the run,
so it no longer gives a runtime error such as
466 of 467 requested tests ran.dry-run time and the worker count. It is at least the runner timeout. A
loaded host no longer turns the mutants of a restarted companion into
runtime errors.
no_coveragemutant has atest_reachobject with the class of theaudittest reach:mocked-only,none,none-file,transitiveordirect. A mutant in an exported declaration gets the class of thatexport and its name. For
mocked-only, the object lists the tests andthe mocked modules. The human no-coverage lines count the mutants of
each file by class, and the plan prints one line with these counts.
Fallow cache. The key covers the mutant, its covering tests, the content
of the covering test files and the files that they import, the runner
setup files (also the Bun
preloadfiles), the lockfile, the runnerconfig and the files that it imports, the configs that the root
tsconfig.jsonextends, and the runner. A change to one of them runs themutant again. Dynamic imports that the module graph does not resolve, and
files that a test reads, are not in the key. Each cache entry and the
cacheobject of the report record this limit. A reused result hascached: true.--no-cacheruns each mutant.cache. A staging directory and a rename install it, so a concurrent run
on the same plan never reads a partial file. A run reuses a directory
with the expected content, and removes old run directories beyond the 5
most recently used ones.
mutationconfig section sets the runner, the operators, theminimum score and the concurrency.
--typecheckormutation.typechecktype-checks each survivor throughthe
--type-awarecompanion after the tests. A survivor that adds a typeerror to its file gets the status
compile_errorand leaves the score.The check reads only the mutated file and never writes a project file.
Without a companion, the run exits with 2 before the tests start.
The checker cannot read a Vue, Svelte or Astro component, so a survivor
in a component stays
survivedwith the skip reasonunsupported-file. (#3471)fallow health --mutation-report <file>reads a saved run report andadds the mutation score of each complexity finding
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.