feat!: convert the package to ESM only - #323
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Caution MetaMask internal reviewing guidelines:
|
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
73194e8 to
7742fac
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.
7742fac to
ccfbfb6
Compare

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 removes CommonJS entrypoints and breaks
require()for all downstream apps until they migrate to ESM imports.Overview
Breaking:
@metamask/utilsis now ESM-only.require('@metamask/utils')fails withERR_REQUIRE_ESM;main/moduleand dualimport/requireexport conditions are removed..and./noderesolve to single./dist/*.jsfiles with./dist/*.d.tstypes.The build switches from
ts-bridge(dual.cjs/.mjsoutput) totsc, withrimraf-based clean scripts and@ts-bridge/cliremoved.package.jsonsets"type": "module", andconstraints.proenforces the new export layout.Sources and tests adopt explicit
.jsextensions on relative imports; ESLint addsn/file-extension-in-import(and related import rules). Jest keeps running tests as CommonJS viats-jesttransform overrides and amoduleNameMapperthat strips.jsfrom relative paths.Runtime fixes for ESM:
lodashmemoization useslodash/memoize.js(default import), and the BIP-39 wordlist deep import adds.js.Reviewed by Cursor Bugbot for commit ccfbfb6. Bugbot is set up for automated code reviews on this repo. Configure here.