Repository navigation
chore(deps): update dependency fallow to v3.41.0 - #967
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 #967 +/- ##
=======================================
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:
|
439307a to
6e424b7
Compare
6e424b7 to
9ed0997
Compare
58273ad to
c94ab66
Compare
c94ab66 to
855595e
Compare
55f4474 to
3285ce4
Compare
3285ce4 to
2ddd231
Compare
|
@copilot fix ci fail |
Co-authored-by: fengmk2 <156269+fengmk2@users.noreply.github.com>
Fixed in |
Edited/Blocked NotificationRenovate will not automatically rebase this PR, because it does not recognize the last commit author and assumes somebody else may have edited the PR. You can manually request rebase by checking the rebase/retry box above. |
Note
This PR body was truncated due to platform limits.
This PR contains the following updates:
3.36.0→3.41.0Release Notes
fallow-rs/fallow (fallow)
v3.41.0Compare Source
Added
fallow mutation taskslists the open tasks of a mutation report as one batch. A task is asurvivedorno_coveragemutant with 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 the newmutation-tasksenvelope. Without--report, the command reads the report of the last run. It runs no code. The new MCP toolmutation_tasksreturns the same batch and runs no mutants.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: it reruns eachsurvivedandno_coveragemutant of the saved report (or the ids of--mutant), and the newrecheckobject of the report gives each mutant the outcomekilled,survived,no_coverage,tests_fail_on_originalorunsettled. The worktree may change test files only: a selected file that changed after the report stops the recheck withFALLOW_MUTATION_SOURCE_CHANGED, and 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.fallow list --entry-pointsand--trace-filename the package.json script of a script entry point.The human output shows the script key, for example
scripts/build.js (package.json script `build`)and(entry point, package.json script `build`). The JSON output adds theoptional field
scriptto eachentry_points[]item and the optional fieldentry_scriptto the file trace. Thesourcestring stayspackage.json script. When two scripts run the same file, the output namesthe script with the first key in alphabetical order.
(Closes #4175)
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 each mutant that waskilledortimeoutin the saved report and issurvivedorno_coveragenow asregressed, with the earlierprevious_killing_tests. The human output names the test that killed the mutant before. Aflakymutant in either run isinconclusive, and a mutant id in one run only isaddedorremoved. A saved report or a run that is not complete exits 2 withFALLOW_MUTATION_COMPARE_INCOMPLETE.(Closes #4168)
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. The members that itspackageslist declares are now workspaces, andfallow list --workspacesshows them under the nested project root. Acatalog:orcatalog:<name>reference in the nested project resolves against the nestedpnpm-workspace.yaml. Theunused-catalog-entries,empty-catalog-groupsandunresolved-catalog-referencesfindings of the nested project name the nested files, andfallow fixremoves an unused entry from the nested file. Apackagesentry of the nestedpnpm-workspace.yamlwhose directory does not exist gets aworkspace-pattern-dir-missingdiagnostic, and the unused entries of the nested catalog are then not reported. A member that a root workspace pattern declares keeps the root catalog. The root catalog checks do not change. Nested npm and yarn workspaces are not read.(Closes #4177)
Changed
More repository checks run in cargo-fallow. The
Rust StructureCI job now also checks the bounded parser call sites, the Miri test build, the feature sets offallow-cli, and glob re-exports of the internal type modules. It also enforces the growth of large.rsfiles in the samecargo fallow checkrun. cargo-fallow replacesscripts/check-parse-sites.mjs,scripts/check-miri-cfg.mjsandscripts/check-cargo-features.mjs. The duplication gate now counts production code only. This change affects contributors only.The Rust structure checks use cargo-fallow 0.3.0. The parser call site rule now fails when a listed file has fewer calls than its
max, and the files that wrap their own calls must callparse_bounded. The visibility check counts the uses in thefuzz/workspace, so the suppression forfallow_core::parse_scriptis gone. This change affects contributors only.An audit review comment of an inherited complexity finding names the base values. In a
fallow auditrun with--format review-github,review-gitlaborcodeclimate, the text of an inherited complexity finding now 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. (Closes #4196)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 removes only the candidates of the targets that cover it. Before, one incomplete mutant removed the candidates of all targets. The run-wide reasonspartial-mutation-run,baseline-failure, andmissing-test-file-metadatastill remove all candidates. The top-levelstatuskeeps its meaning, socandidatescan be nonempty whilestatusisincomplete. The human report shows the status of each target.(Closes #4169)
The JS test suites no longer leave
fallow-*folders in the system temp directory. The mutation runner tests give each companion a private temp directory and remove it when the companion ends, as the mutation API does. The script tests create their temp folders throughscripts/lib/temp-dir.mjs, which removes them when the tests of the file end. The newscripts/check-temp-leaks.mjsruns a command and fails when the command leaves a newfallow-*entry in the temp directory. CI runs each JS test suite through it. This change affects contributors only.Removed
fallow-api,fallow-types,fallow-output,fallow-config,fallow-report-protocol,fallow-licenseandfallow-v8-coveragekeep their exported items as entry points of the visibility check. The other workspace crates lost thepubitems that no other crate uses, including the deprecatedfallow_core::analyze::feature_flagsfunctions. Usefallow_api::run_feature_flags. The workspace Rust crates are not a semver-stable API (seedocs/backwards-compatibility.md). The CLI, JSON, npm and protocol surfaces do not change.Fixed
banned-call,banned-effectandboundaries.calls.forbiddenreport each call site. Before, these rules reported only the first call of a callee path in a file. Onefallow-ignore-next-linecomment above that call hid every later call of the same path, so a gate passed that must fail. Now each call ornewsite gets its own finding, and a suppression comment covers only the site on its next line. After the upgrade, a project with repeated calls of a banned callee can see more findings. A baseline that you saved before the upgrade covers only the first site of each path, so the other sites show as new findings. The extraction cache version changes, so the first run after the upgrade does a full parse. Thanks @Smrtnyk for the report. (Closes #4210)--type-aware-require completeno longer fails on a member with two or more exact uses. Before, asymbol-useresult kept one evidence item and reported the other references as anevidence-limitgap. Aconfirmed-usedresult was thenpartial, and the strict gate failed the run. Now asymbol-useresult keeps one evidence item for each file that references the symbol, up to 40 items, and aconfirmed-usedresult with truncated evidence iscomplete. Thus a read in a reachable file still credits the use when an unreachable file references the member first.total_evidence_countandtruncatedstill report the number of references. Other gaps, for exampledynamic-member-access, still make the resultpartial. A negative assertion, for exampleconfirmed-no-static-references, still needs complete evidence. When the 40 returned items give no reachable read and the limit dropped references, the candidate now shows theevidence-limitreason. Thanks @BarrMan for the report. (Closes #4229)The graph cache now misses when a file appears at a path that import resolution tried and did not find. Before this fix, a warm run could keep a stale import target after a new extensionless file, a new
.jsonfile, or new build output in an ignored directory. For example, a new filesrc/utilchanges the target of./utilfromsrc/util.ts, but discovery does not list it. The graph cache now stores the paths that resolution checked and did not find, and the resolved targets that discovery does not list. A warm run rebuilds the graph when one of these paths appears or is removed. (#4233)fallow health --csscounts@keyframesin SCSS and Less sources. This includes a Vue or Svelte<style lang="scss">block. Ananimation: spin 1snext to its@keyframes spinno longer gives a false undefined-keyframes finding. When a Sass or Less animation value holds a variable or an interpolation that fallow cannot resolve, the unreferenced check skips the@keyframesblocks that Sass or Less sources define. The new summary fieldkeyframes_uncheckedgives their count. Thanks @Smrtnyk for the report. (Closes #4214)fallow mutationno longer counts bench files and__tests__helpers as test files. Before, the plan counted a Vitest bench file (benchmarks/add.bench.ts) and a helper module below__tests__that another module imports (src/__tests__/utils.ts) as static test files. The runner did not list them, so each run waspartialwithtest-files-not-discovered, also when every real test ran. Now the plan leaves these files out, and such a run iscomplete. A source file that only a bench file reaches has no coverage. (Closes #4245)The mutation result cache now keys on the
bunfig.tomlpreload files when no package enables the Bun plugin. Before, only a project with@types/bunorbun-typesgave its preload files to the key. In another project, a change to a preload file kept the stored results, so abun testrun could report a stalekilledorsurvivedstatus. Eachpreloadand[test] preloadfile of the rootbunfig.toml, and each file that it imports, is now a key part.fallow mutationaccepts a test name with a lone UTF-16 surrogate. Before, a Vitest test name such as"a\uD800"stopped the dry run withFALLOW_MUTATION_RUNNER_FAILEDand the messagea response line is not JSON. Now the companion writes each lone surrogate as U+FFFD, in the test id and in each other string of a response. A mutant run selects the test with the same id, so the test still runs. (Closes #4244)The Nuxt plugin reads the
dirobject ofnuxt.config. A string value ofdir.pages,dir.layouts,dir.middlewareordir.pluginsadds theconvention entry points of that directory, relative to
srcDir. A stringvalue of
dir.modulesadds them relative to the project root, as Nuxt does.Files in a custom directory are no longer reported as unused.
(Closes #4176)
A
sh -cscript keeps the entry points of each chained command. In apackage.json script such as
sh -c "echo start && node tool.js", fallowfound only the command before the first operator. It reported
tool.jsasan unused file. Fallow now reads the quoted body of
sh -c,bash -c,zsh -canddash -cas a command list, with&&,||,;and|.This is also true when
cis in a flag cluster, as inbash -lc. Anoperator in quotes no longer splits a command, so a quoted argument such as
"x; node b.js"does not give a false entry point.(Closes #4174)
fallow rule-pack init,listandtestshow the noun again. The human output printed3 sfor three rules and1for one finding. It now prints3 rulesand1 finding.fallow hooks install --target agent --gitignore-claudereports the real.gitignorechange. When.gitignorealready lists.claude/, the summary line saysunchanged (.claude/ already listed). Before, it said.claude/ addedand the file did not change. A dry run now also shows the.gitignoreline, withwould add .claude/orunchanged.A dry run of
fallow hooks install --target agentsayswould createfor the hook config file. The.claude/settings.jsonand.codex/hooks.jsonlines saidcreated(orupdated) although the dry run writes no file. They now use the samewould ...wording as the other dry-run lines.fallow ci-template gitlab | headexits quietly. A closed stdout reader made the command abort with a panic. The command now stops the output and exits with code 0, as the other commands do.The help of
fallow ci-template gitlab --vendornames only the files that it writes. The help said that the flag also writesaction/files. The flag writes only theci/files.The output schema describes
is_internal_dependencyoffallow workspacescorrectly. The field istruewhen another workspace of the project lists the package as a dependency.fallow mutationno longer leaves the temp files of killed test runs on the disk. When the runner killed a test run (for example after a timeout), the teardown of the test suite did not run, and its temp files stayed in the system temp directory. A large suite could fill the disk this way. Each companion now gets its own temp directory throughTMPDIR,TMPandTEMP, and Fallow deletes it when the companion ends. The next run also deletes the directory of a Fallow process that ended on a signal or a crash. This applies to each runner (Vitest,node:testandbun test).fallow securityreports raw SQL methods on a call result or athisfield. Thesql-injectionrows forwhereRaw,havingRaw,orderByRaw,$queryRawUnsafeand$executeRawUnsafenow match a call on a call result, for exampledb("t").whereRaw(x),knex("users").where("id", 1).orderByRaw(x)orgetPrisma().$queryRawUnsafe(x). They also match a call on athisfield, for examplethis.prisma.$queryRawUnsafe(x). The knex builder and a NestJS Prisma service use these forms. The callee readsdb().whereRaworthis.prisma.$queryRawUnsafe. A literal argument gives no candidate, and other*.rows keep their current behavior. (Closes #4225)A framework path alias of a workspace package no longer resolves into another package. In a monorepo with two SvelteKit apps,
$lib/fooin one app could resolve tofooin thesrc/libof the other app. Fallow then reported falseboundary-violationfindings and reported the real files of the importing app as unused. A plugin alias from a workspace package (SvelteKit$lib, Nuxt~/and the others) now applies only to the files of that package. An alias from the project root still applies everywhere. When the package has no target for the import, fallow now reports anunresolved-importfinding, also for SvelteKit$lib. SvelteKit$libalso followskit.files.libinsvelte.config.js.Thanks @MikeSpock for the report. (Closes #4246)
fallow dupesreports each section of identical Vue, Svelte and Astro files. When two or more component files were identical across more than one section, only the last section was reported. The<template>block and the<script>block (for example anexport default {}body) got no clone group. The detector now ends each clone at a section border, so each section of a copied component forms its own clone group with the correct line range in the component file.Thanks @levi-broeksma for the report. (Closes #4251)
fallow mutationserves a TypeScript mutant behind a project loader that compiles TypeScript. A loader such as amodule.register()hook can compile a.ts,.mtsor.ctsfile and give the formatmoduleorcommonjs. The overlay hook of the runner now strips the types of the mutant before it serves the mutant in that format. Before, the child process loaded raw TypeScript as JavaScript and failed with aSyntaxError. The run records an overlay error that names the file in two cases: type stripping does not accept the mutant (for example anenum), or the loader givescommonjsbut the mutant keepsimportorexportsyntax.(Closes #4172)
A root-only
pnpm installnow gives the type-aware sidecar its TypeScript. The rootpackage.jsonliststypescript7.0.2 as an exact devDependency. Before, only an override inpnpm-workspace.yamlnamedtypescript, and no root dependency used it, so the root install had notypescript. The lockstep test of the sidecar now checks the devDependency and the override.docs/development/quality-gates.mdnow says that the sidecartsrsbackend test needs the sidecar-local install. This change affects contributors only.(Closes #4202)
fallow healthnow scores the functions in Astro<script>blocks. Before, health scored only the frontmatter and the template markup of an.astrofile, so a complex client script passed the gate. Health now scores each inline<script>block, includingis:inlineanddefine:varsscripts, as it scores a Vue or Svelte<script>block. Each finding has its line and column in the full.astrofile. A block with asrcattribute or a datatype(for exampleapplication/ld+json) gets no score. Existing Astro projects can get new complexity and CRAP findings after the upgrade. The extraction cache version changes, so the first run after the upgrade parses each file again.Thanks @Djkde01 for the report. (Closes #4261)
An ESLint
extendsentry with a subpath now credits its package. Fallowused the full entry, such as
eslint-config-shared/back/typescript, as thepackage name. The package
eslint-config-sharedwas then reported as anunused dev dependency. Fallow now applies the ESLint naming rules and drops
the subpath, also for scoped names and short names such as
airbnb/hooks.When the config package is a workspace package that is linked into
node_modules, the file that the entry loads is now used, and its ownextends,pluginsandparserentries get credit.A workspace
eslint.config.mjsno longer hides the root.eslintrc.json.When a workspace flat config had an
import, fallow did not read the root.eslintrc.json,.eslintrc.yamlor.eslintrc.yml. The packages in itsextendswere then reported as unused. A plugin config in a source file nowdoes not stop the discovery of the JSON and YAML config files of the same
plugin.
A
thresholdOverridesentry that sets a lower limit no longer readsstalewhile it applies.An entry can set a
maxFileLines,maxCyclomatic,maxCognitive,maxUnitSizeormaxCraplimit that is lower than the global limit, or amaxFileLineslimit where the global limit is off. When the matched codebreaches that lower limit, the row now reads
active, andoutstandingnames the breach. Before, the row read
stale, and that told you to removean entry that produced the finding. A row reads
staleonly when the entrychanges no result. When a later entry replaces the lower limit, the earlier
entry reads
stale.(Closes #4173)
fallow mutationdeletes the companion temp directories when you stop a run with Ctrl+C. A signal ends Fallow without its normal cleanup, so the temp directories of the companions stayed on the disk until the next run. The signal handler now deletes them after it ends the companion processes. After the signal, Fallow starts no new companion and creates no new temp directory. A companion that starts while the handler runs is killed at once.A shadowing binding no longer credits class members through a
thismethod result local. In a class method,const table = this.find()credits the members that the method reads throughtable. A callback parameter, a loop head, acatchparameter or a block declaration with the same name holds another value. A read through that binding now gives no credit to the return type offind(). A shadow that aninstanceofguard, an element type or a same-file factory binds to a class credits that class. After the nested scope ends, the outer local gives credit again. This applies to a method of the class itself and to a method of a base class.(Closes #4198)
The graph cache now misses after a Bun version change of a hoisted package and after a Deno install. A
bun installcan replace a hoisted package with another version and keepbun.lockas it is, for example after a pull of a lockfile change. Before this fix, a warm run then kept a stale import target. The new version can have otherexports. The graph cache now stores theversionof each hoisted Bun package that import resolution read, and a warm run rebuilds the graph when one of these versions changes. The cold run reads only the packages that the imports reach, so the size ofnode_modulesdoes not change the cost of a warm run. The graph cache key now also readsdeno.lockand the package entries of the Deno store innode_modules/.deno. (Closes #4197)fallow securityreports a sink that a default or namespace import ofchild_processorvmbinds. Before,import cp from "node:child_process"; cp.exec(cmd)andimport * as proc from "child_process"; proc.execSync(cmd)gave nocommand-injectioncandidate, while the same code withrequire()gave one. A default or namespace import ofnode:child_processornode:vmnow resolves to thenode:path, as arequire()binding does. The callee inevidenceand incandidate.sink.calleechanges to match, for examplechild_process.exec()afterimport * as child_process from "child_process"becomesnode:child_process.exec(). A default or namespace import offskeeps its written callee, such asfs.readFile(), because the gatednode:fsrows lose an advisory hit of the security corpus.finding_iddoes not change. Thesecurity-check-removedevidence of the audit brief names such avmsink as for a named import:new Function sink replaced by runInNewContext. (Closes #4234)fallow securityresolves a call through a member binding of a Node module.import { promises as fsp } from "node:fs"; fsp.readFile(path)now givesnode:fs.readFile, and apath-traversalcandidate when the argument or the module reads untrusted input. Before, the callee kept its written path.fsp.readFilematched no row, and an alias with the namefs(import { promises as fs } from "node:fs") matched the ungatedfs.readFilerow. That row reports any computed path, so build and CLI code that uses this alias now gives fewer candidates.A
banned-effectpolicy rule follows the local name of an aliased destructuredrequire().const { exec: run } = require("child_process"); run(cmd)now gives ashelleffect. Before, the rule missedrun, and a local function namedexecin the same file matched instead.A Ctrl+C during
fallow mutationdeletes the companion temp directory also when the companion exit and the signal cleanup overlap. The directory now stays registered for the signal cleanup until its owner has deleted it, and the cleanup tries again when the owner deletes the same directory at the same time.fallow dead-codematches a dynamic import pattern with a literal suffix and no extension. An import such asimport(`./icons/__${name}__`)now loadsicons/__solid__.tsand the other files whose name ends with the suffix and a module extension. Before, these files and their exports showed as unused. A bundler adds the extension to the request, so fallow now does the same.fallow dead-codefollows a dynamic import of aconststring. Inconst name = 'some-package'; await import(name), the import now usessome-package, also when theconstis in a function. Before, the package showed as an unused dependency. A parameter, alet, or acatchbinding with the same name still gives no specifier.fallow dead-codefollows a method chain through methods that returnthis. Inthis.setId(id).setLabel(label), a method with the return typethisgives an instance of its class, sosetLabelnow counts as used. Before, each member after the first call in the chain showed as unused.fallow dead-codeno longer reports an npm alias from a catalog as an unused dependency when a script runs its binary. A manifest can declare"@typescript/native": "catalog:"with the catalog entrynpm:typescript@~7.0.2. Fallow now reads the catalog spec, so a script that runstsccredits@typescript/native, as it does for the inlinenpm:spec. This works for the default catalog and for named catalogs inpnpm-workspace.yamland in a Bun rootpackage.json. A catalog alias next to a plain declaration of the same package stays reported, because the plain declaration provides the binary.Thanks @cloud-walker for the report. (Closes #4258)
Audit review comments state the attribution of dead-code, stale-suppression, duplication and styling findings.
review-githubandreview-gitlabputintroducedorinheritedafter the severity label of these findings, as they do for complexity findings. Under--gate new-onlyan inherited finding now shows**error** · inherited · not gated (`--gate new-only`), because it does not gate the audit. The live audit run andfallow report --fromon a saved audit give the same text. The CodeClimate output and the fingerprints do not change.(Closes #4195)
A tsconfig
pathsentry of one workspace no longer makes the package imports of other workspaces unresolved. A"react": ["./node_modules/@types/react"]entry in one workspace made everyreactimport in every workspace an unresolved import when dependencies were not installed, and each finding had the incomplete import graph caveat. When the alias target is in thenode_modulesof another workspace, the import now keeps the package name that it declares. When the alias target is a package that is not installed, the import credits that package. For an@typestarget, the credit goes to the package that the@typespackage belongs to (reactfor@types/react). When thenode_modulesdirectory of the alias target exists but the package is missing, the import stays unresolved.emitDeclarationOnly: trueno longer stops the map from build output to source. With this setting,tscemits only declaration files, and another tool can emit the JavaScript intooutDir. An import ofdist/cli.js, for example from abinscript, now resolves tosrc/cli.tsunderrootDirwhen that file exists. Before, the source file was reported as an unused file. When no source file has the same name, the other entry fallbacks still apply.An
exportsorimportscondition that points to a missing file no longer hides the conditions after it. Fallow now tries each active condition of a package map in manifest order until one target resolves. Whentypescomes first and points to a declaration file indistthat is not built,resolve.conditions: ["source"]now makes a subpath import from another workspace resolve to thesourcefile, and the workspace dependency is no longer reported as unused. Node.js uses only the first matching condition, so a packageimportsentry such as"#x": {"import": "./missing.js", "default": "pkg"}now creditspkg. The cached import resolution changes, soGRAPH_CACHE_VERSIONgoes to 107.fallow dead-codecredits class members through an inherited method result in two more shapes. A subclass can read the result of an inheritedthis.find()when the base class is a class that the file does not export, at any depth of theextendschain. A local of a call with arguments, such asconst table = this.findBy(1); table?.reset();, now creditsreseton the return type, as the directthis.findBy(1)?.reset()does. Before, both shapes reported the members of the return type as unused class members. The extraction cache version changes, so the first run after the upgrade parses each file again.(Closes #4199)
fallow dupesreports clone line ranges that match the matched tokens. Before this fix, some tokens such asconst,if,{and binary operators had the source span of their full statement or block. A clone that started or ended on such a token covered all the lines of that block. One clone of 54 tokens covered 352 lines, andduplicated_linesandduplication_percentagewere too high. Each token now has the span of its own text. On a large monorepo, the reported duplication went from 22.6% to 16.9%. Some clone groups that the long ranges hid are now reported. Clone line ranges change, so afallow-ignorecomment that only matched a line outside the real clone no longer hides it, and a duplication baseline without fingerprints can need a new save. The duplication token cache version changes, so the first run after the upgrade tokenizes all files again.--save-baselinewarns when the path is in the fallow cache directory. Fallow writes a.gitignorewith*into its cache directory (.fallow/by default), so Git does not commit a baseline that you save there, for example--save-baseline .fallow/dead-code.json. Barefallow,dead-code,architecture,dupesandhealthnow print a warning on stderr for such a path. The warning tells you to use a committed path, for examplefallow-baselines/dead-code.json. The bundled skill now usesfallow-baselines/dead-code.jsonin its baseline example.fallow dead-codereads the tool that a Yarnruncall starts. Apackage script such as
run -T jest --config ./jest.config.front.jsoryarn run -T jest ...now counts as ajestcall. The Jest plugin thenreads the config file and its setup files. Yarn 2 and later puts the
runcommand on the
PATHof each package script. Fallow now also skips theyarn runflags-T,-B,--inspect,--inspect-brkand--require.fallow dead-codeno longer reports a JestmoduleNameMappertarget asan unused file. A target that names one file with
<rootDir>, such as'<rootDir>/src/test/mocks/inquirer.js', is now an entry point. A targetwith a
$1capture group or without<rootDir>adds no entry point.fallow dead-codefinds the scripts that the changesets GitHub Actionruns. The
versionandpublishinputs ofchangesets/actionare shellcommands, so a file that such an input runs, for example
node .github/changeset-version.js, is now an entry point. Thecwdinputsets the directory of these commands. The inputs of other actions add no
entry points.
A Ctrl+C during the dry run of
fallow mutationno longer prints a runner error. The signal kills the companion, and the run reported that exit asFALLOW_MUTATION_RUNNER_FAILEDwith"exit_code": 2, while the process exited with130. Fallow now prints no error while a signal ends the process, so the exit code and the output agree. A run that a signal stops during the mutant runs still prints the report of the finished mutants. (Closes #4302)An ESLint
extendsentry credits the files of a workspace package without an install. An entry such asshared/back/typescriptloads a file of the workspace packageeslint-config-shared. Fallow now finds that package by name when nonode_moduleslink exists, so the file and the configs that it extends are no longer unused files.A Jest
presetthat is a relative path is read as Jest config. A value such aspreset: "../../jest-preset.front.js"names a file, not a package. Fallow now marks the file as used and credits the packages and files that it names. A<rootDir>path in the preset resolves against the config that names the preset. A package insetupFiles,setupFilesAfterEnv,globalSetuporglobalTeardown, such astest-utils/setup, now credits that package. Manual mocks in__mocks__stay used when a config setstestMatchortestRegex, becausejest.mockreads them in all cases.Each condition target of a package.json
importsentry is used. For"#x": { "browser": "./x.browser.ts", "default": "./x.ts" }, an import of#xnow uses both files and the names that it imports from them. The targets of one entry are not reported as duplicate exports.Security
The VS Code extension requires an
Ed25519signature for every binary that it downloads. Before this change, the extension accepted a release asset without a.sigfile when the asset matched the digest of the GitHub API. A person who can upload release assets also controls that digest, so the check did not stop an unsigned upload. Now the extension refuses an asset without a signature before it downloads the binary. Every release since v2.45.1 signs each binary, so a normal install does not change.falloworfallow-lspbinary that has only a.sha256marker and no signature is deleted and downloaded again on the next start.fallow.lspPathto afallow-lspbinary that you trust, then reload the window.agent install,hooks install,initand their uninstall commands no longer write through a symlink that leaves the project. A cloned repository could commit a project file such as.mcp.json,.cursor/mcp.json,.codex/config.toml, a skill directory,AGENTS.md,.husky/pre-commitor.gitignoreas a symlink to a file outside the project. Fallow then wrote, backed up or deleted that outside file, and--forcecopied its content into the working tree. Fallow now refuses a project path when the path, or a directory on the way to it, is a symlink that resolves outside the project root or a dangling link.agent installreports the row asskippedwith the new reasonsymlinked_path.hooks installandinitexit with code 2. A link that stays inside the project, such asCLAUDE.md -> AGENTS.mdor.claude/skills -> ../.agents/skills, still works.--usertargets and.git/hooksstill follow symlinks. The skill step also readsnode_modules/fallow/skillsonly from files inside the fallow package, so a link in the package cannot copy an outside file into the project.v3.40.0Compare Source
Added
The
fallowskill now points agents at the hosted Fallow Cloud MCP server.references/mcp.mdgives the endpointPOST https://api.fallow.cloud/mcp, the Bearer read-only or live API key, the Claude Code connect command, the explicit arguments that the hosted server needs, and the tools that only the hosted server has:get_file_context,verify_deployment,list_errors,get_branch_usage,get_runtime_dependencies,get_deploymentsandget_repo_activation.fallow coverage upload-inventory --with-callersuploads the entry routes that can reach each function. The upload body (version 4) adds aroutesarray and arouteIdsarray per function. Thenextjs,remix,sveltekitandnuxtplugins find the route files, and the CLI takes each route pattern from the file name, never from a request path. A route handler gives one route per exported HTTP method. Reach follows the runtime imports of the module graph, so it means "can reach", not "did reach". The upload keeps at most 500 routes and at most 50 route ids per function, lowers the per-function cap when the total would grow too large, and reports each cut in arouteLimitsheader and a warning.fallow mutation --kill-matrix-for <target>checks whether named tests are redundant without a full kill matrix. A target is a test file or a test ID (file::name). Repeat the flag for each target. Only the mutants that a target test covers run. Each mutant runs its target tests with bail disabled, then its other covering tests with bail enabled, up to the first failure. The other mutants areprunedwithprune_reason: "no-target-coverage". For each target test,kill_matrix.testsreportskills,unique_kills(kills that no other test detects), and the newtarget_only_killsfield (kills that only other targets share, each with those targets). The newkill_matrix.targetsfield lists each target with the test IDs that it selects. A target that selects no test of the dry run exits2withFALLOW_MUTATION_TARGET_UNMATCHED. The flag and--kill-matrixare exclusive. Thanks for the detailed proposal.(Closes #4061)
The new
duplicate-export-aliasesrule reports one binding that one file exports under two or more names. Examples areexport const layer = make(); export default layer;,export { a, a as b }andexport { a as default }next toexport const a. Named re-exports of one source name from one source also count. Fallow does not report two bindings with the same value, a value and a type that share a name, function overloads,export *, or a file with CommonJS exports (export =). The existingduplicate-exportsrule does not change: it still reports one export name in several files. The new rule defaults towarn, so a run without--fail-on-issueskeeps its exit code.--fail-on-issuespromoteswarntoerror, so a project with aliased exports can now exit with code 1 under that flag. To keep the old result, set"duplicate-export-aliases": "off"inrules.duplicate_export_aliases. Each entry haspath,local_name,export_names,line,col,locations(one{export_name, line, col}for each name),finding_id, andactions. The actions are a manualremove-duplicatefix and a line suppression. Neither action is auto-fixable.summary.duplicate_export_aliasescounts the findings.fallow/duplicate-export-alias. The human, compact, markdown, GitHub summary, GitHub annotation, LSP, and baseline outputs report the finding too.ignoreExportsexempts a whole file with"*", or a binding when the entry lists one of its names.// fallow-ignore-next-line duplicate-export-aliasabove any of the names hides the finding.// fallow-ignore-file duplicate-export-aliashides all findings of the file.--duplicate-export-aliaseslimits a dead-code run to this rule.fallow migratemaps theduplicatesissue type of the source config toduplicate-export-aliases, and still maps it toduplicate-exports. A file-scopedduplicatesentry inignoreIssuesnow becomes an override that turnsduplicate-export-aliasesoff for the glob. Before this change, migrate warned that the entry had no equivalent.New
unlisted-binariesrule. Fallow now reports a binary that apackage.jsonscript or a process call in source runs, when no installed or listed package provides it. Source calls arespawn,spawnSync,exec,execSync,execFileandexecFileSyncofnode:child_process,execa,execaCommandand the$template ofexecaandzx, andBun.spawnandBun.$, each with a literal command. A binary counts as provided when anode_modules/.binlink exists, when a declared dependency or a workspace package declares it inbin, or when it maps to a declared dependency. Shell builtins, commands thatnpxorpnpm dlxdownload, and a fixed list of common operating-system tools (such asgit,ssh-keygen,curlandffmpeg) are skipped. A project without a rootnode_modulesreports nothing. The rule defaults towarn.New
ignoreBinariesconfig. It takes exact binary names and globs, likeignoreDependencies. Each finding carries anadd-to-configaction for it.fallow migratemapsignoreBinaries(top level and per workspace) and thebinariesrule.The JSON envelope gains the
unlisted_binariesarray andsummary.unlisted_binaries. SARIF, CodeClimate, compact, markdown, human output, GitHub annotations and summaries, the LSP, the MCPanalyzetool and the VS Code extension report the finding. The--unlisted-binariesflag scopes a run to it.The parse cache version changes because the cache now stores the command calls of each file. The first run after the upgrade parses every file again.
New rule
unused-namespace-membersreports an unused member of an exported TypeScript namespace. Forexport namespace Legacy { export const used = 1; export const unused = 2 }, fallow now reportsLegacy.unusedwhen no code reads it. The rule also coversexport declare namespacein a.tsfile, nested namespaces (the member name is a dotted path such asOuter.Inner.x), and a namespace that merges with a class, a function or an enum. A member counts as used when code reads it asN.a(as a value or a type), destructures it, aliases it withimport x = N.a, or reads it inside the namespace body. When the namespace object leaves its chain (an argument, an alias, a spread orObject.keys(N)), every member counts as used. Fallow does not check declaration files,declare global, module augmentations, local namespaces, or a namespace that an entry point exports. The findings appear in JSON (unused_namespace_members), SARIF, CodeClimate, compact, markdown and human output, in the LSP, the MCP server, the VS Code extension, the GitHub Action and the GitLab template. A finding on a run with an incomplete import graph carriesreachability_caveats[]. Ten arrays carry it:unused_namespace_members[]joins the nine arrays that carried it before. Suppress a finding with// fallow-ignore-next-line unused-namespace-member. The rule defaults towarn, so the CLI exit code does not change. The dead-code count gate of the GitHub Action countswarnfindings unlessfail-on-issuesisfalse. To turn the rule off for generated files, set"unused-namespace-members": "off"inoverrides.fallow migratemaps thenamespaceMembersissue type of a migrated config to this rule. (Closes #4209)The parse cache version changes because the cache now stores the namespace member reads of each file. The first run after the upgrade parses every file again.
fallow securityrecognizes more callees in existing sink categories. The new callees are:command-injection:shell.execfromshelljs, andexecFileandexecFileSyncfromnode:child_process.execFileruns no shell, so it reports only when the argument or the module reads untrusted input.ssti:Handlebars.compile,pug.render,_.templateandlodash.template.ssrf:axios.put,axios.patch,axios.delete,axios.head, a bareaxios()call with a template, a concatenation or a call result as its URL, thegotmethod calls, andhttp.getandhttps.getfrom the Node modules. Anaxios(config)call stays quiet, and so does anhttp.getthat the module does not import fromnode:http.unsafe-deserialization:loadAllfromjs-yaml.xxe:libxmljs.parseXml.jquery-html:$.parseHTMLandjQuery.parseHTML.route-send-file:res.downloadandresponse.downloadin Express projects.path-traversal: namednode:fsandnode:fs/promisesimports such asimport { readFileSync } from "node:fs", and thefswrite, append, delete and copy calls. These rows report only when the argument or the module reads untrusted input, as thepath.joinrow does. A local helper with the same name as anfsfunction stays quiet.Existing candidates do not change.
workspaces.settingssetsignoreDependenciesandincludeEntryExportsfor each workspace. A key is a workspace root asfallow list --workspacesprints it, or a glob such aspackages/*. A workspace gets the globalignoreDependenciesplus the list of each key that selects it. An unused dependency uses the workspace of itspackage.json, and an unlisted dependency uses the workspace of the importing file. ForincludeEntryExports, an exact root wins over a glob, and a longer glob wins over a shorter glob. The--include-entry-exportsflag still turns the check on everywhere. A key that selects no workspace gives a warning, and an unknown key underworkspacesgives a warning too. The global keys do not change.supportEntrydeclares entry points that do not run in production. In a normal run, asupportEntryfile keeps its imports reachable, the same asentry. In production mode (--productionorproduction: { deadCode: true }), fallow leaves a file that onlysupportEntrymatches out of the analysis. Its imports then credit no export, file or dependency. A file thatentry, a plugin or apackage.jsonfield also declares stays an entry point.fallow migratekeeps per-workspace settings and production entry markers. WorkspaceignoreDependenciesandincludeEntryExportsbecome `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.