Skip to content

fix: align Server Function proxy transforms with @vitejs/plugin-rsc - #4630

Merged
james-elicx merged 1 commit into
mainfrom
test/client-sourcemap-server-fns
Nov 9, 2026
Merged

james-elicx merged 1 commit into
mainfrom
test/client-sourcemap-server-fns

Conversation

@james-elicx

@james-elicx james-elicx commented Nov 9, 2026 •

Copy link
Copy Markdown
Member

Summary

Upgrades Vite+ to 1.5.1 and aligns vinext's Server Function proxy transforms with @vitejs/plugin-rsc. In builds, transforms that replace a module with createServerReference() proxies now return an empty sourcemap, so client sourcemaps (environments.client.build.sourcemap) only map the code the browser actually receives.

Two upstream releases handle plugin-rsc's own "use server" proxies:

vinext has three Server Function proxy transforms of its own that the upstream fixes don't cover:

  • "use cache" modules imported by Client Components;
  • "use server" modules imported by Web Workers;
  • "use cache" modules imported by Web Workers.

These kept generateMap({ hires: "boundary" }), which maps the proxy back to the original module, so rolldown 1.2.18 rightly keeps that module's content. This PR gives all three the same treatment as plugin-rsc: an empty map in builds, through a shared serverReferenceProxyResult() in plugins/transform-result.ts. Dev keeps the full map.

Vite+ 1.5.1

vite → npm:@voidzero-dev/vite-plus-core@1.5.1 and vite-plus → 1.5.1 in the catalog, like #4602. The lockfile diff is only the Vite+ packages plus postcss 8.5.31 → 8.5.32 (published 2026-10-26), which vite-plus-core depends on.

Vite+ 1.5.1 was published today, so the lockfile was resolved once with --config.minimum-release-age=0. Workspace config is unchanged and no minimumReleaseAgeExclude is needed: frozen-lockfile installs don't apply the release-age check.

