fix(rsc): serve app-router page modules in local dev under experimental.rsc (#3662) - #3677
Conversation
With `experimental: { rsc: true }`, `veryfront dev` 404'd every app-router
client module (`/_vf_modules/app/page.js`, `/layout.js`, …) and nothing
hydrated — a regression from #3290, which added RSC client-boundary module
admission but wired `rscEnabled` unconditionally rather than scoping it to
hosted runtimes.
RSC client boundaries are a hosted-transport concern: hosted/preview/production
ship only `use client` islands and render server components via the RSC
endpoint, so a plain app-router server component is (correctly) refused as a
browser module. A local `veryfront dev` project has no client flight consumer —
the browser hydrates by importing the whole page module, exactly as the
non-RSC path does. Applying the hosted admission to local dev therefore
refused the very module the local hydration pipeline requests.
Scope `requiresClientBoundary` to non-local projects (a new `isLocalProject`
input on the browser-module source policy). Local dev now serves app-router
pages/layouts through the same plain-serve branch the non-RSC path uses, so
they hydrate. The hosted 404-for-server-components contract is unchanged
(still pinned by the sibling test), and server surfaces (api/actions/routes)
stay protected in local dev — those gates never depended on `rscEnabled`.
Closes #3662.
|
Warning Review limit reached
Next review available in: 2 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
Comment |
Bug
With
experimental: { rsc: true },veryfront devreturns 404 for every app-router client module (/_vf_modules/app/page.js,/_vf_modules/app/layout.js,/_vf_modules/app/<route>/page.js), the client throwsFailed to fetch dynamically imported module, and nothing hydrates (it then also probes non-existent/_vf_modules/pages/*). Green on0.1.1123, red on0.1.1231, and specific to theexperimental.rscpath — without the flag the same app-router routes hydrate fine.Root cause (a regression from #3290)
#3290 introduced RSC-aware browser-module admission:
browser-module-admission.tssetsrequiresClientBoundary: rscEnabled && appRelative, and the bundler 404s an app-relative module with nouse clientdirective. But it wiredrscEnabled: isRSCEnabled(config)unconditionally (module-server.ts) — not scoped to hosted — and did not touch the hydration pipeline.RSC client boundaries are a hosted-transport concern: hosted/preview/production ship only
use clientislands and render server components via the/_veryfront/rsc/endpoint, so refusing a plain server-component page as a browser module is correct there. A localveryfront devproject has no client flight consumer — the browser hydrates by importing the whole page module, exactly as the non-RSC path does (which still works becauseisRSCEnabledis false there). So the hosted admission refused the very module the local hydration pipeline requests → 404 → dead hydration.(Flipping the client-module strategy to
rsc-modulewas ruled out: the RSC module endpoint also enforcesrequireClientBoundary, so it 404s server components too. The break is purely in module admission.)Fix
Scope
requiresClientBoundaryto non-local projects via a newisLocalProjectinput onBrowserModuleSourcePolicyOptions:module-server.tspassesisLocalProject: options.isLocalProjectinto the classifier. Local dev now serves app-router pages/layouts through the same plain-serve branch the non-RSC path already uses, so they hydrate. KeepingrscEnabledtruthful (rather than forcing it false in local dev) means any futurerscEnabled-dependent behavior can't silently regress local dev.Non-regressing:
isLocalProject: false⇒requiresClientBoundarystays true ⇒ server components still 404. Pinned by the existingrequires an explicit client boundary for RSC app modulestest.api/actions/server-route/middleware/metadata protections are independent ofrscEnabled.Tests (red→green)
Added
serves RSC app-router page and layout modules in local dev so they hydratetomodule-server.test.ts(mirrors the hosted pinned test,isLocalProject: true): assertsapp/page.js+app/layout.jsserve 200 with their server markers, and thatapp/api/save.jsstays 404. Fails onmain(404), passes with the fix. Fullmodule-server+browser-module-admission+client-module-strategy+endpoint-routersuites green;deno check/lint/fmt+docs:api-reference:checkclean.Closes #3662.