Skip to content

chore(deps): bump @vitejs/plugin-rsc to ^0.5.34 - #836

Merged
ivogt merged 3 commits into
mainfrom
chore/plugin-rsc-0-5-34
Aug 28, 2026
Merged

chore(deps): bump @vitejs/plugin-rsc to ^0.5.34#836
ivogt merged 3 commits into
mainfrom
chore/plugin-rsc-0-5-34

Conversation

@ivogt

@ivogt ivogt commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Problem

We were pinned to @vitejs/plugin-rsc@0.5.31. From 0.5.33 the plugin deprecates import.meta.viteRsc.loadBootstrapScriptContent in favor of getClientEntryUrl() from @vitejs/plugin-rsc/ssr, and tells frameworks to import @vitejs/plugin-rsc/rsc/server and /rsc/client instead of the combined /rsc barrel so Vite can drop the unused react-server-dom protocol side. 0.5.34 also lets file-level "use cache" and "use server" coexist in one module — plugin-rsc hoists the server functions and rebinds the original export to registerServerReference($$hoist_*, ...), which our file-level "use cache" wrap treated as a non-function and rejected at build.

Before vs after

Generated SSR entry (headScripts: "preinit", the default):

// before
loadBootstrapScriptContent: () =>
  import.meta.viteRsc.loadBootstrapScriptContent("index"),

// after
import { getClientEntryUrl } from "@rangojs/router/internal/deps/ssr";
createSSRHandler({
  getClientEntryUrl,
  headScripts: "preinit",
});

RSC runtime imports:

// before
import { renderToReadableStream, createFromReadableStream } from "@vitejs/plugin-rsc/rsc";

// after
import { renderToReadableStream } from "@vitejs/plugin-rsc/rsc/server";
import { createFromReadableStream } from "@vitejs/plugin-rsc/rsc/client";

A custom SSR entry that only passes loadBootstrapScriptContent still works; conversion to bootstrapModules still requires an explicit headScripts: "preinit".

Consumer example

A default rango() app needs no source change. After pnpm update @vitejs/plugin-rsc@^0.5.34 (and @rangojs/router that depends on it), document renders take the client-entry URL from getClientEntryUrl() and Fizz emits bootstrapModules — the same <link rel="modulepreload"> + executing module script path headScripts: "preinit" already used, without parsing an inline import("…") string.

A custom SSR entry that has not adopted getClientEntryUrl keeps compiling:

export const renderHTML = createSSRHandler({
  createFromReadableStream,
  renderToReadableStream,
  injectRSCPayload,
  loadBootstrapScriptContent: () =>
    import.meta.viteRsc.loadBootstrapScriptContent("index"),
});

Mixed directives in one file now typecheck and transform:

"use cache";
export async function getData() { return db.query("…"); }
export async function save() {
  "use server";
  return "saved";
}

getData is wrapped with registerCachedFunction. save is left for plugin-rsc (both the $$hoist_* helper and the registerServerReference rebind).

Semantics

Case Behavior
Generated entry, headScripts: "preinit" (default) getClientEntryUrl()bootstrapModules. No loadBootstrapScriptContent.
Generated entry, headScripts: "preload" Still loadBootstrapScriptContent (inline bootstrap).
Custom SSR entry with only loadBootstrapScriptContent Unchanged. Regex conversion to bootstrapModules still requires explicit "preinit".
getClientEntryUrl without "preinit" Construction console.warn; inline bootstrap path is used. Throws at construction if that path is also missing.
Empty getClientEntryUrl() under "preinit" Falls through to loadBootstrapScriptContent if present.
Neither bootstrap dep usable createSSRHandler / createShellCaptureHandler throw at construction, not per request.
File-level "use cache" + inline "use server" Cache wrap skips $$hoist_* exports and registerServerReference($$hoist_*, …) rebinds.
Peer @vitejs/plugin-rsc 0.5.31–0.5.33 Module-link failure at boot (/rsc/server, /rsc/client, getClientEntryUrl do not exist). 0.5.34 is a hard floor.

Code-change summary

  • Catalog / lockfile / @rangojs/router dep+peer: ^0.5.34.
  • Generated virtual SSR entry threads getClientEntryUrl on preinit; loadBootstrapScriptContent only on preload.
  • resolveBootstrap(deps, deadline?) is the single bootstrap helper (sync URL short-circuit, inline load, capture deadline race). assertBootstrapDeps runs at handler construction.
  • Handler / internal/deps/rsc import /rsc/server; cache-runtime / segment decode import /rsc/client (internal/deps/rsc-client).
  • use-cache-transform skips hoisted server-reference rebinds. Regression test runs plugin-rsc's real transformServerActionServer first.
  • Handler-level vi.mocks consolidated into handler-mock-factories.ts; /rsc/client mocks carry the client surface.

Test plan

  • pnpm run typecheck
  • pnpm run test:unit:all (unit + Flight/RSC, every package)
  • pnpm run lint (oxlint --deny-warnings)
  • pnpm run format (oxfmt --check)
  • use-cache-transform: mixed "use cache" / "use server", including post-transformServerActionServer rebind (red-before-green)
  • preinit-client-references: getClientEntryUrl preferred, empty URL fallback, construction throw
  • shell-handlers: capture construction throw when neither bootstrap dep is usable
  • Dev + production e2e of a document render (existing suites; first place getClientEntryUrl hits a real Vite SSR environment). Not run locally in this PR — CI e2e.yml.

Notes

  • SSRDependencies.loadBootstrapScriptContent is now optional. Custom entries that already passed it are unaffected.
  • A runtime version check is not possible from the pure re-export bridges; the CHANGELOG states the hard floor and the opaque link failure an in-range older peer produces.
  • rangoTestAliases now stubs /rsc, /rsc/server, /rsc/client, and /rsc/static.

Generated SSR entries use getClientEntryUrl() for headScripts: "preinit"
instead of the deprecated loadBootstrapScriptContent. RSC runtime imports
split onto @vitejs/plugin-rsc/rsc/server and /rsc/client (new
internal/deps/rsc-client bridge). File-level "use cache" leaves mixed
"use server" exports for plugin-rsc. Handler-level test mocks
consolidated into handler-mock-factories.ts.
@pkg-pr-new

pkg-pr-new Bot commented Aug 28, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@rangojs/router@836

commit: c413cff

ivogt added 2 commits August 28, 2026 11:29
…null

plugin-rsc 0.5.34 matchDirective calls stmt.directive.match() after a
`"directive" in node` check. Vite/oxc parseAst now emits directive: null
on ordinary ExpressionStatements, so a file that mixes a cached function
with a sibling handler whose first statement is an expression threw and
the wrap was dropped — cache-tag and inline-handler e2e never hit.
CFCacheStore L1 writes are waitUntil-scheduled. Two back-to-back GETs in
vite-plugin-cloudflare dev can both miss before the put is visible; wrangler
preview usually lands in time. Poll until consecutive reads agree, matching
the cloudflare-basic cache-tag e2e, so a later miss cannot overwrite the
first in-flight put and fail a first-timestamp comparison.
@ivogt
ivogt merged commit 18b9bd3 into main Aug 28, 2026
43 checks passed
@ivogt
ivogt deleted the chore/plugin-rsc-0-5-34 branch August 28, 2026 09:34
ivogt added a commit that referenced this pull request Aug 28, 2026
Adopt @vitejs/plugin-rsc 0.5.34 APIs and the file-level "use cache" hoist
fix from #836. 0.5.34 is a hard floor — upgrade the peer with the router.
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