-
Notifications
You must be signed in to change notification settings - Fork 26k
/
render.ts
151 lines (138 loc) · 3.77 KB
/
render.ts
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import { NextRequest } from '../../../../server/web/spec-extension/request'
import { renderToHTML } from '../../../../server/web/render'
import RenderResult from '../../../../server/render-result'
import { toNodeHeaders } from '../../../../server/web/utils'
const createHeaders = (args?: any) => ({
...args,
'x-middleware-ssr': '1',
})
export function getRender({
App,
Document,
pageMod,
errorMod,
rscManifest,
buildManifest,
reactLoadableManifest,
isServerComponent,
restRenderOpts,
}: {
App: any
Document: any
pageMod: any
errorMod: any
rscManifest: object
buildManifest: any
reactLoadableManifest: any
isServerComponent: boolean
restRenderOpts: any
}) {
return async function render(request: NextRequest) {
const { nextUrl: url, cookies, headers } = request
const { pathname, searchParams } = url
const query = Object.fromEntries(searchParams)
// Preflight request
if (request.method === 'HEAD') {
return new Response(null, {
headers: createHeaders(),
})
}
const renderServerComponentData = isServerComponent
? query.__flight__ !== undefined
: false
const serverComponentProps =
isServerComponent && query.__props__
? JSON.parse(query.__props__)
: undefined
delete query.__flight__
delete query.__props__
const req = {
url: pathname,
cookies,
headers: toNodeHeaders(headers),
}
const renderOpts = {
...restRenderOpts,
// Locales are not supported yet.
// locales: i18n?.locales,
// locale: detectedLocale,
// defaultLocale,
// domainLocales: i18n?.domains,
dev: process.env.NODE_ENV !== 'production',
App,
Document,
buildManifest,
Component: pageMod.default,
pageConfig: pageMod.config || {},
getStaticProps: pageMod.getStaticProps,
getServerSideProps: pageMod.getServerSideProps,
getStaticPaths: pageMod.getStaticPaths,
reactLoadableManifest,
env: process.env,
supportsDynamicHTML: true,
concurrentFeatures: true,
renderServerComponentData,
serverComponentProps,
serverComponentManifest: isServerComponent ? rscManifest : null,
ComponentMod: null,
}
const transformStream = new TransformStream()
const writer = transformStream.writable.getWriter()
const encoder = new TextEncoder()
let result: RenderResult | null
try {
result = await renderToHTML(
req as any,
{} as any,
pathname,
query,
renderOpts
)
} catch (err: any) {
const errorRes = { statusCode: 500, err }
try {
result = await renderToHTML(
req as any,
errorRes as any,
'/_error',
query,
{
...renderOpts,
Component: errorMod.default,
getStaticProps: errorMod.getStaticProps,
getServerSideProps: errorMod.getServerSideProps,
getStaticPaths: errorMod.getStaticPaths,
}
)
} catch (err2: any) {
return new Response(
(
err2 || 'An error occurred while rendering ' + pathname + '.'
).toString(),
{
status: 500,
headers: createHeaders(),
}
)
}
}
if (!result) {
return new Response(
'An error occurred while rendering ' + pathname + '.',
{
status: 500,
headers: createHeaders(),
}
)
}
result.pipe({
write: (str: string) => writer.write(encoder.encode(str)),
end: () => writer.close(),
// Not implemented: cork/uncork/on/removeListener
} as any)
return new Response(transformStream.readable, {
headers: createHeaders(),
status: 200,
})
}
}