Skip to content

build(deps): bump commander from 14.0.3 to 15.0.0 (with jest ESM transform fix) - #91

Merged
sjsyrek merged 1 commit into
mainfrom
deps/commander-15
Jul 27, 2026
Merged

build(deps): bump commander from 14.0.3 to 15.0.0 (with jest ESM transform fix)#91
sjsyrek merged 1 commit into
mainfrom
deps/commander-15

Conversation

@sjsyrek

@sjsyrek sjsyrek commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Completes the commander 14.0.3 → 15.0.0 bump that #83 could not land on its own. Supersedes #83 — the dependency bump is dependabot's; this adds the one change needed to make it pass.

Why #83 failed

commander 15 changed two things that mattered:

commander 14 commander 15
Module format commonjs module (ESM-only)
engines.node >=22.12.0

Under ts-jest's CommonJS test execution, importing an ESM-only package produced:

node_modules/commander/index.js:1
import { Argument } from './lib/argument.js';
^^^^^^
SyntaxError: Cannot use import statement outside a module

That failed 28 suites at load time — note the original run reported 5105 passed, 0 failed, because nothing got as far as asserting.

The engines requirement also made it genuinely unmergeable until the Node 24 baseline landed in #90.

Changes Made

  • package.json / package-lock.jsoncommander ^14.0.3^15.0.0

  • jest.config.js — added commander to the existing transformIgnorePatterns ESM allowlist, alongside p-limit, yocto-queue, fast-glob, chalk, chokidar, readdirp:

    - 'node_modules/(?!(p-limit|yocto-queue|fast-glob|chalk|chokidar|readdirp)/)',
    + 'node_modules/(?!(p-limit|yocto-queue|fast-glob|chalk|chokidar|readdirp|commander)/)',
  • CHANGELOG.md### Changed entry under Unreleased

No source changes. commander 15's API is compatible with our usage; only the test harness needed adjusting. This follows the project's stated policy of refactoring for ESM rather than pinning back to avoid it.

Test Coverage

No new tests. Verified on Node 24.18.0:

npm run type-check    pass
npm test              231 suites / 5493 tests passed, 0 failed

Backward Compatibility

No CLI behaviour change — argument parsing, help output, and exit codes are unaffected; the full integration and e2e suites pass unmodified.
⚠️ Raises the effective floor to Node ≥22.12.0 via commander's own engines. Already satisfied by the Node 24 CI baseline, and engines.node in this package is bumped to >=24 separately as part of the v2.0.0 work.
Breaking changes: none.

Size: Small ✓

Three files; one dependency bump and one regex addition.

Closes #83.

commander 15 is ESM-only ("type": "module", was "commonjs") and requires
Node >=22.12.0. Both made it unmergeable before the Node 24 baseline
landed in #90.

Under ts-jest's CommonJS execution, importing an ESM-only package fails
with "SyntaxError: Cannot use import statement outside a module" — 28
suites failed to load on the original dependabot run. Adding commander
to the existing transformIgnorePatterns ESM allowlist (alongside
p-limit, chalk, chokidar et al.) lets ts-jest transform it.

Verified on Node 24.18.0: 231 suites / 5493 tests pass, zero failures.

Supersedes the original dependabot branch for #83; the bump is
dependabot's, the jest transform fix is the additional work needed.
@sjsyrek
sjsyrek merged commit 269929a into main Jul 27, 2026
2 of 3 checks passed
sjsyrek added a commit that referenced this pull request Jul 27, 2026
The watchAndSync block intermittently failed CI on unrelated dependency
PRs (#81 Jun 30 Node 22, #91 and #93 Jul 27 Node 24), always as a 10s
test timeout and never as an assertion, hitting a different test each
time. Both #81 and #91 passed on plain re-run with no code change.

Three changes, in order of importance:

1. jest.setTimeout(30_000) for the block. The suite runs in ~1.8s locally
   but was observed at 10.8-10.9s on GitHub runners with 232 suites
   competing for ~4 cores — i.e. sitting on the 10s default, which tipped
   an arbitrary test over each time. This is the headroom the block
   actually needed.

2. flushWatchSetup now waits for an observable readiness signal
   (mockWatcherOn having been called) instead of burning a fixed round
   count, then drains a settle budget. attachDebouncedWatchLoop calls
   watcher.on() in the same synchronous continuation as the
   process.on('SIGINT') registration, so listeners-attached implies the
   handler each test uses for shutdown is registered. A fixed count could
   under-wait; this cannot. On a genuine stall it throws with the pending
   state instead of timing out mutely.

3. Settle rounds 250 -> 25. Every test in the block passes with as few as
   5 (measured), so 25 keeps a 5x margin.

On method: the historical escalation was 20 -> 50 -> 250 rounds, each
assuming the budget was too small. Suite duration is flat across
250/25/5 rounds, so widening it could never have worked. Four further
hypotheses were tested and disproven — slow sweepStaleBackups I/O (its
projectRoot '/test' does not exist, so readdir ENOENTs immediately), a
hang in controller.shutdown() (microtask-only), expensive flush rounds
(0.00ms per 250), and CPU contention (old code passed 12/12 under 16
spinners on 11 cores). Details in bead sync-94ua.

Honest limitation: the flake could not be reproduced locally — ~58 runs
across five contention profiles (CPU, I/O+CPU, full-suite, starved
budget) produced one failure whose identity was lost. So this targets the
signature the evidence supports rather than a reproduction, and the new
diagnostic ensures the next occurrence identifies itself.

Verified: 49 tests in the block pass; 20/20 under combined I/O and CPU
contention; lint, type-check, and 5501 tests green on Node 24.18.0.
@sjsyrek
sjsyrek deleted the deps/commander-15 branch July 28, 2026 12:15
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.

1 participant