Skip to content

fix: name the client entry in virtual:solid-manifest instead of guessing it (#353) - #354

Merged
ryansolid merged 1 commit into
nextfrom
fix/353-manifest-entry
Sep 10, 2026
Merged

fix: name the client entry in virtual:solid-manifest instead of guessing it (#353)#354
ryansolid merged 1 commit into
nextfrom
fix/353-manifest-entry

Conversation

@ryansolid

Copy link
Copy Markdown
Member

Closes #353

Problem

Regression from #347 (shipped in 3.0.0-next.40). With filesystem-routing's fileRoutes({ routers: { client }, buildInputs: 'client' }) every route module becomes a configured build.rollupOptions.input; since #347 those manifest records rightly keep isEntry (they are genuine entries). The generated start-mode handler's resolveClientEntry() picked the first isEntry record in manifest key order — and src/routes/... sorts ahead of virtual:solid-ssr-entry-client.tsx.

Confirmed with the issue's repro on next.40 (dumping the generated handler): it picks src/routes/index.tsx?pick=default&pick=$css&lang.tsx. Consequences:

  • <script type="module" src="/assets/index-Bm1I8-o3.js" async> — the route chunk is booted as the client entry, so the page never hydrates (worse than the reported CSS symptom).
  • <head> links the route's CSS (index-BDXsaL_t.css) and the modulepreload graph of the route; the entry graph's global stylesheet (virtual_solid-ssr-entry-client-*.css) is never linked — the "1 stylesheet link / linked in head: false" in the report.

@solidjs/web's registerEntryAssets has the same first-isEntry scan, which is why the CSS/preload set was the route's too.

Fix

Stop guessing. The plugin knows the entry it injected:

  • startServe reports the client input it adds (virtual:solid-ssr-entry-client.tsx or the authored entry's absolute path) to the main plugin via a new internal onClientEntryResolved callback.
  • virtual:solid-manifest (build flavor) now stamps manifest._entry = <key> (matched by key or record.src, same spellings as isConfiguredEntry) and serializes the entry's record first, so consumers that still identify the entry by the first isEntry record (@solidjs/web's registerEntryAssets, hand-rolled server entries) agree with _entry. Outside start mode _entry derives from the single configured input when there is exactly one (incl. Vite's default index.html); with several inputs and no start entry it is left absent.
  • The generated handler's resolveClientEntry() reads _entry first and only falls back to the first-isEntry scan when the stamp is absent. Stale "exactly one real entry remains flagged" comment replaced.
  • Keep genuine configured entries when reclassifying emitted lazy facades #347's semantics are unchanged: other configured inputs stay isEntry.
  • ViteManifest type gains _entry?: string.

The dev flavor of the manifest is a resolver (no records), so nothing to stamp there; registerEntryAssets already returns early for it.

Regression test

examples/start-ssr gains an extra-input mode (EXTRA_CLIENT_INPUT=1): src/ExtraInput.tsx (with its own CSS) is both an extra configured client input and lazily imported by App.tsx — the buildInputs shape. Asserts on the built SSR output that the entry <link rel="stylesheet"> is present, the <script type="module" src> is the real entry chunk, the extra input's chunk is neither, the baked manifest carries _entry and keeps the extra input flagged, and the extra input still works as a lazy route (both stylesheets). 3/8 on next, 8/8 with the fix.

Issue repro against a packed tarball: linked in <head>: true, stylesheet links: 2, script tag is the real entry.

Gate (local)

ssr 12/12 + boundary 8/8 · css-matrix 87/87 + bridge 19/19 · start-ssr 508/508 + http-bridge 10/10 + components-warning 9/9 · start-client 45/45 · start-env 47/47 · vite-8 vitest 1/1 · cypress 1/1.

Follow-up for @solidjs/web (not needed for this fix): registerEntryAssets could read manifest._entry directly instead of relying on record order.

…ing it

With filesystem-routing's `fileRoutes({ routers: { client }, buildInputs:
'client' })` every route module is a configured client build input, and
since #347 (next.40) those manifest records rightly keep `isEntry`. The
generated start-mode handler resolved the client entry by taking the first
`isEntry` record in manifest key order, so a `src/routes/...` key sorting
ahead of `virtual:solid-ssr-entry-client.tsx` won: the document booted the
route chunk (no hydration) and <head> linked the route's CSS while the
entry graph's global stylesheet was never linked (#353).

The plugin knows which input is the application entry — start mode injects
it — so the manifest module now says so: `_entry` carries the entry's key
(the start-mode entry reported by startServe, or the single configured
input outside start mode) and the entry record is serialized first, so
@solidjs/web's registerEntryAssets (first-`isEntry` scan) links the same
chunk's CSS/preload graph. resolveClientEntry() reads `_entry` and keeps
the scan only as the fallback for hand-rolled manifests. Other configured
inputs keep `isEntry`.

Regression test: start-ssr `extra-input` mode builds with an additional
configured client input that App.tsx also lazily imports and asserts the
entry stylesheet is linked, the module script is the real entry chunk, and
the extra input's chunk is neither.

Closes #353
@changeset-bot

changeset-bot Bot commented Sep 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 38434e1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@solidjs/vite-plugin Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Sep 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@solidjs/vite-plugin@354

commit: 38434e1

@ryansolid
ryansolid merged commit 8f34ceb into next Sep 10, 2026
6 checks passed
@ryansolid
ryansolid deleted the fix/353-manifest-entry branch September 10, 2026 00:31
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.

1 participant