feat!: convert the package to ESM only - #323
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
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. Ignoring alerts on:
|
0504ee1 to
a2d5883
Compare
a7b0926 to
a2d5883
Compare
a2d5883 to
0067215
Compare
0067215 to
f80e63f
Compare
f80e63f to
5d24967
Compare
5d24967 to
483dd65
Compare
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, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 483dd65. Configure here.
483dd65 to
73194e8
Compare
7742fac to
ccfbfb6
Compare
| - **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. | ||
| - **BREAKING:** The package is now ESM only ([#323](https://github.com/MetaMask/utils/pull/323)) | ||
| - The CommonJS build is gone. `require('@metamask/utils')` now fails with `ERR_REQUIRE_ESM`; use `import` instead. |
There was a problem hiding this comment.
This is not accurate. Modern versions of Node.js support require(esm).
There was a problem hiding this comment.
Yes thats right that was thrown in 18 which we already dropped, fixing
4b45e57 to
88435d0
Compare
88435d0 to
ed955af
Compare
ed955af to
4b00bd6
Compare
Core is moving to an ESM only monorepo (MetaMask/core#9536), so this package should arrive already converted rather than landing as the sole hybrid one. BREAKING: the CommonJS build is gone. require('@metamask/utils') now fails with ERR_REQUIRE_ESM. `main` and `module` are removed, and both `.` and `./node` resolve through `exports` to a single ./dist/*.js with ./dist/*.d.ts types. Consumers already using `import` are unaffected. package.json adds "type": "module", collapses the dual exports map build ts-bridge -> tsc, since ts-bridge exists to emit both formats @ts-bridge/cli removed, rimraf added for build:only-clean tsconfig.build drops emitDeclarationOnly, tsc now emits the JS too 102 relative import specifiers across 45 files gained explicit .js extensions, which ESM requires. Directories resolve to /index.js. Core's sources already look like this and enforce it with n/file-extension-in-import, so the same three import rules are adopted here verbatim. Two things only surfaced by running the built output rather than the tests: lodash `import { memoize } from 'lodash'` throws at runtime under ESM, because Node's lexer cannot see named exports through lodash's CJS. Switched to `lodash/memoize.js`, a default import of the single method. Core solved the same problem by moving to lodash-es plus a jest moduleNameMapper; this needs neither. scure-bip39 the deep wordlist import needed an explicit .js. Every other CJS dependency survives named imports untouched: semver, superstruct, @scure/base, @noble/hashes and pony-cause all have lexer friendly CJS. Verified by importing all 27 built modules individually. jest.config.js and .prettierrc.js are renamed to .cjs, since "type": "module" makes bare .js ESM. Tests still compile to CommonJS through a ts-jest transform override, matching core, with a moduleNameMapper stripping the .js specifiers back off. constraints.pro is rewritten for the single entrypoint shape.
test:source runs `jest && jest-it-up`, and jest-it-up defaults to looking for jest.config.js, which is now jest.config.cjs. It supports --config, so point it there. Caught by CI rather than locally: I had been running `yarn jest` directly to work around a broken watchman on this machine, which skipped jest-it-up entirely, so test:source was never actually exercised.
4b00bd6 to
af16832
Compare
|
@SocketSecurity ignore npm/rimraf@5.0.10 |

Top of stack #315, on #322.
Core is moving to an ESM only monorepo (MetaMask/core#9536), so this package should arrive already converted rather than landing as the only hybrid one in it.
Breaking
require('@metamask/utils')now fails withERR_REQUIRE_ESM.mainandmoduleare gone, and both.and./noderesolve throughexportsto a single./dist/*.jswith./dist/*.d.tstypes. Consumers already usingimportare unaffected.ts-bridgetsc.cjs+.mjs+.d.cts+.d.mts.js+.d.ts@ts-bridge/clirimrafadded for cleaning)ts-bridgeexists to emit both formats, so it goes with the CJS half.Import specifiers
102 relative specifiers across 45 files gained explicit
.jsextensions, which ESM requires; directories resolve to/index.js. Core's sources already look like this and enforce it withn/file-extension-in-import, so the same three import rules are adopted here verbatim.Two things only the built output revealed
Neither would have been caught by the test suite, because tests run against
src/:lodash—import { memoize } from 'lodash'throws at runtime under ESM: Node's lexer cannot see named exports through lodash's CJS. Switched tolodash/memoize.js, a default import of the single method. Core hit the same wall and solved it withlodash-esplus a jestmoduleNameMapper; this approach needs neither.@metamask/scure-bip39— the deep wordlist import needed an explicit.js.Every other CJS dependency survives named imports untouched.
semver,@metamask/superstruct,@scure/base,@noble/hashesandpony-causeall have lexer friendly CJS. Verified by importing all 27 built modules individually, plus the exports map by bare specifier.Tooling
jest.config.jsand.prettierrc.jsare renamed to.cjs, since"type": "module"makes bare.jsfiles ESM. Tests still compile to CommonJS via a ts-jesttransformoverride (matching core) with amoduleNameMapperstripping the.jsspecifiers back off.constraints.prois rewritten for the single entrypoint shape.Note
High Risk
Breaking module format and export surface affects every consumer still on CommonJS
require, and lodash/mnemonic import changes can cause runtime failures if missed in downstream bundles.Overview
Breaking:
@metamask/utilsis now ESM-only—require()of the package fails unless consumers use Node 22+require(esm)or dynamicimport().main/moduleand dualrequire/importexport conditions are removed;.and./noderesolve to a single./dist/*.jswith matching.d.tstypes.The build switches from
ts-bridge(dual CJS/ESM artifacts) totsc, dropping@ts-bridge/*and addingrimraffor clean builds.constraints.pro, ESLint (n/file-extension-in-import), and Jest (ts-jest CJS transform +moduleNameMapperto strip.jsin tests) are aligned with MetaMask core’s ESM-only pattern.Relative imports across
src/now use explicit.jsextensions. Runtime fixes for ESM interop:hex.tsuseslodash/memoize.jsinstead of a namedlodashimport, and the@metamask/scure-bip39wordlist deep import adds.js.Reviewed by Cursor Bugbot for commit ab4c590. Bugbot is set up for automated code reviews on this repo. Configure here.