Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/plugin-rsc/e2e/react-router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ test.describe('dev-cloudflare', () => {
mode: 'dev',
command: 'pnpm cf-dev',
})
defineTest(f)
defineTest(f, 'cloudflare')
})

test.describe('build-cloudflare', () => {
Expand All @@ -35,10 +35,10 @@ test.describe('build-cloudflare', () => {
buildCommand: 'pnpm cf-build',
command: 'pnpm cf-preview',
})
defineTest(f)
defineTest(f, 'cloudflare')
})

function defineTest(f: Fixture) {
function defineTest(f: Fixture, variant?: 'cloudflare') {
test('loader', async ({ page }) => {
await page.goto(f.url())
await expect(page.getByText(`loaderData: {"name":"Unknown"}`)).toBeVisible()
Expand Down Expand Up @@ -81,7 +81,7 @@ function defineTest(f: Fixture) {
)
const manifest = JSON.parse(
readFileSync(
f.root + '/dist/ssr/__vite_rsc_assets_manifest.js',
`${f.root}/${variant === 'cloudflare' ? 'dist/rsc/ssr' : 'dist/ssr'}/__vite_rsc_assets_manifest.js`,
'utf-8',
).slice('export default '.length),
)
Expand Down
4 changes: 2 additions & 2 deletions packages/plugin-rsc/examples/react-router/cf/entry.rsc.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { fetchServer } from '../react-router-vite/entry.rsc'
import handler from '../react-router-vite/entry.rsc'

console.log('[debug:cf-rsc-entry]')

export default {
fetch(request: Request) {
return fetchServer(request)
return handler(request)
},
}
9 changes: 0 additions & 9 deletions packages/plugin-rsc/examples/react-router/cf/entry.ssr.tsx

This file was deleted.

29 changes: 15 additions & 14 deletions packages/plugin-rsc/examples/react-router/cf/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
// import inspect from 'vite-plugin-inspect'

export default defineConfig({
export default defineConfig((env) => ({
clearScreen: false,
build: {
minify: false,
Expand All @@ -17,22 +17,16 @@ export default defineConfig({
rsc({
entries: {
client: './react-router-vite/entry.browser.tsx',
ssr: './react-router-vite/entry.ssr.tsx',
},
serverHandler: false,
loadModuleDevProxy: true,
}),
cloudflare({
configPath: './cf/wrangler.ssr.jsonc',
configPath: './cf/wrangler.jsonc',
viteEnvironment: {
name: 'ssr',
name: 'rsc',
},
auxiliaryWorkers: [
{
configPath: './cf/wrangler.rsc.jsonc',
viteEnvironment: {
name: 'rsc',
},
},
],
}),
],
environments: {
Expand All @@ -42,14 +36,21 @@ export default defineConfig({
},
},
ssr: {
optimizeDeps: {
exclude: ['react-router'],
keepProcessEnv: false,
build: {
outDir: './dist/rsc/ssr',
},
resolve:
env.command === 'build'
? {
noExternal: true,
}
: undefined,
},
rsc: {
optimizeDeps: {
exclude: ['react-router'],
},
},
},
})
}))
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "https://www.unpkg.com/[email protected]/config-schema.json",
"name": "vite-rsc-react-router-rsc",
"name": "vite-rsc-react-router",
"main": "./entry.rsc.tsx",
"workers_dev": true,
"compatibility_date": "2025-04-01",
Expand Down

This file was deleted.

4 changes: 2 additions & 2 deletions packages/plugin-rsc/examples/react-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
"cf-dev": "vite -c ./cf/vite.config.ts",
"cf-build": "vite -c ./cf/vite.config.ts build",
"cf-preview": "vite -c ./cf/vite.config.ts preview",
"cf-release": "wrangler deploy -c dist/rsc/wrangler.json && wrangler deploy"
"cf-release": "wrangler deploy"
},
"dependencies": {
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router": "7.8.2"
"react-router": "0.0.0-nightly-353d05fd9-20250830"
},
"devDependencies": {
"@cloudflare/vite-plugin": "^1.11.7",
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,20 @@ import {
import { unstable_matchRSCServerRequest as matchRSCServerRequest } from 'react-router'
import { routes } from '../app/routes'

export function fetchServer(request: Request) {
export default async function handler(request: Request) {
const ssr = await import.meta.viteRsc.loadModule<
typeof import('./entry.ssr')
>('ssr', 'index')
const rscResponse = await fetchServer(request)
const ssrResponse = await ssr.generateHTML(
request.url,
request.headers,
rscResponse,
)
return ssrResponse
}

function fetchServer(request: Request) {
return matchRSCServerRequest({
// Provide the React Server touchpoints.
createTemporaryReferenceSet,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@ import {
unstable_RSCStaticRouter as RSCStaticRouter,
} from 'react-router'

// pass serializable values (via turbo-stream) to ssr environment.
// passing entire `request` and `fetchServer` are not necessary since `routeRSCServerRequest` works like this
// https://github.com/remix-run/react-router/blob/20d8307d4a51c219f6e13e0b66461e7162d944e4/packages/react-router/lib/rsc/server.ssr.tsx#L95-L102

export async function generateHTML(
request: Request,
fetchServer: (request: Request) => Promise<Response>,
url: string,
headers: Headers,
rscResponse: Response,
): Promise<Response> {
return await routeRSCServerRequest({
// The incoming request.
request,
request: new Request(url, { headers }),
// How to call the React Server.
fetchServer,
fetchServer: async () => rscResponse,
// Provide the React Server touchpoints.
createFromReadableStream,
// Render the router to HTML.
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-rsc/examples/react-router/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default defineConfig({
entries: {
client: './react-router-vite/entry.browser.tsx',
ssr: './react-router-vite/entry.ssr.tsx',
rsc: './react-router-vite/entry.rsc.single.tsx',
rsc: './react-router-vite/entry.rsc.tsx',
},
}),
],
Expand Down
54 changes: 51 additions & 3 deletions packages/plugin-rsc/src/utils/rpc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { decode, encode } from 'turbo-stream'
import {
decode,
encode,
type DecodeOptions,
type DecodePlugin,
type EncodeOptions,
type EncodePlugin,
} from 'turbo-stream'

type RequestPayload = {
method: string
Expand All @@ -17,6 +24,7 @@ export function createRpcServer<T extends object>(handlers: T) {
}
const reqPayload = await decode<RequestPayload>(
request.body.pipeThrough(new TextDecoderStream()),
decodeOptions,
)
const handler = (handlers as any)[reqPayload.method]
if (!handler) {
Expand All @@ -31,7 +39,7 @@ export function createRpcServer<T extends object>(handlers: T) {
resPayload.ok = false
resPayload.data = e
}
return new Response(encode(resPayload))
return new Response(encode(resPayload, encodeOptions))
}
}

Expand All @@ -41,7 +49,9 @@ export function createRpcClient<T>(options: { endpoint: string }): T {
method,
args,
}
const body = encode(reqPayload).pipeThrough(new TextEncoderStream())
const body = encode(reqPayload, encodeOptions).pipeThrough(
new TextEncoderStream(),
)
const res = await fetch(options.endpoint, {
method: 'POST',
body,
Expand All @@ -55,6 +65,7 @@ export function createRpcClient<T>(options: { endpoint: string }): T {
}
const resPayload = await decode<ResponsePayload>(
res.body.pipeThrough(new TextDecoderStream()),
decodeOptions,
)
if (!resPayload.ok) {
throw resPayload.data
Expand All @@ -74,3 +85,40 @@ export function createRpcClient<T>(options: { endpoint: string }): T {
},
) as any
}

const encodePlugin: EncodePlugin = (value) => {
if (value instanceof Response) {
const data: ConstructorParameters<typeof Response> = [
value.body,
{
status: value.status,
statusText: value.statusText,
headers: value.headers,
},
]
return ['vite-rsc/response', ...data]
}
if (value instanceof Headers) {
const data: ConstructorParameters<typeof Headers> = [[...value]]
return ['vite-rsc/headers', ...data]
}
}

const decodePlugin: DecodePlugin = (type, ...data) => {
if (type === 'vite-rsc/response') {
const value = new Response(...(data as any))
return { value }
}
if (type === 'vite-rsc/headers') {
const value = new Headers(...(data as any))
return { value }
}
}

const encodeOptions: EncodeOptions = {
plugins: [encodePlugin],
}

const decodeOptions: DecodeOptions = {
plugins: [decodePlugin],
}
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading