From 005f9da5ac0e8fa796fd12796fa465a0af53f610 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 17 Aug 2025 17:03:02 +0900 Subject: [PATCH 1/3] refactor(rsc): replace wildcard imports with named imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace wildcard imports with specific named imports across examples and documentation: - @vitejs/plugin-rsc/rsc: renderToReadableStream, createTemporaryReferenceSet, etc. - @vitejs/plugin-rsc/browser: createFromReadableStream, createFromFetch, etc. - @vitejs/plugin-rsc/ssr: createFromReadableStream - react-dom/client: hydrateRoot, createRoot - react-dom/server.edge: renderToReadableStream This improves code clarity and import specificity while maintaining full functionality. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- packages/plugin-rsc/README.md | 26 +++++++++--------- .../basic/src/framework/entry.browser.tsx | 24 ++++++++++------- .../basic/src/framework/entry.rsc.tsx | 24 ++++++++++------- .../basic/src/framework/entry.ssr.tsx | 8 +++--- .../basic/src/framework/use-cache-runtime.tsx | 22 +++++++++------ .../src/framework/entry.browser.tsx | 24 ++++++++++------- .../browser-mode/src/framework/entry.rsc.tsx | 27 +++++++++++-------- .../no-ssr/src/framework/entry.browser.tsx | 23 +++++++++------- .../no-ssr/src/framework/entry.rsc.tsx | 24 ++++++++++------- .../ssg/src/framework/entry.browser.tsx | 14 +++++----- .../examples/ssg/src/framework/entry.rsc.tsx | 6 ++--- .../examples/ssg/src/framework/entry.ssr.tsx | 8 +++--- .../src/framework/entry.browser.tsx | 24 ++++++++++------- .../src/framework/entry.rsc.tsx | 24 ++++++++++------- .../src/framework/entry.ssr.tsx | 8 +++--- .../starter/src/framework/entry.browser.tsx | 24 ++++++++++------- .../starter/src/framework/entry.rsc.tsx | 24 ++++++++++------- .../starter/src/framework/entry.ssr.tsx | 8 +++--- 18 files changed, 199 insertions(+), 143 deletions(-) diff --git a/packages/plugin-rsc/README.md b/packages/plugin-rsc/README.md index f4fcc44de..f7f8e25e5 100644 --- a/packages/plugin-rsc/README.md +++ b/packages/plugin-rsc/README.md @@ -131,7 +131,7 @@ export default defineConfig({ - [`entry.rsc.tsx`](./examples/starter/src/framework/entry.rsc.tsx) ```tsx -import * as ReactServer from '@vitejs/plugin-rsc/rsc' // re-export of react-server-dom/server.edge and client.edge +import { renderToReadableStream } from '@vitejs/plugin-rsc/rsc' // re-export of react-server-dom/server.edge and client.edge // the plugin assumes `rsc` entry having default export of request handler export default async function handler(request: Request): Promise { @@ -143,7 +143,7 @@ export default async function handler(request: Request): Promise { ) - const rscStream = ReactServer.renderToReadableStream(root) + const rscStream = renderToReadableStream(root) // respond direct RSC stream request based on framework's convention if (request.url.endsWith('.rsc')) { @@ -173,19 +173,19 @@ export default async function handler(request: Request): Promise { - [`entry.ssr.tsx`](./examples/starter/src/framework/entry.ssr.tsx) ```tsx -import * as ReactClient from '@vitejs/plugin-rsc/ssr' // re-export of react-server-dom/client.edge -import * as ReactDOMServer from 'react-dom/server.edge' +import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr' // re-export of react-server-dom/client.edge +import { renderToReadableStream } from 'react-dom/server.edge' export async function handleSsr(rscStream: ReadableStream) { // deserialize RSC stream back to React VDOM - const root = await ReactClient.createFromReadableStream(rscStream) + const root = await createFromReadableStream(rscStream) // helper API to allow referencing browser entry content from SSR environment const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent('index') // render html (traditional SSR) - const htmlStream = ReactDOMServer.renderToReadableStream(root, { + const htmlStream = renderToReadableStream(root, { bootstrapScriptContent, }) @@ -196,16 +196,16 @@ export async function handleSsr(rscStream: ReadableStream) { - [`entry.browser.tsx`](./examples/starter/src/framework/entry.browser.tsx) ```tsx -import * as ReactClient from '@vitejs/plugin-rsc/browser' // re-export of react-server-dom/client.browser -import * as ReactDOMClient from 'react-dom/client' +import { createFromReadableStream } from '@vitejs/plugin-rsc/browser' // re-export of react-server-dom/client.browser +import { hydrateRoot } from 'react-dom/client' async function main() { // fetch and deserialize RSC stream back to React VDOM const rscResponse = await fetch(window.location.href + '.rsc') - const root = await ReactClient.createFromReadableStream(rscResponse.body) + const root = await createFromReadableStream(rscResponse.body) // hydration (traditional CSR) - ReactDOMClient.hydrateRoot(document, root) + hydrateRoot(document, root) } main() @@ -342,13 +342,11 @@ const htmlStream = await renderToReadableStream(reactNode, { This event is fired when server modules are updated, which can be used to trigger re-fetching and re-rendering of RSC components on browser. ```js -import * as ReactClient from '@vitejs/plugin-rsc/browser' +import { createFromFetch } from '@vitejs/plugin-rsc/browser' import.meta.hot.on('rsc:update', async () => { // re-fetch RSC stream - const rscPayload = await ReactClient.createFromFetch( - fetch(window.location.href + '.rsc'), - ) + const rscPayload = await createFromFetch(fetch(window.location.href + '.rsc')) // re-render ... }) ``` diff --git a/packages/plugin-rsc/examples/basic/src/framework/entry.browser.tsx b/packages/plugin-rsc/examples/basic/src/framework/entry.browser.tsx index 1998e86ff..551f4aac9 100644 --- a/packages/plugin-rsc/examples/basic/src/framework/entry.browser.tsx +++ b/packages/plugin-rsc/examples/basic/src/framework/entry.browser.tsx @@ -1,6 +1,12 @@ -import * as ReactClient from '@vitejs/plugin-rsc/browser' +import { + createFromReadableStream, + createFromFetch, + setServerCallback, + createTemporaryReferenceSet, + encodeReply, +} from '@vitejs/plugin-rsc/browser' import React from 'react' -import * as ReactDOMClient from 'react-dom/client' +import { hydrateRoot } from 'react-dom/client' import { rscStream } from 'rsc-html-stream/client' import type { RscPayload } from './entry.rsc' @@ -10,7 +16,7 @@ async function main() { let setPayload: (v: RscPayload) => void // deserialize RSC stream back to React VDOM for CSR - const initialPayload = await ReactClient.createFromReadableStream( + const initialPayload = await createFromReadableStream( // initial RSC stream is injected in SSR stream as rscStream, ) @@ -33,7 +39,7 @@ async function main() { // re-fetch RSC and trigger re-rendering async function fetchRscPayload() { - const payload = await ReactClient.createFromFetch( + const payload = await createFromFetch( fetch(window.location.href), ) setPayload(payload) @@ -41,13 +47,13 @@ async function main() { // register a handler which will be internally called by React // on server function request after hydration. - ReactClient.setServerCallback(async (id, args) => { + setServerCallback(async (id, args) => { const url = new URL(window.location.href) - const temporaryReferences = ReactClient.createTemporaryReferenceSet() - const payload = await ReactClient.createFromFetch( + const temporaryReferences = createTemporaryReferenceSet() + const payload = await createFromFetch( fetch(url, { method: 'POST', - body: await ReactClient.encodeReply(args, { temporaryReferences }), + body: await encodeReply(args, { temporaryReferences }), headers: { 'x-rsc-action': id, }, @@ -64,7 +70,7 @@ async function main() { ) - ReactDOMClient.hydrateRoot(document, browserRoot, { + hydrateRoot(document, browserRoot, { formState: initialPayload.formState, }) diff --git a/packages/plugin-rsc/examples/basic/src/framework/entry.rsc.tsx b/packages/plugin-rsc/examples/basic/src/framework/entry.rsc.tsx index ff7c2068d..853c4686e 100644 --- a/packages/plugin-rsc/examples/basic/src/framework/entry.rsc.tsx +++ b/packages/plugin-rsc/examples/basic/src/framework/entry.rsc.tsx @@ -1,4 +1,11 @@ -import * as ReactServer from '@vitejs/plugin-rsc/rsc' +import { + renderToReadableStream, + createTemporaryReferenceSet, + decodeReply, + loadServerAction, + decodeAction, + decodeFormState, +} from '@vitejs/plugin-rsc/rsc' import type { ReactFormState } from 'react-dom/client' import type React from 'react' @@ -40,28 +47,25 @@ export async function handleRequest({ const body = contentType?.startsWith('multipart/form-data') ? await request.formData() : await request.text() - temporaryReferences = ReactServer.createTemporaryReferenceSet() - const args = await ReactServer.decodeReply(body, { temporaryReferences }) - const action = await ReactServer.loadServerAction(actionId) + temporaryReferences = createTemporaryReferenceSet() + const args = await decodeReply(body, { temporaryReferences }) + const action = await loadServerAction(actionId) returnValue = await action.apply(null, args) } else { // otherwise server function is called via `
` // before hydration (e.g. when javascript is disabled). // aka progressive enhancement. const formData = await request.formData() - const decodedAction = await ReactServer.decodeAction(formData) + const decodedAction = await decodeAction(formData) const result = await decodedAction() - formState = await ReactServer.decodeFormState(result, formData) + formState = await decodeFormState(result, formData) } } const url = new URL(request.url) const rscPayload: RscPayload = { root: getRoot(), formState, returnValue } const rscOptions = { temporaryReferences } - const rscStream = ReactServer.renderToReadableStream( - rscPayload, - rscOptions, - ) + const rscStream = renderToReadableStream(rscPayload, rscOptions) // respond RSC stream without HTML rendering based on framework's convention. // here we use request header `content-type`. diff --git a/packages/plugin-rsc/examples/basic/src/framework/entry.ssr.tsx b/packages/plugin-rsc/examples/basic/src/framework/entry.ssr.tsx index 982dacf16..a0d6147e9 100644 --- a/packages/plugin-rsc/examples/basic/src/framework/entry.ssr.tsx +++ b/packages/plugin-rsc/examples/basic/src/framework/entry.ssr.tsx @@ -1,7 +1,7 @@ -import * as ReactClient from '@vitejs/plugin-rsc/ssr' // RSC API +import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr' // RSC API import React from 'react' import type { ReactFormState } from 'react-dom/client' -import * as ReactDOMServer from 'react-dom/server.edge' +import { renderToReadableStream } from 'react-dom/server.edge' import { injectRSCPayload } from 'rsc-html-stream/server' import type { RscPayload } from './entry.rsc' @@ -23,7 +23,7 @@ export async function renderHTML( function SsrRoot() { // deserialization needs to be kicked off inside ReactDOMServer context // for ReactDomServer preinit/preloading to work - payload ??= ReactClient.createFromReadableStream(rscStream1) + payload ??= createFromReadableStream(rscStream1) return {React.use(payload).root} } @@ -34,7 +34,7 @@ export async function renderHTML( // render html (traditional SSR) const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent('index') - const htmlStream = await ReactDOMServer.renderToReadableStream(, { + const htmlStream = await renderToReadableStream(, { bootstrapScriptContent: options?.debugNojs ? undefined : bootstrapScriptContent, diff --git a/packages/plugin-rsc/examples/basic/src/framework/use-cache-runtime.tsx b/packages/plugin-rsc/examples/basic/src/framework/use-cache-runtime.tsx index 44e011050..a25e56c6c 100644 --- a/packages/plugin-rsc/examples/basic/src/framework/use-cache-runtime.tsx +++ b/packages/plugin-rsc/examples/basic/src/framework/use-cache-runtime.tsx @@ -1,4 +1,11 @@ -import * as ReactRsc from '@vitejs/plugin-rsc/rsc' +import { + createClientTemporaryReferenceSet, + encodeReply, + createTemporaryReferenceSet, + decodeReply, + renderToReadableStream, + createFromReadableStream, +} from '@vitejs/plugin-rsc/rsc' // based on // https://github.com/vercel/next.js/pull/70435 @@ -28,9 +35,8 @@ export default function cacheWrapper(fn: (...args: any[]) => Promise) { // those arguments to be included as a cache key and it doesn't achieve // "use cache static shell + dynamic children props" pattern. // cf. https://nextjs.org/docs/app/api-reference/directives/use-cache#non-serializable-arguments - const clientTemporaryReferences = - ReactRsc.createClientTemporaryReferenceSet() - const encodedArguments = await ReactRsc.encodeReply(args, { + const clientTemporaryReferences = createClientTemporaryReferenceSet() + const encodedArguments = await encodeReply(args, { temporaryReferences: clientTemporaryReferences, }) const serializedCacheKey = await replyToCacheKey(encodedArguments) @@ -38,8 +44,8 @@ export default function cacheWrapper(fn: (...args: any[]) => Promise) { // cache `fn` result as stream // (cache value is promise so that it dedupes concurrent async calls) const entryPromise = (cacheEntries[serializedCacheKey] ??= (async () => { - const temporaryReferences = ReactRsc.createTemporaryReferenceSet() - const decodedArgs = await ReactRsc.decodeReply(encodedArguments, { + const temporaryReferences = createTemporaryReferenceSet() + const decodedArgs = await decodeReply(encodedArguments, { temporaryReferences, }) @@ -47,7 +53,7 @@ export default function cacheWrapper(fn: (...args: any[]) => Promise) { const result = await fn(...decodedArgs) // serialize result to a ReadableStream - const stream = ReactRsc.renderToReadableStream(result, { + const stream = renderToReadableStream(result, { environmentName: 'Cache', temporaryReferences, }) @@ -56,7 +62,7 @@ export default function cacheWrapper(fn: (...args: any[]) => Promise) { // deserialized cached stream const stream = (await entryPromise).get() - const result = ReactRsc.createFromReadableStream(stream, { + const result = createFromReadableStream(stream, { environmentName: 'Cache', replayConsoleLogs: true, temporaryReferences: clientTemporaryReferences, diff --git a/packages/plugin-rsc/examples/browser-mode/src/framework/entry.browser.tsx b/packages/plugin-rsc/examples/browser-mode/src/framework/entry.browser.tsx index bb36e3626..a8ce1e792 100644 --- a/packages/plugin-rsc/examples/browser-mode/src/framework/entry.browser.tsx +++ b/packages/plugin-rsc/examples/browser-mode/src/framework/entry.browser.tsx @@ -1,13 +1,19 @@ import * as React from 'react' -import * as ReactDOMClient from 'react-dom/client' -import * as ReactClient from '@vitejs/plugin-rsc/react/browser' +import { createRoot } from 'react-dom/client' +import { + createFromFetch, + setRequireModule, + setServerCallback, + createTemporaryReferenceSet, + encodeReply, +} from '@vitejs/plugin-rsc/react/browser' import type { RscPayload } from './entry.rsc' let fetchServer: typeof import('./entry.rsc').fetchServer export function initialize(options: { fetchServer: typeof fetchServer }) { fetchServer = options.fetchServer - ReactClient.setRequireModule({ + setRequireModule({ load: (id) => import(/* @vite-ignore */ id), }) } @@ -15,7 +21,7 @@ export function initialize(options: { fetchServer: typeof fetchServer }) { export async function main() { let setPayload: (v: RscPayload) => void - const initialPayload = await ReactClient.createFromFetch( + const initialPayload = await createFromFetch( fetchServer(new Request(window.location.href)), ) @@ -29,14 +35,14 @@ export async function main() { return payload.root } - ReactClient.setServerCallback(async (id, args) => { + setServerCallback(async (id, args) => { const url = new URL(window.location.href) - const temporaryReferences = ReactClient.createTemporaryReferenceSet() - const payload = await ReactClient.createFromFetch( + const temporaryReferences = createTemporaryReferenceSet() + const payload = await createFromFetch( fetchServer( new Request(url, { method: 'POST', - body: await ReactClient.encodeReply(args, { temporaryReferences }), + body: await encodeReply(args, { temporaryReferences }), headers: { 'x-rsc-action': id, }, @@ -53,5 +59,5 @@ export async function main() { ) - ReactDOMClient.createRoot(document.body).render(browserRoot) + createRoot(document.body).render(browserRoot) } diff --git a/packages/plugin-rsc/examples/browser-mode/src/framework/entry.rsc.tsx b/packages/plugin-rsc/examples/browser-mode/src/framework/entry.rsc.tsx index 343e6751c..44adc4bac 100644 --- a/packages/plugin-rsc/examples/browser-mode/src/framework/entry.rsc.tsx +++ b/packages/plugin-rsc/examples/browser-mode/src/framework/entry.rsc.tsx @@ -1,4 +1,12 @@ -import * as ReactServer from '@vitejs/plugin-rsc/react/rsc' +import { + setRequireModule, + renderToReadableStream, + createTemporaryReferenceSet, + decodeReply, + loadServerAction, + decodeAction, + decodeFormState, +} from '@vitejs/plugin-rsc/react/rsc' import type React from 'react' import { Root } from '../root' import type { ReactFormState } from 'react-dom/client' @@ -12,7 +20,7 @@ export type RscPayload = { declare let __vite_rsc_raw_import__: (id: string) => Promise export function initialize() { - ReactServer.setRequireModule({ load: (id) => __vite_rsc_raw_import__(id) }) + setRequireModule({ load: (id) => __vite_rsc_raw_import__(id) }) } export async function fetchServer(request: Request): Promise { @@ -27,24 +35,21 @@ export async function fetchServer(request: Request): Promise { const body = contentType?.startsWith('multipart/form-data') ? await request.formData() : await request.text() - temporaryReferences = ReactServer.createTemporaryReferenceSet() - const args = await ReactServer.decodeReply(body, { temporaryReferences }) - const action = await ReactServer.loadServerAction(actionId) + temporaryReferences = createTemporaryReferenceSet() + const args = await decodeReply(body, { temporaryReferences }) + const action = await loadServerAction(actionId) returnValue = await action.apply(null, args) } else { const formData = await request.formData() - const decodedAction = await ReactServer.decodeAction(formData) + const decodedAction = await decodeAction(formData) const result = await decodedAction() - formState = await ReactServer.decodeFormState(result, formData) + formState = await decodeFormState(result, formData) } } const rscPayload: RscPayload = { root: , formState, returnValue } const rscOptions = { temporaryReferences } - const rscStream = ReactServer.renderToReadableStream( - rscPayload, - rscOptions, - ) + const rscStream = renderToReadableStream(rscPayload, rscOptions) return new Response(rscStream, { headers: { diff --git a/packages/plugin-rsc/examples/no-ssr/src/framework/entry.browser.tsx b/packages/plugin-rsc/examples/no-ssr/src/framework/entry.browser.tsx index 0d3451c56..f33a65500 100644 --- a/packages/plugin-rsc/examples/no-ssr/src/framework/entry.browser.tsx +++ b/packages/plugin-rsc/examples/no-ssr/src/framework/entry.browser.tsx @@ -1,6 +1,11 @@ -import * as ReactClient from '@vitejs/plugin-rsc/browser' +import { + createFromFetch, + setServerCallback, + createTemporaryReferenceSet, + encodeReply, +} from '@vitejs/plugin-rsc/browser' import React from 'react' -import * as ReactDOMClient from 'react-dom/client' +import { createRoot } from 'react-dom/client' import type { RscPayload } from './entry.rsc' async function main() { @@ -8,7 +13,7 @@ async function main() { // from outside of `BrowserRoot` component (e.g. server function call, navigation, hmr) let setPayload: (v: RscPayload) => void - const initialPayload = await ReactClient.createFromFetch( + const initialPayload = await createFromFetch( fetch(window.location.href), ) @@ -30,7 +35,7 @@ async function main() { // re-fetch RSC and trigger re-rendering async function fetchRscPayload() { - const payload = await ReactClient.createFromFetch( + const payload = await createFromFetch( fetch(window.location.href), ) setPayload(payload) @@ -38,13 +43,13 @@ async function main() { // register a handler which will be internally called by React // on server function request after hydration. - ReactClient.setServerCallback(async (id, args) => { + setServerCallback(async (id, args) => { const url = new URL(window.location.href) - const temporaryReferences = ReactClient.createTemporaryReferenceSet() - const payload = await ReactClient.createFromFetch( + const temporaryReferences = createTemporaryReferenceSet() + const payload = await createFromFetch( fetch(url, { method: 'POST', - body: await ReactClient.encodeReply(args, { temporaryReferences }), + body: await encodeReply(args, { temporaryReferences }), headers: { 'x-rsc-action': id, }, @@ -61,7 +66,7 @@ async function main() { ) - ReactDOMClient.createRoot(document.body).render(browserRoot) + createRoot(document.body).render(browserRoot) // implement server HMR by trigering re-fetch/render of RSC upon server code change if (import.meta.hot) { diff --git a/packages/plugin-rsc/examples/no-ssr/src/framework/entry.rsc.tsx b/packages/plugin-rsc/examples/no-ssr/src/framework/entry.rsc.tsx index 439cfddc9..27a5ce931 100644 --- a/packages/plugin-rsc/examples/no-ssr/src/framework/entry.rsc.tsx +++ b/packages/plugin-rsc/examples/no-ssr/src/framework/entry.rsc.tsx @@ -1,4 +1,11 @@ -import * as ReactServer from '@vitejs/plugin-rsc/rsc' +import { + renderToReadableStream, + createTemporaryReferenceSet, + decodeReply, + loadServerAction, + decodeAction, + decodeFormState, +} from '@vitejs/plugin-rsc/rsc' import type { ReactFormState } from 'react-dom/client' import { Root } from '../root.tsx' @@ -20,24 +27,21 @@ export default async function handler(request: Request): Promise { const body = contentType?.startsWith('multipart/form-data') ? await request.formData() : await request.text() - temporaryReferences = ReactServer.createTemporaryReferenceSet() - const args = await ReactServer.decodeReply(body, { temporaryReferences }) - const action = await ReactServer.loadServerAction(actionId) + temporaryReferences = createTemporaryReferenceSet() + const args = await decodeReply(body, { temporaryReferences }) + const action = await loadServerAction(actionId) returnValue = await action.apply(null, args) } else { const formData = await request.formData() - const decodedAction = await ReactServer.decodeAction(formData) + const decodedAction = await decodeAction(formData) const result = await decodedAction() - formState = await ReactServer.decodeFormState(result, formData) + formState = await decodeFormState(result, formData) } } const rscPayload: RscPayload = { root: , formState, returnValue } const rscOptions = { temporaryReferences } - const rscStream = ReactServer.renderToReadableStream( - rscPayload, - rscOptions, - ) + const rscStream = renderToReadableStream(rscPayload, rscOptions) return new Response(rscStream, { headers: { diff --git a/packages/plugin-rsc/examples/ssg/src/framework/entry.browser.tsx b/packages/plugin-rsc/examples/ssg/src/framework/entry.browser.tsx index 7f77627e5..381be8dc5 100644 --- a/packages/plugin-rsc/examples/ssg/src/framework/entry.browser.tsx +++ b/packages/plugin-rsc/examples/ssg/src/framework/entry.browser.tsx @@ -1,6 +1,9 @@ -import * as ReactClient from '@vitejs/plugin-rsc/browser' +import { + createFromFetch, + createFromReadableStream, +} from '@vitejs/plugin-rsc/browser' import React from 'react' -import ReactDomClient from 'react-dom/client' +import { hydrateRoot } from 'react-dom/client' import { rscStream } from 'rsc-html-stream/client' import { RSC_POSTFIX, type RscPayload } from './shared' @@ -8,12 +11,11 @@ async function hydrate(): Promise { async function onNavigation() { const url = new URL(window.location.href) url.pathname = url.pathname + RSC_POSTFIX - const payload = await ReactClient.createFromFetch(fetch(url)) + const payload = await createFromFetch(fetch(url)) setPayload(payload) } - const initialPayload = - await ReactClient.createFromReadableStream(rscStream) + const initialPayload = await createFromReadableStream(rscStream) let setPayload: (v: RscPayload) => void @@ -37,7 +39,7 @@ async function hydrate(): Promise { ) - ReactDomClient.hydrateRoot(document, browserRoot) + hydrateRoot(document, browserRoot) if (import.meta.hot) { import.meta.hot.on('rsc:update', () => { diff --git a/packages/plugin-rsc/examples/ssg/src/framework/entry.rsc.tsx b/packages/plugin-rsc/examples/ssg/src/framework/entry.rsc.tsx index 6af940baa..7b87fdc18 100644 --- a/packages/plugin-rsc/examples/ssg/src/framework/entry.rsc.tsx +++ b/packages/plugin-rsc/examples/ssg/src/framework/entry.rsc.tsx @@ -1,4 +1,4 @@ -import * as ReactServer from '@vitejs/plugin-rsc/rsc' +import { renderToReadableStream } from '@vitejs/plugin-rsc/rsc' import { Root, getStaticPaths } from '../root' import { RSC_POSTFIX, type RscPayload } from './shared' @@ -13,7 +13,7 @@ export default async function handler(request: Request): Promise { } const rscPayload: RscPayload = { root: } - const rscStream = ReactServer.renderToReadableStream(rscPayload) + const rscStream = renderToReadableStream(rscPayload) if (isRscRequest) { return new Response(rscStream, { @@ -44,7 +44,7 @@ export async function handleSsg(request: Request): Promise<{ }> { const url = new URL(request.url) const rscPayload: RscPayload = { root: } - const rscStream = ReactServer.renderToReadableStream(rscPayload) + const rscStream = renderToReadableStream(rscPayload) const [rscStream1, rscStream2] = rscStream.tee() const ssr = await import.meta.viteRsc.loadModule< diff --git a/packages/plugin-rsc/examples/ssg/src/framework/entry.ssr.tsx b/packages/plugin-rsc/examples/ssg/src/framework/entry.ssr.tsx index d5ee6264d..e47f8b188 100644 --- a/packages/plugin-rsc/examples/ssg/src/framework/entry.ssr.tsx +++ b/packages/plugin-rsc/examples/ssg/src/framework/entry.ssr.tsx @@ -1,6 +1,6 @@ -import * as ReactClient from '@vitejs/plugin-rsc/ssr' +import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr' import React from 'react' -import * as ReactDomServer from 'react-dom/server.edge' +import { renderToReadableStream } from 'react-dom/server.edge' import { injectRSCPayload } from 'rsc-html-stream/server' import type { RscPayload } from './shared' @@ -14,7 +14,7 @@ export async function renderHtml( let payload: Promise function SsrRoot() { - payload ??= ReactClient.createFromReadableStream(rscStream1) + payload ??= createFromReadableStream(rscStream1) const root = React.use(payload).root return root } @@ -22,7 +22,7 @@ export async function renderHtml( const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent('index') - const htmlStream = await ReactDomServer.renderToReadableStream(, { + const htmlStream = await renderToReadableStream(, { bootstrapScriptContent, }) if (options?.ssg) { diff --git a/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.browser.tsx b/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.browser.tsx index 473fc492d..c4c0e4ade 100644 --- a/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.browser.tsx +++ b/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.browser.tsx @@ -1,6 +1,12 @@ -import * as ReactClient from '@vitejs/plugin-rsc/browser' +import { + createFromReadableStream, + createFromFetch, + setServerCallback, + createTemporaryReferenceSet, + encodeReply, +} from '@vitejs/plugin-rsc/browser' import React from 'react' -import * as ReactDOMClient from 'react-dom/client' +import { hydrateRoot } from 'react-dom/client' import { rscStream } from 'rsc-html-stream/client' import type { RscPayload } from './entry.rsc' @@ -10,7 +16,7 @@ async function main() { let setPayload: (v: RscPayload) => void // deserialize RSC stream back to React VDOM for CSR - const initialPayload = await ReactClient.createFromReadableStream( + const initialPayload = await createFromReadableStream( // initial RSC stream is injected in SSR stream as rscStream, ) @@ -33,7 +39,7 @@ async function main() { // re-fetch RSC and trigger re-rendering async function fetchRscPayload() { - const payload = await ReactClient.createFromFetch( + const payload = await createFromFetch( fetch(window.location.href), ) setPayload(payload) @@ -41,13 +47,13 @@ async function main() { // register a handler which will be internally called by React // on server function request after hydration. - ReactClient.setServerCallback(async (id, args) => { + setServerCallback(async (id, args) => { const url = new URL(window.location.href) - const temporaryReferences = ReactClient.createTemporaryReferenceSet() - const payload = await ReactClient.createFromFetch( + const temporaryReferences = createTemporaryReferenceSet() + const payload = await createFromFetch( fetch(url, { method: 'POST', - body: await ReactClient.encodeReply(args, { temporaryReferences }), + body: await encodeReply(args, { temporaryReferences }), headers: { 'x-rsc-action': id, }, @@ -64,7 +70,7 @@ async function main() { ) - ReactDOMClient.hydrateRoot(document, browserRoot, { + hydrateRoot(document, browserRoot, { formState: initialPayload.formState, }) diff --git a/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.rsc.tsx b/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.rsc.tsx index 1113c4bcc..767164f1e 100644 --- a/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.rsc.tsx +++ b/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.rsc.tsx @@ -1,4 +1,11 @@ -import * as ReactServer from '@vitejs/plugin-rsc/rsc' +import { + renderToReadableStream, + createTemporaryReferenceSet, + decodeReply, + loadServerAction, + decodeAction, + decodeFormState, +} from '@vitejs/plugin-rsc/rsc' import type { ReactFormState } from 'react-dom/client' import { Root } from '../root.tsx' @@ -22,18 +29,18 @@ async function handler(request: Request): Promise { const body = contentType?.startsWith('multipart/form-data') ? await request.formData() : await request.text() - temporaryReferences = ReactServer.createTemporaryReferenceSet() - const args = await ReactServer.decodeReply(body, { temporaryReferences }) - const action = await ReactServer.loadServerAction(actionId) + temporaryReferences = createTemporaryReferenceSet() + const args = await decodeReply(body, { temporaryReferences }) + const action = await loadServerAction(actionId) returnValue = await action.apply(null, args) } else { // otherwise server function is called via `` // before hydration (e.g. when javascript is disabled). // aka progressive enhancement. const formData = await request.formData() - const decodedAction = await ReactServer.decodeAction(formData) + const decodedAction = await decodeAction(formData) const result = await decodedAction() - formState = await ReactServer.decodeFormState(result, formData) + formState = await decodeFormState(result, formData) } } @@ -43,10 +50,7 @@ async function handler(request: Request): Promise { // to achieve single round trip to mutate and fetch from server. const rscPayload: RscPayload = { root: , formState, returnValue } const rscOptions = { temporaryReferences } - const rscStream = ReactServer.renderToReadableStream( - rscPayload, - rscOptions, - ) + const rscStream = renderToReadableStream(rscPayload, rscOptions) // respond RSC stream without HTML rendering based on framework's convention. // here we use request header `content-type`. diff --git a/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.ssr.tsx b/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.ssr.tsx index 9fae3468b..fed9e142e 100644 --- a/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.ssr.tsx +++ b/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.ssr.tsx @@ -1,7 +1,7 @@ -import * as ReactClient from '@vitejs/plugin-rsc/ssr' // RSC API +import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr' // RSC API import React from 'react' import type { ReactFormState } from 'react-dom/client' -import * as ReactDOMServer from 'react-dom/server.edge' +import { renderToReadableStream } from 'react-dom/server.edge' import { injectRSCPayload } from 'rsc-html-stream/server' import type { RscPayload } from './entry.rsc' @@ -25,14 +25,14 @@ export async function renderHTML( function SsrRoot() { // deserialization needs to be kicked off inside ReactDOMServer context // for ReactDomServer preinit/preloading to work - payload ??= ReactClient.createFromReadableStream(rscStream1) + payload ??= createFromReadableStream(rscStream1) return React.use(payload).root } // render html (traditional SSR) const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent('index') - const htmlStream = await ReactDOMServer.renderToReadableStream(, { + const htmlStream = await renderToReadableStream(, { bootstrapScriptContent: options?.debugNojs ? undefined : bootstrapScriptContent, diff --git a/packages/plugin-rsc/examples/starter/src/framework/entry.browser.tsx b/packages/plugin-rsc/examples/starter/src/framework/entry.browser.tsx index 473fc492d..c4c0e4ade 100644 --- a/packages/plugin-rsc/examples/starter/src/framework/entry.browser.tsx +++ b/packages/plugin-rsc/examples/starter/src/framework/entry.browser.tsx @@ -1,6 +1,12 @@ -import * as ReactClient from '@vitejs/plugin-rsc/browser' +import { + createFromReadableStream, + createFromFetch, + setServerCallback, + createTemporaryReferenceSet, + encodeReply, +} from '@vitejs/plugin-rsc/browser' import React from 'react' -import * as ReactDOMClient from 'react-dom/client' +import { hydrateRoot } from 'react-dom/client' import { rscStream } from 'rsc-html-stream/client' import type { RscPayload } from './entry.rsc' @@ -10,7 +16,7 @@ async function main() { let setPayload: (v: RscPayload) => void // deserialize RSC stream back to React VDOM for CSR - const initialPayload = await ReactClient.createFromReadableStream( + const initialPayload = await createFromReadableStream( // initial RSC stream is injected in SSR stream as rscStream, ) @@ -33,7 +39,7 @@ async function main() { // re-fetch RSC and trigger re-rendering async function fetchRscPayload() { - const payload = await ReactClient.createFromFetch( + const payload = await createFromFetch( fetch(window.location.href), ) setPayload(payload) @@ -41,13 +47,13 @@ async function main() { // register a handler which will be internally called by React // on server function request after hydration. - ReactClient.setServerCallback(async (id, args) => { + setServerCallback(async (id, args) => { const url = new URL(window.location.href) - const temporaryReferences = ReactClient.createTemporaryReferenceSet() - const payload = await ReactClient.createFromFetch( + const temporaryReferences = createTemporaryReferenceSet() + const payload = await createFromFetch( fetch(url, { method: 'POST', - body: await ReactClient.encodeReply(args, { temporaryReferences }), + body: await encodeReply(args, { temporaryReferences }), headers: { 'x-rsc-action': id, }, @@ -64,7 +70,7 @@ async function main() { ) - ReactDOMClient.hydrateRoot(document, browserRoot, { + hydrateRoot(document, browserRoot, { formState: initialPayload.formState, }) diff --git a/packages/plugin-rsc/examples/starter/src/framework/entry.rsc.tsx b/packages/plugin-rsc/examples/starter/src/framework/entry.rsc.tsx index 58b0c60be..5f346b568 100644 --- a/packages/plugin-rsc/examples/starter/src/framework/entry.rsc.tsx +++ b/packages/plugin-rsc/examples/starter/src/framework/entry.rsc.tsx @@ -1,4 +1,11 @@ -import * as ReactServer from '@vitejs/plugin-rsc/rsc' +import { + renderToReadableStream, + createTemporaryReferenceSet, + decodeReply, + loadServerAction, + decodeAction, + decodeFormState, +} from '@vitejs/plugin-rsc/rsc' import type { ReactFormState } from 'react-dom/client' import { Root } from '../root.tsx' @@ -32,18 +39,18 @@ export default async function handler(request: Request): Promise { const body = contentType?.startsWith('multipart/form-data') ? await request.formData() : await request.text() - temporaryReferences = ReactServer.createTemporaryReferenceSet() - const args = await ReactServer.decodeReply(body, { temporaryReferences }) - const action = await ReactServer.loadServerAction(actionId) + temporaryReferences = createTemporaryReferenceSet() + const args = await decodeReply(body, { temporaryReferences }) + const action = await loadServerAction(actionId) returnValue = await action.apply(null, args) } else { // otherwise server function is called via `` // before hydration (e.g. when javascript is disabled). // aka progressive enhancement. const formData = await request.formData() - const decodedAction = await ReactServer.decodeAction(formData) + const decodedAction = await decodeAction(formData) const result = await decodedAction() - formState = await ReactServer.decodeFormState(result, formData) + formState = await decodeFormState(result, formData) } } @@ -53,10 +60,7 @@ export default async function handler(request: Request): Promise { // to achieve single round trip to mutate and fetch from server. const rscPayload: RscPayload = { root: , formState, returnValue } const rscOptions = { temporaryReferences } - const rscStream = ReactServer.renderToReadableStream( - rscPayload, - rscOptions, - ) + const rscStream = renderToReadableStream(rscPayload, rscOptions) // respond RSC stream without HTML rendering based on framework's convention. // here we use request header `content-type`. diff --git a/packages/plugin-rsc/examples/starter/src/framework/entry.ssr.tsx b/packages/plugin-rsc/examples/starter/src/framework/entry.ssr.tsx index d4944bc5d..4f9f2538f 100644 --- a/packages/plugin-rsc/examples/starter/src/framework/entry.ssr.tsx +++ b/packages/plugin-rsc/examples/starter/src/framework/entry.ssr.tsx @@ -1,7 +1,7 @@ -import * as ReactClient from '@vitejs/plugin-rsc/ssr' // RSC API +import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr' // RSC API import React from 'react' import type { ReactFormState } from 'react-dom/client' -import * as ReactDOMServer from 'react-dom/server.edge' +import { renderToReadableStream } from 'react-dom/server.edge' import { injectRSCPayload } from 'rsc-html-stream/server' import type { RscPayload } from './entry.rsc' @@ -23,7 +23,7 @@ export async function renderHTML( function SsrRoot() { // deserialization needs to be kicked off inside ReactDOMServer context // for ReactDomServer preinit/preloading to work - payload ??= ReactClient.createFromReadableStream(rscStream1) + payload ??= createFromReadableStream(rscStream1) return {React.use(payload).root} } @@ -39,7 +39,7 @@ export async function renderHTML( // render html (traditional SSR) const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent('index') - const htmlStream = await ReactDOMServer.renderToReadableStream(, { + const htmlStream = await renderToReadableStream(, { bootstrapScriptContent: options?.debugNojs ? undefined : bootstrapScriptContent, From 9ec5c3bb8b4c66c0fa99413854b7b63dd22161ab Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 17 Aug 2025 17:06:58 +0900 Subject: [PATCH 2/3] tweak --- packages/plugin-rsc/README.md | 6 +++--- .../plugin-rsc/examples/starter/src/framework/entry.ssr.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/plugin-rsc/README.md b/packages/plugin-rsc/README.md index f7f8e25e5..bc1063613 100644 --- a/packages/plugin-rsc/README.md +++ b/packages/plugin-rsc/README.md @@ -131,7 +131,7 @@ export default defineConfig({ - [`entry.rsc.tsx`](./examples/starter/src/framework/entry.rsc.tsx) ```tsx -import { renderToReadableStream } from '@vitejs/plugin-rsc/rsc' // re-export of react-server-dom/server.edge and client.edge +import { renderToReadableStream } from '@vitejs/plugin-rsc/rsc' // the plugin assumes `rsc` entry having default export of request handler export default async function handler(request: Request): Promise { @@ -173,7 +173,7 @@ export default async function handler(request: Request): Promise { - [`entry.ssr.tsx`](./examples/starter/src/framework/entry.ssr.tsx) ```tsx -import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr' // re-export of react-server-dom/client.edge +import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr' import { renderToReadableStream } from 'react-dom/server.edge' export async function handleSsr(rscStream: ReadableStream) { @@ -196,7 +196,7 @@ export async function handleSsr(rscStream: ReadableStream) { - [`entry.browser.tsx`](./examples/starter/src/framework/entry.browser.tsx) ```tsx -import { createFromReadableStream } from '@vitejs/plugin-rsc/browser' // re-export of react-server-dom/client.browser +import { createFromReadableStream } from '@vitejs/plugin-rsc/browser' import { hydrateRoot } from 'react-dom/client' async function main() { diff --git a/packages/plugin-rsc/examples/starter/src/framework/entry.ssr.tsx b/packages/plugin-rsc/examples/starter/src/framework/entry.ssr.tsx index 4f9f2538f..d55f967cb 100644 --- a/packages/plugin-rsc/examples/starter/src/framework/entry.ssr.tsx +++ b/packages/plugin-rsc/examples/starter/src/framework/entry.ssr.tsx @@ -1,4 +1,4 @@ -import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr' // RSC API +import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr' import React from 'react' import type { ReactFormState } from 'react-dom/client' import { renderToReadableStream } from 'react-dom/server.edge' From 1713638ee292f79886eb0675a09a8e047c56f8cc Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sun, 17 Aug 2025 17:12:28 +0900 Subject: [PATCH 3/3] chore: tweak --- packages/plugin-rsc/examples/basic/src/framework/entry.ssr.tsx | 2 +- .../examples/starter-cf-single/src/framework/entry.ssr.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin-rsc/examples/basic/src/framework/entry.ssr.tsx b/packages/plugin-rsc/examples/basic/src/framework/entry.ssr.tsx index a0d6147e9..736a29b9a 100644 --- a/packages/plugin-rsc/examples/basic/src/framework/entry.ssr.tsx +++ b/packages/plugin-rsc/examples/basic/src/framework/entry.ssr.tsx @@ -1,4 +1,4 @@ -import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr' // RSC API +import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr' import React from 'react' import type { ReactFormState } from 'react-dom/client' import { renderToReadableStream } from 'react-dom/server.edge' diff --git a/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.ssr.tsx b/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.ssr.tsx index fed9e142e..c3ea6b9c4 100644 --- a/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.ssr.tsx +++ b/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.ssr.tsx @@ -1,4 +1,4 @@ -import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr' // RSC API +import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr' import React from 'react' import type { ReactFormState } from 'react-dom/client' import { renderToReadableStream } from 'react-dom/server.edge'