Skip to content

Bump @exadev/eslint-config to 2.12.1 - #31

Merged
Mearman merged 12 commits into
mainfrom
chore/bump-exadev-eslint-config
Sep 14, 2026
Merged

Mearman merged 12 commits into
mainfrom
chore/bump-exadev-eslint-config

Conversation

@Mearman

@Mearman Mearman commented Sep 14, 2026

Copy link
Copy Markdown
Member

Bumps @exadev/eslint-config from ^2.1.1 to the exact 2.12.1, and picks up everything else this repo's own dependency-hygiene conventions call for while touching it.

What's in here

  • @exadev/eslint-config pinned to the exact 2.12.1 (was a caret range).
  • saveExact/minimumReleaseAgeExclude settings in pnpm-workspace.yaml (the location pnpm 11+ actually reads) plus a .npmrc fallback for pre-10.17 pnpm 10.x. The existing @exadev/* exclude entry already covered @exadev/eslint-config, so nothing needed adding there.
  • The pinned packageManager bumped from pnpm@11.6.0 to the exact pnpm@12.4.1, via corepack use, as its own commit.

Fallout from the version jump

This repo's old ^2.1.1 pin predated several breaking-in-practice changes upstream: the config's default export switched from recommendedTypeChecked to strictTypeChecked (2.7.0), a JSDoc/TSDoc quality pass was added (2.11.0), and the two rules the task specifically called out (no-warning-comments for Stryker suppression comments, max-lines) landed in 2.12.0. Neither of the two headline rules actually fired here (no Stryker comments, no file over 800 lines), but the strictTypeChecked switch alone surfaced real violations that needed fixing at the root, not suppressing:

  • PackageReleaseOutcome's version/gitTag/type fields were three independently-optional fields that every producer set together or not at all — now a proper released-discriminated union.
  • promisify(execFile) in git.ts/pnpm.ts tripped strict-void-return (execFile synchronously returns a ChildProcess, which tsc tolerates but the stricter rule doesn't); replaced with a small hand-written promise wrapper.
  • Several magic numbers, an undertyped lastRelease shape from semantic-release's own types, a handful of scoped-package-name doc comments parsing as JSDoc tags, and a few other strict-mode violations.

Each is its own commit with a description of the actual code change. Everything is a real fix, not a suppression — no eslint-disable, no downgraded dependency, no file-level ignore.

Verification

lint, typecheck, test (94 tests), and build all pass, both before and after the pnpm@12.4.1 bump (also re-verified pnpm install --frozen-lockfile under the new engine).

Moves off the ^2.1.1 range onto an exact version, matching this repo's
new saveExact default for @ExaDev packages. Pulls in strictTypeChecked
as the config's default export (up from recommendedTypeChecked), the
barrel-policy and readonly-safety rule families added since 2.1.x, and
the new no-warning-comments/max-lines rules from 2.12.0.
Sets saveExact in pnpm-workspace.yaml (the only location pnpm 11+
reads project settings from) so every future pnpm add records the
resolved version rather than a caret range, and adds a matching
.npmrc as a fallback for a pnpm 10.x release older than 10.17. The
existing @exadev/* minimumReleaseAgeExclude entry already covers
@exadev/eslint-config, so no change was needed there.
typescript-eslint's own tseslint.config is deprecated as of the
version this repo now pulls in, in favour of the array-flattening
helper ESLint core itself now ships. Behaviourally identical here:
same nested arrays and spread configs, just built by defineConfig
instead of the deprecated wrapper.
…en promise wrapper

Replaces promisify(execFile) in git.ts and pnpm.ts with a small,
explicitly-typed execFile helper: execFile synchronously returns a
ChildProcess in addition to invoking its callback, which the newly
enabled strict-void-return rule correctly flags as a value-returning
function handed to promisify where a void-returning one is expected,
a mismatch tsc itself accepts under its own return-type leniency.
Calling execFile directly with our own void callback avoids the
mismatch instead of working around it.

git.ts's own git() now derives its 100 MiB maxBuffer from a single
literal default parameter rather than a multiplied module constant,
sanitizeGitEnv() deletes discovery-affecting env keys via
Reflect.deleteProperty instead of the dynamic delete operator, and
workingTreeChanges() slices a porcelain entry down to its path and
checks for emptiness instead of comparing its length against a bare
minimum, removing every magic number from the parse.

git-workspace-fixture.ts drops its own duplicate promisify(execFile)
lockfile regeneration in favour of calling pnpm.ts's regenerateLockfile
directly.
semantic-release's own getLastRelease returns {} for a package with
no prior tag, not undefined and not a fully-populated LastRelease,
contradicting the gitHead: string its own type declares. A new
hasGitHead structural guard reads gitHead only when it is genuinely
present, replacing an optional chain and a `?? undefined` that were
provably redundant against the (inaccurate) declared type. The two
inline analyzeCommits/generateNotes plugins now check their upstream
result with typeof rather than a bare truthy check, since both can
return the empty string as a real, meaningful value distinct from
false/undefined. Also renames the deprecated NodeRequire type to
NodeJS.Require, converts DependencyBumpSource.bumpsFor to a property
signature per this config's method-signature-style rule, and escapes
scoped package names in two doc comments so they don't parse as
inline JSDoc tags.
version, gitTag, and type were three independently-optional fields
that every one of the four sites building a PackageReleaseOutcome
actually set together or not at all, tied to whether the package
released. Splitting the type into a released: true branch (where all
three are required strings) and a released: false branch (where none
are present) lets a caller that has already checked pkg.released read
gitTag/type as plain strings, and turns every constructor into an
explicit branch on the same condition instead of three parallel
optional-chained assignments.

Also stringifies the numeric counts interpolated into several log and
error template literals, wraps four logger callbacks that previously
returned shared.log's own return value in a block body so they return
void as their callers expect, and escapes scoped package names in a
few doc comments so they don't parse as inline JSDoc tags.
restrict-template-expressions no longer allows a bare number in a
template literal. Both are simple .length counts, so String() is a
plain, behaviour-preserving wrap. Also widens collectRepeated's
previous parameter to readonly string[], since it only ever reads it.
…s array

validate-npm-package-name's own overloaded return type guarantees
errors: string[] (not optional) once validForOldPackages is false, so
the ?? [] fallback could never actually run; TypeScript's own
narrowing already proves this once the guard above it is in scope.
@ExaDev, @semantic-release, and @types read as inline JSDoc/TSDoc
tags to the new doc-comment quality rules this bump enables.
Backtick-wrapping each name as an identifier resolves both, and
reads no differently.
Each callback's body is a bare return of rm(...), a promise-returning
call, which promise-function-async now requires the callback itself
to be declared async for.
…tions

Every fixture-backed test's third-argument timeout (240_000, 60_000,
or 20_000ms) was a bare literal repeated across four files, which the
newly enabled no-magic-numbers rule flags wherever it appears,
including a well-named module constant: the shared config exempts
enum members from that rule but not const declarations. A new
TestTimeoutMs enum in test-timeouts.ts replaces every one of those
literals with a named tier (Long/Medium/Short) shared across the
suite instead of redeclaring it per file.

A handful of assertions also hardcoded a package or commit count that
was really a property of the fixture array already in scope
(chainPackages.length, or a newly-named cyclePackages.length + 1 for
createWorkspaceFixture's own "one scaffold commit plus one per
package" invariant) -- deriving it from that array instead of a
literal removes the magic number and keeps the assertion correct if
the fixture ever grows.
Run via `corepack use pnpm@12.4.1`, which also regenerates the
lockfile for the new engine: pnpm 12 records its own executable as a
packageManagerDependency in a new leading YAML document ahead of the
existing workspace lockfile, rather than changing that document's own
shape. install (plain and --frozen-lockfile), lint, typecheck, test,
and build all pass unchanged under the new engine.
@Mearman
Mearman marked this pull request as ready for review September 14, 2026 09:06
@Mearman
Mearman merged commit bb7ebcc into main Sep 14, 2026
9 checks passed
@Mearman
Mearman deleted the chore/bump-exadev-eslint-config branch September 14, 2026 09:07
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 14, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review Completed 2026-09-14T09:12:22.873143Z 5946048 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.3.6 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant