Reproduction link or steps
Minimal reproduction (no Nitro, no TanStack, no framework - real world use case outlined in additional comments) - a plain Vite 8 SSR build with rolldown:
package.json
{
"name": "rolldown-cjs-require-repro",
"private": true,
"type": "module",
"scripts": { "build": "vite build --ssr src/entry.ts --outDir dist" },
"dependencies": {
"react": "19.2.6",
"use-sync-external-store": "1.6.0"
},
"devDependencies": {
"vite": "8.0.13"
}
}
src/entry.ts
import * as React from "react";
// the CJS shim transitively used by @tanstack/react-router, react-query, etc.
import { useSyncExternalStore } from "use-sync-external-store/shim";
export default function handler() {
return { React: typeof React, useSyncExternalStore: typeof useSyncExternalStore };
}
vite.config.ts
import { defineConfig } from "vite";
export default defineConfig({
ssr: { target: "node", noExternal: true },
build: { ssr: true, target: "node20", minify: false },
});
Steps:
pnpm install
pnpm build
grep -n '__require("react")' dist/entry.mjs
grep -n 'var require_react' dist/entry.mjs
Both greps return hits. The bundle inlines a full copy of React under var require_react = /* @__PURE__ */ __commonJSMin(...) and leaves __require("react") calls untouched inside the wrapped use-sync-external-store-shim.production.js.
(Happy to push this to a GitHub repo if a maintainer would prefer that — the four files above are the entire repro.)
What is expected?
When rolldown's CJS plugin wraps a CJS module in __commonJSMin(...), the require("id") calls inside that wrapper should be rewritten to reference the bundled module if id is bundled, the same way @rollup/plugin-commonjs rewrites them.
In the repro the build graph already contains React (as the require_react wrapper). The require("react") inside the wrapped use-sync-external-store-shim.production.js should therefore be rewritten to call require_react() — not left as a runtime __require("react").
__require (the Node createRequire-based escape hatch) should only be emitted when:
- The require target is external (and listed as external in the config), or
- The require expression is dynamic (
require(someVar)) and cannot be statically resolved.
Neither applies here.
What is actually happening?
require("react") inside the wrapped CJS module is emitted verbatim as __require("react"), so the deployed code falls back to Node's runtime resolver. When the output is deployed to an environment where react isn't installed in node_modules (e.g. a Nitro/Vercel serverless function whose tracer only follows static imports), the function crashes on first request with:
Error: Cannot find module 'react'
Require stack:
- /var/task/_ssr/<chunk>.mjs
at Module._resolveFilename (node:internal/modules/cjs/loader:1456:15)
...
code: 'MODULE_NOT_FOUND',
requireStack: [ '/var/task/_ssr/<chunk>.mjs' ]
Excerpt from the produced bundle (real output from our Vite 8 + Nitro 3 + TanStack Start project):
// Inlined React — proves rolldown bundled it:
var require_react_production = /* @__PURE__ */ __commonJSMin(((exports) => { /* full React source */ }));
var require_react = /* @__PURE__ */ __commonJSMin(((exports, module) => {
module.exports = require_react_production();
}));
// ...elsewhere in the same output graph:
var require_use_sync_external_store_shim_production = /* @__PURE__ */ __commonJSMin(((exports) => {
var React$1 = __require("react"); // ← should be `require_react()`
// ...
}));
var require_with_selector_production = /* @__PURE__ */ __commonJSMin(((exports) => {
var React = __require("react"), shim = require_shim(); // ← same
// ...
}));
Both require_react and the dangling __require("react") are produced by the same build invocation, so it's not an external/inline disagreement — it's a missed rewrite at the CJS wrapper boundary.
System Info
System:
OS: macOS 26.2
CPU: (12) arm64 Apple M3 Pro
Binaries:
Node: 24.3.0
npm: 11.4.2
pnpm: 11.1.2
npmPackages:
rolldown: 1.0.1 (transitive via vite 8.0.13)
vite: 8.0.13
react: 19.2.6
use-sync-external-store: 1.6.0
Any additional comments?
Hit this in production: a Vite 8 + Nitro 3 + TanStack Start app deployed to Vercel. The function builds successfully but every request 500s with Cannot find module 'react'. The build also inlines React, which made the root cause non-obvious — both the inlined copy and the dangling __require("react") live in the same output graph.
Likely also affects every transitively-CJS React peer dep that uses require("react") at module top level: use-sync-external-store/shim and …/shim/with-selector are the obvious ones, but scheduler, react-side-effect-style packages, and older test/utility libs would behave the same.
Workarounds (none of these should be necessary):
- Tell the deployer to copy
react/react-dom into the runtime node_modules so the runtime __require("react") resolves. For Nitro: nitro({ traceDeps: ["react", "react-dom"] }).
- Mark
react/react-dom as external so rolldown emits real ESM import statements (which trackers like Nitro's nft can see) — at the cost of losing the inlined React.
- Patch
use-sync-external-store to use a static ESM import — not viable, it's a third-party shim.
For comparison, building the same src/entry.ts with Vite 7 (rollup + @rollup/plugin-commonjs) emits a single import React from "react" at the top of the output and no __require("react") calls anywhere — which is the behaviour we'd expect from rolldown.
Happy to help narrow down the failing rewrite path if a maintainer can point me at the relevant part of the CJS pass — it looks like the wrapper-internal require() is being lowered before the bundled-module lookup table is consulted.
Reproduction link or steps
Minimal reproduction (no Nitro, no TanStack, no framework - real world use case outlined in additional comments) - a plain Vite 8 SSR build with rolldown:
package.json{ "name": "rolldown-cjs-require-repro", "private": true, "type": "module", "scripts": { "build": "vite build --ssr src/entry.ts --outDir dist" }, "dependencies": { "react": "19.2.6", "use-sync-external-store": "1.6.0" }, "devDependencies": { "vite": "8.0.13" } }src/entry.tsvite.config.tsSteps:
Both greps return hits. The bundle inlines a full copy of React under
var require_react = /* @__PURE__ */ __commonJSMin(...)and leaves__require("react")calls untouched inside the wrappeduse-sync-external-store-shim.production.js.(Happy to push this to a GitHub repo if a maintainer would prefer that — the four files above are the entire repro.)
What is expected?
When rolldown's CJS plugin wraps a CJS module in
__commonJSMin(...), therequire("id")calls inside that wrapper should be rewritten to reference the bundled module ifidis bundled, the same way@rollup/plugin-commonjsrewrites them.In the repro the build graph already contains React (as the
require_reactwrapper). Therequire("react")inside the wrappeduse-sync-external-store-shim.production.jsshould therefore be rewritten to callrequire_react()— not left as a runtime__require("react").__require(the NodecreateRequire-based escape hatch) should only be emitted when:require(someVar)) and cannot be statically resolved.Neither applies here.
What is actually happening?
require("react")inside the wrapped CJS module is emitted verbatim as__require("react"), so the deployed code falls back to Node's runtime resolver. When the output is deployed to an environment wherereactisn't installed innode_modules(e.g. a Nitro/Vercel serverless function whose tracer only follows static imports), the function crashes on first request with:Excerpt from the produced bundle (real output from our Vite 8 + Nitro 3 + TanStack Start project):
Both
require_reactand the dangling__require("react")are produced by the same build invocation, so it's not an external/inline disagreement — it's a missed rewrite at the CJS wrapper boundary.System Info
Any additional comments?
Hit this in production: a Vite 8 + Nitro 3 + TanStack Start app deployed to Vercel. The function builds successfully but every request 500s with
Cannot find module 'react'. The build also inlines React, which made the root cause non-obvious — both the inlined copy and the dangling__require("react")live in the same output graph.Likely also affects every transitively-CJS React peer dep that uses
require("react")at module top level:use-sync-external-store/shimand…/shim/with-selectorare the obvious ones, butscheduler,react-side-effect-style packages, and older test/utility libs would behave the same.Workarounds (none of these should be necessary):
react/react-dominto the runtimenode_modulesso the runtime__require("react")resolves. For Nitro:nitro({ traceDeps: ["react", "react-dom"] }).react/react-domasexternalso rolldown emits real ESMimportstatements (which trackers like Nitro's nft can see) — at the cost of losing the inlined React.use-sync-external-storeto use a static ESM import — not viable, it's a third-party shim.For comparison, building the same
src/entry.tswith Vite 7 (rollup +@rollup/plugin-commonjs) emits a singleimport React from "react"at the top of the output and no__require("react")calls anywhere — which is the behaviour we'd expect from rolldown.Happy to help narrow down the failing rewrite path if a maintainer can point me at the relevant part of the CJS pass — it looks like the wrapper-internal
require()is being lowered before the bundled-module lookup table is consulted.