-
-
Notifications
You must be signed in to change notification settings - Fork 140
/
Copy pathindex.ts
120 lines (100 loc) · 3.19 KB
/
index.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
import type { Component } from 'vue'
import { createApp as createClientApp, createSSRApp } from 'vue'
import { createMemoryHistory, createRouter, createWebHistory } from 'vue-router'
import type { MergeHead, VueHeadClient } from '@unhead/vue'
import { createHead } from '@unhead/vue'
import { deserializeState } from '../utils/state'
import { documentReady } from '../utils/document-ready'
import type { RouterOptions, ViteSSGClientOptions, ViteSSGContext } from '../types'
import { ClientOnly } from './components/ClientOnly'
export * from '../types'
export function ViteSSG(
App: Component,
routerOptions: RouterOptions,
fn?: (context: ViteSSGContext<true>) => Promise<void> | void,
options: ViteSSGClientOptions = {},
) {
const {
transformState,
registerComponents = true,
useHead = true,
rootContainer = '#app',
} = options
const isClient = typeof window !== 'undefined'
async function createApp(client = false, routePath?: string) {
const app = client
? createClientApp(App)
: createSSRApp(App)
let head: VueHeadClient<MergeHead> | undefined
if (useHead) {
head = createHead()
app.use(head)
}
const router = createRouter({
history: client
? createWebHistory(routerOptions.base)
: createMemoryHistory(routerOptions.base),
...routerOptions,
})
const { routes } = routerOptions
if (registerComponents)
app.component('ClientOnly', ClientOnly)
const appRenderCallbacks: (() => void)[] = []
const onSSRAppRendered = client
? () => {}
: (cb: () => void) => appRenderCallbacks.push(cb)
const triggerOnSSRAppRendered = () => {
return Promise.all(appRenderCallbacks.map(cb => cb()))
}
const context: ViteSSGContext<true> = {
app,
head,
isClient,
router,
routes,
onSSRAppRendered,
triggerOnSSRAppRendered,
initialState: {},
transformState,
routePath,
}
if (client) {
await documentReady()
// @ts-expect-error global variable
context.initialState = transformState?.(window.__INITIAL_STATE__ || {}) || deserializeState(window.__INITIAL_STATE__)
}
await fn?.(context)
app.use(router)
let entryRoutePath: string | undefined
let isFirstRoute = true
router.beforeEach((to, from, next) => {
if (isFirstRoute || (entryRoutePath && entryRoutePath === to.path)) {
// The first route is rendered in the server and its state is provided globally.
isFirstRoute = false
entryRoutePath = to.path
to.meta.state = context.initialState
}
next()
})
if (!client) {
const route = context.routePath ?? '/'
router.push(route)
await router.isReady()
context.initialState = router.currentRoute.value.meta.state as Record<string, any> || {}
}
const initialState = context.initialState
return {
...context,
initialState,
} as ViteSSGContext<true>
}
if (isClient) {
(async () => {
const { app, router } = await createApp(true)
// wait until page component is fetched before mounting
await router.isReady()
app.mount(rootContainer, true)
})()
}
return createApp
}