-
Notifications
You must be signed in to change notification settings - Fork 0
/
preact.ts
77 lines (63 loc) · 2.11 KB
/
preact.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
import renderToString from 'https://esm.sh/preact-render-to-string@6.0.2'
import { createContext, h, type VNode } from 'https://esm.sh/preact@10.20.0'
import { useContext } from 'https://esm.sh/preact@10.20.0/hooks'
import { type Context } from './deps/oak.ts'
export type ResponseValue = {
status?: number
headers?: Record<string, string>
}
type RenderResult = {
html: string
status: number
headers: Record<string, string>
}
export const ResponseContext = createContext<ResponseValue>({})
export function setResponseStatus(status: number): void {
const context = useContext(ResponseContext)
context.status = status
}
export function setResponseHeader(name: string, value: string): void {
const context = useContext(ResponseContext)
context.headers ||= {}
context.headers[name] = value
}
export async function renderFile(url: URL, routerContext: Context): Promise<void> {
const mod = await import(url.href)
return renderResponse(h(mod.default, {}, []), routerContext)
}
export function renderResponse(
// deno-lint-ignore no-explicit-any
vnode: VNode<any>,
routerContext: Context
): void {
try {
const { status, html, headers } = render(vnode)
routerContext.response.status = status
for (const [name, value] of Object.entries(headers)) {
routerContext.response.headers.set(name, value)
}
routerContext.response.body = '<!doctype html>' + html
routerContext.response.type = 'html'
} catch (e) {
console.error('renderSync threw')
console.error(e)
routerContext.response.status = 500
routerContext.response.body = 'our rendering broke'
routerContext.response.type = 'text'
}
}
export function render(
// deno-lint-ignore no-explicit-any
vnode: VNode<any>
): RenderResult {
const mutableResponseValue: ResponseValue = {}
const wrapped = h(ResponseContext.Provider, {
value: mutableResponseValue,
children: vnode
})
// deno-lint-ignore no-explicit-any
const html = renderToString(wrapped as VNode<any>)
const status = mutableResponseValue.status || 200
const headers = mutableResponseValue.headers || {}
return { html, status, headers }
}