Skip to content

feat!: require Node 24 and migrate CJS to ESM output#3045

Draft
bennypowers wants to merge 7 commits intonode24/test-migrationfrom
node24/esm-and-node24
Draft

feat!: require Node 24 and migrate CJS to ESM output#3045
bennypowers wants to merge 7 commits intonode24/test-migrationfrom
node24/esm-and-node24

Conversation

@bennypowers
Copy link
Member

@bennypowers bennypowers commented Mar 17, 2026

Summary

BREAKING CHANGE: Node 24+ is now required. All packages emit ESM output.

  • Add "type": "module" to all TypeScript package.json files
    (JSDoc packages remain CJS until PR4 converts them)
  • Update exports to use single "default" condition
  • Remove "module" field (replaced by "type": "module")
  • Update engines to require node >=24.0.0
  • Delete 34 index.mjs shim files (no longer needed with native ESM)
    • Exception: parse5-utils keeps index.mjs as ESM wrapper for CJS source
  • Fix CJS interop issues:
    • require.resolve() -> import.meta.resolve() / createRequire()
    • __dirname -> import.meta.dirname
    • Default import interop for CJS packages
  • Add .nvmrc (v24.11.1)

This is PR 3 of 4 in the Node 24 migration stack.

Stack

  1. node24/typescript-upgrade -- TypeScript upgrade + .ts imports (chore: upgrade TypeScript to 5.9.3 and enable .ts imports #3043)
  2. node24/test-migration -- mocha/chai to node:test migration (chore: migrate tests from mocha/chai to node:test #3044)
  3. This PR -- CJS to ESM + Node 24 requirement
  4. node24/parse5-and-jsdoc -- parse5 v8 + JSDoc to TS conversions (feat!: migrate parse5 to v8 and convert JSDoc packages to TypeScript #3046)

Test plan

  • npm run build passes with zero TS errors
  • Lint passes
  • Build output verified as ESM (export/import syntax)
  • CI passes

Generated with Claude Code

@changeset-bot
Copy link

changeset-bot bot commented Mar 17, 2026

🦋 Changeset detected

Latest commit: dec9658

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@bennypowers bennypowers force-pushed the node24/esm-and-node24 branch from f2b5763 to f8015fd Compare March 17, 2026 11:52
@bennypowers bennypowers force-pushed the node24/test-migration branch from a38ded7 to 010ef78 Compare March 17, 2026 11:52
@bennypowers bennypowers force-pushed the node24/esm-and-node24 branch from f8015fd to 18a0066 Compare March 17, 2026 12:53
@bennypowers bennypowers force-pushed the node24/test-migration branch 2 times, most recently from 8bd2017 to 268a3ff Compare March 17, 2026 14:57
@bennypowers bennypowers force-pushed the node24/esm-and-node24 branch 2 times, most recently from a2a3658 to eb87c8d Compare March 17, 2026 16:48
@bennypowers bennypowers force-pushed the node24/test-migration branch from 268a3ff to 61bf92a Compare March 17, 2026 16:48
@bennypowers bennypowers force-pushed the node24/esm-and-node24 branch 2 times, most recently from b1a23e2 to b77e600 Compare March 17, 2026 18:02
@bennypowers bennypowers force-pushed the node24/test-migration branch from b5e6238 to 32ba55e Compare March 17, 2026 18:02
@bennypowers bennypowers marked this pull request as draft March 17, 2026 18:34
@bennypowers bennypowers force-pushed the node24/esm-and-node24 branch from b77e600 to 10e9722 Compare March 17, 2026 19:01
@bennypowers bennypowers force-pushed the node24/test-migration branch from 32ba55e to 5abe25a Compare March 17, 2026 19:01
@bennypowers bennypowers force-pushed the node24/esm-and-node24 branch from 10e9722 to d3acddd Compare March 18, 2026 12:08
@bennypowers bennypowers force-pushed the node24/test-migration branch 2 times, most recently from 4fb3367 to 4926432 Compare March 18, 2026 13:33
@bennypowers bennypowers force-pushed the node24/esm-and-node24 branch 2 times, most recently from 441c093 to e99e801 Compare March 18, 2026 14:47
@bennypowers bennypowers force-pushed the node24/test-migration branch 2 times, most recently from 6aab5fb to 1effc4f Compare March 18, 2026 14:58
@bennypowers bennypowers force-pushed the node24/esm-and-node24 branch from e99e801 to f9df49b Compare March 18, 2026 14:58
@bennypowers bennypowers force-pushed the node24/test-migration branch from 1effc4f to 74db169 Compare March 18, 2026 17:03
@bennypowers bennypowers force-pushed the node24/esm-and-node24 branch from f9df49b to 16c8d24 Compare March 18, 2026 17:03
@bennypowers bennypowers force-pushed the node24/test-migration branch from 74db169 to 2c9b144 Compare March 19, 2026 06:13
@bennypowers bennypowers force-pushed the node24/esm-and-node24 branch 2 times, most recently from e8cf07b to e4d58b4 Compare March 19, 2026 07:07
@bennypowers bennypowers force-pushed the node24/test-migration branch from 2c9b144 to 6de56b9 Compare March 19, 2026 07:07
@bennypowers bennypowers force-pushed the node24/test-migration branch 2 times, most recently from 4841973 to 0a62b2c Compare March 19, 2026 17:01
@bennypowers bennypowers force-pushed the node24/esm-and-node24 branch from 5549599 to 83a016b Compare March 19, 2026 17:01
@bennypowers bennypowers force-pushed the node24/test-migration branch from 0a62b2c to badf76c Compare March 19, 2026 17:40
@bennypowers bennypowers force-pushed the node24/esm-and-node24 branch 2 times, most recently from f92e889 to fe5ef17 Compare March 19, 2026 17:56
@bennypowers bennypowers force-pushed the node24/test-migration branch from badf76c to 059908a Compare March 19, 2026 17:56
@bennypowers bennypowers force-pushed the node24/esm-and-node24 branch from fe5ef17 to fa00235 Compare March 19, 2026 18:03
@bennypowers bennypowers force-pushed the node24/test-migration branch from c40dc3e to fdff965 Compare March 20, 2026 06:53
@bennypowers bennypowers force-pushed the node24/esm-and-node24 branch from fa00235 to 6152fa0 Compare March 20, 2026 06:53
@bennypowers bennypowers force-pushed the node24/test-migration branch from fdff965 to 037552b Compare March 20, 2026 12:20
@bennypowers bennypowers force-pushed the node24/esm-and-node24 branch from 6152fa0 to 853196c Compare March 22, 2026 03:58
@bennypowers bennypowers force-pushed the node24/test-migration branch from 037552b to 0c3de8d Compare March 22, 2026 05:41
@bennypowers bennypowers force-pushed the node24/esm-and-node24 branch 3 times, most recently from 1e7a44c to 3b0e109 Compare March 22, 2026 08:26
@bennypowers bennypowers force-pushed the node24/test-migration branch from 49b4991 to 2cc8e4e Compare March 22, 2026 09:42
@bennypowers bennypowers force-pushed the node24/esm-and-node24 branch 4 times, most recently from 50f5b06 to 708a7ce Compare March 23, 2026 06:02
BREAKING CHANGE: Node 24+ is now required. All packages emit ESM.

- Add "type": "module" to all package.json files
- Update exports to use single "default" condition (remove CJS/ESM dual exports)
- Update engines to require Node >=24.0.0
- Delete all index.mjs shim files (34 files)
- Fix CJS interop issues across source files:
  - require.resolve → import.meta.resolve / createRequire
  - __dirname → import.meta.dirname
  - Default import interop for CJS packages (rollup plugins, saucelabs, etc.)
- Add .nvmrc (v24.11.1)
- Add changeset for breaking node version requirement

Assisted-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Restore parse5 to ^6.0.1 in all packages (v8 migration is PR4)
- Add @ts-ignore for rollupPluginExternalGlobals usage sites
- Regenerate package-lock.json

Assisted-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Assisted-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update all package.json exports to use "default" condition
- Remove "module" field from packages (ESM via "type": "module")
- Fix "main" field for packages with changed entry points
- Keep JSDoc packages (parse5-utils, config-loader, rollup-plugin-copy,
  rollup-plugin-import-meta-assets, storybook-utils) WITHOUT "type": "module"
  since their .js source uses require() -- converted to ESM in PR4
- Restore parse5-utils index.mjs ESM wrapper with createRequire
- Apply CJS interop fixes (import.meta.resolve, createRequire) for
  source files that used require.resolve/__dirname

Assisted-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
JSDoc packages (config-loader, rollup-plugin-copy,
rollup-plugin-import-meta-assets) have CJS source in src/,
not compiled output in dist/. The exports map must point to
src/ for runtime resolution to work.

Assisted-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
browserScript.ts reads compiled serialize.js and logUncaughtErrors.js
at runtime and injects them into the browser via page.evaluate().
With ESM output, these files have `export` keywords which are invalid
in non-module browser script context. Strip them on read.

Assisted-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The test reads dist/serialize.js directly and injects it into the
browser via page.evaluate. Need to strip ESM export keyword there
too, not just in browserScript.ts.

Assisted-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

1 participant