Skip to content

Align nuxt-app on Node 24 - #243

Merged
Jan0707 merged 2 commits into
mainfrom
node-24-alignment
Aug 5, 2026
Merged

Align nuxt-app on Node 24#243
Jan0707 merged 2 commits into
mainfrom
node-24-alignment

Conversation

@Jan0707

@Jan0707 Jan0707 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Nothing about the runtime changes. Four declarations that disagreed with it now agree.

Production has been on Node 24 all along

From the live build log for main (3bde75d):

Warning: Due to "engines": { "node": "^22.19.0 || ^24.11.0 || >=26.0.0" } in your package.json,
the Node.js Version defined in your Project Settings ("20.x") will not apply,
Node.js Version "24.x" will be used instead.

engines.node is authoritative on Vercel — it beats the project setting and .nvmrc, which Vercel never consults. So every declaration said 22 or 20 while 24.x was actually running.

declaration was now
engines.node ^22.19.0 || ^24.11.0 || >=26.0.0 ^24.11.0 || >=26.0.0
nuxt-app/.nvmrc v22 24
run_tests.yml (nuxt-app job) node-version: 22 node-version-file: nuxt-app/.nvmrc
smoke_tests.yml node-version: 22 node-version-file: nuxt-app/.nvmrc
Vercel project nodeVersion 20.x unchanged — needs you

Narrowing engines keeps Vercel resolving 24.x, which is what makes this safe: the deployed runtime is untouched.

The workflows now read .nvmrc rather than naming a version. Re-syncing three declarations by hand is what produced this drift; making CI read the file removes the opportunity. Same reasoning as the format gate, one layer down.

The Directus extension stays on 22 (both run_tests.yml and algolia-index-maintenance.yml). Separate lockfile, separate Jest setup, and frozen under the licence block — moving its runtime would be an untested change to a tree nobody is permitted to upgrade.

The v22 pin was a floor, not a ceiling

.nvmrc was not missing — it came from #174, "Pin nuxt-app to Node 22 to fix Vercel runtime crash". So I read it before moving it, and the framing matters: that crash was ERR_REQUIRE_ESM, from a lockfile regen pulling jsdom@28 → html-encoding-sniffer@6 → @exodus/bytes (ESM-only), whose CJS require() needs Node 22.12+ or 20.19+. Vercel was on Node 20 and every SSR function died at startup.

That raised a floor. Node 24 clears it by a wider margin than 22 did, so moving forward cannot reintroduce it — and the chain isn't even present now, since Phase 5 pinned isomorphic-dompurify back to ~2.20.0 with jsdom 26.

A commit titled "pin to fix crash" reads like a ceiling. Moving it unread would have been reckless.

npm 11 comes with Node 24 and gates dependency install scripts

npm warn allow-scripts 4 packages have install scripts not yet covered by allowScripts:
npm warn allow-scripts   esbuild@0.28.1, fsevents@2.3.2, fsevents@2.3.3, unrs-resolver@1.12.2

The project's own postinstall still runsnuxt prepare executes and regenerates .nuxt/eslint.config.mjs, which npm run lint depends on. Only dependency scripts are gated, and none is load-bearing: after a clean npm ci with all four blocked, esbuild --version works (the binary comes from the @esbuild/darwin-arm64 optional dependency, not install.js) and every gate passes.

Left blocked rather than allowlisted. Blocking is the safer default, an allowScripts entry is a standing grant to execute arbitrary install-time code, and nothing needs it. Expect those warnings in CI logs.

Also removes nuxt-app/.npmrc

Its two options are pnpm-only no-ops under npm (already analysed in Appendix B), and npm 11 promotes them to Unknown project config … will stop working in the next major version of npm on every command — including in Vercel's build log, visible in the excerpt above. This PR is what puts npm 11 in CI, so it cleans up after itself. Verified no pnpm lockfile or reference exists anywhere first.

Verification — on Node 24, not on 22

Installed 24.19.0 locally and ran everything against it after a clean npm ci, rather than pushing and letting CI find out. A Node major is exactly where a native dependency breaks, and a CI round-trip is a slower way to learn that.

