feat!: switch CLI to ESM and bump minimum Node.js to 20.19.0#1296
Merged
sorccu merged 20 commits intoMay 13, 2026
Merged
Conversation
2 tasks
BREAKING CHANGE: Drop support for Node.js 18. The new engine range is ^20.19.0 || >=22.12.0, which ensures require(esm) support for CJS consumers of the library. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prerequisite for ESM migration. Node.js ESM requires explicit file extensions on relative imports. Test fixtures with their own package.json are excluded since they represent user projects with independent module resolution. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
BREAKING CHANGE: Both packages now emit ESM instead of CJS. CJS consumers on Node 20.19+ can still require() the library thanks to Node's native require(esm) support. - Set "type": "module" in both packages - Switch tsconfig module from node16/commonjs to nodenext - Remove esModuleInterop and ignoreDeprecations (unnecessary with ESM) - Update bin scripts to use oclif's ESM execute() entry point - Fix remaining import paths (from '..' → '../index.js', module augmentation) - Upgrade conf 10→15 and p-queue 6→9 (ESM-only, now compatible) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace Node.js CJS globals with import.meta.url-based alternatives since the packages now emit ESM. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ESM modules don't have require() in scope. Use createRequire from node:module for the remaining cases that need synchronous CJS loading: ts-node file loader, typescript-estree parser, and playwright version detection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Now that both packages emit ESM, update all dependencies that were blocked on ESM-only versions: - chalk 4.1.2 → 5.6.2 - conf 10.2.0 → 15.1.0 (done in ESM switch commit) - indent-string 4.0.0 → 5.0.0 - log-symbols 4.1.0 → 7.0.1 - open 8.4.2 → 11.0.0 - p-queue 6.6.2 → 9.2.0 (done in ESM switch commit) - nanoid 3.3.12 → 5.1.11 - uuid 11.1.1 → 14.0.0 - execa 5.1.1 → 9.6.1 (create-cli) - ora 5.4.1 → 9.4.0 (create-cli) - passwd-user 3.0.0 → 4.0.0 (create-cli) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adapt to export changes in the updated ESM versions: - log-symbols v7: namespace import → default import - execa v9: default import → named import - passwd-user v4: default import → named import Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- config 3.3.12 → 4.4.1 - dotenv 16.6.1 → 17.4.2 - glob 10.5.0 → 13.0.6 - lint-staged 15.5.2 → 16.4.0 - minimatch 9.0.9 → 10.2.5 - rimraf 5.0.10 → 6.1.3 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Now that the package is ESM, dynamic import() for execa is no longer needed. Use regular static imports instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace __filename/__dirname with import.meta.url equivalent - Change encoding option from 'utf-8' to 'utf8' (execa v9 requirement) - Replace ExecaReturnValue type with Result (execa v9 rename) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
json5 only has a default export. The namespace import (import * as JSON5) puts stringify on JSON5.default.stringify, causing "JSON5.stringify is not a function" at runtime. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ora only has a default export. Use type-only import for the Ora interface instead of namespace import. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The config file used module.exports which doesn't work with "type": "module" in package.json. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These .js files used module.exports but inherit "type": "module" from the package.json, so they need to use export default instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This CJS fixture uses require() and module.exports intentionally. Without its own package.json, it inherits "type": "module" from the CLI's package.json, causing Node to reject the CJS syntax. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Temporary debug commit to diagnose CI-only bootstrap test failures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
oclif detects workspace-symlinked packages as "linked" plugins and attempts to load TypeScript source instead of compiled dist, even in production mode. This fails in ESM because Node can't import .ts files without a loader. Disable enableAutoTranspile via oclif settings to always use the compiled dist output. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace snapshot assertions with toContain checks to avoid log-symbols v7 using different Unicode symbols on Windows vs macOS/Linux. Remove unused snapshots. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The account members command was added on main after the ESM migration branch. Add .js extensions to its relative imports for ESM compat. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
c491cda to
c309779
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
BREAKING CHANGE: Minimum Node.js version is now
^20.19.0 || >=22.12.0. Both packages now emit ESM instead of CJS.ESM migration
"type": "module"and"module": "nodenext"in both packages.jsextensions to all relative imports__dirname/__filenamewithimport.meta.urlequivalentsrequire()calls withcreateRequirefromnode:moduleawait import('execa')to static importsexport defaultinstead ofmodule.exports)package.jsonto CJS e2e fixtures that need to stay CJSenableAutoTranspilein bin scripts (prevents oclif from trying to load.tssource via workspace symlinks)execute()entry pointDependency updates (previously blocked on ESM/Node 20)
Test fixes
__dirname)Test plan
🤖 Generated with Claude Code