cloudflare / cloudflare/vinext

RSC assets manifest emits modulepreload URLs for JS chunks that are never built (CSS-only chunks)

Open
#3,064 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
8.8k
Forks
406
Avg merge
2d 6h
Merged PRs (30d)
120

Description

### What happened

After `vinext build`, `dist/server/__vite_rsc_assets_manifest.js` contains `` URLs for JS chunks that were **never emitted**. Every one of them 404s on each page load.

The CSS side is fine — the CSS asset exists and is served correctly. Only the JS URL derived for the same chunk is phantom.

The trigger is a plugin that puts CSS into its own chunk. In our case `@devup-ui/vite-plugin` (a build-time CSS-in-JS extractor) groups its generated `devup-ui*.css` modules into dedicated chunks via Rolldown `output.codeSplitting.groups`. Those chunks contain **only CSS and no JS**, so no `.js` file is produced — but the RSC assets manifest still emits a `.js` URL for them.

### Measurements

Next.js 16.3.2 vs vinext 1.0.0-beta.8, same source, Node target, `output: 'standalone'`:

| app | routes | `modulepreload` in `/ko/` | broken |
| --- | --- | --- | --- |
| marketing site | 13 | 102 | **33** |
| admin console | 110 | — | **286 phantom refs in manifest** |

Verified per reference:

- Vite client manifest (`dist/client/.vite/manifest.json`) contains **only** the CSS asset for these modules — correct.
- RSC assets manifest contains 33 (resp. 286) `/_next/static/chunks/devup-ui*.js` URLs; **none of the files exist on disk**.
- Same probe against a Next.js build of the same app: **0 broken references**.

Example names (note the `.css-` infix, i.e. the chunk is the CSS group):

```
/_next/static/chunks/devup-ui.css-cWLjIIuA.js
/_next/static/chunks/devup-ui-15.css-C8WMy7Jo.js
/_next/static/chunks/devup-ui-23.css-CqAP8F3Z.js
```

### Impact

Low severity, but real:

- Each 404 body is 9 bytes, so **~0.3 KB total** — not a size problem.
- They are `modulepreload` **hints**, so the browser ignores the failures. Rendering, hydration and interaction are unaffected (our a11y and e2e suites pass on vinext).
- The cost is **33 wasted HTTP requests per page load** (and considerably more on the larger app), plus noise for anyone whose CI asserts "no failed requests".

We only noticed because our smoke suite fails a route if any same-origin request fails. That check had to be taught to treat preload hints separately.

### Reproduction

1. Take an App Router project using a Vite plugin that emits CSS-only chunks (e.g. `@devup-ui/vite-plugin` >= 1.0.64, which uses `build.rolldownOptions.output.codeSplitting.groups`).
2. `vinext build` with `output: 'standalone'`.
3. Compare `dist/client/.vite/manifest.json` (CSS present, no JS entry) with `dist/server/__vite_rsc_assets_manifest.js` (JS URLs present).
4. Check the filesystem: the referenced `.js` files do not exist.
5. Load any page — the browser requests each of them and receives 404.

### Expected

A chunk that emits no JS should not contribute a JS URL to the RSC assets manifest. Only its CSS asset should be referenced.

### Environment

- vinext 1.0.0-beta.8, Vite 8.2.2 (Rolldown)
- Next.js 16.3.2 API surface, App Router, RSC
- `--platform=node`, `output: 'standalone'`
- Windows dev machine; production target Linux/Docker

Happy to provide the full manifest dumps or test against a patch.

Contributor guide

Open the contributing guide

Research direction

Run vinext build with a CSS-only chunk and compare dist/client/.vite/manifest.json with dist/server/__vite_rsc_assets_manifest.js. Start from the manifest generation path and trace how chunk assets become modulepreload URLs. Done means CSS assets remain referenced, while CSS-only chunks contribute no phantom .js URLs and the referenced files all exist on disk.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript, vite
Domain
build-system
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.