prettier:check clean · 91 tests passing · lint 0 errors / 127 warnings (identical to Node 22) · typecheck ratchet steady at 263 · SKIP_PRERENDER_ROUTE_DISCOVERY=true npm run build exit 0 · npm ci under npm 11 left package-lock.json byte-identical.

One manual step left

The Vercel project's nodeVersion is still 20.x, now overridden twice over. It's dashboard-only — the MCP surface has no project-update call — so it needs you. The reason isn't today's runtime: if engines.node were ever simplified or dropped, the project would silently fall back to Node 20, below both Nuxt's floor and the require(esm) threshold that caused #174 in the first place.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JkKMceYAzAYLrSyC42FWTf

Nothing about the runtime changes. Four declarations that disagreed with it
now agree.

The production build log for main already says:

  Warning: Due to "engines" ... the Node.js Version defined in your Project
  Settings ("20.x") will not apply, Node.js Version "24.x" will be used

So engines.node is authoritative on Vercel — over the project setting and
over .nvmrc, which Vercel never consults. Narrowing engines to
^24.11.0 || >=26.0.0 keeps it resolving 24.x, so the deployed runtime is
untouched.

Workflows now read nuxt-app/.nvmrc instead of naming a version, so CI and
local development cannot drift apart again: one file, not three.

The Directus extension stays on 22. It has its own lockfile and Jest setup
and is frozen under the licence block, so moving its runtime would be an
untested change to a tree nobody may upgrade.

.nvmrc's v22 was not arbitrary — #174 set it to fix ERR_REQUIRE_ESM on
Node 20. That was a floor, and 24 clears it by a wider margin, so moving
forward cannot reintroduce it.

Also removes nuxt-app/.npmrc. Its two options are pnpm-only no-ops under
npm, and npm 11 (which ships with Node 24) promotes them to deprecation
warnings on every command, including in Vercel's build log.

Verified on Node 24.19.0 locally after a clean npm ci, not by pushing and
reading CI: format clean, 91 tests, lint 0 errors/127 warnings, ratchet
steady at 263, build exit 0, lockfile byte-identical.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JkKMceYAzAYLrSyC42FWTf
Copilot AI lite review requested due to automatic review settings August 4, 2026 14:55
@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
programmierbar-website Ready Ready Preview Aug 5, 2026 2:04pm

Request Review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Aligns the Nuxt 3 app’s declared Node version (local dev + CI + docs) with the Node 24 runtime already used in production (Vercel via engines.node), and removes a now-noisy npm config file.

Changes:

  • Narrow nuxt-app’s engines.node to Node 24.11+ (or 26+), and switch CI workflows to read the version from nuxt-app/.nvmrc.
  • Update project documentation (AGENTS.md, dependency upgrade plan) to reflect the Node 24 alignment and rationale.
  • Remove nuxt-app/.npmrc (pnpm-only options) to avoid npm 11 warnings.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
nuxt-app/package.json Narrows the declared Node engine range to Node 24.11+ (or 26+).
nuxt-app/.nvmrc Updates the Nuxt app’s Node version pin used by CI and local tooling.
nuxt-app/.npmrc Removes pnpm-only config entries that generate npm 11 warnings.
docs/dependency-upgrade-plan.md Records the Node 24 alignment work and updates phase/backlog items.
AGENTS.md Updates repo guidance to reflect Node 24 for nuxt-app and Node 22 for the Directus extension.
.github/workflows/smoke_tests.yml Switches setup-node to node-version-file: nuxt-app/.nvmrc for smoke tests.
.github/workflows/run_tests.yml Keeps Directus extension on Node 22; switches Nuxt job to node-version-file: nuxt-app/.nvmrc.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread nuxt-app/.nvmrc
@@ -1 +1 @@
v22
24

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have found a real gap, and I am leaving it open — because the fix it implies is worse than the gap. Explaining rather than just declining.

The gap is real. .nvmrc: 24 permits 24.0.0–24.10.x, which engines: ^24.11.0 forbids. A PR whose whole claim is "every declaration agrees" should not contain two that disagree.

