Skip to content

chore(argent): declare the Node version the package actually needs - #653

Open
filip131311 wants to merge 2 commits into
mainfrom
filip/declare-node-engines
Open

chore(argent): declare the Node version the package actually needs#653
filip131311 wants to merge 2 commits into
mainfrom
filip/declare-node-engines

Conversation

@filip131311

Copy link
Copy Markdown
Collaborator

Fixes #640.

"engines": { "node": ">=20.12.0" }

Why 20.12.0 and not 20

My first answer was >=20 — CI's lowest tested line, satisfied by every runtime dependency, and the
version the issue reporter (and I) installed successfully. It is wrong, and shipping it would
have told npm it is safe to install onto runtimes where the CLI cannot execute a single line.

The mistake was looking at first-party src/. The published artifacts are four esbuild bundles with
their dependencies inlined, and that is where the real floor lives. Confirmed in
packages/argent/dist/:

blocker added in consequence below it
import { styleText } from "node:util" (via inlined @clack/*, in installer.mjs and cli-cmds.mjs) 20.12.0 a link-time SyntaxErrorargent init and every CLI subcommand fail to load
import.meta.dirname at module scope (bundled-paths.js, cli.js) 20.11.0 TypeError during module init

So 20.12.0 is the lowest version on which the package actually loads. It is also the lowest line CI
exercises (7 workflows pin Node 20), and it satisfies every runtime dependency's own constraint
(@fails-components/webtransport >=20, @modelcontextprotocol/sdk >=18).

electron's >=22.12.0 does not apply — it is an optionalDependency, and npm does not
engine-check those. Verified: installing 0.18.0 on Node 20.19.5 produces no EBADENGINE at all and
electron is never materialized.

I did not go to >=22 on EOL grounds: it would refuse a runtime seven CI workflows still test and
that users demonstrably run.

Docs corrected alongside

  • README.md said 20.11 — one minor below where the package can load.
  • CONTRIBUTING.md said Node.js 18+ — predates what the repo's own lint toolchain needs
    (eslint@10 requires ^20.19.0 || ^22.13.0 || >=24).

packages/argent/README.md is generated by scripts/sync-readme.cjs on prepack, so it follows
automatically and is deliberately untouched.

What a reviewer should know

Enforcement is package-manager-dependent, which matters because argent init installs with
whichever PM the project uses:

behaviour on too-old Node
npm (default) npm warn EBADENGINE — installs anyway
npm + engine-strict=true hard error, install aborts
yarn 1.x hard error by default
pnpm silently ignores it

So this achieves the issue's "warn" but only conditionally its "refuse". There is no .npmrc in the
repo, so contributors are unaffected either way.

Nothing in the repo validates enginesrepo-hygiene, check-workspace-versions.mjs and
pack-mcp.cjs all ignore unknown manifest keys, and next-canary-version.mjs round-trips the
manifest through JSON.parse/stringify, which preserves the new field and stays prettier-clean.

One inconsistency I did not resolve: packages/argent/scripts/bundle-tools.cjs:383 builds with
target: "node22", which now sits above the declared floor. In practice the node20→node22 syntax gap
is negligible (tsc targets ES2022 and I found no post-20 syntax), so I left the shipped artifact
alone rather than change build output in a manifest-only PR — but it should either be lowered to
match or consciously kept. Your call.

Separate bug found while verifying this

ios-remote is broken on the entire Node 20 line, independent of this change: the inlined
@moq/* client uses Promise.withResolvers (Node 22+) ungated — 6 occurrences in
dist/tool-server.cjs, reached from utils/moq-client.ts via the simulator-server blueprint.
Reproduced on Node 20.19.5: TypeError: Promise.withResolvers is not a function.

I deliberately did not raise the floor to >=22 to paper over it — that would refuse Node 20 for
every user to hide one broken path. moq-client.ts already has an ensurePolyfill() that polyfills
WebSocket/WebTransport; this belongs there. Filed separately.

🤖 Generated with Claude Code

Without an `engines` constraint npm cannot say anything when the installed Node
is too old, so an unsupported runtime surfaces as a crash during a command
rather than a message at install time.

The floor is 20.12.0, which is where the published bundles actually start
working — not the 20.0 that the first-party sources alone would suggest. The
shipped artifacts inline their dependencies, and two of those inlined imports set
the real floor: `styleText` is a static named import from `node:util`, which is a
link-time failure below 20.12 and takes the installer and every CLI subcommand
with it, and `import.meta.dirname` is evaluated at module scope, which is a
TypeError below 20.11.

That is also the lowest line CI exercises, and it is satisfied by every runtime
dependency's own constraint.

The README said 20.11, one minor below where the package can load, and
CONTRIBUTING said 18, which predates what the repo's own lint toolchain needs.

Fixes #640
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.

package.json has no engines field

1 participant