vitrify 0.1.0 → 0.1.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 +32 -0
- package/dist/bin/build.js +73 -0
- package/dist/bin/cli.js +137 -0
- package/dist/bin/dev.js +106 -0
- package/dist/bin/run.js +26 -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 +288 -0
- package/dist/plugins/index.js +1 -0
- package/dist/plugins/quasar.js +294 -0
- package/dist/types/app-urls.d.ts +11 -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/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 +64 -0
- package/dist/types/vue/fastify-ssr-plugin.d.ts +14 -0
- package/dist/types/vue/prerender.d.ts +8 -0
- package/dist/types/vue/server.d.ts +9 -0
- package/dist/vitrify-config.js +1 -0
- package/dist/vue/fastify-ssr-plugin.js +91 -0
- package/dist/vue/prerender.js +29 -0
- package/dist/vue/server.js +20 -0
- package/package.json +94 -18
- package/src/vite/vue/components.d.ts +25 -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 +15 -0
- package/src/vite/vue/ssr/server.ts.bak +61 -0
- package/src/vite/vue/ssr/tsconfig.json +9 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { InlineConfig } from 'vite';
|
|
2
|
+
import type { VitrifyConfig } from './vitrify-config.js';
|
|
3
|
+
import type { VitrifyContext } from './bin/run.js';
|
|
4
|
+
import type { VitrifyPlugin } from './plugins/index.js';
|
|
5
|
+
export declare const VIRTUAL_MODULES: string[];
|
|
6
|
+
export declare const baseConfig: ({ ssr, appDir, publicDir, command, mode, framework, pwa }: {
|
|
7
|
+
ssr?: "server" | "client" | "ssg" | undefined;
|
|
8
|
+
appDir?: URL | undefined;
|
|
9
|
+
publicDir?: URL | undefined;
|
|
10
|
+
command?: "build" | "dev" | "test" | undefined;
|
|
11
|
+
mode?: "production" | "development" | undefined;
|
|
12
|
+
framework?: "vue" | undefined;
|
|
13
|
+
pwa?: boolean | undefined;
|
|
14
|
+
}) => Promise<InlineConfig>;
|
|
15
|
+
export type { VitrifyConfig, VitrifyPlugin, VitrifyContext };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
export declare type VitrifyPlugin = ({ ssr, mode, command }: {
|
|
3
|
+
ssr?: 'server' | 'client' | 'ssg' | false;
|
|
4
|
+
pwa?: boolean;
|
|
5
|
+
mode?: 'production' | 'development';
|
|
6
|
+
command?: 'build' | 'dev' | 'test';
|
|
7
|
+
}) => Promise<Plugin | Plugin[]> | Plugin | Plugin[];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { VitrifyPlugin } from './index.js';
|
|
2
|
+
export interface QuasarConf {
|
|
3
|
+
ctx: Record<string, any>;
|
|
4
|
+
css: string[];
|
|
5
|
+
boot: string[];
|
|
6
|
+
framework: {
|
|
7
|
+
components?: string[];
|
|
8
|
+
directives?: string[];
|
|
9
|
+
plugins?: string[];
|
|
10
|
+
};
|
|
11
|
+
animations: string[];
|
|
12
|
+
extras: string[];
|
|
13
|
+
}
|
|
14
|
+
export declare const injectSsrContext: (html: string, ssrContext: Record<string, any>) => string;
|
|
15
|
+
export declare const QuasarPlugin: VitrifyPlugin;
|
|
16
|
+
export default QuasarPlugin;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import type { UserConfig } from 'vite';
|
|
3
|
+
import type { QuasarConf } from './plugins/quasar.js';
|
|
4
|
+
import type { ComponentInternalInstance } from '@vue/runtime-core';
|
|
5
|
+
export declare type BootFunction = ({ app, ssrContext, staticImports }: {
|
|
6
|
+
app: any;
|
|
7
|
+
ssrContext: Record<string, unknown>;
|
|
8
|
+
staticImports: Record<string, any>;
|
|
9
|
+
}) => Promise<void> | void;
|
|
10
|
+
export declare type OnMountedHook = (instance: ComponentInternalInstance) => Promise<void> | void;
|
|
11
|
+
export declare type StaticImports = Record<string, string[]>;
|
|
12
|
+
export declare type SsrFunction = (html: string, ssrContext: Record<string, any>) => string;
|
|
13
|
+
export interface VitrifyConfig extends UserConfig {
|
|
14
|
+
vitrify?: {
|
|
15
|
+
/**
|
|
16
|
+
* Global CSS imports
|
|
17
|
+
*/
|
|
18
|
+
globalCss?: string[];
|
|
19
|
+
/**
|
|
20
|
+
* Functions which run after initializing the app
|
|
21
|
+
*/
|
|
22
|
+
bootFunctions?: BootFunction[];
|
|
23
|
+
/**
|
|
24
|
+
* Functions which run on the server after rendering the app
|
|
25
|
+
*/
|
|
26
|
+
ssrFunctions?: SsrFunction[];
|
|
27
|
+
/**
|
|
28
|
+
* Static imports in the app: packageName: [imports]
|
|
29
|
+
*/
|
|
30
|
+
staticImports?: StaticImports;
|
|
31
|
+
hooks?: {
|
|
32
|
+
/**
|
|
33
|
+
* Functions which run in the onMounted hook of the app
|
|
34
|
+
*/
|
|
35
|
+
onMounted: OnMountedHook[];
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Global SASS variables
|
|
39
|
+
*/
|
|
40
|
+
sassVariables?: Record<string, string>;
|
|
41
|
+
fastify?: {
|
|
42
|
+
/**
|
|
43
|
+
* setup() is called directly after instantiating fastify. Use it to register your own plugins, routes etc.
|
|
44
|
+
*/
|
|
45
|
+
setup: (fastify: FastifyInstance) => any;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Product name of the application. Will be used for the HTML title tag
|
|
49
|
+
*/
|
|
50
|
+
productName?: string;
|
|
51
|
+
/**
|
|
52
|
+
* URL's for common dev paths and packages, automatically generated
|
|
53
|
+
*/
|
|
54
|
+
urls?: {
|
|
55
|
+
app?: URL;
|
|
56
|
+
cli?: URL;
|
|
57
|
+
src?: URL;
|
|
58
|
+
cwd?: URL;
|
|
59
|
+
packages?: Record<string, URL>;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
quasar?: QuasarConf;
|
|
63
|
+
}
|
|
64
|
+
export declare const defineConfig: (config: VitrifyConfig) => VitrifyConfig;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FastifyPluginCallback, FastifyRequest, FastifyReply } from 'fastify';
|
|
2
|
+
import type { ViteDevServer } from 'vite';
|
|
3
|
+
import type { SsrFunction } from '../vitrify-config.js';
|
|
4
|
+
export interface FastifySsrOptions {
|
|
5
|
+
baseUrl?: string;
|
|
6
|
+
provide?: (req: FastifyRequest, res: FastifyReply) => Promise<Record<string, unknown>>;
|
|
7
|
+
vite?: ViteDevServer;
|
|
8
|
+
cliDir?: URL;
|
|
9
|
+
appDir?: URL;
|
|
10
|
+
productName?: string;
|
|
11
|
+
ssrFunctions?: SsrFunction[];
|
|
12
|
+
}
|
|
13
|
+
declare const fastifySsrPlugin: FastifyPluginCallback<FastifySsrOptions>;
|
|
14
|
+
export { fastifySsrPlugin };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SsrFunction } from '../vitrify-config.js';
|
|
2
|
+
export declare const prerender: ({ outDir, templatePath, manifestPath, entryServerPath, ssrFunctions }: {
|
|
3
|
+
outDir: string;
|
|
4
|
+
templatePath: string;
|
|
5
|
+
manifestPath: string;
|
|
6
|
+
entryServerPath: string;
|
|
7
|
+
ssrFunctions: SsrFunction[];
|
|
8
|
+
}) => Promise<void[]>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { FastifyInstance } from 'fastify';
|
|
3
|
+
import type { SsrFunction } from '../vitrify-config.js';
|
|
4
|
+
export declare const createApp: ({ setup, appDir, baseUrl, ssrFunctions }: {
|
|
5
|
+
setup: (fastify: FastifyInstance) => any;
|
|
6
|
+
appDir: URL;
|
|
7
|
+
baseUrl?: string | undefined;
|
|
8
|
+
ssrFunctions?: SsrFunction[] | undefined;
|
|
9
|
+
}) => FastifyInstance<import("http").Server, import("http").IncomingMessage, import("http").ServerResponse, import("fastify").FastifyLoggerInstance> & PromiseLike<FastifyInstance<import("http").Server, import("http").IncomingMessage, import("http").ServerResponse, import("fastify").FastifyLoggerInstance>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const defineConfig = (config) => config;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import fastifyStatic from 'fastify-static';
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
const fastifySsrPlugin = async (fastify, options, done) => {
|
|
4
|
+
if (import.meta.env.MODE === 'development') {
|
|
5
|
+
if (!options.vite)
|
|
6
|
+
throw new Error('Option vite cannot be undefined');
|
|
7
|
+
const middie = (await import('middie')).default;
|
|
8
|
+
await fastify.register(middie);
|
|
9
|
+
fastify.use(options.vite.middlewares);
|
|
10
|
+
fastify.get('*', async (req, res) => {
|
|
11
|
+
try {
|
|
12
|
+
// const url = req.originalUrl
|
|
13
|
+
const url = req.raw.url;
|
|
14
|
+
const ssrContext = {
|
|
15
|
+
req,
|
|
16
|
+
res
|
|
17
|
+
};
|
|
18
|
+
// always read fresh template in dev
|
|
19
|
+
// template = readFileSync(resolve('index.html'), 'utf-8')
|
|
20
|
+
const template = readFileSync(new URL('index.html', options.cliDir)).toString();
|
|
21
|
+
// template = await vite.transformIndexHtml(url, template)
|
|
22
|
+
const entryUrl = new URL('ssr/entry-server.ts', options.cliDir).pathname;
|
|
23
|
+
const render = (await options.vite.ssrLoadModule(entryUrl)).render;
|
|
24
|
+
let manifest;
|
|
25
|
+
// TODO: https://github.com/vitejs/vite/issues/2282
|
|
26
|
+
try {
|
|
27
|
+
manifest = {};
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
manifest = {};
|
|
31
|
+
}
|
|
32
|
+
const [appHtml, preloadLinks] = await render(url, manifest, ssrContext);
|
|
33
|
+
const html = template
|
|
34
|
+
.replace(`<!--preload-links-->`, preloadLinks)
|
|
35
|
+
.replace(`<!--app-html-->`, appHtml)
|
|
36
|
+
.replace('<!--product-name-->', options.productName || 'Product name');
|
|
37
|
+
res.code(200);
|
|
38
|
+
res.type('text/html');
|
|
39
|
+
res.send(html);
|
|
40
|
+
// res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
console.error(e.stack);
|
|
44
|
+
options.vite && options.vite.ssrFixStacktrace(e);
|
|
45
|
+
res.code(500);
|
|
46
|
+
res.send(e.stack);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
options.baseUrl = options.baseUrl || '/';
|
|
52
|
+
fastify.register(fastifyStatic, {
|
|
53
|
+
root: new URL('./dist/ssr/client', options.appDir).pathname,
|
|
54
|
+
wildcard: false,
|
|
55
|
+
index: false,
|
|
56
|
+
prefix: options.baseUrl
|
|
57
|
+
});
|
|
58
|
+
fastify.get(`${options.baseUrl}*`, async (req, res) => {
|
|
59
|
+
const url = req.raw.url;
|
|
60
|
+
const provide = options.provide ? await options.provide(req, res) : {};
|
|
61
|
+
const ssrContext = {
|
|
62
|
+
req,
|
|
63
|
+
res,
|
|
64
|
+
provide
|
|
65
|
+
};
|
|
66
|
+
// template = readFileSync(new URL('../client/index.html', import.meta.url).pathname).toString()
|
|
67
|
+
// manifest = JSON.parse(readFileSync(new URL('../client/ssr-manifest.json', import.meta.url)).toString())
|
|
68
|
+
// render = (await import(new URL('./entry-server.mjs', import.meta.url).pathname)).render
|
|
69
|
+
const template = readFileSync(new URL('./dist/ssr/client/index.html', options.appDir).pathname).toString();
|
|
70
|
+
const manifest = JSON.parse(readFileSync(new URL('./dist/ssr/client/ssr-manifest.json', options.appDir)).toString());
|
|
71
|
+
const render = (await import(new URL('./dist/ssr/server/entry-server.mjs', options.appDir).pathname)).render;
|
|
72
|
+
const [appHtml, preloadLinks] = await render(url, manifest, ssrContext);
|
|
73
|
+
if (!ssrContext.initialState)
|
|
74
|
+
ssrContext.initialState = {};
|
|
75
|
+
ssrContext.initialState.provide = provide;
|
|
76
|
+
let html = template
|
|
77
|
+
.replace(`<!--preload-links-->`, preloadLinks)
|
|
78
|
+
.replace(`<!--app-html-->`, appHtml);
|
|
79
|
+
if (options.ssrFunctions?.length) {
|
|
80
|
+
for (const ssrFunction of options.ssrFunctions) {
|
|
81
|
+
html = ssrFunction(html, ssrContext);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
res.code(200);
|
|
85
|
+
res.type('text/html');
|
|
86
|
+
res.send(html);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
done();
|
|
90
|
+
};
|
|
91
|
+
export { fastifySsrPlugin };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import { routesToPaths } from '../helpers/routes.js';
|
|
3
|
+
export const prerender = async ({ outDir, templatePath, manifestPath, entryServerPath, ssrFunctions }) => {
|
|
4
|
+
const promises = [];
|
|
5
|
+
const template = (await fs.readFile(templatePath)).toString();
|
|
6
|
+
const manifest = await fs.readFile(manifestPath);
|
|
7
|
+
const { render, getRoutes } = await import(entryServerPath);
|
|
8
|
+
const routes = await getRoutes();
|
|
9
|
+
const paths = routesToPaths(routes).filter((i) => !i.includes(':') && !i.includes('*'));
|
|
10
|
+
for (const url of paths) {
|
|
11
|
+
const filename = (url.endsWith('/') ? 'index' : url.replace(/^\//g, '')) + '.html';
|
|
12
|
+
console.log(`Generating ${filename}`);
|
|
13
|
+
const ssrContext = {
|
|
14
|
+
req: { headers: {}, url },
|
|
15
|
+
res: {}
|
|
16
|
+
};
|
|
17
|
+
const [appHtml, preloadLinks] = await render(url, manifest, ssrContext);
|
|
18
|
+
let html = template
|
|
19
|
+
.replace(`<!--preload-links-->`, preloadLinks)
|
|
20
|
+
.replace(`<!--app-html-->`, appHtml);
|
|
21
|
+
if (ssrFunctions?.length) {
|
|
22
|
+
for (const ssrFunction of ssrFunctions) {
|
|
23
|
+
html = ssrFunction(html, ssrContext);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
promises.push(fs.writeFile(outDir + filename, html, 'utf-8'));
|
|
27
|
+
}
|
|
28
|
+
return Promise.all(promises);
|
|
29
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import fastify from 'fastify';
|
|
2
|
+
// import { setup } from 'virtual:fastify-setup'
|
|
3
|
+
import { fastifySsrPlugin } from './fastify-ssr-plugin.js';
|
|
4
|
+
// import { getPkgJsonDir } from '../app-urls.js'
|
|
5
|
+
export const createApp = ({ setup, appDir, baseUrl, ssrFunctions }) => {
|
|
6
|
+
const app = fastify({
|
|
7
|
+
logger: true
|
|
8
|
+
});
|
|
9
|
+
app.register(fastifySsrPlugin, {
|
|
10
|
+
baseUrl,
|
|
11
|
+
appDir,
|
|
12
|
+
ssrFunctions
|
|
13
|
+
});
|
|
14
|
+
setup(app);
|
|
15
|
+
return app;
|
|
16
|
+
};
|
|
17
|
+
// const app = createApp({
|
|
18
|
+
// setup
|
|
19
|
+
// })
|
|
20
|
+
// app.listen(process.env.PORT || 3000, process.env.HOST || '127.0.0.1')
|
package/package.json
CHANGED
|
@@ -1,21 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitrify",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.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/node/build.d.ts"
|
|
29
|
+
],
|
|
30
|
+
"dev": [
|
|
31
|
+
"./dist/types/node/dev.d.ts"
|
|
32
|
+
],
|
|
33
|
+
"help": [
|
|
34
|
+
"./dist/types/node/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
39
|
"node": ">=12.2.0"
|
|
21
40
|
},
|
|
@@ -28,9 +47,66 @@
|
|
|
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.31",
|
|
58
|
+
"@vue/server-renderer": "^3.2.31",
|
|
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": "^7.2.0",
|
|
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.1",
|
|
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.23",
|
|
81
|
+
"@types/ws": "^8.5.3",
|
|
82
|
+
"@vue/runtime-core": "^3.2.31",
|
|
83
|
+
"quasar": "^2.6.6",
|
|
84
|
+
"rollup": "^2.70.1",
|
|
85
|
+
"typescript": "^4.6.3",
|
|
86
|
+
"unplugin-vue-components": "^0.19.2",
|
|
87
|
+
"vite": "^2.9.1",
|
|
88
|
+
"vitrify": "^0.1.1",
|
|
89
|
+
"vue": "^3.2.31",
|
|
90
|
+
"vue-router": "^4.0.14"
|
|
91
|
+
},
|
|
92
|
+
"peerDependencies": {
|
|
93
|
+
"fastify": "^3.28.0",
|
|
94
|
+
"fastify-plugin": "^3.0.1",
|
|
95
|
+
"fastify-sensible": "^3.1.2",
|
|
96
|
+
"fastify-static": "^4.6.1",
|
|
97
|
+
"quasar": "^2.6.6",
|
|
98
|
+
"vue": "^3.2.31",
|
|
99
|
+
"vue-router": "^4.0.14"
|
|
100
|
+
},
|
|
101
|
+
"publishConfig": {
|
|
102
|
+
"access": "public",
|
|
103
|
+
"registry": "https://registry.npmjs.org/"
|
|
104
|
+
},
|
|
105
|
+
"files": [
|
|
106
|
+
"dist",
|
|
107
|
+
"src/node/helpers/ssr.ts",
|
|
108
|
+
"src/vite",
|
|
109
|
+
"!dist/**/*.test.js",
|
|
110
|
+
"!dist/**/test.js"
|
|
111
|
+
]
|
|
112
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// generated by unplugin-vue-components
|
|
2
|
+
// We suggest you to commit this file into source control
|
|
3
|
+
// Read more: https://github.com/vuejs/vue-next/pull/3399
|
|
4
|
+
|
|
5
|
+
declare module '@vue/runtime-core' {
|
|
6
|
+
export interface GlobalComponents {
|
|
7
|
+
QAvatar: typeof import('quasar')['QAvatar']
|
|
8
|
+
QBtn: typeof import('quasar')['QBtn']
|
|
9
|
+
QCard: typeof import('quasar')['QCard']
|
|
10
|
+
QCardSection: typeof import('quasar')['QCardSection']
|
|
11
|
+
QDrawer: typeof import('quasar')['QDrawer']
|
|
12
|
+
QHeader: typeof import('quasar')['QHeader']
|
|
13
|
+
QItemLabel: typeof import('quasar')['QItemLabel']
|
|
14
|
+
QLayout: typeof import('quasar')['QLayout']
|
|
15
|
+
QList: typeof import('quasar')['QList']
|
|
16
|
+
QPage: typeof import('quasar')['QPage']
|
|
17
|
+
QPageContainer: typeof import('quasar')['QPageContainer']
|
|
18
|
+
QToolbar: typeof import('quasar')['QToolbar']
|
|
19
|
+
QToolbarTitle: typeof import('quasar')['QToolbarTitle']
|
|
20
|
+
RouterLink: typeof import('vue-router')['RouterLink']
|
|
21
|
+
RouterView: typeof import('vue-router')['RouterView']
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export {}
|
|
@@ -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
|
+
}
|