Repository navigation
v3.41.0: mutation tasks and recheck, nested pnpm workspaces, fewer false positives
Features
-
fallow mutation taskslists the open tasks of a mutation report as one batch. A task is asurvivedorno_coveragemutant. Each task has its file, line, function, operator, the source lines before and after the change, and the covering test files. The human and--format markdownoutput is a numbered list that a person or a coding agent can use to write the missing tests.--format jsongives themutation-tasksJSON output. Without--report, the command reads the report of the last run. It runs no code. The new MCP toolmutation_tasksreturns the same batch. (#4269) -
fallow mutation --recheckreruns the open tasks of a report against the current tests. Write tests for the batch offallow mutation tasks, then runfallow mutation --recheck. Fallow runs eachsurvivedandno_coveragemutant of the saved report again. With--mutant, only the given ids run. In the newrecheckobject, each mutant has the outcomekilled,survived,no_coverage,tests_fail_on_originalorunsettled. A selected file that changed after the report stops the recheck withFALLOW_MUTATION_SOURCE_CHANGED. An uncommitted change to another non-test file stops it withFALLOW_MUTATION_NON_TEST_CHANGE. A recheck without--mutantreplaces the saved report, sofallow mutation tasksthen lists the mutants that are still open. (#4277) -
fallow mutation --compare <file>lists the mutants that a test change no longer detects. Save a complete report, change the tests, then runfallow mutation --compare <file>. The newcompareobject lists a mutant asregressedwhen it waskilledortimeoutbefore and issurvivedorno_coveragenow. Each regressed mutant has its earlierprevious_killing_tests, and the human output names the test that killed it before. Aflakymutant in either run isinconclusive. A mutant id in one run only isaddedorremoved. When the saved report or the run is not complete, Fallow exits with code 2 andFALLOW_MUTATION_COMPARE_INCOMPLETE. (#4306) -
Fallow analyzes the members of a nested pnpm workspace. A directory with its own
package.jsonandpnpm-workspace.yaml, which no root workspace pattern declares, is an independent pnpm project. Its members are now workspaces, andfallow list --workspacesshows them under the nested project root. Acatalog:reference in the nested project resolves against the nestedpnpm-workspace.yaml. The catalog findings andfallow fixuse the nested file. A missing member directory gives aworkspace-pattern-dir-missingdiagnostic, and Fallow then reports no unused entries of the nested catalog. A member that a root workspace pattern declares keeps the root catalog, and the root catalog checks do not change. Fallow does not read nested npm and yarn workspaces. (#4307) -
fallow list --entry-pointsand--trace-filename the package.json script of a script entry point. The human output shows the script key, for examplescripts/build.js (package.json script `build`). The JSON output adds the optional fieldscriptto eachentry_points[]item andentry_scriptto the file trace. (#4300)
Changed
-
fallow mutation --kill-matrix-forgives each target its own evidence status. Each entry ofkill_matrix.targetsnow has astatus(completeorincomplete) and itsreasons. A mutant with incomplete evidence now removes only the candidates of the targets that cover it. The run-wide reasonspartial-mutation-run,baseline-failureandmissing-test-file-metadatastill remove all candidates. The top-levelstatuskeeps its meaning, socandidatescan be nonempty whilestatusisincomplete. (#4317) -
An audit review comment of an inherited complexity finding names the base values. This applies to
--format review-github,review-gitlabandcodeclimate. The comment names each exceeded metric with its base and head values. For example:(inherited from the base: cyclomatic 23 -> 19, cognitive 22 -> 18). A reviewer can see if the function stayed the same or became simpler. The JSON output does not change, sofallow report --fromon a saved audit shows the comment without the base values. The CodeClimate fingerprints do not change. (#4304) -
Audit review comments state the attribution of dead-code, stale-suppression, duplication and styling findings.
review-githubandreview-gitlabputintroducedorinheritedafter the severity label, as they do for complexity findings. Under--gate new-only, an inherited finding shows that it does not gate the audit. The CodeClimate output and the fingerprints do not change. (#4319) -
Unused internal Rust items are gone. Workspace crates lost the
pubitems that no other crate uses. Onlyfallow-api,fallow-types,fallow-output,fallow-config,fallow-report-protocol,fallow-licenseandfallow-v8-coveragekeep their exported items. The removed items include the deprecatedfallow_core::analyze::feature_flagsfunctions. Usefallow_api::run_feature_flags. The CLI, the JSON output, the npm packages and the protocols do not change. (#4260)
Bug fixes
Dead code and resolution
-
Workspace imports resolve without an install or a build. A tsconfig
pathsentry of one workspace no longer makes the package imports of other workspaces unresolved. WithemitDeclarationOnly: true, an import of build output such asdist/cli.jsnow maps tosrc/cli.tswhen that file exists. Fallow tries each activeexportsorimportscondition in manifest order until one target resolves. Thus atypescondition that points to an unbuilt file no longer hides asourcecondition. (#4310) -
A framework path alias of a workspace package stays in that package. In a monorepo with two SvelteKit apps,
$lib/fooin one app could resolve into the other app. Fallow then reported falseboundary-violationand unused-file findings. A plugin alias from a workspace package (SvelteKit$lib, Nuxt~/and the others) now applies only to the files of that package. When the package has no target for the import, Fallow now reportsunresolved-import. SvelteKit$libalso followskit.files.lib. Thanks @MikeSpock for the report. (#4287) -
ESLint, Jest and package.json
importsentries credit more packages and files. An ESLintextendsentry with a subpath, such aseslint-config-shared/back/typescript, now credits its package. For a workspace package without an install, Fallow now finds the package by name and uses the file that the entry loads. A workspaceeslint.config.mjsno longer hides the root.eslintrc.json. Fallow reads a relative Jestpresetas Jest config, and a package insetupFiles,setupFilesAfterEnv,globalSetuporglobalTeardowngets credit. Manual mocks in__mocks__stay used when a config setstestMatchortestRegex. Each condition target of a package.jsonimportsentry is now used. (#4289, #4334) -
Package scripts give more entry points. Fallow reads the body of
sh -c,bash -c,zsh -canddash -cas a command list. Fallow then finds the entry point of each chained command (#4273). A Yarnrun -T jestcall counts as ajestcall. A JestmoduleNameMappertarget that names one file with<rootDir>is an entry point. Fallow reads theversionandpublishinputs ofchangesets/actionas commands (#4323). When a script runs the binary of an npm alias from a pnpm or Bun catalog, Fallow credits the declared package, for example@typescript/native. Thanks @cloud-walker for the report. (#4321) -
The Nuxt plugin reads the
dirobject ofnuxt.config. Custompages,layouts,middleware,pluginsandmodulesdirectories add their convention entry points. Fallow no longer reports their files as unused. (#4272) -
fallow dead-codefollows more dynamic imports. A pattern with a literal suffix and no extension, such asimport(`./icons/__${name}__`), now loads the matching module files. A dynamic import of aconststring now credits the package. (#4314) -
fallow dead-codecredits class members more accurately. A method chain through methods that returnthisnow credits each member of the chain (#4314). Members of an inheritedthis.find()result get credit when the base class is not exported. A local of a call with arguments, such asconst table = this.findBy(1), now credits the members of the return type (#4320). A shadowing callback parameter, loop head,catchparameter or block declaration no longer credits the return type of athismethod result (#4303).
Mutation testing
-
More mutation runs complete with correct results. Fallow no longer counts bench files and helper modules below
__tests__as test files (#4265). A test name with a lone UTF-16 surrogate no longer stops the dry run (#4264). The runner strips the types of a TypeScript mutant behind a project loader that compiles TypeScript (#4290). The result cache now keys on thebunfig.tomlpreload files also when no package enables the Bun plugin (#4263). -
Mutation runs no longer leave the temp files of killed test runs. Each test runner process gets its own temp directory, and Fallow deletes it when the process ends. This is also true after Ctrl+C (#4284, #4301, #4309, #4315). A Ctrl+C during the dry run no longer prints a runner error (#4327).
Health, duplication and policy
-
fallow healthscores the functions in Astro<script>blocks. Each inline<script>block, includingis:inlineanddefine:varsscripts, now gets a score, as a Vue or Svelte<script>block does. Existing Astro projects can get new complexity and CRAP findings. Thanks @Djkde01 for the report. (#4286) -
fallow health --csscounts@keyframesin SCSS and Less sources. Ananimation: spin 1snext to its@keyframes spinno longer gives a false undefined-keyframes finding. When Fallow cannot resolve a Sass or Less animation value, the unreferenced check skips the Sass and Less@keyframes. The new summary fieldkeyframes_uncheckedgives their count. Thanks @Smrtnyk for the report. (#4262) -
A
thresholdOverridesentry that sets a lower limit readsactivewhile it applies. Before, such an entry readstale. Astalerow tells you to remove the entry, but this entry produced the finding. (#4295) -
fallow dupesreports clone ranges that match the cloned tokens. Each token now has the span of its own text. A clone no longer covers all the lines of a block, soduplicated_linesandduplication_percentagecan go down.fallow dupesnow reports some clone groups that the long ranges hid (#4322). Identical Vue, Svelte and Astro files give a clone group for each section. Thanks @levi-broeksma for the report. (#4288) -
banned-call,banned-effectandboundaries.calls.forbiddenreport each call site. Before, Fallow reported only the first call of a callee path in a file, and one suppression comment hid every later call. Thanks @Smrtnyk for the report. (#4257) -
A
banned-effectrule follows an aliased destructuredrequire().const { exec: run } = require("child_process"); run(cmd)now gives ashelleffect. (#4308) -
--save-baselinewarns when the path is in the Fallow cache directory. Git ignores that directory, so Git does not commit the baseline. Use a committed path such asfallow-baselines/dead-code.json. (#4322)
Security analysis
fallow securitymatches more sink shapes. Raw SQL methods such aswhereRawand$queryRawUnsafenow match on a call result or athisfield, for exampledb("t").whereRaw(x)(#4276). A default or namespace import ofchild_processorvmnow resolves to thenode:path, as arequire()binding does. The callee inevidenceandcandidate.sink.calleechanges to match, for examplenode:child_process.exec().finding_iddoes not change. Afterimport { promises as fsp } from "node:fs", the callfsp.readFile(path)now resolves tonode:fs.readFileand can give apath-traversalcandidate. Code with apromises as fsalias now gives fewer candidates. A default or namespace import offskeeps its written callee. (#4308)
Type-aware analysis and caches
-
--type-aware-require completeno longer fails on a member with two or more exact uses. Asymbol-useresult now keeps one evidence item for each file that references the symbol, up to 40 items. Aconfirmed-usedresult with truncated evidence iscomplete. Thanks @BarrMan for the report. (#4256) -
The graph cache misses in three more cases. A warm run now rebuilds the graph when a file appears at a path that import resolution tried before (#4255). It also rebuilds after a Bun version change of a hoisted package and after a Deno install (#4305).
Command output
rule-pack,hooks install,ci-templateand the output schema give correct text.fallow rule-packprints3 rulesagain.fallow hooks install --target agentreports the real.gitignorechange and sayswould createin a dry run.fallow ci-template gitlab | headexits quietly, and the--vendorhelp names only the files that the flag writes. The output schema describesis_internal_dependencyoffallow workspacescorrectly. (#4279)
Security
-
The VS Code extension requires an Ed25519 signature for every binary that it downloads. Before, the extension accepted a release asset without a
.sigfile when the asset matched the digest of the GitHub API. Every release since v2.45.1 signs each binary, so a normal install does not change. On the next start, the extension deletes an installed binary that has only a.sha256marker and downloads it again. When a release asset has no signature, setfallow.lspPathto afallow-lspbinary that you trust, then reload the window. (#4253) -
agent install,hooks install,initand their uninstall commands no longer write through a symlink that leaves the project. A cloned repository could commit a file such as.mcp.jsonor.husky/pre-commitas a symlink to a file outside the project. Fallow now refuses such a path, and also a dangling link.--usertargets and.git/hooksstill follow symlinks.agent installreports the row asskippedwith the reasonsymlinked_path.hooks installandinitexit with code 2. A link that stays inside the project still works. (#4254)
Upgrade notes
- The extraction, graph and duplication caches change, so the first run after the upgrade parses each file again.
banned-call,banned-effectandboundaries.calls.forbiddennow report each call site. A baseline from before the upgrade covers only the first site of each path, so the other sites show as new findings.fallow dupesclone line ranges change. A duplication baseline without fingerprints can need a new save. Afallow-ignorecomment that only matched a line outside the real clone no longer hides the clone.- Astro projects can get new complexity and CRAP findings from
<script>blocks. fallow securityevidence names thenode:path for a default or namespace import ofchild_processorvm.
Full Changelog: v3.40.0...v3.41.0