But .nvmrc supports no ranges. There is no value meaning "≥24.11 inside major 24". The options are a major (24), a minor series (24.11 → 24.11.x only), or an exact version. Pinning 24.11.0 would hold both CI and developers on a superseded patch with no Node security releases inside the major — CI resolved 24.18.0 today and would install 24.11.0 instead. That trades a narrow, unlikely problem for a permanent one.

What actually happens: both resolvers select the newest available 24.x, so .nvmrc: 24 satisfies engines in every normal case — verified in this PR's own CI run, which picked v24.18.0 from the file. The residual risk is a developer who already has an old 24.x installed: nvm use would select it, and npm then only warns (EBADENGINE).

The only way to close it properly is engine-strict=true, which promotes that warning to a hard install failure and makes the floor genuinely binding. I have not added it here: it changes install behaviour for other people — a wrong local Node would fail npm install rather than warn — and that is outside a PR whose claim is that install behaviour is unchanged. It is one line in a new nuxt-app/.npmrc if the maintainer wants it, and it would be an .npmrc containing an option that actually does something, unlike the one this PR deletes.

Both the gap and that option are now written down in docs/dependency-upgrade-plan.md rather than left implicit.

Comment thread nuxt-app/package.json
Comment on lines 5 to 7
"engines": {
"node": "^22.19.0 || ^24.11.0 || >=26.0.0"
"node": "^24.11.0 || >=26.0.0"
},

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, and fixed — but syncing it turned out to be more interesting than a one-line change, so worth recording how.

