Skip to content

Commit 7c49449

Browse files
feat(vitrify): add onCreateApp hook and type the internal render fn
1 parent 4393912 commit 7c49449

File tree

9 files changed

+142
-58
lines changed

9 files changed

+142
-58
lines changed

.changeset/quick-days-draw.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'vitrify': minor
3+
---
4+
5+
feat(vitrify): add onCreateApp hook and type the internal render fn

packages/vitrify/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
"./plugins": {
3434
"types": "./dist/types/plugins/index.d.ts",
3535
"import": "./dist/plugins/index.js"
36+
},
37+
"./hooks": {
38+
"types": "./dist/types/hooks/index.d.ts",
39+
"import": "./dist/hooks/index.d.ts"
3640
}
3741
},
3842
"engines": {
@@ -99,6 +103,7 @@
99103
"beasties": "^0.3.3",
100104
"css": "^3.0.0",
101105
"css-to-tailwind-translator": "^1.2.8",
106+
"pinia": "^3.0.2",
102107
"quasar": "^2.18.1",
103108
"rollup": "^4.40.1",
104109
"typescript": "^5.8.3",

packages/vitrify/src/node/frameworks/vue/fastify-ssr-plugin.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,11 @@ const renderHtml = async (options: {
184184

185185
const onRendered = options.onRendered ?? []
186186

187-
const [appHtml, preloadLinks] = await options.render(
188-
options.url,
189-
options.manifest,
190-
ssrContext
191-
)
187+
const {
188+
html: appHtml,
189+
preloadLinks,
190+
app
191+
} = await options.render(options.url, options.manifest, ssrContext)
192192

193193
if (!ssrContext.initialState) ssrContext.initialState = {}
194194
ssrContext.initialState.provide = options.provide
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type {
2+
OnBootHook,
3+
OnCreateAppHook,
4+
OnMountedHook,
5+
OnRenderedHook,
6+
OnSetupFile,
7+
OnSetupHook
8+
} from '../vitrify-config.js'
9+
10+
export {
11+
OnBootHook,
12+
OnCreateAppHook,
13+
OnMountedHook,
14+
OnRenderedHook,
15+
OnSetupFile,
16+
OnSetupHook
17+
}

packages/vitrify/src/node/index.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import { visualizer } from 'rollup-plugin-visualizer'
1717
import { fileURLToPath } from 'url'
1818
import type {
1919
StaticImports,
20-
BootFunction,
2120
OnMountedHook,
2221
VitrifyConfig,
2322
VitrifyConfigAsync,
@@ -27,7 +26,8 @@ import type {
2726
VitrifySSRModes,
2827
OnRenderedHook,
2928
OnBootHook,
30-
OnSetupFile
29+
OnSetupFile,
30+
OnCreateAppHook
3131
} from './vitrify-config.js'
3232
import type { VitrifyContext } from './bin/run.js'
3333
import type { VitrifyPlugin } from './plugins/index.js'
@@ -280,6 +280,7 @@ export const baseConfig = async ({
280280
let onBootHooks: OnBootHook[]
281281
let onRenderedHooks: OnRenderedHook[]
282282
let onMountedHooks: OnMountedHook[]
283+
let onCreateAppHooks: OnCreateAppHook[]
283284
let onSetupFiles: OnSetupFile[]
284285
let globalCss: string[] = []
285286
let staticImports: StaticImports
@@ -358,6 +359,7 @@ export const baseConfig = async ({
358359
onBootHooks = config.vitrify?.hooks?.onBoot || []
359360
onRenderedHooks = config.vitrify?.hooks?.onRendered || []
360361
onMountedHooks = config.vitrify?.hooks?.onMounted || []
362+
onCreateAppHooks = config.vitrify?.hooks?.onCreateApp || []
361363
onSetupFiles = config?.vitrify?.hooks?.onSetup || []
362364
globalCss = config.vitrify?.globalCss || []
363365
staticImports = config.vitrify?.staticImports || {}
@@ -395,6 +397,9 @@ export const baseConfig = async ({
395397
export const onRendered = [${onRenderedHooks
396398
.map((fn) => `${String(fn)}`)
397399
.join(', ')}]
400+
export const onCreateApp = [${onCreateAppHooks
401+
.map((fn) => `${String(fn)}`)
402+
.join(', ')}]
398403
export const onSetup = []
399404
${onSetupFiles
400405
.map((url, index) => {
@@ -726,5 +731,5 @@ export type {
726731
VitrifyConfigAsync,
727732
VitrifyPlugin,
728733
VitrifyContext,
729-
BootFunction
734+
OnBootHook
730735
}

packages/vitrify/src/node/vitrify-config.ts

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,82 @@
11
import type { Alias, UserConfig as ViteUserConfig, ViteDevServer } from 'vite'
22
import type { ComponentInternalInstance } from '@vue/runtime-core'
3-
import type { FastifyInstance, FastifyServerOptions } from 'fastify'
3+
import type {
4+
FastifyInstance,
5+
FastifyReply,
6+
FastifyRequest,
7+
FastifyServerOptions
8+
} from 'fastify'
49
import type { VitePWAOptions } from 'vite-plugin-pwa'
510
import type { Options as unpluginVueComponentsOptions } from 'unplugin-vue-components'
611
import type { UserConfig as UnoCSSUserConfig } from '@unocss/core'
7-
import { VitrifyPlugin } from './plugins/index.js'
12+
import type { VitrifyPlugin } from './plugins/index.js'
13+
import type { Router } from 'vue-router'
14+
import type { App } from '@vue/runtime-core'
815

9-
export type BootFunction = ({
16+
export type OnCreateAppHook = ({
1017
app,
11-
ssrContext,
12-
staticImports
18+
router,
19+
initialState,
20+
ctx
1321
}: {
14-
app: any
15-
ssrContext: Record<string, unknown>
16-
staticImports: Record<string, any>
22+
app: App
23+
router: Router
24+
initialState: Record<string, unknown> | null
25+
ctx?: Record<string, unknown>
1726
}) => Promise<void> | void
27+
1828
export type OnBootHook = ({
1929
app,
2030
ssrContext,
2131
staticImports
2232
}: {
23-
app: any
33+
app: App
2434
ssrContext: Record<string, unknown>
2535
staticImports?: Record<string, any>
2636
}) => Promise<void> | void
37+
2738
export type OnMountedHook = (
2839
instance: ComponentInternalInstance
2940
) => Promise<void> | void
3041
export type StaticImports = Record<string, string[]>
31-
export type SsrFunction = (
32-
html: string,
33-
ssrContext: Record<string, any>
34-
) => string
35-
export type OnRenderedHook = (
36-
html: string,
37-
ssrContext: Record<string, any>
38-
) => string
42+
43+
export type OnSetupFile = URL
3944
export type OnSetupHook = (
4045
fastify: FastifyInstance,
4146
options?: {
4247
vite?: ViteDevServer
4348
}
4449
) => any
45-
export type OnSetupFile = URL
50+
51+
export type Render = (
52+
url: string,
53+
manifest: Record<string, unknown>,
54+
ssrContext: {
55+
request:
56+
| FastifyRequest
57+
| {
58+
headers: Record<string, unknown>
59+
url: string
60+
}
61+
reply: FastifyReply | Record<string, unknown>
62+
provide: Record<string, unknown>
63+
},
64+
renderToString: (app: App, ctx?: Record<string, any>) => Promise<string>
65+
) => Promise<{
66+
html: string
67+
preloadLinks: string
68+
app: App
69+
}>
70+
71+
export type OnRenderedHook = ({
72+
html,
73+
ssrContext,
74+
app
75+
}: {
76+
html: string
77+
ssrContext?: Record<string, any>
78+
app?: App
79+
}) => string
4680

4781
export interface VitrifyConfig extends ViteUserConfig {
4882
vitrify?: {
@@ -76,6 +110,10 @@ export interface VitrifyConfig extends ViteUserConfig {
76110
* Functions which run after rendering the app (SSR)
77111
*/
78112
onRendered?: OnRenderedHook[]
113+
/**
114+
* Functions which run directly after initializing the application
115+
*/
116+
onCreateApp?: OnCreateAppHook[]
79117
}
80118
/**
81119
* Global SASS variables

packages/vitrify/src/vite/vue/RootComponent.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
onMounted as onMountedVue,
99
getCurrentInstance
1010
} from 'vue'
11-
import { onBoot, onMounted } from 'virtual:vitrify-hooks'
11+
import { onMounted } from 'virtual:vitrify-hooks'
1212
import * as staticImports from 'virtual:static-imports'
1313
import App from 'src/App.vue'
1414
// import 'vitrify.sass'

packages/vitrify/src/vite/vue/main.ts

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import createRouter from 'src/router'
2-
import { createSSRApp, createApp as createVueApp, ref } from 'vue'
3-
import { onBoot } from 'virtual:vitrify-hooks'
2+
import { App, createSSRApp, createApp as createVueApp, ref } from 'vue'
3+
import { onBoot, onCreateApp } from 'virtual:vitrify-hooks'
44
import routes from 'src/router/routes'
55
import * as staticImports from 'virtual:static-imports'
66
import 'virtual:uno.css'
77

88
import RootComponent from './RootComponent.vue'
9+
10+
declare global {
11+
interface Window {
12+
__INITIAL_STATE__: any
13+
}
14+
}
15+
916
interface ssrContext {
1017
provide?: Record<string, unknown>
1118
[key: string]: unknown
@@ -17,18 +24,44 @@ function capitalizeFirstLetter(string: string) {
1724

1825
export async function createApp(
1926
ssr?: 'client' | 'server',
20-
ssrContext?: ssrContext
27+
ssrContext: ssrContext = {}
2128
) {
22-
let app
29+
const initialState: Record<string, any> | null =
30+
!import.meta.env.SSR && window.__INITIAL_STATE__
31+
? JSON.parse(window.__INITIAL_STATE__)
32+
: null
33+
34+
// Delete for security reasons
35+
if (!import.meta.env.SSR && window.__INITIAL_STATE__)
36+
delete window.__INITIAL_STATE__
37+
38+
let provide: Record<string, any> = {}
39+
if (import.meta.env.SSR) {
40+
if (ssrContext.provide) {
41+
provide = ssrContext?.provide
42+
}
43+
} else {
44+
if (initialState) {
45+
provide = initialState.provide
46+
}
47+
}
48+
49+
let app: App
2350

2451
if (ssr) {
2552
app = createSSRApp(RootComponent)
2653
} else {
2754
app = createVueApp(RootComponent)
2855
}
56+
2957
const router = createRouter()
3058
app.use(router)
3159

60+
const onCreateAppCtx = {}
61+
for (const fn of onCreateApp) {
62+
await fn({ app, router, initialState, ctx: onCreateAppCtx })
63+
}
64+
3265
// Workaround to fix hydration errors when serving html files directly
3366
router.beforeEach((to, from, next) => {
3467
if (to.path.endsWith('.html')) {
@@ -38,20 +71,6 @@ export async function createApp(
3871
next()
3972
})
4073

41-
let initialState: Record<string, any>
42-
let provide: Record<string, any> = {}
43-
if (import.meta.env.SSR) {
44-
if (ssrContext?.provide) {
45-
provide = ssrContext?.provide
46-
}
47-
} else {
48-
// @ts-expect-error undefined
49-
if (window.__INITIAL_STATE__) {
50-
// @ts-expect-error undefined
51-
initialState = JSON.parse(window.__INITIAL_STATE__)
52-
provide = initialState.provide
53-
}
54-
}
5574
for (const key in provide) {
5675
if (provide[key]?.value) {
5776
const refValue = ref(provide[key].value)

packages/vitrify/src/vite/vue/ssr/entry-server.ts

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { type FastifyReply, type FastifyRequest } from 'fastify'
21
import { createApp } from '../main.js'
3-
import { renderToString as renderToStringVue } from 'vue/server-renderer'
2+
import type { Render } from '../../../node/vitrify-config.js'
43

54
const initializeApp = async (
65
url: string,
@@ -37,16 +36,12 @@ export const getRoutes = async () =>
3736
})
3837
).routes
3938

40-
export async function render(
41-
url: string,
42-
manifest: Record<string, unknown>,
43-
ssrContext: {
44-
request: FastifyRequest | { headers: Record<string, unknown>; url: string }
45-
reply: FastifyReply | Record<string, unknown>
46-
provide: Record<string, unknown>
47-
},
48-
renderToString: typeof renderToStringVue
49-
) {
39+
export const render: Render = async (
40+
url,
41+
manifest,
42+
ssrContext,
43+
renderToString
44+
) => {
5045
if (!renderToString)
5146
renderToString = (await import('vue/server-renderer')).renderToString
5247
const { app, router } = await initializeApp(url, ssrContext)
@@ -62,7 +57,7 @@ export async function render(
6257

6358
const preloadLinks = renderPreloadLinks(ctx.modules!, manifest)
6459

65-
return [html, preloadLinks]
60+
return { html, preloadLinks, app }
6661
}
6762

6863
function renderPreloadLinks(

0 commit comments

Comments
 (0)