Skip to content

v3.28.0: baselines carry a kind, request outcomes on every surface, Module Federation and Nuxt reads

Choose a tag to compare

@BartWaardenburg BartWaardenburg released this 22 Sep 06:27
· 638 commits to main since this release
Immutable release. Only release title and notes can be modified.
v3.28.0
bd8fca5

Features

  • Every saved baseline now says which command wrote it. --save-baseline writes a top-level kind field with dead-code, dupes or health. Older baselines without it still load, and an older fallow still loads a baseline saved by this release.
  • Pointing --baseline at the wrong command's file is no longer silent. The run prints a warning with both command names and the path, and the file suppresses nothing. In the JSON output, baseline_staleness.unrecognised_format is true. The same warning appears in the pull-request comment, the merge-request note and the Check Run. The GitHub job summary shows the file path through the new baseline-path output, and fallow audit prints one warning per baseline it cannot use.
  • The JSON output says what narrowed a run. baseline_staleness.scope_reasons lists the reasons, for example changed-since or production. A narrowed run with a baseline also gets a recheck-baseline entry in next_steps, and the bare fallow command gets it too.
  • request_outcomes is now in the output of fallow flags, fallow suppressions and fallow security. The first two report their changed-since entry, and fallow security --sarif-file gets a sarif-file entry.
  • An empty diff no longer looks like a clean project. When a diff filter applies, its request_outcomes entry has a scope_size with the number of added lines. A diff with zero added lines reports scope_size: 0. The Action, the GitLab template, the MCP tools and the pull-request comment then all say that the clean result covers nothing.
  • hotspots-skipped now has a cause: not-a-repository, invalid-since or churn-file-unreadable.
  • A config that a plugin cannot read shows up in workspace_diagnostics. When a Module Federation exposes or remotes is not a static object literal, the run records plugin-config-unreadable with the plugin, the key and the reason. It sets degrades_analysis, so the Action and the GitLab template print it in their degraded-inputs warning. Nuxt has a related case: a components: or imports: shape that fallow cannot model. Such a project records plugin-effect-not-modeled, and that entry is silent.
  • The JSON output and both CI integrations report a run that widened to the whole project. They also report a --sarif-file that could not be written, and a format that cannot group results with --group-by.
  • fallow reads Module Federation exposes and remotes. It reads module-federation.config.* and inline plugin options in webpack, rspack, rsbuild, vite and Next.js configs. The plugin call can sit anywhere in the config: in a nested plugin array, in a variable, under tools.rspack.plugins or in a webpack(config) hook. Options held in a const in the same file are read, and so is the array form of exposes. Each exposes target becomes a runtime entry point, and each remotes alias counts as a provided dependency for the code under that config's directory.
  • Nuxt autoImports credits a global/ or islands/ component under the name Nuxt gives it. Each workspace root now gets its own verdict from its own config. components: true, imports: {} and imports: { dirs: [] } count as the Nuxt default. A name imported or re-exported from #components or #imports marks its file as used, and #layers/<name>/ works as a path alias.

Changed

  • fallow dead-code no longer exits 2 when the baseline belongs to another command. It warns and continues. --fail-on-stale-baseline fails the run on such a file. Saving over a baseline of another kind is refused with exit 2.
  • The GitHub Action and the GitLab template log why a JSON read failed. The cause goes to the debug log, and a green run gets no extra line.
  • fallow security --base warns when it cannot map the analysis root into the base worktree.

Bug fixes

  • A package name in a bundler entry is credited as a dependency. A value like react-hot-loader/patch or webpack-hot-middleware/client?reload=true in a webpack, rspack or rsbuild entry used to become an entry pattern that matched no file. The package then showed up as unused.
  • The MCP audit and decision_surface tools detect the base ref again. This regressed in 3.1.0. Thanks @codingthat for the report and the reproduction (#2699).
  • autoImports: true works in a Nuxt project that turns the scan off. components: false, imports: { scan: false } and the other scan-off shapes no longer count as a custom layout, so unused convention files are reported. Thanks @Tsuyoshi84 for the report (#2695).

Upgrade notes

  • A CI job that points --baseline at another command's file now fails when --fail-on-stale-baseline is set. Give each command its own baseline file.
  • A Nuxt project with autoImports: true and one of the scan-off shapes will see new unused-file findings. Add a file to entry when it is used in a way fallow cannot see.
  • If you switch on workspace_diagnostics[].kind, handle the two new kinds. Every new JSON field is optional.

Full Changelog: v3.27.0...v3.28.0