vitrify 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -0
- package/dist/app-urls.js +36 -0
- package/dist/bin/build.js +73 -0
- package/dist/bin/cli.js +139 -0
- package/dist/bin/dev.js +108 -0
- package/dist/bin/run.js +29 -0
- package/dist/frameworks/vue/fastify-ssr-plugin.js +91 -0
- package/dist/frameworks/vue/prerender.js +29 -0
- package/dist/frameworks/vue/server.js +20 -0
- package/dist/helpers/logger.js +108 -0
- package/dist/helpers/routes.js +24 -0
- package/dist/helpers/utils.js +24 -0
- package/dist/index.js +341 -0
- package/dist/plugins/index.js +1 -0
- package/dist/plugins/quasar.js +299 -0
- package/dist/types/app-urls.d.ts +12 -0
- package/dist/types/bin/build.d.ts +8 -0
- package/dist/types/bin/cli.d.ts +2 -0
- package/dist/types/bin/dev.d.ts +15 -0
- package/dist/types/bin/run.d.ts +8 -0
- package/dist/types/bin/test.d.ts +3 -0
- package/dist/types/frameworks/vue/fastify-ssr-plugin.d.ts +14 -0
- package/dist/types/frameworks/vue/prerender.d.ts +8 -0
- package/dist/types/frameworks/vue/server.d.ts +9 -0
- package/dist/types/helpers/logger.d.ts +23 -0
- package/dist/types/helpers/routes.d.ts +2 -0
- package/dist/types/helpers/utils.d.ts +5 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/plugins/index.d.ts +7 -0
- package/dist/types/plugins/quasar.d.ts +16 -0
- package/dist/types/vitrify-config.d.ts +67 -0
- package/dist/vitrify-config.js +1 -0
- package/package.json +94 -19
- package/src/node/frameworks/vue/fastify-ssr-plugin.ts +137 -0
- package/src/node/frameworks/vue/prerender.ts +49 -0
- package/src/node/frameworks/vue/server.ts +38 -0
- package/src/vite/vue/csr/entry.ts +8 -0
- package/src/vite/vue/index.html +16 -0
- package/src/vite/vue/main.ts +89 -0
- package/src/vite/vue/ssr/entry-client.ts +9 -0
- package/src/vite/vue/ssr/entry-server.ts +97 -0
- package/src/vite/vue/ssr/fastify-ssr-plugin.ts +120 -0
- package/src/vite/vue/ssr/prerender.ts +4 -0
- package/src/vite/vue/ssr/server.ts +18 -0
- package/src/vite/vue/ssr/server.ts.bak +61 -0
- package/src/vite/vue/ssr/tsconfig.json +9 -0
package/package.json
CHANGED
|
@@ -1,23 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitrify",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Stefan van Herwijnen",
|
|
6
|
-
"description": "Pre-configured Vite CLI",
|
|
6
|
+
"description": "Pre-configured Vite CLI for your framework",
|
|
7
|
+
"type": "module",
|
|
7
8
|
"bin": {
|
|
8
|
-
"vitrify": "bin/
|
|
9
|
+
"vitrify": "./dist/bin/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": "./dist/index.js",
|
|
13
|
+
"./*": "./dist/*.js",
|
|
14
|
+
"./build": "./dist/bin/build.js",
|
|
15
|
+
"./dev": "./dist/bin/dev.js",
|
|
16
|
+
"./helpers/*": "./dist/helpers/*.js",
|
|
17
|
+
"./vite/*": "./src/vite/*"
|
|
18
|
+
},
|
|
19
|
+
"typesVersions": {
|
|
20
|
+
"*": {
|
|
21
|
+
"*": [
|
|
22
|
+
"./dist/types/index.d.ts"
|
|
23
|
+
],
|
|
24
|
+
"helpers/*": [
|
|
25
|
+
"./dist/types/helpers/*.d.ts"
|
|
26
|
+
],
|
|
27
|
+
"build": [
|
|
28
|
+
"./dist/types/bin/build.d.ts"
|
|
29
|
+
],
|
|
30
|
+
"dev": [
|
|
31
|
+
"./dist/types/bin/dev.d.ts"
|
|
32
|
+
],
|
|
33
|
+
"help": [
|
|
34
|
+
"./dist/types/bin/help.d.ts"
|
|
35
|
+
]
|
|
36
|
+
}
|
|
9
37
|
},
|
|
10
|
-
"main": "dist/node/index.js",
|
|
11
|
-
"types": "dist/node/index.d.ts",
|
|
12
|
-
"files": [
|
|
13
|
-
"bin",
|
|
14
|
-
"dist",
|
|
15
|
-
"client.d.ts",
|
|
16
|
-
"src/client",
|
|
17
|
-
"types"
|
|
18
|
-
],
|
|
19
38
|
"engines": {
|
|
20
|
-
"node": ">=
|
|
39
|
+
"node": ">=16.0.0"
|
|
21
40
|
},
|
|
22
41
|
"repository": {
|
|
23
42
|
"type": "git",
|
|
@@ -28,9 +47,65 @@
|
|
|
28
47
|
"url": "https://github.com/simsustech/vitrify/issues"
|
|
29
48
|
},
|
|
30
49
|
"homepage": "https://github.com/simsustech/vitrify/tree/main/#readme",
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsc",
|
|
52
|
+
"test": "echo \"Error: no test specified\" && exit 0"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@quasar/extras": "^1.13.5",
|
|
56
|
+
"@vitejs/plugin-vue": "^2.3.1",
|
|
57
|
+
"@vue/compiler-sfc": "^3.2.33",
|
|
58
|
+
"@vue/server-renderer": "^3.2.33",
|
|
59
|
+
"builtin-modules": "^3.2.0",
|
|
60
|
+
"cac": "^6.7.12",
|
|
61
|
+
"chalk": "^5.0.1",
|
|
62
|
+
"cross-env": "^7.0.3",
|
|
63
|
+
"fastify": "^3.28.0",
|
|
64
|
+
"fastify-static": "^4.6.1",
|
|
65
|
+
"glob": "^8.0.1",
|
|
66
|
+
"happy-dom": "^2.55.0",
|
|
67
|
+
"import-meta-resolve": "^1.1.1",
|
|
68
|
+
"local-pkg": "^0.4.1",
|
|
69
|
+
"magic-string": "^0.26.1",
|
|
70
|
+
"merge-deep": "^3.0.3",
|
|
71
|
+
"middie": "^6.0.0",
|
|
72
|
+
"readline": "^1.3.0",
|
|
73
|
+
"sass": "1.50.0",
|
|
74
|
+
"vite": "^2.9.5",
|
|
75
|
+
"vitest": "^0.9.3"
|
|
76
|
+
},
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"@types/glob": "^7.2.0",
|
|
79
|
+
"@types/merge-deep": "^3.0.0",
|
|
80
|
+
"@types/node": "^17.0.24",
|
|
81
|
+
"@types/ws": "^8.5.3",
|
|
82
|
+
"@vue/runtime-core": "^3.2.33",
|
|
83
|
+
"quasar": "^2.6.6",
|
|
84
|
+
"rollup": "^2.70.1",
|
|
85
|
+
"typescript": "^4.6.3",
|
|
86
|
+
"unplugin-vue-components": "^0.19.3",
|
|
87
|
+
"vite": "^2.9.5",
|
|
88
|
+
"vue": "^3.2.33",
|
|
89
|
+
"vue-router": "^4.0.14"
|
|
90
|
+
},
|
|
91
|
+
"peerDependencies": {
|
|
92
|
+
"fastify": "^3.28.0",
|
|
93
|
+
"fastify-plugin": "^3.0.1",
|
|
94
|
+
"fastify-sensible": "^3.1.2",
|
|
95
|
+
"fastify-static": "^4.6.1",
|
|
96
|
+
"quasar": "^2.6.6",
|
|
97
|
+
"vue": "^3.2.33",
|
|
98
|
+
"vue-router": "^4.0.14"
|
|
99
|
+
},
|
|
100
|
+
"publishConfig": {
|
|
101
|
+
"access": "public",
|
|
102
|
+
"registry": "https://registry.npmjs.org/"
|
|
103
|
+
},
|
|
104
|
+
"files": [
|
|
105
|
+
"dist",
|
|
106
|
+
"src/node/frameworks",
|
|
107
|
+
"src/vite",
|
|
108
|
+
"!dist/**/*.test.js",
|
|
109
|
+
"!dist/**/test.js"
|
|
110
|
+
]
|
|
111
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
FastifyPluginCallback,
|
|
3
|
+
FastifyRequest,
|
|
4
|
+
FastifyReply
|
|
5
|
+
} from 'fastify'
|
|
6
|
+
import fastifyStatic from 'fastify-static'
|
|
7
|
+
import { readFileSync } from 'fs'
|
|
8
|
+
// import { injectSsrContext } from '../helpers/ssr.js'
|
|
9
|
+
import type { ViteDevServer } from 'vite'
|
|
10
|
+
import type { SsrFunction } from '../../vitrify-config.js'
|
|
11
|
+
|
|
12
|
+
export interface FastifySsrOptions {
|
|
13
|
+
baseUrl?: string
|
|
14
|
+
provide?: (
|
|
15
|
+
req: FastifyRequest,
|
|
16
|
+
res: FastifyReply
|
|
17
|
+
) => Promise<Record<string, unknown>>
|
|
18
|
+
vite?: ViteDevServer
|
|
19
|
+
cliDir?: URL
|
|
20
|
+
appDir?: URL
|
|
21
|
+
productName?: string
|
|
22
|
+
ssrFunctions?: SsrFunction[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const fastifySsrPlugin: FastifyPluginCallback<FastifySsrOptions> = async (
|
|
26
|
+
fastify,
|
|
27
|
+
options,
|
|
28
|
+
done
|
|
29
|
+
) => {
|
|
30
|
+
if (import.meta.env.MODE === 'development') {
|
|
31
|
+
if (!options.vite) throw new Error('Option vite cannot be undefined')
|
|
32
|
+
const middie = (await import('middie')).default
|
|
33
|
+
await fastify.register(middie)
|
|
34
|
+
fastify.use(options.vite.middlewares)
|
|
35
|
+
|
|
36
|
+
fastify.get('*', async (req, res) => {
|
|
37
|
+
try {
|
|
38
|
+
// const url = req.originalUrl
|
|
39
|
+
const url = req.raw.url
|
|
40
|
+
const ssrContext = {
|
|
41
|
+
req,
|
|
42
|
+
res
|
|
43
|
+
}
|
|
44
|
+
// always read fresh template in dev
|
|
45
|
+
// template = readFileSync(resolve('index.html'), 'utf-8')
|
|
46
|
+
const template = readFileSync(
|
|
47
|
+
new URL('index.html', options.cliDir)
|
|
48
|
+
).toString()
|
|
49
|
+
|
|
50
|
+
// template = await vite.transformIndexHtml(url, template)
|
|
51
|
+
const entryUrl = new URL('ssr/entry-server.ts', options.cliDir).pathname
|
|
52
|
+
const render = (await options.vite!.ssrLoadModule(entryUrl)).render
|
|
53
|
+
let manifest
|
|
54
|
+
// TODO: https://github.com/vitejs/vite/issues/2282
|
|
55
|
+
try {
|
|
56
|
+
manifest = {}
|
|
57
|
+
} catch (e) {
|
|
58
|
+
manifest = {}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const [appHtml, preloadLinks] = await render(url, manifest, ssrContext)
|
|
62
|
+
const html = template
|
|
63
|
+
.replace(`<!--preload-links-->`, preloadLinks)
|
|
64
|
+
.replace(`<!--app-html-->`, appHtml)
|
|
65
|
+
.replace('<!--product-name-->', options.productName || 'Product name')
|
|
66
|
+
|
|
67
|
+
res.code(200)
|
|
68
|
+
res.type('text/html')
|
|
69
|
+
res.send(html)
|
|
70
|
+
// res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
|
|
71
|
+
} catch (e: any) {
|
|
72
|
+
console.error(e.stack)
|
|
73
|
+
options.vite && options.vite.ssrFixStacktrace(e)
|
|
74
|
+
res.code(500)
|
|
75
|
+
res.send(e.stack)
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
} else {
|
|
79
|
+
options.baseUrl = options.baseUrl || '/'
|
|
80
|
+
fastify.register(fastifyStatic, {
|
|
81
|
+
root: new URL('./dist/ssr/client', options.appDir).pathname,
|
|
82
|
+
wildcard: false,
|
|
83
|
+
index: false,
|
|
84
|
+
prefix: options.baseUrl
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
fastify.get(`${options.baseUrl}*`, async (req, res) => {
|
|
88
|
+
const url = req.raw.url
|
|
89
|
+
const provide = options.provide ? await options.provide(req, res) : {}
|
|
90
|
+
const ssrContext: Record<string, any> = {
|
|
91
|
+
req,
|
|
92
|
+
res,
|
|
93
|
+
provide
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// template = readFileSync(new URL('../client/index.html', import.meta.url).pathname).toString()
|
|
97
|
+
// manifest = JSON.parse(readFileSync(new URL('../client/ssr-manifest.json', import.meta.url)).toString())
|
|
98
|
+
// render = (await import(new URL('./entry-server.mjs', import.meta.url).pathname)).render
|
|
99
|
+
const template = readFileSync(
|
|
100
|
+
new URL('./dist/ssr/client/index.html', options.appDir).pathname
|
|
101
|
+
).toString()
|
|
102
|
+
const manifest = JSON.parse(
|
|
103
|
+
readFileSync(
|
|
104
|
+
new URL('./dist/ssr/client/ssr-manifest.json', options.appDir)
|
|
105
|
+
).toString()
|
|
106
|
+
)
|
|
107
|
+
const render = (
|
|
108
|
+
await import(
|
|
109
|
+
new URL('./dist/ssr/server/entry-server.mjs', options.appDir).pathname
|
|
110
|
+
)
|
|
111
|
+
).render
|
|
112
|
+
|
|
113
|
+
const [appHtml, preloadLinks] = await render(url, manifest, ssrContext)
|
|
114
|
+
|
|
115
|
+
if (!ssrContext.initialState) ssrContext.initialState = {}
|
|
116
|
+
ssrContext.initialState.provide = provide
|
|
117
|
+
|
|
118
|
+
let html = template
|
|
119
|
+
.replace(`<!--preload-links-->`, preloadLinks)
|
|
120
|
+
.replace(`<!--app-html-->`, appHtml)
|
|
121
|
+
|
|
122
|
+
if (options.ssrFunctions?.length) {
|
|
123
|
+
for (const ssrFunction of options.ssrFunctions) {
|
|
124
|
+
html = ssrFunction(html, ssrContext)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
res.code(200)
|
|
129
|
+
res.type('text/html')
|
|
130
|
+
res.send(html)
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
done()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export { fastifySsrPlugin }
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { promises as fs } from 'fs'
|
|
2
|
+
import { routesToPaths } from '../../helpers/routes.js'
|
|
3
|
+
import type { SsrFunction } from '../../vitrify-config.js'
|
|
4
|
+
|
|
5
|
+
export const prerender = async ({
|
|
6
|
+
outDir,
|
|
7
|
+
templatePath,
|
|
8
|
+
manifestPath,
|
|
9
|
+
entryServerPath,
|
|
10
|
+
ssrFunctions
|
|
11
|
+
}: {
|
|
12
|
+
outDir: string
|
|
13
|
+
templatePath: string
|
|
14
|
+
manifestPath: string
|
|
15
|
+
entryServerPath: string
|
|
16
|
+
ssrFunctions: SsrFunction[]
|
|
17
|
+
}) => {
|
|
18
|
+
const promises = []
|
|
19
|
+
const template = (await fs.readFile(templatePath)).toString()
|
|
20
|
+
const manifest = await fs.readFile(manifestPath)
|
|
21
|
+
const { render, getRoutes } = await import(entryServerPath)
|
|
22
|
+
const routes = await getRoutes()
|
|
23
|
+
const paths = routesToPaths(routes).filter(
|
|
24
|
+
(i) => !i.includes(':') && !i.includes('*')
|
|
25
|
+
)
|
|
26
|
+
for (const url of paths) {
|
|
27
|
+
const filename =
|
|
28
|
+
(url.endsWith('/') ? 'index' : url.replace(/^\//g, '')) + '.html'
|
|
29
|
+
console.log(`Generating ${filename}`)
|
|
30
|
+
const ssrContext = {
|
|
31
|
+
req: { headers: {}, url },
|
|
32
|
+
res: {}
|
|
33
|
+
}
|
|
34
|
+
const [appHtml, preloadLinks] = await render(url, manifest, ssrContext)
|
|
35
|
+
|
|
36
|
+
let html = template
|
|
37
|
+
.replace(`<!--preload-links-->`, preloadLinks)
|
|
38
|
+
.replace(`<!--app-html-->`, appHtml)
|
|
39
|
+
|
|
40
|
+
if (ssrFunctions?.length) {
|
|
41
|
+
for (const ssrFunction of ssrFunctions) {
|
|
42
|
+
html = ssrFunction(html, ssrContext)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
promises.push(fs.writeFile(outDir + filename, html, 'utf-8'))
|
|
47
|
+
}
|
|
48
|
+
return Promise.all(promises)
|
|
49
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify'
|
|
2
|
+
import fastify from 'fastify'
|
|
3
|
+
import type { SsrFunction } from '../../vitrify-config.js'
|
|
4
|
+
// import { setup } from 'virtual:fastify-setup'
|
|
5
|
+
import { fastifySsrPlugin } from './fastify-ssr-plugin.js'
|
|
6
|
+
// import { getPkgJsonDir } from '../app-urls.js'
|
|
7
|
+
|
|
8
|
+
export const createApp = ({
|
|
9
|
+
setup,
|
|
10
|
+
appDir,
|
|
11
|
+
baseUrl,
|
|
12
|
+
ssrFunctions
|
|
13
|
+
}: {
|
|
14
|
+
setup: (fastify: FastifyInstance) => any
|
|
15
|
+
appDir: URL
|
|
16
|
+
baseUrl?: string
|
|
17
|
+
ssrFunctions?: SsrFunction[]
|
|
18
|
+
}) => {
|
|
19
|
+
const app = fastify({
|
|
20
|
+
logger: true
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
app.register(fastifySsrPlugin, {
|
|
24
|
+
baseUrl,
|
|
25
|
+
appDir,
|
|
26
|
+
ssrFunctions
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
setup(app)
|
|
30
|
+
|
|
31
|
+
return app
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// const app = createApp({
|
|
35
|
+
// setup
|
|
36
|
+
// })
|
|
37
|
+
|
|
38
|
+
// app.listen(process.env.PORT || 3000, process.env.HOST || '127.0.0.1')
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title><!--product-name--></title>
|
|
7
|
+
<!--preload-links-->
|
|
8
|
+
</head>
|
|
9
|
+
|
|
10
|
+
<body>
|
|
11
|
+
<!-- Do not add whitespace or newlines to #app, this will cause hydration errors -->
|
|
12
|
+
<div id="app"><!--app-html--></div>
|
|
13
|
+
<!--entry-script-->
|
|
14
|
+
<!--initial-state-->
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import App from 'src/App.vue'
|
|
2
|
+
import createRouter from 'src/router'
|
|
3
|
+
import {
|
|
4
|
+
createSSRApp,
|
|
5
|
+
createApp as createVueApp,
|
|
6
|
+
h,
|
|
7
|
+
onMounted,
|
|
8
|
+
getCurrentInstance
|
|
9
|
+
} from 'vue'
|
|
10
|
+
// import { Quasar, useQuasar } from 'quasar'
|
|
11
|
+
// import quasarPlugins from 'virtual:quasar-plugins'
|
|
12
|
+
// import bootFunctions from 'virtual:quasar-boot'
|
|
13
|
+
import bootFunctions from 'virtual:boot-functions'
|
|
14
|
+
import onMountedHooks from 'virtual:on-mounted-hooks'
|
|
15
|
+
// import 'virtual:quasar-extras'
|
|
16
|
+
// import * as directives from 'quasar/directives'
|
|
17
|
+
import routes from 'src/router/routes'
|
|
18
|
+
import 'virtual:global-css'
|
|
19
|
+
import * as staticImports from 'virtual:static-imports'
|
|
20
|
+
|
|
21
|
+
interface ssrContext {
|
|
22
|
+
ssr: boolean
|
|
23
|
+
provide?: Record<string, unknown>
|
|
24
|
+
[key: string]: unknown
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function createApp(
|
|
28
|
+
ssr?: 'client' | 'server',
|
|
29
|
+
ssrContext?: ssrContext
|
|
30
|
+
) {
|
|
31
|
+
let app
|
|
32
|
+
const RootComponent = {
|
|
33
|
+
name: 'AppWrapper',
|
|
34
|
+
setup(props) {
|
|
35
|
+
const instance = getCurrentInstance()
|
|
36
|
+
|
|
37
|
+
onMounted(async () => {
|
|
38
|
+
for (let fn of onMountedHooks) {
|
|
39
|
+
await fn(instance, staticImports)
|
|
40
|
+
}
|
|
41
|
+
// onAppMounted()
|
|
42
|
+
// const { proxy: { $q } } = getCurrentInstance()
|
|
43
|
+
// $q.onSSRHydrated !== void 0 && $q.onSSRHydrated()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
return () => h(App, props)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (ssr) {
|
|
50
|
+
app = createSSRApp(RootComponent)
|
|
51
|
+
} else {
|
|
52
|
+
app = createVueApp(RootComponent)
|
|
53
|
+
}
|
|
54
|
+
const router = createRouter()
|
|
55
|
+
app.use(router)
|
|
56
|
+
|
|
57
|
+
// Workaround to fix hydration errors when serving html files directly
|
|
58
|
+
router.beforeEach((to, from, next) => {
|
|
59
|
+
if (to.path.endsWith('.html')) {
|
|
60
|
+
next({ path: to.path.replace('.html', '') })
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
next()
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
// app.use(Quasar, {
|
|
67
|
+
// plugins: quasarPlugins,
|
|
68
|
+
// directives
|
|
69
|
+
// }, ssrContext)
|
|
70
|
+
|
|
71
|
+
let provide: Record<string, unknown> = {}
|
|
72
|
+
if (import.meta.env.SSR) {
|
|
73
|
+
if (ssrContext?.provide) {
|
|
74
|
+
provide = ssrContext?.provide
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
// @ts-ignore
|
|
78
|
+
provide = window.__INITIAL_STATE__?.provide
|
|
79
|
+
}
|
|
80
|
+
for (let key in provide) {
|
|
81
|
+
app.provide(key, provide[key])
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (let fn of bootFunctions) {
|
|
85
|
+
await fn({ app, ssrContext, staticImports })
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { app, router, routes }
|
|
89
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createApp } from '../main.js'
|
|
2
|
+
|
|
3
|
+
// const { app, router } = createApp()
|
|
4
|
+
createApp('client').then(({ app, router }) => {
|
|
5
|
+
// wait until router is ready before mounting to ensure hydration match
|
|
6
|
+
router.isReady().then(() => {
|
|
7
|
+
app.mount('#app')
|
|
8
|
+
})
|
|
9
|
+
})
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { createApp } from '../main.js'
|
|
2
|
+
import { renderToString } from '@vue/server-renderer'
|
|
3
|
+
// import * as ApolloSSR from '@vue/apollo-ssr'
|
|
4
|
+
// import { ApolloClients } from '@vue/apollo-composable'
|
|
5
|
+
// import serialize from 'serialize-javascript'
|
|
6
|
+
|
|
7
|
+
const initializeApp = async (url, ssrContext) => {
|
|
8
|
+
const onRenderedList = []
|
|
9
|
+
Object.assign(ssrContext, {
|
|
10
|
+
_modules: new Set(),
|
|
11
|
+
_meta: {},
|
|
12
|
+
onRendered: (fn) => {
|
|
13
|
+
onRenderedList.push(fn)
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const { app, router, routes } = await createApp('server', ssrContext)
|
|
18
|
+
// set the router to the desired URL before rendering
|
|
19
|
+
|
|
20
|
+
router.push({ path: url })
|
|
21
|
+
ssrContext.initialState = {}
|
|
22
|
+
|
|
23
|
+
onRenderedList.forEach((fn) => {
|
|
24
|
+
fn()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
await router.isReady()
|
|
28
|
+
|
|
29
|
+
return { app, router, routes }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const getRoutes = async () =>
|
|
33
|
+
(
|
|
34
|
+
await initializeApp('/', {
|
|
35
|
+
ssr: false,
|
|
36
|
+
req: { headers: {}, url: '/' },
|
|
37
|
+
res: {}
|
|
38
|
+
})
|
|
39
|
+
).routes
|
|
40
|
+
|
|
41
|
+
export async function render(url, manifest, ssrContext) {
|
|
42
|
+
const { app, router } = await initializeApp(url, ssrContext)
|
|
43
|
+
|
|
44
|
+
// passing SSR context object which will be available via useSSRContext()
|
|
45
|
+
// @vitejs/plugin-vue injects code into a component's setup() that registers
|
|
46
|
+
// itself on ctx.modules. After the render, ctx.modules would contain all the
|
|
47
|
+
// components that have been instantiated during this render call.
|
|
48
|
+
const ctx = {
|
|
49
|
+
__qMetaList: []
|
|
50
|
+
}
|
|
51
|
+
let html = await renderToString(app, ctx)
|
|
52
|
+
// html = injectSsrContext(html, ssrContext)
|
|
53
|
+
// the SSR manifest generated by Vite contains module -> chunk/asset mapping
|
|
54
|
+
// which we can then use to determine what files need to be preloaded for this
|
|
55
|
+
// request.
|
|
56
|
+
const preloadLinks = renderPreloadLinks(ctx.modules, manifest)
|
|
57
|
+
|
|
58
|
+
return [html, preloadLinks]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function renderPreloadLinks(modules, manifest) {
|
|
62
|
+
let links = ''
|
|
63
|
+
const seen = new Set()
|
|
64
|
+
modules.forEach((id) => {
|
|
65
|
+
const files = manifest[id]
|
|
66
|
+
if (files) {
|
|
67
|
+
files.forEach((file) => {
|
|
68
|
+
if (!seen.has(file)) {
|
|
69
|
+
seen.add(file)
|
|
70
|
+
links += renderPreloadLink(file)
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
return links
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function renderPreloadLink(file) {
|
|
79
|
+
if (file.endsWith('.js')) {
|
|
80
|
+
return `<link rel="modulepreload" crossorigin href="${file}">`
|
|
81
|
+
} else if (file.endsWith('.css')) {
|
|
82
|
+
return `<link rel="stylesheet" href="${file}">`
|
|
83
|
+
} else if (file.endsWith('.woff')) {
|
|
84
|
+
return ` <link rel="preload" href="${file}" as="font" type="font/woff" crossorigin>`
|
|
85
|
+
} else if (file.endsWith('.woff2')) {
|
|
86
|
+
return ` <link rel="preload" href="${file}" as="font" type="font/woff2" crossorigin>`
|
|
87
|
+
} else if (file.endsWith('.gif')) {
|
|
88
|
+
return ` <link rel="preload" href="${file}" as="image" type="image/gif">`
|
|
89
|
+
} else if (file.endsWith('.jpg') || file.endsWith('.jpeg')) {
|
|
90
|
+
return ` <link rel="preload" href="${file}" as="image" type="image/jpeg">`
|
|
91
|
+
} else if (file.endsWith('.png')) {
|
|
92
|
+
return ` <link rel="preload" href="${file}" as="image" type="image/png">`
|
|
93
|
+
} else {
|
|
94
|
+
// TODO
|
|
95
|
+
return ''
|
|
96
|
+
}
|
|
97
|
+
}
|