[cli] Migrate build output from CommonJS to ESM#14566
Conversation
🦋 Changeset detectedLatest commit: 50c773f The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
🧪 Test StrategyComparing: Strategy: Code changed outside of a package - running ALL tests Affected packages - 16 (40%)
Unaffected packages - 24 (60%)
Results
This comment is automatically generated based on the affected testing strategy |
|
|
bae3b13 to
1dc8691
Compare
mehulkar
left a comment
There was a problem hiding this comment.
cool, let me know if you want to do more testing (not even sure how that happens)
a08d407 to
4384a91
Compare
📦 CLI Tarball ReadyThe Vercel CLI tarball for this PR is now available! Quick TestYou can test this PR's CLI directly by running: npx https://vercel-aayd39b0d.vercel.sh/tarballs/vercel.tgz --helpUse in vercel.jsonTo use this CLI version in your project builds, add to your {
"build": {
"env": {
"VERCEL_CLI_VERSION": "vercel@https://vercel-aayd39b0d.vercel.sh/tarballs/vercel.tgz"
}
}
} |
This change updates the CLI build process to output ESM (ECMAScript Modules) instead of CommonJS, while maintaining full backwards compatibility. - Add `"type": "module"` to package.json - Update esbuild to output `format: 'esm'` - Add banner shim for CommonJS globals (`require`, `__filename`, `__dirname`) - Update tsconfig.json with `module: 'esnext'` and `moduleResolution: 'node16'` - Rename `builder-worker.js` → `builder-worker.cjs` - Rename `get-latest-worker.js` → `get-latest-worker.cjs` - These standalone worker files use CommonJS and need explicit `.cjs` extension - Update `compile-templates.mjs` to handle doT.js output in ESM context - Rename generated files to `.cjs` before requiring them - Rename `jest.config.js` → `jest.config.cjs` for CommonJS compatibility - Add `.gitignore` entries to prevent test-generated artifacts from being tracked - Enables use of modern npm packages that are ESM-only - Better tree-shaking and static analysis - Aligns with Node.js ecosystem direction - Prerequisite for future enhancements (e.g., Ink for terminal UI) This change is fully backwards compatible: - CLI works identically for end users - All existing functionality preserved - Works with both Node.js and Bun runtimes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The start.js script used require() which fails with "type": "module". Convert to dynamic import() to fix test failures. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The module and moduleResolution settings are not needed since esbuild handles module resolution during bundling. The node16 moduleResolution was causing tsc --noEmit to fail because it requires explicit .js extensions in import paths. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
In ESM, callSite.getFileName() returns a file:// URL instead of a regular file path. This caused path.join to malform the path and the package.json lookup to fail. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Without await, the script could exit before the imported module finishes executing. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
enableCompileCache was added in Node.js 22.1.0. The static import fails on Node 20 because the export doesn't exist. Using dynamic import with try-catch allows graceful degradation on older versions. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Test fixtures were being treated as ES modules because Node.js was walking up the directory tree and finding the CLI's package.json with type: module. This caused Lambda functions to fail with require is not defined in ES module scope errors. The fix adds type: commonjs to: - packages/cli/test/dev/fixtures/package.json (new file) - packages/cli/test/fixtures/package.json (existing file) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add tests to validate ESM migration behavior: - CJS shim globals (require, __filename, __dirname) - enableCompileCache backward compatibility pattern - Worker file resolution and CommonJS syntax - file:// URL handling for ESM stack traces - Test fixture isolation from ESM inheritance Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use platform-specific paths instead of hardcoded Unix paths. fileURLToPath behaves differently on Windows (adds drive letter). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Set module to es2022 to enable import.meta support for ESM unit tests while maintaining compatibility with existing code that doesnt use explicit file extensions. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Explain why we rename to .cjs (ESM package needs explicit CJS extension) and why we delete instead of rename back (generating TypeScript instead). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1a74cfb to
4ee953e
Compare
Replace createRequire/require() with await import() which works with both ESM and CJS modules natively, simplifying the code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Steven <steven@ceriously.com>
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## vercel@50.3.0 ### Minor Changes - Add upgrade command ([#14584](#14584)) - Support typo detection for commands as well as frameworks ([#14586](#14586)) ### Patch Changes - Migrate CLI build output from CommonJS to ESM ([#14566](#14566)) - Change CLI package to use ESM format (`"type": "module"`) - Add ESM shim banner for CommonJS compatibility (`require`, `__filename`, `__dirname`) - Rename worker files to `.cjs` extension for explicit CommonJS handling - Fix `getPackageJSON` to handle ESM `file://` URLs from stack traces - Convert `scripts/start.js` to use dynamic import ## @vercel/python-analysis@0.1.1 ### Patch Changes - Fix release ([#14591](#14591)) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

Summary
This PR migrates the Vercel CLI build output from CommonJS to ESM (ECMAScript Modules), enabling the use of modern ESM-only packages while maintaining full backwards compatibility.
Key Changes
"type": "module"to package.json, updated esbuild to output ESM format with a CJS compatibility shim forrequire,__filename, and__dirnamebuilder-worker.js,get-latest-worker.js) to.cjsextension since they use CommonJS syntaxesnextwithnode16resolution.cjsfor CommonJS compatibility in ESM packageWhy ESM?
Backwards Compatibility
This change is fully backwards compatible:
Test plan
pnpm build --filter=vercelsucceedsvercel --versionworks correctlyvercel whoamiworks correctlyvercel dev --helploads without errors🤖 Generated with Claude Code