fix(solid-start): harden SSR asset manifest wiring for SSR'd lazy() components - #8050
Draft
ryansolid wants to merge 4 commits into
Draft
fix(solid-start): harden SSR asset manifest wiring for SSR'd lazy() components#8050ryansolid wants to merge 4 commits into
ryansolid wants to merge 4 commits into
Conversation
next.24 hardens the client-assets manifest bridge that solid-start swaps into @tanstack/solid-router's SSR renderers (99756c0): - the dev asset resolver answers synchronously once cached, so nested lazy() under re-created router outlets converges instead of looping the SSR pass into a stack overflow against the always-async bridge - transient dev bridge failures stay retryable instead of permanently stripping a module's client assets (and its hydration preload entry) for the rest of the dev session - lazy-asset manifest keys survive module query strings and non-root Vite base paths, so lazy(() => import('./X?query')) and apps served under a base prefix resolve their production manifest entries Co-authored-by: Cursor <cursoragent@cursor.com>
… asset map The client-assets manifest wiring (99756c0) gives SSR'd lazy() components three observable outputs in the served HTML: modulepreload hints for the lazy chunk and its static imports in the initial <head>, the chunk's stylesheet link, and the serialized boundary→chunk asset map that gates client hydration on those imports. The start-manifest e2e only asserted the stylesheet side, so a silent regression to the route-keyed manifest fallback would keep the suite green while losing hints and hydration gating. Two request-level tests against the built app pin the rest: direct SSR entry to /lazy-css-lazy emits exactly one head modulepreload for the lazy chunk and one for its statically imported widget chunk (and none of them on routes that never render the component), and the hydration payload's _assets map points at the same chunk URL the head hinted. Co-authored-by: Cursor <cursoragent@cursor.com>
next.25 and next.26 are patch releases with no changes to the client-assets manifest bridge this branch exercises (custom-extension native-compiler fix, start.env prefix guard, sendWebResponse backpressure fix, Fetchable SSR service entry, vitest browser-mode jsdom default). Re-verified against the new pin: start-manifest e2e 12/12, basic e2e 80 passed / 4 skipped, selective-ssr 11/11, and the solid package unit + type suites all green. Co-authored-by: Cursor <cursoragent@cursor.com>
Contributor
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ryansolid
marked this pull request as draft
August 12, 2026 11:05
vite-plugin-solid is now published as @solidjs/vite-plugin; 3.0.0-next.27 is identical code to 3.0.0-next.26 plus the rename. Every dependency this branch had pinned to the 3.0.0-next.26 line swaps to the new name (workspace solid packages' devDependencies plus the solid e2e/example/benchmark apps), along with the import specifiers in those projects' configs and the plugin-name mentions in the Start manifest bridge comments. The virtual:solid-manifest module id is unchanged upstream, so no runtime logic moves. v1-line references stay: router-devtools-core (vite-plugin-solid ^2.11.10) and router-plugin's published optional peer range. Re-verified against the new pin: start-manifest e2e 12/12, basic e2e 80 passed / 4 skipped, selective-ssr 11/11. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
99756c0 wired vite-plugin-solid's client-assets manifest
(
virtual:solid-manifest) into the Solid SSR renderers, closing the gap whereTanStack Start never fed Vite's client asset manifest into Solid's rendering:
SSR'd
lazy()components (Solid'slazy, not route-level splitting) renderedwithout their chunk's CSS, without
<link rel="modulepreload">hints, andwithout the serialized asset map the client needs to gate hydration of the
boundary on those chunks. Start's own manifest is route-keyed and intentionally
excludes dynamic-import chunks, so it cannot answer those per-module lookups.
Two things were still missing after that commit:
start-manifeste2e asserted the stylesheet side of the fix only. Nothing pinned the head
modulepreload hints or the hydration asset map — a silent regression to the
route-keyed manifest fallback (e.g. the plugin's stub swap failing to
resolve) would keep the suite green while losing early chunk discovery and
asset-gated hydration.
3.0.0-next.23) predates fixes to theexact bridge this feature consumes.
3.0.0-next.24makes the dev assetresolver answer synchronously once cached (nested
lazy()underre-created router outlets — Start's shape — could loop the SSR pass into a
stack overflow against the always-async bridge), keeps transient dev bridge
failures retryable instead of permanently stripping a module's assets for
the dev session, and makes lazy-asset manifest keys survive module query
strings and non-root Vite
basepaths.Mechanism
No production code changes; the injection seam from 99756c0 is untouched
and already general:
manifestrender option(
manifest ?? clientAssetsManifest ?? router.ssr?.manifestinrenderRouterToStream/renderRouterToString). No hint-specific logic livesin Start.
in-tree
</head>of the user Document; post-shell assets stream asmodulepreload / load-gated stylesheet links; arbitrary head tags ride the
same channel. A future head-management (
useHead-style) API flows throughthis seam with zero further Start changes — modulepreload hints are just its
first consumer.
The commits:
3.0.0-next.24— same monorepo-wide rangebump convention as the beta-32 upgrade (101
package.jsonpins +lockfile). next.24 carries the bridge hardening described above.
start-manifestsuite,using its established helpers/style, against the built app:
/lazy-css-lazyemits exactly one headmodulepreloadfor the lazy chunk (SharedWidgetLazy-*.js) and one forits statically imported widget chunk, plus the chunk stylesheet — and
routes that never render the component emit no hints for it;
_assetsmap pointingat the same chunk URL the head hinted, which the client awaits before
hydrating the boundary.
3.0.0-next.26— keeps the pin current.next.25/next.26 are patch releases with no changes to the manifest bridge
(custom-
extensionsnative-compiler fix,start.envprefix guard,streaming backpressure fix, Fetchable SSR service entry, vitest
browser-mode jsdom default).
The tests read only the served HTML: emitted link tags and the documented
_$HY.r["…_assets"]hydration payload shape. They touch zero Solidinternals and no Start internals — if either renderer or bridge changes how
assets are resolved, the assertions keep expressing the user-visible contract.
Verification
All against this branch rebased on
solid-router-v2-pre(solid-js2.0.0-beta.32, vite-plugin-solid3.0.0-next.26):e2e/solid-start/start-manifest: 12 passed (10 pre-existing + 2 new).e2e/solid-start/basic: 80 passed, 4 skipped (pre-existing skips).e2e/solid-start/selective-ssr: 11 passed.@tanstack/solid-router,@tanstack/solid-start,@tanstack/solid-start-client,@tanstack/solid-start-server: all pass,no type errors.
Served HTML for a prod build of the
start-manifestapp, direct request to/lazy-css-lazy— initial<head>(route-manifest hints carry_hk,Solid-renderer hints for the SSR'd
lazy()follow):and the hydration-gating map in the payload:
Dev mode verified manually (
vite dev, same route): modulepreload for the devmodule URL (
/src/components/SharedWidgetLazy.tsx),_assetsmap serialized,no asset-manifest warnings in the server log.