feat!: Drop Node.js 20 support. Node.js >=22.12 is now required#1125
feat!: Drop Node.js 20 support. Node.js >=22.12 is now required#1125macdonst wants to merge 13 commits into
Conversation
Signed-off-by: Simon MacDonald <simon.macdonald@gmail.com>
|
Claude finished @macdonst's task in 3m 48s —— View job Claude PR Review
Status of earlier Cursor bot comments
Findings1. '@types/node': ^22.x
2. The PR description acknowledges this was intentional, and the function is small ( 3. Coverage runner pinned to Node 24 — # Node 24 required: runs .ts files directly via built-in type stripping
node-version: 24Comment is now slightly misleading — Node 22.12+ also supports Notes
Out of scope but noted in PR descriptionThe author flagged • Branch: |
📦 Bundle Stats —
|
| Metric | Value | vs main (cd110e7) |
|---|---|---|
| Internal (raw) | 2.1 KB | - |
| Internal (gzip) | 799 B | - |
| Bundled (raw) | 10.97 MB | -98 B, -0.0% |
| Bundled (gzip) | 2.06 MB | -24 B, -0.0% |
| Import time | 846ms | -1ms, -0.1% |
bin:sanity
| Metric | Value | vs main (cd110e7) |
|---|---|---|
| Internal (raw) | 782 B | -241 B, -23.6% |
| Internal (gzip) | 423 B | -63 B, -13.0% |
| Bundled (raw) | 9.84 MB | -242 B, -0.0% |
| Bundled (gzip) | 1.77 MB | -70 B, -0.0% |
| Import time | 2.01s | +17ms, +0.9% |
🗺️ View treemap · Artifacts
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
📦 Bundle Stats — @sanity/cli-core
Compared against main (cd110e7c)
| Metric | Value | vs main (cd110e7) |
|---|---|---|
| Internal (raw) | 98.1 KB | +1.1 KB, +1.2% |
| Internal (gzip) | 23.3 KB | +580 B, +2.5% |
| Bundled (raw) | 21.61 MB | +1.1 KB, +0.0% |
| Bundled (gzip) | 3.42 MB | +508 B, +0.0% |
| Import time | 811ms | +7ms, +0.9% |
🗺️ View treemap · Artifacts
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
📦 Bundle Stats — create-sanity
Compared against main (cd110e7c)
| Metric | Value | vs main (cd110e7) |
|---|---|---|
| Internal (raw) | 908 B | - |
| Internal (gzip) | 483 B | - |
| Bundled (raw) | 931 B | - |
| Bundled (gzip) | 491 B | - |
| Import time | ❌ ChildProcess denied: node | - |
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
Signed-off-by: Simon MacDonald <simon.macdonald@gmail.com>
Coverage Delta
Comparing 5 changed files against main @ Overall Coverage
|
Signed-off-by: Simon MacDonald <simon.macdonald@gmail.com>
Signed-off-by: Simon MacDonald <simon.macdonald@gmail.com>
Signed-off-by: Simon MacDonald <simon.macdonald@gmail.com>
c7e9f56 to
3d48751
Compare
Signed-off-by: Simon MacDonald <simon.macdonald@gmail.com>
|
Related to sanity-io/sanity#12859 |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 19333cf. Configure here.
Signed-off-by: Simon MacDonald <simon.macdonald@gmail.com>
Signed-off-by: Simon MacDonald <simon.macdonald@gmail.com>
Signed-off-by: Simon MacDonald <simon.macdonald@gmail.com>

DO NOT MERGE
This will create a new major version of the CLI and may screw up some work in progress with plugins. Check with the participants of this slack thread before proceeding.
Description
Node 20 officially exited it's maintenance mode on April 30th, 2026. This means we can finally drop support for this version of Node in the CLI. The purpose of this PR is to update the code to warn users if they are running on a node version less than 22.12, update the engines version of node in all our package.json files and drop node 20 from our testing matrix.
This PR intentionally does not remove any node packages that may not be required anymore. For example:
rimraf- sticks around in case anyone is contributing from Windowstinyglobby- node 22 now supportsfs.globbut our usage oftinyglobbyincludes some options thatfs.globdoesn't support out of the box so more consideration is required here.nanoid- we only use one method in from this package,customAlphabet. To me this is a top candidate to get vendored as we can copy (with attribution) approximately 50 LOC from an MIT project instead of leaving us open to a supply chain for a package that gets over 100 million downloads a week. Single maintainer.execa- used frequently in this repo and we should probably examine if we can replace it withnode:child_processand once again not leave us open to a supply chain for a package that gets over 100 million downloads a week. Single maintainer.This PR replaces: #1033 which was going to be a rebase nightmare.
What to review
Should be pretty self explanatory. Did I miss any node 20 references?
Testing
All existing tests are passing. I didn't think it was necessary to add a test
isSupportedNodeVersion.Note
Medium Risk
Medium risk because it’s a breaking runtime/CI matrix change that will block users and environments still on Node 20, and it adjusts global stubbing behavior for Node 26 web globals.
Overview
Drops Node.js 20 support across the CLI monorepo, bumping
engines.nodeto>=22.12(root + published packages), updating the CLI runtime version gate/error message, and recording the breaking change via a major changeset.Updates CI/test tooling to match the new baseline by removing Node 20 from GitHub Actions matrices (adding Node 26), shifting coverage collection to Node 22, bumping
@types/nodeto^22.x, and targetingnode22for thecli-testesbuild worker bundle.Hardens browser stub injection for newer Node versions:
setupBrowserStubsnow overrides/restores existing getter-only globals via property descriptors, andgetBrowserStubs()always prefers JSDOM’slocalStorage/sessionStorage/Storagefor consistent realm identity; adds a regression test for the Node 26localStoragegetter-only case.Reviewed by Cursor Bugbot for commit 4f01217. Bugbot is set up for automated code reviews on this repo. Configure here.