Skip to content

fix: set module.exports = ReactJWPlayer for Node ESM-for-CJS default-import#208

Open
esetnik wants to merge 1 commit into
micnews:masterfrom
esetnik:fix/vite8-rolldown-cjs-interop
Open

fix: set module.exports = ReactJWPlayer for Node ESM-for-CJS default-import#208
esetnik wants to merge 1 commit into
micnews:masterfrom
esetnik:fix/vite8-rolldown-cjs-interop

Conversation

@esetnik

@esetnik esetnik commented Apr 20, 2026

Copy link
Copy Markdown
Contributor

Closes #207.

Context

When a React app bundled by Vite 8 (or any Node ESM-for-CJS importer) does:

import ReactJWPlayer from "react-jw-player";

…it currently gets the CJS namespace object {default: ReactJWPlayer, __esModule: true} rather than the ReactJWPlayer class, and <ReactJWPlayer /> crashes with Element type is invalid: ... got: object (React error #130).

The split comes from rolldown's (intentional) isNodeMode heuristic — see rolldown/rolldown#8061, closed as expected behavior. When the consumer has "type": "module", rolldown treats the CJS default import as module.exports itself (Node's semantics), not module.exports.default (Babel's semantics).

react-jw-player's compiled dist/react-jw-player.js currently ends with:

exports.default = ReactJWPlayer;

…plus the babel-emitted Object.defineProperty(exports, "__esModule", {value: true}); at the top. It never reassigns module.exports. So under Node semantics the consumer receives the whole namespace and React throws.

Change (this PR)

One-file change in src/react-jw-player.jsx: assign module.exports = ReactJWPlayer (and module.exports.default = ReactJWPlayer to preserve the .default access path) in place of the export default. After babel compiles, dist/react-jw-player.js ends with:

module.exports = ReactJWPlayer;
module.exports.default = ReactJWPlayer;

module.exports now points at the ReactJWPlayer class directly, satisfying both bundler conventions. No new dependency, no build-config change.

Alternative approach

If you'd rather keep src/react-jw-player.jsx as pure ESM (export default ReactJWPlayer), the equivalent fix is to add babel-plugin-add-module-exports to .babelrc and let it emit the module.exports = exports["default"]; tail automatically:

 {
-  "presets": ["es2015", "react"],
-  "plugins": ["transform-object-assign"]
+  "presets": ["es2015", "react"],
+  "plugins": ["transform-object-assign", "add-module-exports"]
 }

That plugin is widely used (ships in many UI libs for exactly this reason) but hasn't had a release since 2019 — given how long this repo has been stable on the current dist/ shape, I went with the source-level approach to avoid introducing any new dep. Happy to switch to the plugin if you prefer.

Verification

  • Rebuilt dist/ with npm run build — diff is localized to the entry file.
  • Dropped the regenerated dist/react-jw-player.js into a Vite 8 app that was crashing with React #130 on <ReactJWPlayer />; the error goes away and the player mounts correctly. Same app continues to work under Vite 7.

Happy to iterate on the comment block in src/react-jw-player.jsx if you'd rather keep the entry terse.

…import

Set `module.exports` (and `module.exports.default`) to the
`ReactJWPlayer` class directly from `src/react-jw-player.jsx`, so the
compiled `dist/react-jw-player.js` works under both of the
default-import conventions that bundlers use for CJS modules:

- Babel-interop path (Rollup, webpack < 5, Vite 7): reads
  `module.exports.default` (or `exports.default`), which we still set
  via the explicit `module.exports.default = ReactJWPlayer` assignment.
- Node ESM-for-CJS path (Vite 8 / rolldown, webpack 5, Node's own
  ESM-importing-CJS): reads `module.exports`, which is the
  `ReactJWPlayer` class itself — not a
  `{default: ReactJWPlayer, __esModule: true}` namespace object.

Before this change the compiled entry ended with
`exports.default = ReactJWPlayer;` + the babel-emitted
`Object.defineProperty(exports, "__esModule", {value: true});`, with
no `module.exports` reassignment, so consumers on the Node ESM-for-CJS
path received the full namespace as their default import and
`<ReactJWPlayer />` crashed with `Element type is invalid: ... got: object`
(React error micnews#130) when bundled by Vite 8.

Closes micnews#207.

References:
- rolldown/rolldown#8061 (rolldown's
  isNodeMode heuristic that triggers the path split)
- https://rolldown.rs/in-depth/bundling-cjs#ambiguous-default-import-from-cjs-modules
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.

React error #130 when bundled by Vite 8 / rolldown: default import is the CJS namespace, not the ReactJWPlayer class

1 participant