Skip to content

refactor(linters): add prefer-coerce-array rule - #45488

Draft
secustor wants to merge 2 commits into
renovatebot:mainfrom
secustor:refactor/45217-prefer-coerce-array
Draft

refactor(linters): add prefer-coerce-array rule#45488
secustor wants to merge 2 commits into
renovatebot:mainfrom
secustor:refactor/45217-prefer-coerce-array

Conversation

@secustor

@secustor secustor commented Aug 26, 2026

Copy link
Copy Markdown
Member

Changes

Adds a renovate/prefer-coerce-array oxlint rule and applies it across lib/.

x ?? [] and coerceArray(x) say the same thing, but only the second one is searchable and consistent, so the rule flags a ?? whose default is a bare [] literal. A meaningful default (x ?? [1]) and an asserted one (x ?? ([] as Foo[])) still pass, as does a destructuring or parameter default, where the helper has no equivalent form.

The rule is enabled for lib/** (including specs). tools/** is left out because it may not import from lib/.

Applying it rewrote 153 sites in 84 files. Three of those the helper cannot cover, and they are handled individually rather than silenced wholesale:

  • lib/workers/repository/package-files.ts — tuple destructuring default; coerceArray() widens the tuple to a union array, so the ?? [] stays with a disable comment
  • lib/workers/repository/updates/generate.spec.ts — defaults matchAll(), which yields an iterator rather than an array; same, with a disable comment
  • lib/modules/manager/mise/upgradeable-tooling.ts — declares a Record<string, string> return, so the ?? [] fallback was the wrong shape to begin with and becomes coerceObject()

A fourth, lib/modules/platform/github/index.ts, defaulted an Object.entries(...).find() tuple only to destructure one element out of it; that one is hoisted into a named variable in a separate commit, which drops the ?? [] altogether.

The rewrite is mechanical and behaviour-preserving otherwise: for a T[] | null | undefined input, coerceArray(x) and x ?? [] are equivalent.

#45216 (coerceObject) builds on this branch — the two touch overlapping lines in several files, so they are best reviewed and merged in order.

Context

Please select one of the following:

AI assistance disclosure

Did you use AI tools to create any part of this pull request?

  • No — I did not use AI for this contribution.
  • Yes — minimal assistance (e.g., IDE autocomplete, small code completions, grammar fixes).
  • Yes — substantive assistance (AI-generated non‑trivial portions of code, tests, or documentation).
  • Yes — other (please describe):

Claude Opus 5 via Claude Code wrote the lint rule and its tests, and ran the codemod that applied the rule across lib/.

Use of AI in replying to PR comments

Who answers review comments:

  • @secustor will read and reply directly. Name the account.
  • An agent will draft replies and @username will read them before they are posted. Name the account.
  • Nobody has explicitly committed to replying.

Documentation (please check one with an [x])

  • I have updated the documentation, or
  • No documentation update is required

How I've tested my work (please select one)

I have verified these changes via:

  • Code inspection only, or
  • Newly added/modified unit tests, or
  • No unit tests, but ran on a real repository, or
  • Both unit tests + ran on a real repository

tools/lint/rules/prefer-coerce-array.spec.ts covers the rule itself. The existing suite covers the rewritten call sites; tsc --noEmit, oxlint, biome and prettier are clean, and vitest run --changed passes apart from two failures that also reproduce on main in this environment (lib/logger/pretty-stdout.spec.ts colour detection and a git clone submodule test blocked by protocol.file.allow).

Closes renovatebot#45217

`x ?? []` and `coerceArray(x)` say the same thing, but only the second
one is searchable and consistent. The rule flags a `??` whose default is
a bare `[]` literal; a non-empty default or an asserted `([] as Foo[])`
still passes.

Applying it across `lib/` surfaced three spots the helper cannot cover,
which keep `?? []` with a disable comment or a narrower fix:

- `package-files.ts` destructures a tuple, which `coerceArray()` widens
- `generate.spec.ts` defaults `matchAll()`, an iterator rather than an array
- `mise/upgradeable-tooling.ts` declares a `Record` return, so its
  `?? []` fallback was wrong to begin with and becomes `coerceObject()`
`Object.entries(...).find()` returns a tuple, and defaulting it with
`?? []` only to destructure the second element reads as an array
default when it is not one. Name the found entry instead, which drops
the `?? []` rather than routing a tuple through `coerceArray()`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint: find usages that should be coerceArray

1 participant