Skip to content

fix: improve CJS-to-ESM transform for better npm compatibility#3697

Merged
bartlomieju merged 4 commits into
mainfrom
fix/cjs-vite-interop
Apr 3, 2026
Merged

fix: improve CJS-to-ESM transform for better npm compatibility#3697
bartlomieju merged 4 commits into
mainfrom
fix/cjs-vite-interop

Conversation

@bartlomieju
Copy link
Copy Markdown
Contributor

@bartlomieju bartlomieju commented Mar 24, 2026

Summary

Fixes several issues in the Fresh Vite plugin's CommonJS-to-ESM Babel transform (commonjs.ts) that caused npm packages to break during builds while working fine in dev mode.

  • Fix .mts/.cts extension detection.cts was incorrectly in the ESM branch (should be .mts), causing TypeScript CJS files to skip the transform
  • Handle require.resolve() — injects createRequire(import.meta.url) polyfill so require.resolve() calls work in ESM output (require or exports is not defined #3619)
  • Polyfill __dirname/__filename — injects fileURLToPath/dirname based polyfill when these CJS globals are used
  • Use var instead of let for _default — avoids TDZ errors when Rollup reorders declarations in bundled output (CJS dependencies cause fatal issues when building/running that go unnoticed in dev mode #3653)
  • Track module.exports.X as named exports — the MemberExpression visitor now handles both exports.X and module.exports.X patterns

Addresses: #3619, #3653

Test plan

  • All 49 commonjs_test.ts tests pass (42 existing + 7 new)
  • New test cases cover each fix:
    • require.resolve injects createRequire
    • require.resolve + require() combined (single injection)
    • .mts files treated as ESM
    • .cts files treated as CJS
    • __dirname/__filename polyfill injection
    • module.exports.X tracked as named export
    • Primitive module.exports with namespace re-export guard

Note on remaining CJS issues

This PR does not fix CJS issues caused by require()-to-import hoisting changing package.json conditional export resolution (#3505, #3478, #3449). Those need resolver-level changes. See #3593 for native addon externalization (also out of scope).

🤖 Generated with Claude Code

…bility

Fixes several issues in the CommonJS Babel transform that caused npm
packages to break during builds while working in dev mode:

- Fix .mts/.cts extension detection (`.cts` was in the ESM branch)
- Handle `require.resolve()` by injecting `createRequire` polyfill
- Polyfill `__dirname` and `__filename` CJS globals in transformed modules
- Use `var` instead of `const` for `_default` to avoid TDZ errors in Rollup bundles
- Guard namespace property spreading against primitive default exports
- Track `module.exports.X` patterns as named exports (not just `exports.X`)

Addresses: #3492, #3619, #3653, #3449, #3505

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Hajime-san
Copy link
Copy Markdown
Contributor

NOTE: When I tried to migrate Vite to v8 and rollup to rolldown for @fresh/plugin-vite, some tests failed due to CJS related problem. This PR might make clearing it.

@bartlomieju
Copy link
Copy Markdown
Contributor Author

Closing in favor of #3734

bartlomieju and others added 2 commits April 3, 2026 15:44
# Conflicts:
#	packages/plugin-vite/src/plugins/patches/commonjs.ts
#	packages/plugin-vite/src/plugins/patches/commonjs_test.ts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@bartlomieju bartlomieju changed the title fix(vite-plugin): improve CJS-to-ESM transform for better npm compatibility fix: improve CJS-to-ESM transform for better npm compatibility Apr 3, 2026
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@bartlomieju bartlomieju merged commit d6b0a16 into main Apr 3, 2026
9 checks passed
@bartlomieju bartlomieju deleted the fix/cjs-vite-interop branch April 3, 2026 14:29
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.

2 participants