npm install --package-lock-only under npm 11 (the npm that ships with Node 24, i.e. what this PR puts in CI) rewrites 168 lines: it strips "dev": true from roughly fifty optional packages (@emnapi/* and similar). The same command under npm 10 changes exactly one line — the engines field you identified.

npm 10 (Node 22):  1 file changed, 1 insertion(+), 1 deletion(-)
npm 11 (Node 24):  1 file changed, 13 insertions(+), 155 deletions(-)

So that churn is npm 11's changed dev/optional classification, not a consequence of narrowing engines. I generated the sync under npm 10 to keep this PR to the field in question, because the reclassification has semantic content — npm ci --omit=dev would install a different set afterwards — and this PR's claim is that nothing about the runtime or install behaviour changes. Bundling fifty unverified reclassifications in would have made that claim false.

Logged as its own backlog item, with the note that it is already live and harmless: Vercel builds on Node 24, so production has been resolving this lockfile with npm 11 for a while without trouble.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 676d5ae679

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread nuxt-app/package.json
"private": true,
"engines": {
"node": "^22.19.0 || ^24.11.0 || >=26.0.0"
"node": "^24.11.0 || >=26.0.0"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Regenerate the lockfile's root engine metadata

The corresponding root package entry in nuxt-app/package-lock.json still contains ^22.19.0 || ^24.11.0 || >=26.0.0. As a result, tools reading the committed lockfile still see Node 22 as supported, and the next npm install rewrites the lockfile solely to synchronize this field. Regenerate and commit the lockfile alongside the package.json change so the repository consistently declares Node 24.

AGENTS.md reference: AGENTS.md:L21-L22

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same finding as the Copilot comment above, and correct — synced.

One detail worth adding for anyone reading this thread later: your prediction that "the next npm install rewrites the lockfile solely to synchronize this field" holds under npm 10, but not under npm 11. Under npm 11 — the npm that ships with Node 24, which this PR puts in CI — the same command rewrites 168 lines, stripping "dev": true from ~50 optional packages.

npm 10 (Node 22):  1 insertion(+), 1 deletion(-)
npm 11 (Node 24):  13 insertions(+), 155 deletions(-)

So I generated the sync under npm 10 to keep this PR to the field in question. The reclassification is not cosmetic — npm ci --omit=dev would install a different set afterwards — and it is logged as its own backlog item with a note to verify a production-shaped install before and after, rather than trusting it.

uses: actions/setup-node@v4
with:
node-version: 22
node-version-file: nuxt-app/.nvmrc

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update the setup guide to require Node 24 for Nuxt

After this workflow begins sourcing Node 24 from .nvmrc, the root README.md still tells Nuxt contributors that Node 19+ is sufficient and that CI uses v22. A developer following those setup instructions can therefore work and validate on a runtime excluded by the new engines.node range and different from CI; distinguish the Nuxt requirement of Node 24 from the Directus tree's Node 22 requirement.

AGENTS.md reference: AGENTS.md:L21-L22

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, and fixed. README.md:13 said:

These install instructions assume you are using Node.js version 19+ (CI uses v22, tested up to v24).

Wrong on both counts after this PR, and wrong in the file a new contributor reads before any other. It also had a **Prerequisites for Node 22+:** heading below it.

Replaced with a table, because your framing is the important part — the two trees genuinely differ now and the README should say so rather than give one number:

tree Node source of truth
nuxt-app 24 nuxt-app/.nvmrc, which CI reads too. engines.node requires ^24.11.0 || >=26.0.0
directus-cms 22 its workflows; deliberately left behind while the tree is frozen

Plus a line noting Node 24 is also what production runs, since Vercel resolves it from engines.node — which is the fact that made this whole alignment a documentation change rather than a runtime one.

AGENTS.md was already updated in the first commit; the README was the one I missed.

Three review findings, all correct — and all of them declarations that a PR
claiming "every declaration agrees" had missed.

package-lock.json still carried ^22.19.0 || ... in packages[""].engines, so
the next npm install would have rewritten it as unexplained noise in
someone else's PR. Synced.

README.md still said "Node.js version 19+ (CI uses v22, tested up to v24)",
wrong on both counts, in the file a new contributor reads first. Replaced
with a table, since the two trees genuinely differ now.

The lockfile sync was produced under npm 10 on purpose. The same command
under npm 11 rewrites 168 lines, stripping "dev": true from ~50 optional
packages — npm 11's changed dev/optional classification, not a consequence
of narrowing engines. That has semantic content (npm ci --omit=dev would
install a different set), so it is logged for its own change rather than
smuggled in here.

Not fixed: .nvmrc's `24` is looser than engines' ^24.11.0. The reviewer is
right, and the suggested fix is worse — .nvmrc supports no ranges, so
pinning 24.11.0 would hold CI and developers on a superseded patch (CI
resolved 24.18.0 today) with no security releases inside the major. The
only real fix is engine-strict=true, which changes install behaviour for
other people, so it is logged for a decision instead.

Verified on Node 24.19.0 after a clean npm ci: format clean, 91 tests, lint
0 errors/127 warnings, ratchet steady at 263, build exit 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JkKMceYAzAYLrSyC42FWTf

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • nuxt-app/package-lock.json: Generated file
Suppressed comments (3)

docs/dependency-upgrade-plan.md:2452

  • Backslashes inside inline code are rendered literally, so this line shows \|\| to readers. If you want to display the semver OR operator, use || directly inside the code span.
| 2026-08-04 | Narrowed `engines.node` to `^24.11.0 \|\| >=26.0.0` after confirming from the production build log that Vercel already resolves **24.x** and that `engines` beats both the project setting and `.nvmrc`. The deployed runtime is therefore unchanged by this PR — the declarations were what disagreed with reality. |

README.md:18

  • In this table cell the Node engine range is inside an inline code span, so the backslashes are rendered literally (readers will see \|\| instead of ||). GitHub-flavored Markdown doesn’t require escaping | inside inline code, so this ends up less readable and harder to copy/paste.
| `nuxt-app` | **24** | `nuxt-app/.nvmrc`, which CI reads too. `engines.node` requires `^24.11.0 \|\| >=26.0.0` |

docs/dependency-upgrade-plan.md:1637

  • These engine ranges are inside inline code spans in a Markdown table; the backslashes are rendered literally, so the document shows \|\| instead of ||. Since this file already uses || unescaped in inline code elsewhere (e.g. ^20.19.0 || >=22.3.0), it’s clearer to drop the escapes here too.

This issue also appears on line 2452 of the same file.

| `nuxt-app/package.json` `engines.node` | `^22.19.0 \|\| ^24.11.0 \|\| >=26.0.0` | `^24.11.0 \|\| >=26.0.0` |

@Jan0707
Jan0707 merged commit 0836913 into main Aug 5, 2026
6 checks passed
@Jan0707
Jan0707 deleted the node-24-alignment branch August 5, 2026 14:08
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.

2 participants