cloudflare / cloudflare/vinext
vinext should give an actionable error (or auto-exclude) when a CJS dep fails Rolldown's dep-pre-bundler
- Dominant language
- TypeScript
- Stars
- 8.8k
- Forks
- 406
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 120
Description
## TL;DR
When a Node-only CJS package (e.g. `wpapi`, `superagent`) lives anywhere in the import graph, `vinext dev` crashes at startup with a raw Rolldown stack trace and exits the Node process — no hint that the fix is to add the package to `optimizeDeps.exclude`. With `nodejs_compat` enabled, these packages would **work at runtime in workerd**; the only failure is the build-time dep optimizer.
It took us several hours to identify which package was the trigger and find the workaround. A short, named diagnostic from vinext (or an auto-exclude heuristic) would make this trivial.
## Versions
- vinext: 0.0.50
- vite: 8.0.13
- rolldown: bundled with Vite 8 (`node_modules/rolldown/...`)
- @cloudflare/vite-plugin: 1.37.0
- compatibility_date: 2026-04-21
- compatibility_flags: `["nodejs_compat"]`
## Symptom
With `optimizeDeps.exclude` left empty (the natural starting state), running `bun run dev`:
```
file:///.../node_modules/vinext/dist/server/socket-error-backstop.js:114
throw err;
^
Error: Error during dependency optimization:
Build failed with 1 error:
[UNRESOLVED_IMPORT] Could not resolve '../..' in node_modules/superagent/lib/node/agent.js
node_modules/superagent/lib/node/agent.js:10:25
10 │ const request = require('../..');
│ ───┬───
│ ╰───── Module not found.
│
│ Help: 'node_modules/superagent/lib/node/agent.js' is imported by the following path:
│ - node_modules/superagent/lib/node/agent.js
│ - node_modules/superagent/lib/node/index.js
│ - node_modules/wpapi/lib/http-transport.js
│ - node_modules/wpapi/wpapi.js
at aggregateBindingErrorsIntoJsError (.../rolldown/dist/shared/error-CkdMJ9ps.mjs:48:18)
at unwrapBindingResult (.../rolldown/dist/shared/error-CkdMJ9ps.mjs:18:128)
at #build (.../rolldown/dist/shared/rolldown-build-BVD3dIdE.mjs:3275:34)
at async Object.build (.../vite/dist/node/chunks/node.js:31729:18)
Node.js v25.2.1
```
Dev server exits with non-zero status. No HTTP server starts.
## Reproduce
Pull any package that uses CJS `require('../..')` (self-reference to its own package root) into your app graph. The simplest:
```ts
// lib/use-wpapi.ts
import WPAPI from 'wpapi';
export const wp = new WPAPI({ endpoint: 'https://example.com/wp-json' });
```
Then `vinext dev` — boom.
(`superagent` itself is enough; `wpapi` just transitively pulls it in.)
## Root cause
Rolldown's dep optimizer doesn't resolve `require('../..')` inside `superagent/lib/node/agent.js`. This is a Rolldown limitation, not strictly a vinext bug — but vinext is the framework that surfaces this to users.
## Why this matters for a Workers-targeted framework
With `compatibility_flags: ["nodejs_compat"]` and a recent compat date, **`node:http`, `node:https`, `node:fs`, `node:net` are all 🟢 supported** in workerd ([docs](https://developers.cloudflare.com/workers/runtime-apis/nodejs/)). So packages like `wpapi`/`superagent` would run fine at the edge. The only reason they break is the dev-time dep optimizer — a tooling artefact, not a runtime constraint.
The workaround (`optimizeDeps.exclude: ['wpapi', 'superagent']`) is one line, but is undiscoverable without:
1. Knowing what "dep optimizer" means in Vite-speak
2. Recognising that "fix this Rolldown unresolved-import" → "exclude from optimization"
3. Reading the import chain in the error to identify *which* package to exclude
## Asks (in order of helpfulness)
**1. Friendly diagnostic wrapper.** When the dep optimizer fails with `UNRESOLVED_IMPORT`, vinext catches the error and prints:
```
[vinext] Dependency optimization failed for "superagent" (pulled in via wpapi).
This is usually a Node-only CJS package that Rolldown's dep-bundler can't pre-build.
At runtime, workerd with nodejs_compat will run it fine.
Fix: add it to optimizeDeps.exclude in vite.config.ts:
optimizeDeps: {
exclude: ['wpapi', 'superagent'],
}
(Both the leaf package and the package that imports it should be listed.)
```
Even just printing the package names from the import chain + the one-line fix would save users hours.
**2. Auto-exclude on Rolldown UNRESOLVED_IMPORT.** On the first dep-optimization run, if a package fails with this specific error code, vinext could:
- Auto-exclude it for the current dev session
- Print a message: "Auto-excluding `superagent` from optimizeDeps — add this to your vite.config.ts to make it permanent"
**3. Bundled known-broken list.** Vinext could ship a tiny known-broken-CJS list (`wpapi`, `superagent`, anything else commonly hit) and add it to `optimizeDeps.exclude` by default. Users can opt out.
Even just **(1)** alone would make the difference between a 10-second fix and a multi-hour debugging session.
## Bonus context
We also hit a separate, much more cryptic dep-runtime crash from the same family — `Cannot read properties of undefined (reading '__cjs_module_runner_transform')` inside `@vitejs/plugin-rsc`'s helper. That one's an upstream plugin-rsc issue (worth its own diagnostic), but the *user-facing* fix in both cases is the same: add the package to `optimizeDeps.exclude`. A single well-worded vinext error message could cover both.
Contributor guide
Assessment
This issue has not been assessed yet.