Skip to content

fix: resolve the dev manifest bridge after early imports - #416

Open
everton-dgn wants to merge 1 commit into
solidjs:nextfrom
everton-dgn:fix/dev-manifest-startup
Open

everton-dgn wants to merge 1 commit into
solidjs:nextfrom
everton-dgn:fix/dev-manifest-startup

Conversation

@everton-dgn

@everton-dgn everton-dgn commented Oct 14, 2026 •

Copy link
Copy Markdown

Fixes #414

Nitro can import virtual:solid-manifest before Vite opens its HTTP port. At that point the plugin has no bridge URL to embed, so the manifest selects its JS-only fallback for the rest of the session. Lazy-route CSS is then missing from the initial SSR HTML.

Defer the bridge URL lookup until assets are requested. A small virtual module reads Vite's resolved origin, including the port chosen when the configured port is occupied. Importing the manifest itself stays nonblocking: Vite awaits environment startup before binding the port, so waiting for listening inside the manifest loader would deadlock those hosts.

The regression tests cover eager imports, imports awaited by an environment's listen(), port fallback, in-process resolution, shutdown before listening and middleware mode. They run in CI after the plugin build.

Validation:

  • pnpm build passed, including declaration generation.
  • All 15 startup assertions passed, along with the existing CSS matrix and HTTP bridge tests.
  • With Nitro's actual dev worker, a manifest imported before HTTP startup now includes lazy-route CSS in the first SSR response.
  • The root Cypress suite could not start locally: its smoke-test process was killed with SIGKILL on macOS.

Middleware mode retains its existing JS-only fallback because it has no HTTP origin of its own. A caller that requests assets before the port opens also gets JS-only assets; later lookups recover once the bridge URL is available. In that case, the first lookup after listening can still return JS-only assets while the URL is refreshed. Workerd was not exercised.

Related: #415 covers stale CSS after edits. The two fixes touch the same resolver; combining them requires preserving both the generation check and the !bridgeMissing cache guard. The joint validation below now covers their composition.

Joint validation of #416 and #417

Both patches were composed on next at ec6b58d0c17290d6f474a9ed93bfa03d1dc9b8a0, using #416 at 00d67467a693cd702daada088543ffdaf5eb3871 and #417 at 81555d8f9c9b3681a6e6b2d949023a942a40d018.

The composition regression fixture is published separately as a test-only commit. It requires both fixes; that branch alone does not contain their runtime changes.

  • Combined build and typecheck passed, together with 21 unit tests, 21 bridge assertions, 15 startup assertions and 7 cache tests.
  • The two composition scenarios failed on the baseline and with either fix alone (0/2 each), and passed with both fixes (2/2).
  • The fixture covers an HMR-enabled isolated runner importing before listen(), recovery on the same resolver, CSS invalidation during a pending fetch, and two runners sharing one server with independent caches.
  • An additional post-reimport assertion also passes: module re-evaluation replaces the subscribed manifest instance. A subsequent CSS edit invalidates the new instance; a retained reference to the old instance keeps its previous cached value.

When resolving the overlap, preserve both guards:

if (generation === startedAt) {
  if (assets && !bridgeMissing) cache.set(key, assets);
  inFlight.delete(key);
}

The local composition also adds node test/startup-cache.mjs to the CSS-matrix test script and the CI bridge/startup/cache step. Carry those entries and the fixture into whichever PR lands second, or a follow-up after both fixes land.

This joint run used Vite's isolated runner transport. It did not rerun Cypress, exercise Workerd, or test two separate Vite servers.

Look up the bridge origin when an isolated runner requests assets instead
of permanently selecting the JS-only fallback during early startup.
Keep manifest imports independent of the HTTP listening event so hosts
can await them from environment startup without deadlocking.

Add regression coverage for eager imports, occupied ports, early asset
lookups, shutdown, in-process resolution and middleware mode.

Fixes solidjs#414
@changeset-bot

changeset-bot Bot commented Oct 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 00d6746

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 Oct 14, 2026

Copy link
Copy Markdown

Open in StackBlitz

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

commit: 00d6746

@rvlzzr

rvlzzr commented Oct 15, 2026

Copy link
Copy Markdown

Tested this branch under @cloudflare/vite-plugin 1.64.0 (workerd), with the pkg.pr.new build of 00d6746 and the setup from #414 (comment). There the manifest loads before the HTTP server listens on every plain vite dev start, so this covers the "Workerd was not exercised" case above.

@solidjs/vite-plugin Starts Lazy CSS in the SSR HTML
3.0.0-next.50 5 cold (node_modules/.vite and .wrangler removed), 3 warm missing from every response
this PR 5 cold, 3 warm present in every response, from the first request after startup

One thing to expect once the bridge is reachable: a lazy module's first lookup after a start is asynchronous, so its <id>_assets can go out with a streamed <Loading> fragment instead of the shell. If hydration starts before that fragment arrives, solid-js clears the revealed content during hydration: solidjs/solid#4087. That bug is not in this PR. #4087 reproduces it without any bridge, with a plain async manifest resolver and also with a sync one when the lazy() first renders inside the fragment, and it has a tested fix.

Versions: @cloudflare/vite-plugin@1.64.0, wrangler@4.151.0, vite@8.3.4, solid-js and @solidjs/web 2.0.0-rc.15, Node 24.21.0, Linux.

(submitted by Claude Opus 5.5 on behalf of rvlzzr)

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.

2 participants