Skip to content

feat(vite): plugin-rsc 0.5.31 alignment — exports-map client-ref dedup, manager API adaptation, react 19.2.8 - #816

Merged
ivogt merged 1 commit into
mainfrom
advisor/plugin-rsc-alignment
Jul 28, 2026
Merged

feat(vite): plugin-rsc 0.5.31 alignment — exports-map client-ref dedup, manager API adaptation, react 19.2.8#816
ivogt merged 1 commit into
mainfrom
advisor/plugin-rsc-alignment

Conversation

@ivogt

@ivogt ivogt commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Problem

Three consumer-observable gaps, one dependency alignment:

  1. Deep third-party client imports lost symbols in dev. When a server component imported a package whose "use client" modules live at deep subpaths (lib/context, not re-exported from the package root), the dev-mode dedup rewrote the module to the bare package root — and every symbol the root did not barrel-export vanished silently. Consumers saw context values render as their NOT_FOUND defaults with no error.
  2. expose-action-id was coupled to a private plugin-rsc shape. It reached into manager.serverReferenceMetaMap by structure; plugin-rsc 0.5.31 introduces manager.serverReferences (the #1310 claims API), and the old lookup would silently miss it.
  3. headScripts: "preload" had zero e2e coverage. The mode changes the document's script strategy (inline bootstrap + modulepreload hints instead of executing head scripts) and nothing pinned it.
  4. plugin-rsc ^0.5.31 (carries #1310 — pluggable server-function registration) and react 19.2.8 peers/catalog.

Before / after

// lib exports "./context" as a subpath only — NOT re-exported from the root
import { DeepValueDisplay } from "rango-e2e-deep-context-lib/context";

Before: dev dedup rewrote the module to rango-e2e-deep-context-lib (the root), the context symbol resolved to undefined, and the page rendered the provider-less default (NOT_FOUND).

After: dedup resolves the file through the package's exports map to the exact public subpath (rango-e2e-deep-context-lib/context) and re-exports from that — the context identity dedupes correctly and the provided value renders.

End-to-end example

The fixture package __test_packages__/rango-e2e-deep-context-lib is the realistic shape: a component library whose exports map exposes ./context with a "use client" provider/consumer pair that is not barrel-exported from the root. A server component renders the provider around a client consumer; the e2e asserts the provided value appears in server HTML and survives hydration, dev + production, in both test-app and cloudflare-basic.

Semantics

Case Behavior
Exports map maps the deep file to a public subpath Re-export from that precise subpath (new)
No public subpath maps to the file Fall back to the bare package root — the pre-existing lossy barrel behavior, now documented at the fallback site (unchanged)
Package listed in client optimizeDeps.exclude Untouched (unchanged)
Resolution fails (transient dev state) Cache entry evicted so the next resolve retries (documented)
plugin-rsc exposes serverReferences.metaMap Used (new shape, 0.5.31)
Only legacy serverReferenceMetaMap present Used (back-compat)
Neither shape present Loud named error at build (new — was a silent miss)

Code changes

  • src/vite/plugins/client-ref-dedup.ts — exports-map public-specifier resolution (star patterns, resolve-back verification, per-source caching), encoded-specifier virtual modules; the lossy root fallback kept and documented.
  • src/vite/plugins/expose-action-id.ts — public getPluginApi(), dual manager-shape support with loud failure.
  • playwright.preload.config.ts (router + cloudflare-basic) — isolated ports (checkoutPortOffset), isolated vite cache, RANGO_E2E_HEAD_SCRIPTS=preloadrango({ headScripts }).
  • .github/workflows/e2e.yml — preload-dev / preload-production matrix entries in both jobs with --config/--no-deps threading; root test:e2e runs both new surfaces.
  • pnpm-workspace.yaml / package.json — plugin-rsc ^0.5.31 (quarantine exemption updated with the review note), react 19.2.8.
  • src/cache/__tests__/segment-codec.test.ts — pins the preserveServerReferences decoder option (fix(router): preserve embedded server actions across cache and prerender hits #584) via inspectable mocks.

Test plan

  • Unit: full test:unit:all green on 0.5.31 (includes the four touched suites, 79 tests directly).
  • E2e client-package-resolution: dev + production, test-app AND cloudflare-basic (8/8) — red case demonstrated by the fixture's NOT_FOUND default.
  • E2e head-script-preload: warmup + dev + production pairs in both apps (5/5 each) — pins inline bootstrap, no executing external head scripts, modulepreload hints for client references.
  • E2e action-id-resolution (production) on 0.5.31 — 10/10, exercises the manager-shape adaptation at build time.
  • Typecheck, lint, format, e2e bucketing guard.

Notes

  • config.createResolver is deprecated in Vite 8 — the plugin should move to the environment-scoped resolver before the next Vite major; left as a follow-up to keep this PR mechanical.
  • 0.5.31's #1310 claims API (manager.serverReferences.{resolve,replaceClaim,deleteClaim}) is the groundwork for named inline actions ("use server <name>"); this PR only adapts the read side.
  • The preload e2e config is deliberately separate from the main playwright config: the env knob changes the built document shape, so it gets its own build, ports, and vite cache rather than a mode branch inside shared suites.

…p, manager API adaptation, react 19.2.8

Dev-mode client-reference dedup now resolves a deep third-party "use
client" module to its PRECISE public subpath through the package's exports
map (single-star patterns included, every candidate verified by resolving
back to the identical file, cached per source) instead of rewriting to the
bare package root — which silently dropped any symbol the root did not
barrel-export. The root rewrite survives only as a documented lossy
fallback when no public subpath maps to the file.

expose-action-id adopts the public getPluginApi() and tolerates both
plugin-rsc manager shapes (serverReferences.metaMap Map and the legacy
serverReferenceMetaMap record), failing loudly with a named error if
neither matches — groundwork for the #1310 claims API (pluggable server
functions, released in 0.5.31, now the pinned catalog version with the
quarantine exemption updated). react/react-dom peers and catalog move to
19.2.8.

New e2e surfaces, dev + production in BOTH test-app and cloudflare-basic:
client-package-resolution (rango-e2e-deep-context-lib fixture package with
non-barrel deep exports pins the dedup rework, red case = NOT_FOUND
context) and head-script-preload (dedicated playwright.preload.config.ts —
isolated ports and vite cache, RANGO_E2E_HEAD_SCRIPTS=preload threads
rango({ headScripts }) — pins the inline-bootstrap + modulepreload
document shape), wired into root test:e2e and both CI matrix jobs.
segment-codec tests pin the preserveServerReferences decoder option
shipped in #584 via inspectable mocks.
@ivogt
ivogt marked this pull request as ready for review July 28, 2026 09:31
@ivogt
ivogt merged commit 685b975 into main Jul 28, 2026
17 of 19 checks passed
@ivogt
ivogt deleted the advisor/plugin-rsc-alignment branch July 28, 2026 09: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