-
-
Notifications
You must be signed in to change notification settings - Fork 256
Expand file tree
/
Copy pathentry.rsc.tsx
More file actions
108 lines (100 loc) · 3.95 KB
/
Copy pathentry.rsc.tsx
File metadata and controls
108 lines (100 loc) · 3.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import * as ReactServer from '@vitejs/plugin-rsc/rsc'
import type { ReactFormState } from 'react-dom/client'
import type React from 'react'
// The schema of payload which is serialized into RSC stream on rsc environment
// and deserialized on ssr/client environments.
export type RscPayload = {
// this demo renders/serializes/deserizlies entire root html element
// but this mechanism can be changed to render/fetch different parts of components
// based on your own route conventions.
root: React.ReactNode
// server action return value of non-progressive enhancement case
returnValue?: unknown
// server action form state (e.g. useActionState) of progressive enhancement case
formState?: ReactFormState
}
// the plugin by default assumes `rsc` entry having default export of request handler.
// however, how server entries are executed can be customized by registering
// own server handler e.g. `@cloudflare/vite-plugin`.
export async function handleRequest({
request,
getRoot,
nonce,
}: {
request: Request
getRoot: () => React.ReactNode
nonce?: string
}): Promise<Response> {
// handle server function request
const isAction = request.method === 'POST'
let returnValue: unknown | undefined
let formState: ReactFormState | undefined
let temporaryReferences: unknown | undefined
if (isAction) {
// x-rsc-action header exists when action is called via `ReactClient.setServerCallback`.
const actionId = request.headers.get('x-rsc-action')
if (actionId) {
const contentType = request.headers.get('content-type')
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)
returnValue = await action.apply(null, args)
} else {
// otherwise server function is called via `<form action={...}>`
// before hydration (e.g. when javascript is disabled).
// aka progressive enhancement.
const formData = await request.formData()
const decodedAction = await ReactServer.decodeAction(formData)
const result = await decodedAction()
formState = await ReactServer.decodeFormState(result, formData)
}
}
const url = new URL(request.url)
const rscPayload: RscPayload = { root: getRoot(), formState, returnValue }
const rscOptions = { temporaryReferences }
const rscStream = ReactServer.renderToReadableStream<RscPayload>(
rscPayload,
rscOptions,
)
// respond RSC stream without HTML rendering based on framework's convention.
// here we use request header `content-type`.
// additionally we allow `?__rsc` and `?__html` to easily view payload directly.
const isRscRequest =
(!request.headers.get('accept')?.includes('text/html') &&
!url.searchParams.has('__html')) ||
url.searchParams.has('__rsc')
if (isRscRequest) {
return new Response(rscStream, {
headers: {
'content-type': 'text/x-component;charset=utf-8',
vary: 'accept',
},
})
}
// Delegate to SSR environment for html rendering.
// The plugin provides `loadSsrModule` helper to allow loading SSR environment entry module
// in RSC environment. however this can be customized by implementing own runtime communication
// e.g. `@cloudflare/vite-plugin`'s service binding.
const ssrEntryModule = await import.meta.viteRsc.loadModule<
typeof import('./entry.ssr.tsx')
>('ssr', 'index')
const htmlStream = await ssrEntryModule.renderHTML(rscStream, {
formState,
nonce,
// allow quick simulation of javscript disabled browser
debugNojs: url.searchParams.has('__nojs'),
})
// respond html
return new Response(htmlStream, {
headers: {
'content-type': 'text/html;charset=utf-8',
vary: 'accept',
},
})
}
if (import.meta.hot) {
import.meta.hot.accept()
}