-
Notifications
You must be signed in to change notification settings - Fork 169
/
renderer.ts
92 lines (90 loc) · 3.14 KB
/
renderer.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
import React, { ComponentType, ReactElement } from 'https://esm.sh/react'
import { renderToString } from 'https://esm.sh/react-dom/server'
import { RouterContext } from './context.ts'
import { AsyncUseDenoError, E400MissingDefaultExportAsComponent, E404Page } from './error.ts'
import events from './events.ts'
import { createPageProps } from './routing.ts'
import type { RouterURL } from './types.ts'
import util from './util.ts'
export { renderHead } from './head.ts'
export async function renderPage(
url: RouterURL,
App: ComponentType<any> | undefined,
E404: ComponentType | undefined,
pageComponentTree: { id: string, Component?: any }[]
) {
let el: ReactElement
let html: string
const pageProps = createPageProps(pageComponentTree)
if (App) {
if (util.isLikelyReactComponent(App)) {
el = React.createElement(App, pageProps)
} else {
el = React.createElement(
E400MissingDefaultExportAsComponent,
{ name: 'Custom App' }
)
}
} else {
if (pageProps.Page == null) {
if (E404) {
if (util.isLikelyReactComponent(E404)) {
el = React.createElement(E404)
} else {
el = React.createElement(
E400MissingDefaultExportAsComponent,
{ name: 'Custom 404' }
)
}
} else {
el = React.createElement(E404Page)
}
} else {
el = React.createElement(pageProps.Page, pageProps.pageProps)
}
}
const data: Record<string, any> = {}
const useDenEvent = `useDeno://${url.pathname + '?' + url.query.toString()}`
const useDenoAsyncCalls: Array<Promise<any>> = []
const orginFetch = window.fetch
events.on(useDenEvent, (id: string, ret: any, async: boolean) => {
if (async) {
useDenoAsyncCalls.push(ret)
} else {
data[id] = ret
}
})
Object.assign(window, {
_useDenoAsyncData: {},
fetch: (input: Request | URL | string, init?: RequestInit) => {
console.log(`[ renderer ] fetch '${input}' ...`)
return orginFetch(input, init)
}
})
while (true) {
try {
if (useDenoAsyncCalls.length > 0) {
const iter = [...useDenoAsyncCalls]
useDenoAsyncCalls.splice(0, useDenoAsyncCalls.length)
await Promise.all(iter)
}
html = renderToString(
React.createElement(
RouterContext.Provider,
{ value: url },
el
)
)
break
} catch (error) {
if (error instanceof AsyncUseDenoError) {
continue
}
Object.assign(window, { _useDenoAsyncData: null, fetch: orginFetch })
throw error
}
}
Object.assign(window, { _useDenoAsyncData: null, fetch: orginFetch })
events.removeAllListeners(useDenEvent)
return [html, Object.keys(data).length > 0 ? data : null]
}