Vite 8.3.6 (vitejs/vite#23995) HTML-escapes attribute values, so the Pages Router dev hydration script is now served as src="…?html-proxy&index=0.js". Browsers decode that to &, but tests/pages-router.test.ts scraped the raw attribute and fetched a 404, so its html-proxy lookups now go through a getHtmlProxyScriptPath() helper that decodes &. vinext's own HTML handling is unaffected.

For users: projects on Vite+ < 1.5.1 or Vite < 8.3.7 still need to upgrade for plugin-rsc's "use server" proxies. vinext's own proxies are fixed on any Vite.

Supersedes #3817

This supersedes #3817 (draft). That PR scrubbed sourcesContent in generateBundle with a ~265-line plugin. With the upstream fixes, no scrubbing is needed. I left #3817 open for you to close. main has no other code that post-processes client sourcemaps for Server Functions. The only sourcesContent reader is the dev stack-frame code in server/dev-stack-sourcemap.ts, which is unrelated.

Tests

The tests are in the existing app-action-ownership production suite (tests/e2e/app-router/action-owner-production.browser.spec.ts), which runs in the Chrome and WebKit browser-specific projects. Its build now enables environments.client.build.sourcemap, like Next's actions fixture enables productionBrowserSourceMaps, so every ownership test runs against a sourcemapped build.

A new route, /ownership/sourcemap, adds:

  • a "use server" module imported by a Client Component;
  • an inline "use server" action;
  • a file-level "use cache" module imported by the same Client Component;
  • an inline "use cache" function;
  • a Web Worker that imports both modules.

Each body has a unique marker. Two new tests assert:

  • no .js or .js.map under dist/client contains use server, use cache or any marker. This also covers the fixture's existing client-imported actions;
  • the maps still carry client-buttons.tsx and worker.ts with their content, so the test fails if sourcemaps silently stop being emitted;
  • on the running app, the proxies still call the server and the worker runs;
  • every served script's sourceMappingURL returns 200 and contains none of the server-only text.

The new route adds two "use cache" references, so findOnlyCacheActionId became findCacheActionId(source, "report/cache-owner/actions.ts:cachedProtectedAction"). It matches the reference id registered for that export.

The two new tests on each combination (Chrome):

Toolchain vinext proxy maps Result What the maps still contain
Vite+ 1.5.0 (rolldown 1.2.16) before ❌ use server in 6 other chunks (the fixture's existing client-imported actions), plus the new route's chunk and worker map
Vite+ 1.5.1 (rolldown 1.2.18) before ❌ client chunk: cached.ts; worker map: both modules
Vite+ 1.5.0 (rolldown 1.2.16) after ❌ same as the first row
Vite+ 1.5.1 (rolldown 1.2.18) after ✅ none

Also run locally on the final head:

  • full action-owner-production.browser.spec.ts: 29 passed in Chrome and 29 in WebKit;
  • vp test run tests/transform-result.test.ts tests/worker-use-server.test.ts tests/use-cache-transform.test.ts: 60 passed. The worker test's hook context now passes environment: { mode: "build" };
  • vp test run tests/pages-router.test.ts: 498 passed (5 failed on Vite+ 1.5.1 before the helper);
  • vp check, vp run knip: clean.

Earlier, with the same vinext changes: e2e web-worker-vinext 1 passed; app-router-isr-prod use-cache.spec.ts + action-bound-args.spec.ts 27 passed.

Performance

Builds now skip generating these proxies' maps; dev is unchanged. With the vinext changes alone, the tests/fixtures/app-basic production build median was main 9871 ms vs branch 9598 ms (3 alternating runs, within noise). The CI perf comparison on the final head (9e64e6e vs base): 0 improved, 0 regressed, 7 within ±1.5% — production build 2.65 s → 2.67 s, dev cold start 2.17 s → 2.14 s, large Pages Router dev cold start 17.96 s → 17.89 s, bundle sizes unchanged.

@pkg-pr-new

pkg-pr-new Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@vinext/cloudflare

npm i https://pkg.pr.new/@vinext/cloudflare@4630

create-vinext-app

npm i https://pkg.pr.new/create-vinext-app@4630

@vinext/types

npm i https://pkg.pr.new/@vinext/types@4630

vinext

npm i https://pkg.pr.new/vinext@4630

@cloudflare/workers-response-store

npm i https://pkg.pr.new/@cloudflare/workers-response-store@4630

commit: e4af518

@github-actions

github-actions Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared e4af518 against base ddd9a06 using alternating same-runner rounds. Next.js was unchanged and skipped.

0 improved · 0 regressed · 7 within ±1.5%

Scenario Framework Baseline Current Change
Client bundle size (gzip) vinext 152.3 KB 152.3 KB ⚫ +0.0%
Client entry size (gzip) vinext 140.5 KB 140.6 KB ⚫ +0.0%
Dev server cold start vinext 2.78 s 2.76 s ⚫ -0.5%
Dev server cold start (large Pages Router app) vinext 24.85 s 24.58 s ⚫ -1.1%
Production build time vinext 3.63 s 3.64 s ⚫ +0.2%
RSC entry closure size (gzip) vinext 141.5 KB 141.5 KB ⚫ -0.0%
Server bundle size (gzip) vinext 248.2 KB 248.2 KB ⚫ +0.0%

View detailed results and traces

🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head

@james-elicx james-elicx changed the title fix: keep Server Function source out of client sourcemaps fix: align Server Function proxy transforms with @vitejs/plugin-rsc Nov 9, 2026
@james-elicx
james-elicx force-pushed the test/client-sourcemap-server-fns branch from 4d83a3e to 6fc8909 Compare November 9, 2026 14:05
@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor
Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original
response-store-demo preview production
workers-cache preview production
kv preview production
static-assets-cache preview production
static-assets-pages preview production
static-assets-pages-i18n preview production
static-export preview production
web preview production

@james-elicx
james-elicx force-pushed the test/client-sourcemap-server-fns branch from 6fc8909 to 4284ac7 Compare November 9, 2026 14:45
@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

Please review exact head 4284ac72714bcf1ad2c8853170ab23085d93735f without modifying or pushing the branch. Report all actionable findings within your 8-minute time limit.

@ask-bonk

ask-bonk Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

LGTM!

github run

@james-elicx
james-elicx force-pushed the test/client-sourcemap-server-fns branch from 4284ac7 to 9e64e6e Compare November 9, 2026 14:56
@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

Please review exact head 9e64e6e281c9558ee7a5e3093c7deb863df5b982 without modifying or pushing the branch. Report all actionable findings within your 8-minute time limit.

Comment thread pnpm-workspace.yaml
use-count-up: 3.0.1
validator: ^13.15.26
vite: npm:@voidzero-dev/vite-plus-core@1.5.0
vite: npm:@voidzero-dev/vite-plus-core@1.5.1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This catalog bump fixes the workspace build, but it does not constrain consumers: packages/vinext/package.json still declares vite: ^8.0.0. Vite 8.3.6 depends on Rolldown ~1.2.16, whose empty-map bug retains the pre-transform sourcesContent (rolldown/rolldown#11176), so users on the currently supported Vite 8.0-8.3.6 range will still publish the bodies behind all three new proxies. Please raise the published Vite peer minimum to 8.3.7 (and account for the Vite+ alias if needed), or provide a compatibility path that actually removes the source on older Rolldown versions.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declining this one; the PR body's "For users" note documents the Vite ≥ 8.3.7 / Vite+ ≥ 1.5.1 requirement instead.

  • Client sourcemaps are opt-in: vinext never enables build.sourcemap for the client (Next's productionBrowserSourceMaps also defaults to off), so a peer minimum of 8.3.7 would force every vinext user to upgrade Vite for a setting most don't use.
  • Older Rolldown isn't a regression from this PR. Before it, these proxies returned hires: "boundary" maps that carried the original module on every Rolldown version; the empty map is strictly no worse there.
  • plugin-rsc's own "use server" change (fix(rsc): drop Server Function source from client sourcemaps vitejs/vite-plugin-react#1497) has the same Rolldown requirement, and plugin-rsc 0.5.38 keeps vite: "*". Raising only vinext's floor would diverge from upstream.
  • A compatibility path for old Rolldown means post-processing sourcesContent in generateBundle, which is exactly the workaround (fix(build): match Next.js sourcemap output for "use server" modules #3817) this PR replaces with the upstream fixes.

@ask-bonk

ask-bonk Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

Review: 1 findings.

Posted 1 actionable inline finding. No branch modifications made.

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

Please review exact head 9e64e6e281c9558ee7a5e3093c7deb863df5b982 without modifying or pushing the branch. Report all actionable findings within your 8-minute time limit.

@ask-bonk

ask-bonk Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

LGTM!

github run

@james-elicx
james-elicx marked this pull request as ready for review November 9, 2026 15:06

@clanky-the-clanker clanky-the-clanker Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superseded by the review of e4af518e.

This review, of 9e64e6e2

💬 Approved with comments — The change is correct and has good end-to-end tests. The only notes are an optional unit test for the new helper and a JSDoc comment that should mention the Rolldown version requirement.

Findings: 1 info · 1 suggestion (2 are inline comments on the diff)

The decision is advisory: this review is a comment and never approves, blocks or requests changes on the pull request.


📋 Code Quality: 💬 1 suggestion

The change is correct. The new helper has no focused unit test.

Recommendation: 💬 Optional Nits

  • [SUGGESTION] No unit test for the new serverReferenceProxyResult helper's build and dev branches (packages/vinext/src/plugins/transform-result.ts:47)
View full review

The three call sites (use-cache-callable.ts and the two worker transforms in worker-use-server.ts) use the new helper correctly. The { mappings: "" } type flows through the generic, and dev keeps the lazy full map.

The new serverReferenceProxyResult helper has no unit test of its own. At the moment only the browser e2e projects cover it, and those only fail on Rolldown ≥ 1.2.18. A couple of cases in tests/transform-result.test.ts would pin both branches in the fast Vitest suite.


📝 Documentation: 💬 1 info

The helper's JSDoc overstates the guarantee on older Rolldown.

Recommendation: 💬 Optional Nits

  • [INFO] JSDoc doesn't mention that the empty-map guarantee needs Rolldown ≥ 1.2.18 (packages/vinext/src/plugins/transform-result.ts:45)
View full review

The JSDoc on serverReferenceProxyResult says the empty map keeps the replaced module's source out of sourcemaps. Rolldown before 1.2.18 refills empty maps with the pre-transform source, as the description's own results table shows.


✅ All other checks passed

🚀 Release: ✅ LGTM

Risk: low. The catalog bump to Vite+ 1.5.1 only changes the Vite+ packages and the postcss version they bring in.


🔒 Security: ✅ LGTM

No security issues. The change reduces how much server-only source can end up in client sourcemaps.


📖 AGENTS.md: ✅ LGTM

No issues found.


⚡ Performance: ✅ LGTM

Builds no longer generate maps for these proxies, and dev is unchanged.


Reviewed commit: 9e64e6e2

/**
* Build the result of a transform that replaces a module with Server Function
* reference proxies. Like @vitejs/plugin-rsc's `rsc:use-server`, builds return
* an empty map so the replaced module's source doesn't ship in sourcemaps.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ INFO · Documentation

JSDoc doesn't mention that the empty-map guarantee needs Rolldown ≥ 1.2.18

Rolldown before 1.2.18 (Vite < 8.3.7 / Vite+ < 1.5.1) refills an empty map's sourcesContent with the module's pre-transform code. On those versions the replaced module's source still ships in the sourcemap, which your own results table shows for Vite+ 1.5.0. A short note here, such as "requires Rolldown ≥ 1.2.18 to take effect", would stop future readers from assuming the guarantee holds on any Vite.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in e4af518: the JSDoc now notes that Rolldown < 1.2.18 (Vite < 8.3.7) refills empty maps with the original code, so the helper needs Rolldown >= 1.2.18 to take effect.

* reference proxies. Like @vitejs/plugin-rsc's `rsc:use-server`, builds return
* an empty map so the replaced module's source doesn't ship in sourcemaps.
*/
export function serverReferenceProxyResult<TMap = ReturnType<MagicString["generateMap"]>>(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 SUGGESTION · Code Quality

No unit test for the new serverReferenceProxyResult helper's build and dev branches

Only the browser-specific e2e projects cover this helper, and they only fail on Rolldown ≥ 1.2.18. A regression in the mode check or the dev fallthrough would get past the Vitest suite. Consider adding cases to tests/transform-result.test.ts. With { mode: "build" }, assert map equals { mappings: "" } and that generateMap is never called. With { mode: "dev" }, assert the result is a real map with the expected sources.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in e4af518: tests/transform-result.test.ts now covers both branches. With { mode: "build" } the result is { mappings: "" } and generateMap is never called; with { mode: "dev" } the map is generated with the passed options (source included). 62 passed across transform-result, worker-use-server and use-cache-transform.

Build-mode Server Function proxies return an empty map, like plugin-rsc's
"use server" proxies. Covers vinext's "use cache" client proxy and the
Web Worker "use server" and "use cache" proxies.

Upgrades Vite+ to 1.5.1 (Vite 8.3.7, Rolldown 1.2.18), and the
app-action-ownership production e2e now builds with client sourcemaps.
Pages Router tests decode the html-proxy script src, which Vite now
HTML-escapes (`&amp;`).
@james-elicx
james-elicx force-pushed the test/client-sourcemap-server-fns branch from 9e64e6e to e4af518 Compare November 9, 2026 15:09
@james-elicx

Copy link
Copy Markdown
Member Author

clanky review

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

Please review exact head e4af518e9a3574367f474f6a2fdc5f039458a343 without modifying or pushing the branch. Report all actionable findings within your 8-minute time limit.

@clanky-the-clanker clanky-the-clanker Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Approved — Both earlier notes, the missing unit test and the Rolldown version in the JSDoc, are fixed at e4af518, and no new issues were found.

The decision is advisory: this review is a comment and never approves, blocks or requests changes on the pull request.


✅ All checks passed

🚀 Release: ✅ LGTM

Risk: Low. The catalog bump to Vite+ 1.5.1 only changes the Vite+ packages and the postcss version they bring in.


🔒 Security: ✅ LGTM

No security issues. The change reduces how much server-only source can end up in client sourcemaps.


📖 AGENTS.md: ✅ LGTM

No issues found.


📋 Code Quality: ✅ LGTM

tests/transform-result.test.ts now tests the build branch of serverReferenceProxyResult (an empty map, with generateMap never called) and the dev branch (the map is generated with the options passed in). All three call sites pass this.environment to the helper, and the worker unit test now gives its hook context environment: { mode: "build" }.


⚡ Performance: ✅ LGTM

Builds no longer generate maps for these proxy transforms. Dev still generates the map lazily, as before.


📝 Documentation: ✅ LGTM

The JSDoc for serverReferenceProxyResult now says that Rolldown < 1.2.18 refills empty maps with the module's original code.


Reviewed commit: e4af518e

@ask-bonk

ask-bonk Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

LGTM!

github run

@james-elicx
james-elicx merged commit b79b7ef into main Nov 9, 2026
108 of 109 checks passed
@james-elicx
james-elicx deleted the test/client-sourcemap-server-fns branch November 9, 2026 15:35
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