vitrify 0.19.1 → 0.21.0
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/dist/app-urls.js +1 -1
- package/dist/bin/cli.js +11 -4
- package/dist/frameworks/vue/fastify-ssr-plugin.js +92 -71
- package/dist/frameworks/vue/prerender.js +12 -18
- package/dist/index.js +4 -2
- package/dist/types/app-urls.d.ts +1 -1
- package/dist/types/frameworks/vue/fastify-ssr-plugin.d.ts +28 -4
- package/dist/types/frameworks/vue/prerender.d.ts +6 -4
- package/package.json +18 -18
- package/src/node/app-urls.ts +2 -2
- package/src/node/bin/cli.ts +13 -6
- package/src/node/frameworks/vue/fastify-ssr-plugin.ts +150 -102
- package/src/node/frameworks/vue/prerender.ts +20 -25
- package/src/node/index.ts +6 -2
- package/src/vite/vue/csr/app.ts +0 -1
- package/src/vite/vue/main.ts +1 -2
- package/src/vite/vue/ssr/entry-server.ts +29 -9
- package/src/vite/vue/ssr/fastify-ssr-plugin.ts +6 -2
package/dist/app-urls.js
CHANGED
|
@@ -17,7 +17,7 @@ export const getPkgJsonDir = (dir) => {
|
|
|
17
17
|
}
|
|
18
18
|
return getPkgJsonDir(new URL('..', dir));
|
|
19
19
|
};
|
|
20
|
-
export const getAppDir = () => getPkgJsonDir(new URL(`file://${process.cwd()}/`));
|
|
20
|
+
export const getAppDir = (dir) => getPkgJsonDir(dir ?? new URL(`file://${process.cwd()}/`));
|
|
21
21
|
export const getCliDir = () => getPkgJsonDir(new URL('./', import.meta.url));
|
|
22
22
|
export const getCliViteDir = (cliDir) => new URL('src/vite/', cliDir);
|
|
23
23
|
export const getSrcDir = (appDir) => new URL('src/', appDir);
|
package/dist/bin/cli.js
CHANGED
|
@@ -5,6 +5,7 @@ import { getAppDir, parsePath } from '../app-urls.js';
|
|
|
5
5
|
import { printHttpServerUrls, exitLogs } from '../helpers/logger.js';
|
|
6
6
|
import { build as esbuild } from 'esbuild';
|
|
7
7
|
import { readdir } from 'fs/promises';
|
|
8
|
+
import { loadSSRAssets } from '../frameworks/vue/fastify-ssr-plugin.js';
|
|
8
9
|
const cli = cac('vitrify');
|
|
9
10
|
cli
|
|
10
11
|
.command('build')
|
|
@@ -73,12 +74,18 @@ cli
|
|
|
73
74
|
...args,
|
|
74
75
|
outDir: fileURLToPath(new URL('ssr/server/', baseOutDir))
|
|
75
76
|
});
|
|
76
|
-
({ prerender
|
|
77
|
+
({ prerender } = await import(new URL('ssr/server/prerender.mjs', baseOutDir).pathname));
|
|
78
|
+
const { template, manifest, render, getRoutes, onRendered } = await loadSSRAssets({
|
|
79
|
+
mode: 'ssg',
|
|
80
|
+
distDir: baseOutDir
|
|
81
|
+
});
|
|
82
|
+
const routes = await getRoutes();
|
|
77
83
|
prerender({
|
|
78
84
|
outDir: fileURLToPath(new URL('static/', baseOutDir)),
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
template,
|
|
86
|
+
manifest,
|
|
87
|
+
render,
|
|
88
|
+
routes,
|
|
82
89
|
onRendered
|
|
83
90
|
});
|
|
84
91
|
break;
|
|
@@ -2,6 +2,7 @@ import fastifyStatic from '@fastify/static';
|
|
|
2
2
|
import { readFileSync } from 'fs';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { addOrReplaceAppDiv, appendToBody, appendToHead } from '../../helpers/utils.js';
|
|
5
|
+
import { getAppDir } from '../../app-urls.js';
|
|
5
6
|
const fastifySsrPlugin = async (fastify, options) => {
|
|
6
7
|
options.baseUrl = options.baseUrl || '/';
|
|
7
8
|
options.mode = options.mode || process.env.MODE || import.meta.env.MODE;
|
|
@@ -32,11 +33,6 @@ const fastifySsrPlugin = async (fastify, options) => {
|
|
|
32
33
|
try {
|
|
33
34
|
const url = req.raw.url?.replace(options.baseUrl, '/');
|
|
34
35
|
const provide = options.provide ? await options.provide(req, res) : {};
|
|
35
|
-
const ssrContext = {
|
|
36
|
-
req,
|
|
37
|
-
res,
|
|
38
|
-
provide
|
|
39
|
-
};
|
|
40
36
|
let template = readFileSync(new URL('index.html', frameworkDir)).toString();
|
|
41
37
|
template = await vite.transformIndexHtml(url, template);
|
|
42
38
|
const entryUrl = fileURLToPath(new URL('ssr/entry-server.ts', frameworkDir));
|
|
@@ -49,39 +45,16 @@ const fastifySsrPlugin = async (fastify, options) => {
|
|
|
49
45
|
catch (e) {
|
|
50
46
|
manifest = {};
|
|
51
47
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<script>
|
|
63
|
-
__INITIAL_STATE__ = '${JSON.stringify(ssrContext.initialState)}'
|
|
64
|
-
</script>`;
|
|
65
|
-
const renderHtml = (html) => {
|
|
66
|
-
return appendToHead(preloadLinks, appendToBody(initialStateScript, addOrReplaceAppDiv(appHtml, html)));
|
|
67
|
-
};
|
|
68
|
-
let html = renderHtml(template);
|
|
69
|
-
// let html = template
|
|
70
|
-
// .replace(`<!--app-html-->`, appHtml)
|
|
71
|
-
// .replace('<!--product-name-->', options.productName || 'Product name')
|
|
72
|
-
// // .replace('<!--dev-ssr-css-->', css)
|
|
73
|
-
// .replace(
|
|
74
|
-
// '<!--initial-state-->',
|
|
75
|
-
// `<script>
|
|
76
|
-
// __INITIAL_STATE__ = '${JSON.stringify(ssrContext.initialState)}'
|
|
77
|
-
// </script>`
|
|
78
|
-
// )
|
|
79
|
-
// html = appendToHead(preloadLinks, html)
|
|
80
|
-
if (options.onRendered?.length) {
|
|
81
|
-
for (const ssrFunction of options.onRendered) {
|
|
82
|
-
html = ssrFunction(html, ssrContext);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
48
|
+
const html = await renderHtml({
|
|
49
|
+
request: req,
|
|
50
|
+
reply: res,
|
|
51
|
+
url: url ?? '/',
|
|
52
|
+
provide,
|
|
53
|
+
onRendered: options.onRendered,
|
|
54
|
+
template,
|
|
55
|
+
manifest,
|
|
56
|
+
render
|
|
57
|
+
});
|
|
85
58
|
res.code(200);
|
|
86
59
|
res.type('text/html');
|
|
87
60
|
res.send(html);
|
|
@@ -108,42 +81,90 @@ const fastifySsrPlugin = async (fastify, options) => {
|
|
|
108
81
|
fastify.get(`${options.baseUrl}*`, async (req, res) => {
|
|
109
82
|
const url = req.raw.url?.replace(options.baseUrl, '/');
|
|
110
83
|
const provide = options.provide ? await options.provide(req, res) : {};
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
<script>
|
|
125
|
-
__INITIAL_STATE__ = '${JSON.stringify(ssrContext.initialState)}'
|
|
126
|
-
</script>`;
|
|
127
|
-
const renderHtml = (html) => {
|
|
128
|
-
return appendToHead(preloadLinks, appendToBody(initialStateScript, addOrReplaceAppDiv(appHtml, html)));
|
|
129
|
-
};
|
|
130
|
-
let html = renderHtml(template);
|
|
131
|
-
// let html = template.replace(`<!--app-html-->`, appHtml).replace(
|
|
132
|
-
// '<!--initial-state-->',
|
|
133
|
-
// `<script>
|
|
134
|
-
// __INITIAL_STATE__ = '${JSON.stringify(ssrContext.initialState)}'
|
|
135
|
-
// </script>`
|
|
136
|
-
// )
|
|
137
|
-
// html = appendToHead(preloadLinks, html)
|
|
138
|
-
if (options.onRendered?.length) {
|
|
139
|
-
for (const ssrFunction of options.onRendered) {
|
|
140
|
-
html = ssrFunction(html, ssrContext);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
84
|
+
const { template, manifest, render, onRendered } = await loadSSRAssets({
|
|
85
|
+
distDir: new URL('./dist/', options.appDir)
|
|
86
|
+
});
|
|
87
|
+
const html = await renderHtml({
|
|
88
|
+
request: req,
|
|
89
|
+
reply: res,
|
|
90
|
+
url: url ?? '/',
|
|
91
|
+
provide,
|
|
92
|
+
onRendered,
|
|
93
|
+
template,
|
|
94
|
+
manifest,
|
|
95
|
+
render
|
|
96
|
+
});
|
|
143
97
|
res.code(200);
|
|
144
98
|
res.type('text/html');
|
|
145
99
|
res.send(html);
|
|
146
100
|
});
|
|
147
101
|
}
|
|
148
102
|
};
|
|
149
|
-
|
|
103
|
+
const renderTemplate = ({ template, initialStateScript, appHtml, preloadLinks }) => {
|
|
104
|
+
return appendToHead(preloadLinks, appendToBody(initialStateScript, addOrReplaceAppDiv(appHtml, template)));
|
|
105
|
+
};
|
|
106
|
+
const renderHtml = async (options) => {
|
|
107
|
+
const ssrContext = {
|
|
108
|
+
req: options.request,
|
|
109
|
+
res: options.reply,
|
|
110
|
+
provide: options.provide
|
|
111
|
+
};
|
|
112
|
+
const onRendered = options.onRendered ?? [];
|
|
113
|
+
const [appHtml, preloadLinks] = await options.render(options.url, options.manifest, ssrContext);
|
|
114
|
+
if (!ssrContext.initialState)
|
|
115
|
+
ssrContext.initialState = {};
|
|
116
|
+
ssrContext.initialState.provide = options.provide;
|
|
117
|
+
const initialStateScript = `
|
|
118
|
+
<script>
|
|
119
|
+
__INITIAL_STATE__ = '${JSON.stringify(ssrContext.initialState)}'
|
|
120
|
+
</script>`;
|
|
121
|
+
let html = renderTemplate({
|
|
122
|
+
template: options.template,
|
|
123
|
+
appHtml,
|
|
124
|
+
initialStateScript,
|
|
125
|
+
preloadLinks
|
|
126
|
+
});
|
|
127
|
+
if (onRendered?.length) {
|
|
128
|
+
for (const ssrFunction of onRendered) {
|
|
129
|
+
html = ssrFunction(html, ssrContext);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return html;
|
|
133
|
+
};
|
|
134
|
+
const loadSSRAssets = async ({ mode, distDir } = {
|
|
135
|
+
mode: 'ssr'
|
|
136
|
+
}) => {
|
|
137
|
+
const appDir = getAppDir(new URL(import.meta.url));
|
|
138
|
+
const baseOutDir = distDir || new URL('dist/', appDir);
|
|
139
|
+
let templatePath, manifestPath, entryServerPath;
|
|
140
|
+
const onRenderedPath = fileURLToPath(new URL('ssr/server/virtual_vitrify-hooks.mjs', baseOutDir));
|
|
141
|
+
if (mode === 'ssg') {
|
|
142
|
+
templatePath = fileURLToPath(new URL('static/index.html', baseOutDir));
|
|
143
|
+
manifestPath = fileURLToPath(new URL('static/.vite/ssr-manifest.json', baseOutDir));
|
|
144
|
+
entryServerPath = fileURLToPath(new URL('ssr/server/entry-server.mjs', baseOutDir));
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
templatePath = fileURLToPath(new URL('ssr/client/index.html', baseOutDir));
|
|
148
|
+
manifestPath = fileURLToPath(new URL('ssr/client/.vite/ssr-manifest.json', baseOutDir));
|
|
149
|
+
entryServerPath = fileURLToPath(new URL('ssr/server/entry-server.mjs', baseOutDir));
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const template = readFileSync(templatePath).toString();
|
|
153
|
+
const manifest = JSON.parse(readFileSync(manifestPath).toString());
|
|
154
|
+
const entryServer = await import(entryServerPath);
|
|
155
|
+
const { render, getRoutes } = entryServer;
|
|
156
|
+
const onRendered = (await import(onRenderedPath)).onRendered;
|
|
157
|
+
return {
|
|
158
|
+
template,
|
|
159
|
+
manifest,
|
|
160
|
+
render,
|
|
161
|
+
getRoutes,
|
|
162
|
+
onRendered
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
console.error(e);
|
|
167
|
+
throw new Error('Unable to load SSR asset files');
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
export { fastifySsrPlugin, renderHtml, loadSSRAssets };
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import { existsSync, promises as fs, mkdirSync } from 'fs';
|
|
2
2
|
import { routesToPaths } from '../../helpers/routes.js';
|
|
3
|
-
import {
|
|
4
|
-
export const prerender = async ({ outDir,
|
|
3
|
+
import { renderHtml } from './fastify-ssr-plugin.js';
|
|
4
|
+
export const prerender = async ({ outDir, template, manifest, render, routes, onRendered }) => {
|
|
5
5
|
const promises = [];
|
|
6
|
-
const template = (await fs.readFile(templatePath)).toString();
|
|
7
|
-
const manifest = JSON.parse((await fs.readFile(manifestPath)).toString());
|
|
8
|
-
const { render, getRoutes } = await import(entryServerPath);
|
|
9
|
-
const routes = await getRoutes();
|
|
10
6
|
const paths = routesToPaths(routes).filter((i) => !i.includes(':') && !i.includes('*'));
|
|
11
7
|
const beasties = new (await import('beasties')).default({
|
|
12
8
|
path: outDir,
|
|
@@ -22,18 +18,16 @@ export const prerender = async ({ outDir, templatePath, manifestPath, entryServe
|
|
|
22
18
|
}
|
|
23
19
|
const filename = (url.endsWith('/') ? 'index' : url.replace(/^\//g, '')) + '.html';
|
|
24
20
|
console.log(`Generating ${filename}`);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
}
|
|
21
|
+
let html = await renderHtml({
|
|
22
|
+
url,
|
|
23
|
+
manifest,
|
|
24
|
+
provide: {},
|
|
25
|
+
render,
|
|
26
|
+
request: { headers: {}, url },
|
|
27
|
+
reply: {},
|
|
28
|
+
template,
|
|
29
|
+
onRendered
|
|
30
|
+
});
|
|
37
31
|
html = await beasties.process(html);
|
|
38
32
|
promises.push(fs.writeFile(outDir + filename, html, 'utf-8'));
|
|
39
33
|
}
|
package/dist/index.js
CHANGED
|
@@ -43,11 +43,12 @@ const manualChunkNames = [
|
|
|
43
43
|
];
|
|
44
44
|
const moduleChunks = {
|
|
45
45
|
vue: ['vue', '@vue', 'vue-router'],
|
|
46
|
-
quasar: ['quasar',
|
|
46
|
+
quasar: ['quasar'],
|
|
47
|
+
atQuasar: ['@quasar']
|
|
47
48
|
};
|
|
48
49
|
const manualChunksFn = (manualChunkList) => {
|
|
49
50
|
return (id) => {
|
|
50
|
-
const matchedModule = Object.entries(moduleChunks).find(([chunkName, moduleNames]) => moduleNames.some((moduleName) =>
|
|
51
|
+
const matchedModule = Object.entries(moduleChunks).find(([chunkName, moduleNames]) => moduleNames.some((moduleName) => new RegExp(`\/${moduleName}\/`).test(id)));
|
|
51
52
|
if (id.includes('vitrify/src/')) {
|
|
52
53
|
const name = id.split('/').at(-1)?.split('.').at(0);
|
|
53
54
|
if (name && manualChunkNames.includes(name))
|
|
@@ -528,6 +529,7 @@ export const baseConfig = async ({ ssr, appDir, publicDir, base = '/', command =
|
|
|
528
529
|
input: [
|
|
529
530
|
fileURLToPath(new URL('ssr/entry-server.ts', frameworkDir)),
|
|
530
531
|
fileURLToPath(new URL('ssr/prerender.ts', frameworkDir)),
|
|
532
|
+
fileURLToPath(new URL('ssr/fastify-ssr-plugin.ts', frameworkDir)),
|
|
531
533
|
fileURLToPath(new URL('ssr/server.ts', frameworkDir))
|
|
532
534
|
],
|
|
533
535
|
external,
|
package/dist/types/app-urls.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const resolve: (packageName: string, base: URL, counter?: number) => URL;
|
|
2
2
|
export declare const getPkgJsonDir: (dir: URL) => URL;
|
|
3
|
-
export declare const getAppDir: () => URL;
|
|
3
|
+
export declare const getAppDir: (dir?: URL) => URL;
|
|
4
4
|
export declare const getCliDir: () => URL;
|
|
5
5
|
export declare const getCliViteDir: (cliDir: URL) => URL;
|
|
6
6
|
export declare const getSrcDir: (appDir: URL) => URL;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { FastifyPluginAsync, FastifyRequest, FastifyReply } from 'fastify';
|
|
2
2
|
import type { ViteDevServer } from 'vite';
|
|
3
3
|
import type { OnRenderedHook } from '../../vitrify-config.js';
|
|
4
|
+
type ProvideFn = (req: FastifyRequest, res: FastifyReply) => Promise<Record<string, unknown | {
|
|
5
|
+
value: unknown;
|
|
6
|
+
}>>;
|
|
4
7
|
export interface FastifySsrOptions {
|
|
5
8
|
baseUrl?: string;
|
|
6
|
-
provide?:
|
|
7
|
-
value: unknown;
|
|
8
|
-
}>>;
|
|
9
|
+
provide?: ProvideFn;
|
|
9
10
|
vitrifyDir?: URL;
|
|
10
11
|
vite?: ViteDevServer;
|
|
11
12
|
onRendered?: OnRenderedHook[];
|
|
@@ -15,5 +16,28 @@ export interface FastifySsrOptions {
|
|
|
15
16
|
host?: string;
|
|
16
17
|
}
|
|
17
18
|
declare const fastifySsrPlugin: FastifyPluginAsync<FastifySsrOptions>;
|
|
18
|
-
|
|
19
|
+
declare const renderHtml: (options: {
|
|
20
|
+
url: string;
|
|
21
|
+
request: FastifyRequest | {
|
|
22
|
+
headers: Record<string, unknown>;
|
|
23
|
+
url: string;
|
|
24
|
+
};
|
|
25
|
+
reply: FastifyReply | Record<string, unknown>;
|
|
26
|
+
provide: Record<string, unknown>;
|
|
27
|
+
onRendered?: OnRenderedHook[];
|
|
28
|
+
template: string;
|
|
29
|
+
manifest: Record<string, unknown>;
|
|
30
|
+
render: any;
|
|
31
|
+
}) => Promise<string>;
|
|
32
|
+
declare const loadSSRAssets: ({ mode, distDir }?: {
|
|
33
|
+
mode?: "ssr" | "ssg";
|
|
34
|
+
distDir?: URL;
|
|
35
|
+
}) => Promise<{
|
|
36
|
+
template: string;
|
|
37
|
+
manifest: any;
|
|
38
|
+
render: any;
|
|
39
|
+
getRoutes: any;
|
|
40
|
+
onRendered: any;
|
|
41
|
+
}>;
|
|
42
|
+
export { fastifySsrPlugin, renderHtml, loadSSRAssets };
|
|
19
43
|
export type FastifySsrPlugin = typeof fastifySsrPlugin;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { OnRenderedHook } from 'src/node/vitrify-config.js';
|
|
2
|
-
|
|
2
|
+
import { type RouteRecordRaw } from 'vue-router';
|
|
3
|
+
export declare const prerender: ({ outDir, template, manifest, render, routes, onRendered }: {
|
|
3
4
|
outDir: string;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
template: string;
|
|
6
|
+
manifest: Record<string, unknown>;
|
|
7
|
+
render: unknown;
|
|
8
|
+
routes: RouteRecordRaw[];
|
|
7
9
|
onRendered: OnRenderedHook[];
|
|
8
10
|
}) => Promise<void[]>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitrify",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Stefan van Herwijnen",
|
|
6
6
|
"description": "Vite as your Full Stack development tool",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"@fastify/static": "^8.1.1",
|
|
53
53
|
"@unocss/core": "^66.0.0",
|
|
54
54
|
"@unocss/preset-uno": "^66.0.0",
|
|
55
|
-
"@unocss/preset-web-fonts": "66.1.0-beta.
|
|
55
|
+
"@unocss/preset-web-fonts": "66.1.0-beta.13",
|
|
56
56
|
"@unocss/preset-wind": "^66.0.0",
|
|
57
57
|
"@vitejs/plugin-vue": "^5.2.3",
|
|
58
58
|
"ajv": "^8.17.1",
|
|
@@ -60,23 +60,23 @@
|
|
|
60
60
|
"cac": "^6.7.14",
|
|
61
61
|
"chalk": "^5.4.1",
|
|
62
62
|
"cross-env": "^7.0.3",
|
|
63
|
-
"esbuild": "^0.25.
|
|
64
|
-
"fastify": "^5.
|
|
65
|
-
"glob": "^11.0.
|
|
66
|
-
"happy-dom": "^17.4.
|
|
63
|
+
"esbuild": "^0.25.3",
|
|
64
|
+
"fastify": "^5.3.2",
|
|
65
|
+
"glob": "^11.0.2",
|
|
66
|
+
"happy-dom": "^17.4.6",
|
|
67
67
|
"is-port-reachable": "^4.0.0",
|
|
68
68
|
"magic-string": "^0.30.17",
|
|
69
69
|
"merge-deep": "^3.0.3",
|
|
70
70
|
"readline": "^1.3.0",
|
|
71
71
|
"rollup-plugin-visualizer": "^5.14.0",
|
|
72
|
-
"sass": "1.
|
|
72
|
+
"sass": "1.87.0",
|
|
73
73
|
"ts-node": "^10.9.2",
|
|
74
74
|
"unocss": "^66.0.0",
|
|
75
|
-
"unplugin-vue-components": "^28.
|
|
76
|
-
"vite": "^6.
|
|
75
|
+
"unplugin-vue-components": "^28.5.0",
|
|
76
|
+
"vite": "^6.3.4",
|
|
77
77
|
"vite-plugin-pwa": "^1.0.0",
|
|
78
78
|
"vitefu": "^1.0.6",
|
|
79
|
-
"vitest": "^3.1.
|
|
79
|
+
"vitest": "^3.1.2",
|
|
80
80
|
"workbox-window": "^7.3.0"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
@@ -87,25 +87,25 @@
|
|
|
87
87
|
"@types/connect": "^3.4.38",
|
|
88
88
|
"@types/glob": "^8.1.0",
|
|
89
89
|
"@types/merge-deep": "^3.0.3",
|
|
90
|
-
"@types/node": "^22.
|
|
90
|
+
"@types/node": "^22.15.3",
|
|
91
91
|
"@types/ws": "^8.18.1",
|
|
92
92
|
"@unocss/preset-icons": "^66.0.0",
|
|
93
93
|
"@vue/runtime-core": "^3.5.13",
|
|
94
|
-
"beasties": "^0.3.
|
|
94
|
+
"beasties": "^0.3.3",
|
|
95
95
|
"css": "^3.0.0",
|
|
96
96
|
"css-to-tailwind-translator": "^1.2.8",
|
|
97
97
|
"quasar": "^2.18.1",
|
|
98
|
-
"rollup": "^4.
|
|
98
|
+
"rollup": "^4.40.1",
|
|
99
99
|
"typescript": "^5.8.3",
|
|
100
100
|
"vue": "^3.5.13",
|
|
101
|
-
"vue-router": "^4.5.
|
|
101
|
+
"vue-router": "^4.5.1"
|
|
102
102
|
},
|
|
103
103
|
"peerDependencies": {
|
|
104
|
-
"@fastify/static": "^8.1.
|
|
105
|
-
"fastify": "^5.2
|
|
106
|
-
"quasar": "^2.
|
|
104
|
+
"@fastify/static": "^8.1.1",
|
|
105
|
+
"fastify": "^5.3.2",
|
|
106
|
+
"quasar": "^2.18.1",
|
|
107
107
|
"vue": "^3.5.13",
|
|
108
|
-
"vue-router": "^4.5.
|
|
108
|
+
"vue-router": "^4.5.1"
|
|
109
109
|
},
|
|
110
110
|
"publishConfig": {
|
|
111
111
|
"access": "public",
|
package/src/node/app-urls.ts
CHANGED
|
@@ -19,8 +19,8 @@ export const getPkgJsonDir = (dir: URL): URL => {
|
|
|
19
19
|
}
|
|
20
20
|
return getPkgJsonDir(new URL('..', dir))
|
|
21
21
|
}
|
|
22
|
-
export const getAppDir = () =>
|
|
23
|
-
getPkgJsonDir(new URL(`file://${process.cwd()}/`))
|
|
22
|
+
export const getAppDir = (dir?: URL) =>
|
|
23
|
+
getPkgJsonDir(dir ?? new URL(`file://${process.cwd()}/`))
|
|
24
24
|
export const getCliDir = () => getPkgJsonDir(new URL('./', import.meta.url))
|
|
25
25
|
export const getCliViteDir = (cliDir: URL) => new URL('src/vite/', cliDir)
|
|
26
26
|
export const getSrcDir = (appDir: URL) => new URL('src/', appDir)
|
package/src/node/bin/cli.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { ResolvedConfig, ViteDevServer } from 'vite'
|
|
|
8
8
|
import type { Server } from 'net'
|
|
9
9
|
import type { FastifyInstance } from 'fastify'
|
|
10
10
|
import { readdir } from 'fs/promises'
|
|
11
|
+
import { loadSSRAssets } from '../frameworks/vue/fastify-ssr-plugin.js'
|
|
11
12
|
|
|
12
13
|
const cli = cac('vitrify')
|
|
13
14
|
cli
|
|
@@ -85,17 +86,23 @@ cli
|
|
|
85
86
|
...args,
|
|
86
87
|
outDir: fileURLToPath(new URL('ssr/server/', baseOutDir))
|
|
87
88
|
})
|
|
88
|
-
;({ prerender
|
|
89
|
+
;({ prerender } = await import(
|
|
89
90
|
new URL('ssr/server/prerender.mjs', baseOutDir).pathname
|
|
90
91
|
))
|
|
91
92
|
|
|
93
|
+
const { template, manifest, render, getRoutes, onRendered } =
|
|
94
|
+
await loadSSRAssets({
|
|
95
|
+
mode: 'ssg',
|
|
96
|
+
distDir: baseOutDir
|
|
97
|
+
})
|
|
98
|
+
const routes = await getRoutes()
|
|
99
|
+
|
|
92
100
|
prerender({
|
|
93
101
|
outDir: fileURLToPath(new URL('static/', baseOutDir)),
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
entryServerPath: new URL('ssr/server/entry-server.mjs', baseOutDir),
|
|
102
|
+
template,
|
|
103
|
+
manifest,
|
|
104
|
+
render,
|
|
105
|
+
routes,
|
|
99
106
|
onRendered
|
|
100
107
|
})
|
|
101
108
|
break
|
|
@@ -9,12 +9,16 @@ import {
|
|
|
9
9
|
} from '../../helpers/utils.js'
|
|
10
10
|
import type { ViteDevServer } from 'vite'
|
|
11
11
|
import type { OnRenderedHook } from '../../vitrify-config.js'
|
|
12
|
+
import { getAppDir } from '../../app-urls.js'
|
|
13
|
+
|
|
14
|
+
type ProvideFn = (
|
|
15
|
+
req: FastifyRequest,
|
|
16
|
+
res: FastifyReply
|
|
17
|
+
) => Promise<Record<string, unknown | { value: unknown }>>
|
|
18
|
+
|
|
12
19
|
export interface FastifySsrOptions {
|
|
13
20
|
baseUrl?: string
|
|
14
|
-
provide?:
|
|
15
|
-
req: FastifyRequest,
|
|
16
|
-
res: FastifyReply
|
|
17
|
-
) => Promise<Record<string, unknown | { value: unknown }>>
|
|
21
|
+
provide?: ProvideFn
|
|
18
22
|
vitrifyDir?: URL
|
|
19
23
|
vite?: ViteDevServer
|
|
20
24
|
// frameworkDir?: URL
|
|
@@ -66,12 +70,6 @@ const fastifySsrPlugin: FastifyPluginAsync<FastifySsrOptions> = async (
|
|
|
66
70
|
const url = req.raw.url?.replace(options.baseUrl!, '/')
|
|
67
71
|
const provide = options.provide ? await options.provide(req, res) : {}
|
|
68
72
|
|
|
69
|
-
const ssrContext: Record<string, any> = {
|
|
70
|
-
req,
|
|
71
|
-
res,
|
|
72
|
-
provide
|
|
73
|
-
}
|
|
74
|
-
|
|
75
73
|
let template = readFileSync(
|
|
76
74
|
new URL('index.html', frameworkDir)
|
|
77
75
|
).toString()
|
|
@@ -90,47 +88,16 @@ const fastifySsrPlugin: FastifyPluginAsync<FastifySsrOptions> = async (
|
|
|
90
88
|
manifest = {}
|
|
91
89
|
}
|
|
92
90
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const initialStateScript = `
|
|
105
|
-
<script>
|
|
106
|
-
__INITIAL_STATE__ = '${JSON.stringify(ssrContext.initialState)}'
|
|
107
|
-
</script>`
|
|
108
|
-
const renderHtml = (html: string) => {
|
|
109
|
-
return appendToHead(
|
|
110
|
-
preloadLinks,
|
|
111
|
-
appendToBody(initialStateScript, addOrReplaceAppDiv(appHtml, html))
|
|
112
|
-
)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
let html = renderHtml(template)
|
|
116
|
-
// let html = template
|
|
117
|
-
// .replace(`<!--app-html-->`, appHtml)
|
|
118
|
-
// .replace('<!--product-name-->', options.productName || 'Product name')
|
|
119
|
-
// // .replace('<!--dev-ssr-css-->', css)
|
|
120
|
-
// .replace(
|
|
121
|
-
// '<!--initial-state-->',
|
|
122
|
-
// `<script>
|
|
123
|
-
// __INITIAL_STATE__ = '${JSON.stringify(ssrContext.initialState)}'
|
|
124
|
-
// </script>`
|
|
125
|
-
// )
|
|
126
|
-
|
|
127
|
-
// html = appendToHead(preloadLinks, html)
|
|
128
|
-
|
|
129
|
-
if (options.onRendered?.length) {
|
|
130
|
-
for (const ssrFunction of options.onRendered) {
|
|
131
|
-
html = ssrFunction(html, ssrContext)
|
|
132
|
-
}
|
|
133
|
-
}
|
|
91
|
+
const html = await renderHtml({
|
|
92
|
+
request: req,
|
|
93
|
+
reply: res,
|
|
94
|
+
url: url ?? '/',
|
|
95
|
+
provide,
|
|
96
|
+
onRendered: options.onRendered,
|
|
97
|
+
template,
|
|
98
|
+
manifest,
|
|
99
|
+
render
|
|
100
|
+
})
|
|
134
101
|
|
|
135
102
|
res.code(200)
|
|
136
103
|
res.type('text/html')
|
|
@@ -156,67 +123,148 @@ const fastifySsrPlugin: FastifyPluginAsync<FastifySsrOptions> = async (
|
|
|
156
123
|
fastify.get(`${options.baseUrl}*`, async (req, res) => {
|
|
157
124
|
const url = req.raw.url?.replace(options.baseUrl!, '/')
|
|
158
125
|
const provide = options.provide ? await options.provide(req, res) : {}
|
|
159
|
-
const ssrContext: Record<string, any> = {
|
|
160
|
-
req,
|
|
161
|
-
res,
|
|
162
|
-
provide
|
|
163
|
-
}
|
|
164
126
|
|
|
165
|
-
const template =
|
|
166
|
-
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
).render
|
|
127
|
+
const { template, manifest, render, onRendered } = await loadSSRAssets({
|
|
128
|
+
distDir: new URL('./dist/', options.appDir)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
const html = await renderHtml({
|
|
132
|
+
request: req,
|
|
133
|
+
reply: res,
|
|
134
|
+
url: url ?? '/',
|
|
135
|
+
provide,
|
|
136
|
+
onRendered,
|
|
137
|
+
template,
|
|
138
|
+
manifest,
|
|
139
|
+
render
|
|
140
|
+
})
|
|
180
141
|
|
|
181
|
-
|
|
142
|
+
res.code(200)
|
|
143
|
+
res.type('text/html')
|
|
144
|
+
res.send(html)
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
}
|
|
182
148
|
|
|
183
|
-
|
|
184
|
-
|
|
149
|
+
const renderTemplate = ({
|
|
150
|
+
template,
|
|
151
|
+
initialStateScript,
|
|
152
|
+
appHtml,
|
|
153
|
+
preloadLinks
|
|
154
|
+
}: {
|
|
155
|
+
template: string
|
|
156
|
+
initialStateScript: string
|
|
157
|
+
appHtml: string
|
|
158
|
+
preloadLinks: string
|
|
159
|
+
}) => {
|
|
160
|
+
return appendToHead(
|
|
161
|
+
preloadLinks,
|
|
162
|
+
appendToBody(initialStateScript, addOrReplaceAppDiv(appHtml, template))
|
|
163
|
+
)
|
|
164
|
+
}
|
|
185
165
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
166
|
+
const renderHtml = async (options: {
|
|
167
|
+
url: string
|
|
168
|
+
request: FastifyRequest | { headers: Record<string, unknown>; url: string }
|
|
169
|
+
reply: FastifyReply | Record<string, unknown>
|
|
170
|
+
provide: Record<string, unknown>
|
|
171
|
+
onRendered?: OnRenderedHook[]
|
|
172
|
+
template: string
|
|
173
|
+
manifest: Record<string, unknown>
|
|
174
|
+
render: any
|
|
175
|
+
}) => {
|
|
176
|
+
const ssrContext: Record<string, any> = {
|
|
177
|
+
req: options.request,
|
|
178
|
+
res: options.reply,
|
|
179
|
+
provide: options.provide
|
|
180
|
+
}
|
|
196
181
|
|
|
197
|
-
|
|
182
|
+
const onRendered = options.onRendered ?? []
|
|
198
183
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
// )
|
|
184
|
+
const [appHtml, preloadLinks] = await options.render(
|
|
185
|
+
options.url,
|
|
186
|
+
options.manifest,
|
|
187
|
+
ssrContext
|
|
188
|
+
)
|
|
205
189
|
|
|
206
|
-
|
|
190
|
+
if (!ssrContext.initialState) ssrContext.initialState = {}
|
|
191
|
+
ssrContext.initialState.provide = options.provide
|
|
207
192
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
193
|
+
const initialStateScript = `
|
|
194
|
+
<script>
|
|
195
|
+
__INITIAL_STATE__ = '${JSON.stringify(ssrContext.initialState)}'
|
|
196
|
+
</script>`
|
|
213
197
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
198
|
+
let html = renderTemplate({
|
|
199
|
+
template: options.template,
|
|
200
|
+
appHtml,
|
|
201
|
+
initialStateScript,
|
|
202
|
+
preloadLinks
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
if (onRendered?.length) {
|
|
206
|
+
for (const ssrFunction of onRendered) {
|
|
207
|
+
html = ssrFunction(html, ssrContext)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return html
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const loadSSRAssets = async (
|
|
215
|
+
{
|
|
216
|
+
mode,
|
|
217
|
+
distDir
|
|
218
|
+
}: {
|
|
219
|
+
mode?: 'ssr' | 'ssg'
|
|
220
|
+
distDir?: URL
|
|
221
|
+
} = {
|
|
222
|
+
mode: 'ssr'
|
|
223
|
+
}
|
|
224
|
+
) => {
|
|
225
|
+
const appDir = getAppDir(new URL(import.meta.url))
|
|
226
|
+
const baseOutDir = distDir || new URL('dist/', appDir)
|
|
227
|
+
|
|
228
|
+
let templatePath, manifestPath, entryServerPath
|
|
229
|
+
const onRenderedPath = fileURLToPath(
|
|
230
|
+
new URL('ssr/server/virtual_vitrify-hooks.mjs', baseOutDir)
|
|
231
|
+
)
|
|
232
|
+
if (mode === 'ssg') {
|
|
233
|
+
templatePath = fileURLToPath(new URL('static/index.html', baseOutDir))
|
|
234
|
+
manifestPath = fileURLToPath(
|
|
235
|
+
new URL('static/.vite/ssr-manifest.json', baseOutDir)
|
|
236
|
+
)
|
|
237
|
+
entryServerPath = fileURLToPath(
|
|
238
|
+
new URL('ssr/server/entry-server.mjs', baseOutDir)
|
|
239
|
+
)
|
|
240
|
+
} else {
|
|
241
|
+
templatePath = fileURLToPath(new URL('ssr/client/index.html', baseOutDir))
|
|
242
|
+
manifestPath = fileURLToPath(
|
|
243
|
+
new URL('ssr/client/.vite/ssr-manifest.json', baseOutDir)
|
|
244
|
+
)
|
|
245
|
+
entryServerPath = fileURLToPath(
|
|
246
|
+
new URL('ssr/server/entry-server.mjs', baseOutDir)
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
const template = readFileSync(templatePath).toString()
|
|
251
|
+
const manifest = JSON.parse(readFileSync(manifestPath).toString())
|
|
252
|
+
const entryServer = await import(entryServerPath)
|
|
253
|
+
const { render, getRoutes } = entryServer
|
|
254
|
+
const onRendered = (await import(onRenderedPath)).onRendered
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
template,
|
|
258
|
+
manifest,
|
|
259
|
+
render,
|
|
260
|
+
getRoutes,
|
|
261
|
+
onRendered
|
|
262
|
+
}
|
|
263
|
+
} catch (e) {
|
|
264
|
+
console.error(e)
|
|
265
|
+
throw new Error('Unable to load SSR asset files')
|
|
218
266
|
}
|
|
219
267
|
}
|
|
220
268
|
|
|
221
|
-
export { fastifySsrPlugin }
|
|
269
|
+
export { fastifySsrPlugin, renderHtml, loadSSRAssets }
|
|
222
270
|
export type FastifySsrPlugin = typeof fastifySsrPlugin
|
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
import { existsSync, promises as fs, mkdirSync } from 'fs'
|
|
2
2
|
import type { OnRenderedHook } from 'src/node/vitrify-config.js'
|
|
3
3
|
import { routesToPaths } from '../../helpers/routes.js'
|
|
4
|
-
import {
|
|
4
|
+
import { renderHtml } from './fastify-ssr-plugin.js'
|
|
5
|
+
import { type RouteRecordRaw } from 'vue-router'
|
|
5
6
|
|
|
6
7
|
export const prerender = async ({
|
|
7
8
|
outDir,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
template,
|
|
10
|
+
manifest,
|
|
11
|
+
render,
|
|
12
|
+
routes,
|
|
11
13
|
onRendered
|
|
12
14
|
}: {
|
|
13
15
|
outDir: string
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
template: string
|
|
17
|
+
manifest: Record<string, unknown>
|
|
18
|
+
render: unknown
|
|
19
|
+
routes: RouteRecordRaw[]
|
|
17
20
|
onRendered: OnRenderedHook[]
|
|
18
21
|
}) => {
|
|
19
22
|
const promises = []
|
|
20
|
-
const template = (await fs.readFile(templatePath)).toString()
|
|
21
|
-
const manifest = JSON.parse((await fs.readFile(manifestPath)).toString())
|
|
22
|
-
const { render, getRoutes } = await import(entryServerPath)
|
|
23
|
-
const routes = await getRoutes()
|
|
24
23
|
const paths = routesToPaths(routes).filter(
|
|
25
24
|
(i) => !i.includes(':') && !i.includes('*')
|
|
26
25
|
)
|
|
@@ -44,21 +43,17 @@ export const prerender = async ({
|
|
|
44
43
|
const filename =
|
|
45
44
|
(url.endsWith('/') ? 'index' : url.replace(/^\//g, '')) + '.html'
|
|
46
45
|
console.log(`Generating ${filename}`)
|
|
47
|
-
const ssrContext = {
|
|
48
|
-
req: { headers: {}, url },
|
|
49
|
-
res: {}
|
|
50
|
-
}
|
|
51
|
-
const [appHtml, preloadLinks] = await render(url, manifest, ssrContext)
|
|
52
|
-
|
|
53
|
-
let html = addOrReplaceAppDiv(appHtml, template)
|
|
54
|
-
html = appendToHead(preloadLinks, html)
|
|
55
|
-
|
|
56
|
-
if (onRendered?.length) {
|
|
57
|
-
for (const ssrFunction of onRendered) {
|
|
58
|
-
html = ssrFunction(html, ssrContext)
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
46
|
|
|
47
|
+
let html = await renderHtml({
|
|
48
|
+
url,
|
|
49
|
+
manifest,
|
|
50
|
+
provide: {},
|
|
51
|
+
render,
|
|
52
|
+
request: { headers: {}, url },
|
|
53
|
+
reply: {},
|
|
54
|
+
template,
|
|
55
|
+
onRendered
|
|
56
|
+
})
|
|
62
57
|
html = await beasties.process(html)
|
|
63
58
|
|
|
64
59
|
promises.push(fs.writeFile(outDir + filename, html, 'utf-8'))
|
package/src/node/index.ts
CHANGED
|
@@ -74,13 +74,16 @@ const manualChunkNames = [
|
|
|
74
74
|
|
|
75
75
|
const moduleChunks = {
|
|
76
76
|
vue: ['vue', '@vue', 'vue-router'],
|
|
77
|
-
quasar: ['quasar',
|
|
77
|
+
quasar: ['quasar'],
|
|
78
|
+
atQuasar: ['@quasar']
|
|
78
79
|
}
|
|
79
80
|
const manualChunksFn = (manualChunkList?: string[]): ManualChunksOption => {
|
|
80
81
|
return (id: string) => {
|
|
81
82
|
const matchedModule = Object.entries(moduleChunks).find(
|
|
82
83
|
([chunkName, moduleNames]) =>
|
|
83
|
-
moduleNames.some((moduleName) =>
|
|
84
|
+
moduleNames.some((moduleName) =>
|
|
85
|
+
new RegExp(`\/${moduleName}\/`).test(id)
|
|
86
|
+
)
|
|
84
87
|
)
|
|
85
88
|
if (id.includes('vitrify/src/')) {
|
|
86
89
|
const name = id.split('/').at(-1)?.split('.').at(0)
|
|
@@ -652,6 +655,7 @@ export const baseConfig = async ({
|
|
|
652
655
|
input: [
|
|
653
656
|
fileURLToPath(new URL('ssr/entry-server.ts', frameworkDir)),
|
|
654
657
|
fileURLToPath(new URL('ssr/prerender.ts', frameworkDir)),
|
|
658
|
+
fileURLToPath(new URL('ssr/fastify-ssr-plugin.ts', frameworkDir)),
|
|
655
659
|
fileURLToPath(new URL('ssr/server.ts', frameworkDir))
|
|
656
660
|
],
|
|
657
661
|
external,
|
package/src/vite/vue/csr/app.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { getAppDir } from '../../../node/app-urls.js'
|
|
|
3
3
|
// import { setup } from 'virtual:fastify-setup'
|
|
4
4
|
import { onSetup } from 'virtual:vitrify-hooks'
|
|
5
5
|
import { fastifyCsrPlugin } from './fastify-csr-plugin'
|
|
6
|
-
import type { ViteDevServer } from 'vite'
|
|
7
6
|
|
|
8
7
|
// const appDir = getPkgJsonDir(import.meta.url)
|
|
9
8
|
const getString = (str?: string) => str
|
package/src/vite/vue/main.ts
CHANGED
|
@@ -7,12 +7,11 @@ import 'virtual:uno.css'
|
|
|
7
7
|
|
|
8
8
|
import RootComponent from './RootComponent.vue'
|
|
9
9
|
interface ssrContext {
|
|
10
|
-
ssr: boolean
|
|
11
10
|
provide?: Record<string, unknown>
|
|
12
11
|
[key: string]: unknown
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
function capitalizeFirstLetter(string) {
|
|
14
|
+
function capitalizeFirstLetter(string: string) {
|
|
16
15
|
return string.charAt(0).toUpperCase() + string.slice(1)
|
|
17
16
|
}
|
|
18
17
|
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type FastifyReply, type FastifyRequest } from 'fastify'
|
|
2
|
+
import { createApp } from '../main.js'
|
|
3
|
+
import { renderToString as renderToStringVue } from 'vue/server-renderer'
|
|
2
4
|
|
|
3
|
-
const initializeApp = async (
|
|
4
|
-
|
|
5
|
+
const initializeApp = async (
|
|
6
|
+
url: string,
|
|
7
|
+
ssrContext: Record<string, unknown>
|
|
8
|
+
) => {
|
|
9
|
+
const onRenderedList: (() => unknown)[] = []
|
|
5
10
|
Object.assign(ssrContext, {
|
|
6
11
|
_modules: new Set(),
|
|
7
12
|
_meta: {},
|
|
8
|
-
onRendered: (fn) => {
|
|
13
|
+
onRendered: (fn: () => unknown) => {
|
|
9
14
|
onRenderedList.push(fn)
|
|
10
15
|
}
|
|
11
16
|
})
|
|
@@ -27,28 +32,43 @@ const initializeApp = async (url, ssrContext) => {
|
|
|
27
32
|
export const getRoutes = async () =>
|
|
28
33
|
(
|
|
29
34
|
await initializeApp('/', {
|
|
30
|
-
ssr: false,
|
|
31
35
|
req: { headers: {}, url: '/' },
|
|
32
36
|
res: {}
|
|
33
37
|
})
|
|
34
38
|
).routes
|
|
35
39
|
|
|
36
|
-
export async function render(
|
|
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
|
+
) {
|
|
37
50
|
if (!renderToString)
|
|
38
51
|
renderToString = (await import('vue/server-renderer')).renderToString
|
|
39
52
|
const { app, router } = await initializeApp(url, ssrContext)
|
|
40
53
|
|
|
41
|
-
const ctx
|
|
54
|
+
const ctx: {
|
|
55
|
+
modules?: Map<unknown, unknown>
|
|
56
|
+
transports?: Record<string, unknown>
|
|
57
|
+
__qMetaList: unknown[]
|
|
58
|
+
} = {
|
|
42
59
|
__qMetaList: []
|
|
43
60
|
}
|
|
44
61
|
const html = await renderToString(app, ctx)
|
|
45
62
|
|
|
46
|
-
const preloadLinks = renderPreloadLinks(ctx.modules
|
|
63
|
+
const preloadLinks = renderPreloadLinks(ctx.modules!, manifest)
|
|
47
64
|
|
|
48
65
|
return [html, preloadLinks]
|
|
49
66
|
}
|
|
50
67
|
|
|
51
|
-
function renderPreloadLinks(
|
|
68
|
+
function renderPreloadLinks(
|
|
69
|
+
modules: Map<unknown, unknown>,
|
|
70
|
+
manifest: Record<string, unknown>
|
|
71
|
+
) {
|
|
52
72
|
let links = ''
|
|
53
73
|
const seen = new Set()
|
|
54
74
|
modules.forEach((id) => {
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
fastifySsrPlugin,
|
|
3
|
+
renderHtml,
|
|
4
|
+
loadSSRAssets
|
|
5
|
+
} from '../../../node/frameworks/vue/fastify-ssr-plugin.js'
|
|
2
6
|
|
|
3
|
-
export { fastifySsrPlugin }
|
|
7
|
+
export { fastifySsrPlugin, renderHtml, loadSSRAssets }
|