-
Notifications
You must be signed in to change notification settings - Fork 922
/
renderPage.ts
114 lines (104 loc) · 3.12 KB
/
renderPage.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
import type { App as VueApp } from 'vue'
import type { Router as VueRouter } from 'vue-router'
import { renderToString } from '@vue/server-renderer'
import type { SSRContext } from '@vue/server-renderer'
import type { Page, App } from '@vuepress/core'
import { removeLeadingSlash } from '@vuepress/shared'
import type { VuepressSSRContext } from '@vuepress/shared'
import { fs, renderHead } from '@vuepress/utils'
import { renderPagePrefetchLinks } from './renderPagePrefetchLinks'
import { renderPagePreloadLinks } from './renderPagePreloadLinks'
import { renderPageScripts } from './renderPageScripts'
import { renderPageStyles } from './renderPageStyles'
import { resolvePageClientFilesMeta } from './resolvePageClientFilesMeta'
import type { FileMeta, ModuleFilesMetaMap } from './types'
interface PageRenderContext extends SSRContext, VuepressSSRContext {
/**
* Injected by vuepress-loader
*
* Store the module request of components that used by current page
*/
_registeredComponents: Set<string>
}
/**
* Render page to html file, return the html file path
*/
export const renderPage = async ({
app,
page,
vueApp,
vueRouter,
ssrTemplate,
allFilesMeta,
initialFilesMeta,
asyncFilesMeta,
moduleFilesMetaMap,
}: {
app: App
page: Page
vueApp: VueApp
vueRouter: VueRouter
ssrTemplate: string
allFilesMeta: FileMeta[]
initialFilesMeta: FileMeta[]
asyncFilesMeta: FileMeta[]
moduleFilesMetaMap: ModuleFilesMetaMap
}): Promise<string> => {
// switch to current page route
await vueRouter.push(page.path)
await vueRouter.isReady()
// create vue ssr context with default values
const ssrContext: PageRenderContext = {
_registeredComponents: new Set(),
lang: 'en',
head: [],
}
// render current page to string
const pageRendered = await renderToString(vueApp, ssrContext)
// resolve client files that used by this page
const pageClientFilesMeta = resolvePageClientFilesMeta({
moduleRequests: Array.from(ssrContext._registeredComponents),
moduleFilesMetaMap,
})
// generate html string
const html = ssrTemplate
// page lang
.replace('{{ lang }}', ssrContext.lang)
// page head
.replace(
'<!--vuepress-ssr-head-->',
ssrContext.head.map(renderHead).join('')
)
// page preload & prefetch links
.replace(
'<!--vuepress-ssr-resources-->',
`${renderPagePreloadLinks({
app,
initialFilesMeta,
pageClientFilesMeta,
})}${renderPagePrefetchLinks({
app,
asyncFilesMeta,
pageClientFilesMeta,
})}`
)
// page styles
.replace(
'<!--vuepress-ssr-styles-->',
renderPageStyles({ app, initialFilesMeta, pageClientFilesMeta })
)
.replace('<!--vuepress-ssr-app-->', pageRendered)
// page scripts
.replace(
'<!--vuepress-ssr-scripts-->',
renderPageScripts({ app, initialFilesMeta, pageClientFilesMeta })
)
// TODO: teleports
// get html file name
const htmlFilename = app.dir.dest(
removeLeadingSlash(page.path.replace(/\/$/, '/index.html'))
)
// write html file
await fs.outputFile(htmlFilename, html)
return htmlFilename
}