Skip to content
This repository was archived by the owner on Sep 11, 2026. It is now read-only.

feat!: drop support for Node 18 and 20 - #328

Merged
cryptodev-2s merged 5 commits into
mainfrom
migrate/drop-node-18-20
Sep 10, 2026
Merged

feat!: drop support for Node 18 and 20#328
cryptodev-2s merged 5 commits into
mainfrom
migrate/drop-node-18-20

Conversation

@cryptodev-2s

@cryptodev-2s cryptodev-2s commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Replaces #324, which GitHub auto-closed as merged during a stack reorder when its head briefly became an ancestor of its base. The changes never reached main; this carries the same two commits.

Mirrors MetaMask/core#9976, the bottom of core's foundational stack.

Before After
engines.node ^18.18 || ^20.14 || >=22 ^22.14.0 || ^24
@types/node ~18.18.14 ^22.13.14
CI matrix 18, 20, 22 22, 24

constraints.pro is updated so yarn constraints enforces the new range. Core makes the @types/node bump in this same PR rather than with its TypeScript change, since the types track the supported runtime.

Position in the stack

This now sits below the ESM conversion (#323), so everything up to and including this PR is still a hybrid CJS/ESM build:

main → … → #322 → this → #323 (ESM only) → #325 (TypeScript)

That matters for testing. A preview build from here still resolves through main: ./dist/index.cjs with the require condition intact, so it can be consumed by metamask-extension as-is. Everything below the ESM cut can therefore be verified against a real downstream consumer before the breaking change lands.


Note

Medium Risk
Semver-breaking runtime requirement may block consumers still on Node 18 or 20; in-repo changes are mostly policy, CI, and test cleanup with limited logic impact.

Overview
Breaking: supported Node is now ^22.14.0 || ^24 instead of 18/20/22. That is enforced in package.json, constraints.pro, and documented under Unreleased in CHANGELOG.md.

CI prepare, test, and compatibility-test jobs now run on Node 22.x and 24.x only (18 and 20 dropped from the matrix). Dev typings move from @types/node ~18 to ^22.13.14, with matching yarn.lock updates.

Test and lint tooling align with the new floor: ESLint comments for n/no-unsupported-features/node-builtins reflect that global crypto is expected on 22+, and hashing.test.ts drops the Node 18 webcrypto polyfill/beforeEach setup—tests assume globalThis.crypto.subtle exists.

Reviewed by Cursor Bugbot for commit ba81eca. Bugbot is set up for automated code reviews on this repo. Configure here.

@socket-security

socket-security Bot commented Sep 8, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updated@​types/​node@​18.18.14 ⏵ 22.20.1100 +121008196100

View full report

@cryptodev-2s

Copy link
Copy Markdown
Contributor Author

@metamaskbot publish-preview

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

The following preview build has been published:

@metamask-previews/utils@11.12.1-preview-c44fd50

Learn how to use preview builds in other projects.

@cryptodev-2s
cryptodev-2s removed this pull request from stack #329 September 9, 2026 11:46
@cryptodev-2s
cryptodev-2s force-pushed the migrate/drop-node-18-20 branch from c44fd50 to 08c239d Compare September 9, 2026 11:46
@socket-security

socket-security Bot commented Sep 9, 2026

Copy link
Copy Markdown

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

View full report

@cryptodev-2s
cryptodev-2s changed the base branch from migrate/pr2h-fix-tsd to migrate/swap-depcheck-for-knip September 9, 2026 11:47
@cryptodev-2s
cryptodev-2s added this pull request to stack #331 September 9, 2026 11:47
@cryptodev-2s
cryptodev-2s force-pushed the migrate/drop-node-18-20 branch 2 times, most recently from ced5e47 to 4f428cc Compare September 9, 2026 16:10
@cryptodev-2s
cryptodev-2s force-pushed the migrate/drop-node-18-20 branch from 4f428cc to 424c070 Compare September 9, 2026 16:32
@cryptodev-2s
cryptodev-2s force-pushed the migrate/drop-node-18-20 branch from 424c070 to 8f59757 Compare September 9, 2026 18:31
@cryptodev-2s
cryptodev-2s force-pushed the migrate/drop-node-18-20 branch from 8f59757 to 07bb86e Compare September 9, 2026 18:47
Comment thread CHANGELOG.md Outdated
### Changed

- **BREAKING:** Drop support for Node 18 and 20 ([#328](https://github.com/MetaMask/utils/pull/328))
- The supported range is now `^22.14.0 || ^24`, matching core.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
- The supported range is now `^22.14.0 || ^24`, matching core.

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.

dropped here 60e7277

Comment thread constraints.pro

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are these even enforced anymore? I thought Yarn 4 dropped support for Prolog constraints.

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.

They are just deprecated

@cryptodev-2s
cryptodev-2s force-pushed the migrate/drop-node-18-20 branch from 60e7277 to f25a5de Compare September 10, 2026 10:15
@cryptodev-2s
cryptodev-2s force-pushed the migrate/drop-node-18-20 branch from f25a5de to cdb33a5 Compare September 10, 2026 10:28
@cryptodev-2s
cryptodev-2s removed this pull request from stack #331 September 10, 2026 10:40
@cryptodev-2s
cryptodev-2s force-pushed the migrate/drop-node-18-20 branch from cdb33a5 to 1fa7e31 Compare September 10, 2026 10:41
@cryptodev-2s
cryptodev-2s added this pull request to stack #335 September 10, 2026 10:41
@cryptodev-2s
cryptodev-2s requested a review from Mrtenz September 10, 2026 10:46
stack merge was automatically disabled September 10, 2026 11:48

Pull Request is not mergeable

@cryptodev-2s
cryptodev-2s force-pushed the migrate/drop-node-18-20 branch from 1fa7e31 to 5c1c602 Compare September 10, 2026 12:46
@cryptodev-2s
cryptodev-2s force-pushed the migrate/drop-node-18-20 branch from 5c1c602 to 878cf81 Compare September 10, 2026 13:03
@cryptodev-2s
cryptodev-2s force-pushed the migrate/drop-node-18-20 branch from 878cf81 to 25b9dd1 Compare September 10, 2026 13:08
@cryptodev-2s
cryptodev-2s force-pushed the migrate/drop-node-18-20 branch from 25b9dd1 to 2aea0ee Compare September 10, 2026 13:15
@cryptodev-2s
cryptodev-2s force-pushed the migrate/drop-node-18-20 branch from 2aea0ee to 1be1f1c Compare September 10, 2026 13:21
Base automatically changed from migrate/swap-depcheck-for-knip to main September 10, 2026 13:27
Mirrors MetaMask/core#9976, the bottom of core's foundational stack.

BREAKING: the supported range moves from ^18.18 || ^20.14 || >=22 to
^22.14.0 || ^24, the same range core adopts. The CI matrix drops to 22 and
24, and constraints.pro is updated to match.

This lands before the TypeScript and target/lib changes on purpose. Core
sequenced its stack Node, then ESM, then TypeScript, then target/lib, and
that order is a dependency chain rather than an arbitrary grouping: raising
the runtime floor first is what justifies each step above it, and emitting
a higher target is only defensible once the floor guarantees it.
Follows from the runtime floor moving to Node 22. Core makes the same bump
in the same PR as its Node drop (MetaMask/core#9976), rather than alongside
the TypeScript change, since the types track the supported runtime.
Follow up to removing the Node 18 polyfill in the same PR. The remaining
`webcrypto` usage was the other half of the same workaround:

  const originalSubtle = globalThis.crypto?.subtle ?? webcrypto.subtle;

The fallback only mattered when globalThis.crypto could be absent, which was
Node 18. The floor here is now ^22.14.0 || ^24, where the global is always
present, so this reads `globalThis.crypto.subtle` and the `crypto` import
goes with it.

All 12 tests still pass. The node-builtins disable is still required: with it
removed the rule still reports nine occurrences, because it treats the global
`crypto` as experimental until Node 23.
@cryptodev-2s
cryptodev-2s force-pushed the migrate/drop-node-18-20 branch from 1be1f1c to ba81eca Compare September 10, 2026 13:27
@cryptodev-2s
cryptodev-2s merged commit 1de19fa into main Sep 10, 2026
19 checks passed
@cryptodev-2s
cryptodev-2s deleted the migrate/drop-node-18-20 branch September 10, 2026 13:33
cryptodev-2s added a commit that referenced this pull request Sep 10, 2026
Top of stack #331, on #325. Mirrors
[MetaMask/core#10019](MetaMask/core#10019), the
last of core's foundational chain.

| | Before | After |
| --- | --- | --- |
| `target` | `ES2020` | `ES2022` |
| `lib` | `["ES2020", "DOM"]` | `["ES2022", "DOM"]` |

Correctly last in the order: raising the emit target is only defensible
once the Node floor guarantees the runtime supports it, which #328
established.

## It is not cosmetic

`wrapError` sets `cause` behind two `@ts-ignore` comments, with this
note in the source:

> Error causes are not supported by our current tsc target (ES2020, we
need ES2022 to make this work)

`Error.cause` is ES2022, so both suppressions are now obsolete and are
removed. The types are genuine rather than ignored.

The evidence came from enabling test type checking: with `lib: ES2020` a
`tsc` pass over the tests reports **11 errors**, nearly all `Property
'cause' does not exist`. With `lib: ES2022` it reports **zero**. That is
why the type checking PR stacks on top of this one rather than below it.

## Verification

Build, tests, tsd, eslint and lint all pass.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Breaking for consumers on older runtimes or TS configs that assumed
ES2020 output; error utilities are widely used but the change is
configuration and type hygiene rather than new logic.
> 
> **Overview**
> **BREAKING:** Raises the TypeScript **`target`** and **`lib`** from
**ES2020** to **ES2022**, so published output expects a modern runtime
or bundler that can consume ES2022. The unreleased changelog documents
this alongside the existing Node/ESM breaking changes.
> 
> In **`wrapError`**, the **`Error(message, { cause })`** path no longer
needs **`@ts-ignore`** or the comment about ES2020 lacking
**`Error.cause`** types—ES2022 **`lib`** makes **`cause`** a first-class
typed option.
> 
> No runtime behavior change in **`wrapError`** beyond what was already
implemented; this is primarily a compile/emit floor and type-checking
cleanup that unblocks stricter test **`tsc`** (e.g. **`cause`** on
errors) in follow-up work.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
6242b3e. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants