zova-module-a-ssrserver 5.0.0 → 5.1.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/LICENSE +21 -0
- package/dist/index.js +208 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -23
- package/src/.metadata/index.ts +64 -0
- package/src/.metadata/this.ts +2 -0
- package/src/index.ts +2 -0
- package/src/monkeySys.ts +13 -0
- package/src/service/ssrHandler.ts +242 -0
- package/src/types/index.ts +1 -0
- package/src/types/ssr.ts +20 -0
- package/README.md +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2016-present Zova
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { BeanBase, BeanInfo, BeanScopeBase, BeanSimple, Use, UseScope, cast } from "zova";
|
|
2
|
+
import { catchError } from "@cabloy/utils";
|
|
3
|
+
import fse from "fs-extra";
|
|
4
|
+
import ms from "ms";
|
|
5
|
+
import path, { basename } from "node:path";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
7
|
+
import { Scope, Service } from "zova-module-a-bean";
|
|
8
|
+
//#region src/service/ssrHandler.ts
|
|
9
|
+
var _dec$1, _dec2$1, _dec3, _dec4, _dec5, _dec6, _class$1, _class2, _descriptor, _descriptor2;
|
|
10
|
+
function _initializerDefineProperty(e, i, r, l) {
|
|
11
|
+
r && Object.defineProperty(e, i, {
|
|
12
|
+
enumerable: r.enumerable,
|
|
13
|
+
configurable: r.configurable,
|
|
14
|
+
writable: r.writable,
|
|
15
|
+
value: r.initializer ? r.initializer.call(l) : void 0
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function _applyDecoratedDescriptor(i, e, r, n, l) {
|
|
19
|
+
var a = {};
|
|
20
|
+
return Object.keys(n).forEach(function(i) {
|
|
21
|
+
a[i] = n[i];
|
|
22
|
+
}), a.enumerable = !!a.enumerable, a.configurable = !!a.configurable, ("value" in a || a.initializer) && (a.writable = !0), a = r.slice().reverse().reduce(function(r, n) {
|
|
23
|
+
return n(i, e, r) || r;
|
|
24
|
+
}, a), l && void 0 !== a.initializer && (a.value = a.initializer ? a.initializer.call(l) : void 0, a.initializer = void 0), void 0 === a.initializer ? (Object.defineProperty(i, e, a), null) : a;
|
|
25
|
+
}
|
|
26
|
+
var jsRE = /\.js$/;
|
|
27
|
+
var cssRE = /\.css$/;
|
|
28
|
+
var woffRE = /\.woff$/;
|
|
29
|
+
var woff2RE = /\.woff2$/;
|
|
30
|
+
var gifRE = /\.gif$/;
|
|
31
|
+
var jpgRE = /\.jpe?g$/;
|
|
32
|
+
var pngRE = /\.png$/;
|
|
33
|
+
var ServiceSsrHandler = (_dec$1 = Service(), _dec2$1 = BeanInfo({ module: "a-ssrserver" }), _dec3 = Use("a-router.sys.router"), _dec4 = Reflect.metadata("design:type", typeof SysRouter === "undefined" ? Object : SysRouter), _dec5 = UseScope("a-ssr"), _dec6 = Reflect.metadata("design:type", typeof ScopeModuleASsr === "undefined" ? Object : ScopeModuleASsr), _dec$1(_class$1 = _dec2$1(_class$1 = (_class2 = class ServiceSsrHandler extends BeanBase {
|
|
34
|
+
constructor(...args) {
|
|
35
|
+
super(...args);
|
|
36
|
+
this._siteAssetDir = void 0;
|
|
37
|
+
this._handlerPromise = void 0;
|
|
38
|
+
this._handlerInstance = void 0;
|
|
39
|
+
this._clientManifest = void 0;
|
|
40
|
+
this._ssrModulesZovaCache = {};
|
|
41
|
+
_initializerDefineProperty(this, "$$sysRouter", _descriptor, this);
|
|
42
|
+
_initializerDefineProperty(this, "$$scopeSsr", _descriptor2, this);
|
|
43
|
+
}
|
|
44
|
+
__init__(siteAssetDir) {
|
|
45
|
+
this._siteAssetDir = siteAssetDir;
|
|
46
|
+
}
|
|
47
|
+
__dispose__() {
|
|
48
|
+
this.dispose();
|
|
49
|
+
}
|
|
50
|
+
dispose() {
|
|
51
|
+
this.sys.close();
|
|
52
|
+
this._handlerInstance = void 0;
|
|
53
|
+
}
|
|
54
|
+
async resolvePath(filename) {
|
|
55
|
+
if (filename === "") filename = "index.html";
|
|
56
|
+
const fileAsset = path.join(this._siteAssetDir, "client", filename);
|
|
57
|
+
if (await fse.exists(fileAsset)) return fileAsset;
|
|
58
|
+
}
|
|
59
|
+
async render(options) {
|
|
60
|
+
const pagePathFull = options.state?.pagePathFull ?? this.sys.util.getPagePathFromAbsoluteUrl(options.req.url);
|
|
61
|
+
const route = await this.$$sysRouter.resolveRoute(pagePathFull, true, false);
|
|
62
|
+
if (!route) return;
|
|
63
|
+
const { serverEntry, renderToString, renderTemplate } = this._handlerInstance;
|
|
64
|
+
const onRenderedList = [];
|
|
65
|
+
const ssrContext = {
|
|
66
|
+
...options,
|
|
67
|
+
_meta: {},
|
|
68
|
+
onRendered: (fn) => {
|
|
69
|
+
onRenderedList.push(fn);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const renderFn = await serverEntry(ssrContext);
|
|
73
|
+
const [runtimePageContent, err] = await catchError(() => {
|
|
74
|
+
return renderToString(renderFn, ssrContext);
|
|
75
|
+
});
|
|
76
|
+
const error = ssrContext._meta.renderError ?? err;
|
|
77
|
+
onRenderedList.forEach((fn) => {
|
|
78
|
+
fn(error);
|
|
79
|
+
});
|
|
80
|
+
cast(ssrContext).rendered(error);
|
|
81
|
+
if (error) {
|
|
82
|
+
if (error instanceof Error) throw error;
|
|
83
|
+
return error;
|
|
84
|
+
}
|
|
85
|
+
ssrContext._meta.runtimePageContent = runtimePageContent;
|
|
86
|
+
// @vitejs/plugin-vue injects code into a component's setup() that registers
|
|
87
|
+
ssrContext._meta.endingHeadTags += this._renderModulesPreload_zova(cast(ssrContext).modules, { ssrContext });
|
|
88
|
+
ssrContext._meta.endingHeadTags += this._renderModulesPreload(cast(ssrContext).modules, { ssrContext });
|
|
89
|
+
const html = renderTemplate(ssrContext);
|
|
90
|
+
await this._renderTransferCache(options, route);
|
|
91
|
+
return html;
|
|
92
|
+
}
|
|
93
|
+
async ensureReady(handlerNonce) {
|
|
94
|
+
if (!this._handlerInstance) {
|
|
95
|
+
if (!this._handlerPromise) this._handlerPromise = this._prepareHandler(handlerNonce);
|
|
96
|
+
this._handlerInstance = await this._handlerPromise;
|
|
97
|
+
}
|
|
98
|
+
return this._handlerInstance;
|
|
99
|
+
}
|
|
100
|
+
async _prepareHandler(handlerNonce) {
|
|
101
|
+
const handlerInstance = await import(`${pathToHref(path.join(this._siteAssetDir, "handler.js"))}?${handlerNonce}`);
|
|
102
|
+
const fileManifest = path.join(this._siteAssetDir, "quasar.manifest.json");
|
|
103
|
+
const contentManifest = await fse.readFile(fileManifest, { encoding: "utf-8" });
|
|
104
|
+
this._clientManifest = JSON.parse(contentManifest);
|
|
105
|
+
return handlerInstance;
|
|
106
|
+
}
|
|
107
|
+
_renderModulesPreload(modules, opts) {
|
|
108
|
+
let links = "";
|
|
109
|
+
const seen = /* @__PURE__ */ new Set();
|
|
110
|
+
modules.forEach((id) => {
|
|
111
|
+
const files = this._clientManifest[id];
|
|
112
|
+
if (files === void 0) return;
|
|
113
|
+
files.forEach((file) => {
|
|
114
|
+
if (seen.has(file) === true) return;
|
|
115
|
+
seen.add(file);
|
|
116
|
+
const filename = basename(file);
|
|
117
|
+
if (this._clientManifest[filename] !== void 0) {
|
|
118
|
+
for (const depFile of this._clientManifest[filename]) if (seen.has(depFile) === false) {
|
|
119
|
+
links += this._renderPreloadTag(depFile, opts);
|
|
120
|
+
seen.add(depFile);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
links += this._renderPreloadTag(file, opts);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
return links;
|
|
127
|
+
}
|
|
128
|
+
_renderModulesPreload_zova(modules2, opts) {
|
|
129
|
+
let links = "";
|
|
130
|
+
const seen = /* @__PURE__ */ new Set();
|
|
131
|
+
modules2.forEach((id) => {
|
|
132
|
+
if (!id.startsWith("@@")) return;
|
|
133
|
+
if (seen.has(id) === true) return;
|
|
134
|
+
let cache = this._ssrModulesZovaCache[id];
|
|
135
|
+
if (!cache) {
|
|
136
|
+
for (const key in this._clientManifest) {
|
|
137
|
+
const prefix = `${id.substring(2)}-`;
|
|
138
|
+
if (key.startsWith(prefix) && key.endsWith(".js")) {
|
|
139
|
+
cache = this._resolveUrlPath(`/assets/${key}`);
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
this._ssrModulesZovaCache[id] = cache;
|
|
144
|
+
}
|
|
145
|
+
links += this._renderPreloadTag(cache, opts);
|
|
146
|
+
seen.add(id);
|
|
147
|
+
});
|
|
148
|
+
return links;
|
|
149
|
+
}
|
|
150
|
+
_resolveUrlPath(url) {
|
|
151
|
+
let publicPath = this.sys.env.APP_PUBLIC_PATH;
|
|
152
|
+
if (publicPath) publicPath = `/${publicPath}`;
|
|
153
|
+
return publicPath + url;
|
|
154
|
+
}
|
|
155
|
+
_renderPreloadTag(file, _opts) {
|
|
156
|
+
file = this.sys.util.getAbsoluteUrlFromPagePath(file, false, true);
|
|
157
|
+
if (jsRE.test(file) === true) return `<link rel="modulepreload" href="${file}" crossorigin>`;
|
|
158
|
+
if (cssRE.test(file) === true) return `<link rel="stylesheet" href="${file}" crossorigin>`;
|
|
159
|
+
if (woffRE.test(file) === true) return `<link rel="preload" href="${file}" as="font" type="font/woff" crossorigin>`;
|
|
160
|
+
if (woff2RE.test(file) === true) return `<link rel="preload" href="${file}" as="font" type="font/woff2" crossorigin>`;
|
|
161
|
+
if (gifRE.test(file) === true) return `<link rel="preload" href="${file}" as="image" type="image/gif" crossorigin>`;
|
|
162
|
+
if (jpgRE.test(file) === true) return `<link rel="preload" href="${file}" as="image" type="image/jpeg" crossorigin>`;
|
|
163
|
+
if (pngRE.test(file) === true) return `<link rel="preload" href="${file}" as="image" type="image/png" crossorigin>`;
|
|
164
|
+
return "";
|
|
165
|
+
}
|
|
166
|
+
async _renderTransferCache(options, route) {
|
|
167
|
+
const { res } = options;
|
|
168
|
+
const transferCache = route.meta.transferCache ?? this.$$scopeSsr.config.transferCache;
|
|
169
|
+
if (transferCache === false) return;
|
|
170
|
+
const transferCacheExpires = transferCache.expires ?? 0;
|
|
171
|
+
const expires = typeof transferCacheExpires === "string" ? ms(transferCacheExpires) / 1e3 : transferCacheExpires;
|
|
172
|
+
if (expires === 0) res.appendHeader("cache-control", "no-cache, no-store, must-revalidate");
|
|
173
|
+
else res.appendHeader("cache-control", `public, max-age=${expires}`);
|
|
174
|
+
}
|
|
175
|
+
}, _descriptor = _applyDecoratedDescriptor(_class2.prototype, "$$sysRouter", [_dec3, _dec4], {
|
|
176
|
+
configurable: true,
|
|
177
|
+
enumerable: true,
|
|
178
|
+
writable: true,
|
|
179
|
+
initializer: null
|
|
180
|
+
}), _descriptor2 = _applyDecoratedDescriptor(_class2.prototype, "$$scopeSsr", [_dec5, _dec6], {
|
|
181
|
+
configurable: true,
|
|
182
|
+
enumerable: true,
|
|
183
|
+
writable: true,
|
|
184
|
+
initializer: null
|
|
185
|
+
}), _class2)) || _class$1) || _class$1);
|
|
186
|
+
function pathToHref(fileName) {
|
|
187
|
+
return pathToFileURL(fileName).href;
|
|
188
|
+
}
|
|
189
|
+
//#endregion
|
|
190
|
+
//#region src/monkeySys.ts
|
|
191
|
+
var MonkeySys = class extends BeanSimple {
|
|
192
|
+
async sysInitialize() {
|
|
193
|
+
this.sys.meta.$getSsrHandler = async (siteAssetDir) => {
|
|
194
|
+
return await this.sys.bean._getBean(ServiceSsrHandler, false, siteAssetDir);
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region src/.metadata/index.ts
|
|
200
|
+
/** monkeySys: end */
|
|
201
|
+
/** scope: begin */
|
|
202
|
+
var _dec, _dec2, _class;
|
|
203
|
+
var ScopeModuleASsrserver = (_dec = Scope(), _dec2 = BeanInfo({ module: "a-ssrserver" }), _dec(_class = _dec2(_class = class ScopeModuleASsrserver extends BeanScopeBase {}) || _class) || _class);
|
|
204
|
+
/** scope: end */
|
|
205
|
+
//#endregion
|
|
206
|
+
export { MonkeySys, ScopeModuleASsrserver, ServiceSsrHandler, pathToHref };
|
|
207
|
+
|
|
208
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/service/ssrHandler.ts","../src/monkeySys.ts","../src/.metadata/index.ts"],"sourcesContent":["import { catchError } from '@cabloy/utils';\nimport { RouteLocationResolvedGeneric } from '@cabloy/vue-router';\nimport fse from 'fs-extra';\nimport ms from 'ms';\nimport path, { basename } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { BeanBase, cast, Use, UseScope } from 'zova';\nimport { Service } from 'zova-module-a-bean';\nimport { SysRouter } from 'zova-module-a-router';\nimport { ScopeModuleASsr } from 'zova-module-a-ssr';\n\nimport { ISsrHandlerRenderOptionsInner, TypeEventResolvePathResult } from '../types/ssr.js';\n\nconst jsRE = /\\.js$/;\nconst cssRE = /\\.css$/;\nconst woffRE = /\\.woff$/;\nconst woff2RE = /\\.woff2$/;\nconst gifRE = /\\.gif$/;\nconst jpgRE = /\\.jpe?g$/;\nconst pngRE = /\\.png$/;\n\n@Service()\nexport class ServiceSsrHandler extends BeanBase {\n private _siteAssetDir: string;\n private _handlerPromise: Promise<any>;\n private _handlerInstance: any;\n private _clientManifest: any;\n private _ssrModulesZovaCache: any = {};\n\n @Use()\n $$sysRouter: SysRouter;\n\n @UseScope()\n $$scopeSsr: ScopeModuleASsr;\n\n protected __init__(siteAssetDir: string) {\n this._siteAssetDir = siteAssetDir;\n }\n\n protected __dispose__() {\n this.dispose();\n }\n\n public dispose() {\n this.sys.close();\n this._handlerInstance = undefined;\n }\n\n async resolvePath(filename: string): Promise<TypeEventResolvePathResult> {\n if (filename === '') filename = 'index.html';\n // assets\n const fileAsset = path.join(this._siteAssetDir, 'client', filename);\n if (await fse.exists(fileAsset)) return fileAsset;\n // not found\n return undefined;\n }\n\n public async render(options: ISsrHandlerRenderOptionsInner) {\n // resolve route\n const pagePathFull = options.state?.pagePathFull ?? this.sys.util.getPagePathFromAbsoluteUrl(options.req.url!);\n const route = await this.$$sysRouter.resolveRoute(pagePathFull, true, false);\n if (!route) return;\n // handler\n const { serverEntry, renderToString, renderTemplate } = this._handlerInstance;\n // ssrContext\n const onRenderedList: Function[] = [];\n const ssrContext = {\n ...options,\n _meta: {} as any,\n onRendered: fn => {\n onRenderedList.push(fn);\n },\n };\n // render\n const renderFn = await serverEntry(ssrContext);\n const [runtimePageContent, err] = await catchError(() => {\n return renderToString(renderFn, ssrContext);\n });\n\n const error = ssrContext._meta.renderError ?? err;\n onRenderedList.forEach(fn => {\n fn(error);\n });\n cast(ssrContext).rendered(error);\n if (error) {\n if (error instanceof Error) throw error;\n return error;\n }\n\n ssrContext._meta.runtimePageContent = runtimePageContent;\n\n // @vitejs/plugin-vue injects code into a component's setup() that registers\n // itself on ctx.modules. After the render, ctx.modules would contain all the\n // components that have been instantiated during this render call.\n ssrContext._meta.endingHeadTags += this._renderModulesPreload_zova(cast(ssrContext).modules, { ssrContext });\n ssrContext._meta.endingHeadTags += this._renderModulesPreload(cast(ssrContext).modules, { ssrContext });\n\n const html = renderTemplate(ssrContext);\n\n // transferCache\n await this._renderTransferCache(options, route);\n\n // todo: ssg\n\n return html;\n }\n\n public async ensureReady(handlerNonce: number) {\n if (!this._handlerInstance) {\n if (!this._handlerPromise) {\n this._handlerPromise = this._prepareHandler(handlerNonce);\n }\n this._handlerInstance = await this._handlerPromise;\n }\n return this._handlerInstance;\n }\n\n private async _prepareHandler(handlerNonce: number) {\n // handler\n const fileHandler = path.join(this._siteAssetDir, 'handler.js');\n const fileUrl = `${pathToHref(fileHandler)}?${handlerNonce}`;\n const handlerInstance = await import(/* @vite-ignore */ fileUrl);\n // clientManifest\n const fileManifest = path.join(this._siteAssetDir, 'quasar.manifest.json');\n const contentManifest = await fse.readFile(fileManifest, { encoding: 'utf-8' });\n this._clientManifest = JSON.parse(contentManifest);\n // ok\n return handlerInstance;\n }\n\n private _renderModulesPreload(modules, opts) {\n let links = '';\n const seen = new Set();\n\n modules.forEach(id => {\n const files = this._clientManifest[id];\n if (files === void 0) return;\n\n files.forEach(file => {\n if (seen.has(file) === true) return;\n\n seen.add(file);\n const filename = basename(file);\n\n if (this._clientManifest[filename] !== void 0) {\n for (const depFile of this._clientManifest[filename]) {\n if (seen.has(depFile) === false) {\n links += this._renderPreloadTag(depFile, opts);\n seen.add(depFile);\n }\n }\n }\n\n links += this._renderPreloadTag(file, opts);\n });\n });\n\n return links;\n }\n\n private _renderModulesPreload_zova(modules2, opts) {\n let links = '';\n const seen = /* @__PURE__ */ new Set();\n modules2.forEach(id => {\n if (!id.startsWith('@@')) return;\n if (seen.has(id) === true) return;\n let cache = this._ssrModulesZovaCache[id];\n if (!cache) {\n for (const key in this._clientManifest) {\n const prefix = `${id.substring(2)}-`;\n const postfix = '.js';\n if (key.startsWith(prefix) && key.endsWith(postfix)) {\n cache = this._resolveUrlPath(`/assets/${key}`);\n break;\n }\n }\n this._ssrModulesZovaCache[id] = cache;\n }\n links += this._renderPreloadTag(cache, opts);\n seen.add(id);\n });\n return links;\n }\n\n private _resolveUrlPath(url: string) {\n let publicPath = this.sys.env.APP_PUBLIC_PATH;\n if (publicPath) publicPath = `/${publicPath}`;\n return publicPath + url;\n }\n\n private _renderPreloadTag(file: string, _opts) {\n file = this.sys.util.getAbsoluteUrlFromPagePath(file, false, true);\n if (jsRE.test(file) === true) {\n return `<link rel=\"modulepreload\" href=\"${file}\" crossorigin>`;\n }\n\n if (cssRE.test(file) === true) {\n return `<link rel=\"stylesheet\" href=\"${file}\" crossorigin>`;\n }\n\n if (woffRE.test(file) === true) {\n return `<link rel=\"preload\" href=\"${file}\" as=\"font\" type=\"font/woff\" crossorigin>`;\n }\n\n if (woff2RE.test(file) === true) {\n return `<link rel=\"preload\" href=\"${file}\" as=\"font\" type=\"font/woff2\" crossorigin>`;\n }\n\n if (gifRE.test(file) === true) {\n return `<link rel=\"preload\" href=\"${file}\" as=\"image\" type=\"image/gif\" crossorigin>`;\n }\n\n if (jpgRE.test(file) === true) {\n return `<link rel=\"preload\" href=\"${file}\" as=\"image\" type=\"image/jpeg\" crossorigin>`;\n }\n\n if (pngRE.test(file) === true) {\n return `<link rel=\"preload\" href=\"${file}\" as=\"image\" type=\"image/png\" crossorigin>`;\n }\n\n return '';\n }\n\n private async _renderTransferCache(options: ISsrHandlerRenderOptionsInner, route: RouteLocationResolvedGeneric) {\n const { res } = options;\n const transferCache = route.meta.transferCache ?? this.$$scopeSsr.config.transferCache;\n if (transferCache === false) return;\n // expires\n const transferCacheExpires = transferCache.expires ?? 0;\n const expires = typeof transferCacheExpires === 'string' ? ms(transferCacheExpires) / 1000 : transferCacheExpires;\n if (expires === 0) {\n res.appendHeader('cache-control', 'no-cache, no-store, must-revalidate');\n } else {\n res.appendHeader('cache-control', `public, max-age=${expires}`);\n }\n }\n}\n\nexport function pathToHref(fileName: string): string {\n return pathToFileURL(fileName).href;\n // return Path.sep === '\\\\' ? pathToFileURL(fileName).href : fileName;\n}\n","import type { IMonkeySysInitialize } from 'zova';\n\nimport { BeanSimple } from 'zova';\n\nimport { ServiceSsrHandler } from './service/ssrHandler.js';\n\nexport class MonkeySys extends BeanSimple implements IMonkeySysInitialize {\n async sysInitialize() {\n this.sys.meta.$getSsrHandler = async (siteAssetDir: string): Promise<ServiceSsrHandler> => {\n return await this.sys.bean._getBean(ServiceSsrHandler, false, siteAssetDir);\n };\n }\n}\n","// eslint-disable\n/** service: begin */\nexport * from '../service/ssrHandler.js';\n\nimport 'zova-module-a-bean';\ndeclare module 'zova-module-a-bean' {\n \n export interface IServiceRecord {\n 'a-ssrserver:ssrHandler': never;\n }\n\n \n}\ndeclare module 'zova-module-a-ssrserver' {\n \n export interface ServiceSsrHandler {\n /** @internal */\n get scope(): ScopeModuleASsrserver;\n }\n\n export interface ServiceSsrHandler {\n get $beanFullName(): 'a-ssrserver.service.ssrHandler';\n get $onionName(): 'a-ssrserver:ssrHandler';\n \n } \n}\n/** service: end */\n/** service: begin */\nimport { ServiceSsrHandler } from '../service/ssrHandler.js';\nimport 'zova';\ndeclare module 'zova' {\n export interface IBeanRecordGeneral {\n 'a-ssrserver.service.ssrHandler': ServiceSsrHandler;\n }\n}\n/** service: end */\n/** monkeySys: begin */\nexport * from '../monkeySys.js';\n/** monkeySys: end */\n/** scope: begin */\nimport { BeanScopeBase, type BeanScopeUtil } from 'zova';\nimport { Scope } from 'zova-module-a-bean';\n\n@Scope()\nexport class ScopeModuleASsrserver extends BeanScopeBase {}\n\nexport interface ScopeModuleASsrserver {\n util: BeanScopeUtil;\n}\n\nimport 'zova';\ndeclare module 'zova' {\n export interface IBeanScopeRecord {\n 'a-ssrserver': ScopeModuleASsrserver;\n }\n \n \n\n \n\n \n}\n \n/** scope: end */\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,37 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zova-module-a-ssrserver",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"Zova Module"
|
|
7
|
+
],
|
|
8
|
+
"author": "",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"mock",
|
|
12
|
+
"src",
|
|
13
|
+
"icons",
|
|
14
|
+
"assets",
|
|
15
|
+
"rest"
|
|
16
|
+
],
|
|
4
17
|
"type": "module",
|
|
5
|
-
"publishConfig": {
|
|
6
|
-
"access": "public"
|
|
7
|
-
},
|
|
8
18
|
"exports": {
|
|
9
19
|
".": {
|
|
10
20
|
"types": [
|
|
11
21
|
"./src/index.ts",
|
|
12
22
|
"./dist/index.d.ts"
|
|
13
23
|
],
|
|
14
|
-
"
|
|
15
|
-
"import": "./dist/index.js",
|
|
16
|
-
"default": "./src/index.ts"
|
|
24
|
+
"default": "./dist/index.js"
|
|
17
25
|
},
|
|
18
|
-
"
|
|
26
|
+
"./*": "./*"
|
|
19
27
|
},
|
|
20
|
-
"description": "",
|
|
21
|
-
"files": [
|
|
22
|
-
"dist",
|
|
23
|
-
"static",
|
|
24
|
-
"typings"
|
|
25
|
-
],
|
|
26
28
|
"scripts": {
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
29
|
+
"clean": "rimraf dist tsconfig.build.tsbuildinfo",
|
|
30
|
+
"tsc:publish": "npm run clean && zova :bin:buildModule --sourcemap",
|
|
31
|
+
"prepublishOnly": "npm run tsc:publish",
|
|
32
|
+
"prepack": "clean-package",
|
|
33
|
+
"postpack": "clean-package restore && npm run clean"
|
|
31
34
|
},
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
"
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"ms": "^2.1.3"
|
|
37
|
+
},
|
|
38
|
+
"title": "a-ssrserver",
|
|
39
|
+
"zovaModule": {
|
|
40
|
+
"capabilities": {
|
|
41
|
+
"meta": {
|
|
42
|
+
"appMode": "ssr"
|
|
43
|
+
},
|
|
44
|
+
"server": true,
|
|
45
|
+
"monkey": true
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"gitHead": "bf1ceb4582381c74ba41ccf11b89d3b9e7b73628"
|
|
37
49
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// eslint-disable
|
|
2
|
+
/** service: begin */
|
|
3
|
+
export * from '../service/ssrHandler.js';
|
|
4
|
+
|
|
5
|
+
import 'zova-module-a-bean';
|
|
6
|
+
declare module 'zova-module-a-bean' {
|
|
7
|
+
|
|
8
|
+
export interface IServiceRecord {
|
|
9
|
+
'a-ssrserver:ssrHandler': never;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
}
|
|
14
|
+
declare module 'zova-module-a-ssrserver' {
|
|
15
|
+
|
|
16
|
+
export interface ServiceSsrHandler {
|
|
17
|
+
/** @internal */
|
|
18
|
+
get scope(): ScopeModuleASsrserver;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ServiceSsrHandler {
|
|
22
|
+
get $beanFullName(): 'a-ssrserver.service.ssrHandler';
|
|
23
|
+
get $onionName(): 'a-ssrserver:ssrHandler';
|
|
24
|
+
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** service: end */
|
|
28
|
+
/** service: begin */
|
|
29
|
+
import { ServiceSsrHandler } from '../service/ssrHandler.js';
|
|
30
|
+
import 'zova';
|
|
31
|
+
declare module 'zova' {
|
|
32
|
+
export interface IBeanRecordGeneral {
|
|
33
|
+
'a-ssrserver.service.ssrHandler': ServiceSsrHandler;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** service: end */
|
|
37
|
+
/** monkeySys: begin */
|
|
38
|
+
export * from '../monkeySys.js';
|
|
39
|
+
/** monkeySys: end */
|
|
40
|
+
/** scope: begin */
|
|
41
|
+
import { BeanScopeBase, type BeanScopeUtil } from 'zova';
|
|
42
|
+
import { Scope } from 'zova-module-a-bean';
|
|
43
|
+
|
|
44
|
+
@Scope()
|
|
45
|
+
export class ScopeModuleASsrserver extends BeanScopeBase {}
|
|
46
|
+
|
|
47
|
+
export interface ScopeModuleASsrserver {
|
|
48
|
+
util: BeanScopeUtil;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
import 'zova';
|
|
52
|
+
declare module 'zova' {
|
|
53
|
+
export interface IBeanScopeRecord {
|
|
54
|
+
'a-ssrserver': ScopeModuleASsrserver;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** scope: end */
|
package/src/index.ts
ADDED
package/src/monkeySys.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IMonkeySysInitialize } from 'zova';
|
|
2
|
+
|
|
3
|
+
import { BeanSimple } from 'zova';
|
|
4
|
+
|
|
5
|
+
import { ServiceSsrHandler } from './service/ssrHandler.js';
|
|
6
|
+
|
|
7
|
+
export class MonkeySys extends BeanSimple implements IMonkeySysInitialize {
|
|
8
|
+
async sysInitialize() {
|
|
9
|
+
this.sys.meta.$getSsrHandler = async (siteAssetDir: string): Promise<ServiceSsrHandler> => {
|
|
10
|
+
return await this.sys.bean._getBean(ServiceSsrHandler, false, siteAssetDir);
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { catchError } from '@cabloy/utils';
|
|
2
|
+
import { RouteLocationResolvedGeneric } from '@cabloy/vue-router';
|
|
3
|
+
import fse from 'fs-extra';
|
|
4
|
+
import ms from 'ms';
|
|
5
|
+
import path, { basename } from 'node:path';
|
|
6
|
+
import { pathToFileURL } from 'node:url';
|
|
7
|
+
import { BeanBase, cast, Use, UseScope } from 'zova';
|
|
8
|
+
import { Service } from 'zova-module-a-bean';
|
|
9
|
+
import { SysRouter } from 'zova-module-a-router';
|
|
10
|
+
import { ScopeModuleASsr } from 'zova-module-a-ssr';
|
|
11
|
+
|
|
12
|
+
import { ISsrHandlerRenderOptionsInner, TypeEventResolvePathResult } from '../types/ssr.js';
|
|
13
|
+
|
|
14
|
+
const jsRE = /\.js$/;
|
|
15
|
+
const cssRE = /\.css$/;
|
|
16
|
+
const woffRE = /\.woff$/;
|
|
17
|
+
const woff2RE = /\.woff2$/;
|
|
18
|
+
const gifRE = /\.gif$/;
|
|
19
|
+
const jpgRE = /\.jpe?g$/;
|
|
20
|
+
const pngRE = /\.png$/;
|
|
21
|
+
|
|
22
|
+
@Service()
|
|
23
|
+
export class ServiceSsrHandler extends BeanBase {
|
|
24
|
+
private _siteAssetDir: string;
|
|
25
|
+
private _handlerPromise: Promise<any>;
|
|
26
|
+
private _handlerInstance: any;
|
|
27
|
+
private _clientManifest: any;
|
|
28
|
+
private _ssrModulesZovaCache: any = {};
|
|
29
|
+
|
|
30
|
+
@Use()
|
|
31
|
+
$$sysRouter: SysRouter;
|
|
32
|
+
|
|
33
|
+
@UseScope()
|
|
34
|
+
$$scopeSsr: ScopeModuleASsr;
|
|
35
|
+
|
|
36
|
+
protected __init__(siteAssetDir: string) {
|
|
37
|
+
this._siteAssetDir = siteAssetDir;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
protected __dispose__() {
|
|
41
|
+
this.dispose();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public dispose() {
|
|
45
|
+
this.sys.close();
|
|
46
|
+
this._handlerInstance = undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async resolvePath(filename: string): Promise<TypeEventResolvePathResult> {
|
|
50
|
+
if (filename === '') filename = 'index.html';
|
|
51
|
+
// assets
|
|
52
|
+
const fileAsset = path.join(this._siteAssetDir, 'client', filename);
|
|
53
|
+
if (await fse.exists(fileAsset)) return fileAsset;
|
|
54
|
+
// not found
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public async render(options: ISsrHandlerRenderOptionsInner) {
|
|
59
|
+
// resolve route
|
|
60
|
+
const pagePathFull = options.state?.pagePathFull ?? this.sys.util.getPagePathFromAbsoluteUrl(options.req.url!);
|
|
61
|
+
const route = await this.$$sysRouter.resolveRoute(pagePathFull, true, false);
|
|
62
|
+
if (!route) return;
|
|
63
|
+
// handler
|
|
64
|
+
const { serverEntry, renderToString, renderTemplate } = this._handlerInstance;
|
|
65
|
+
// ssrContext
|
|
66
|
+
const onRenderedList: Function[] = [];
|
|
67
|
+
const ssrContext = {
|
|
68
|
+
...options,
|
|
69
|
+
_meta: {} as any,
|
|
70
|
+
onRendered: fn => {
|
|
71
|
+
onRenderedList.push(fn);
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
// render
|
|
75
|
+
const renderFn = await serverEntry(ssrContext);
|
|
76
|
+
const [runtimePageContent, err] = await catchError(() => {
|
|
77
|
+
return renderToString(renderFn, ssrContext);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const error = ssrContext._meta.renderError ?? err;
|
|
81
|
+
onRenderedList.forEach(fn => {
|
|
82
|
+
fn(error);
|
|
83
|
+
});
|
|
84
|
+
cast(ssrContext).rendered(error);
|
|
85
|
+
if (error) {
|
|
86
|
+
if (error instanceof Error) throw error;
|
|
87
|
+
return error;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
ssrContext._meta.runtimePageContent = runtimePageContent;
|
|
91
|
+
|
|
92
|
+
// @vitejs/plugin-vue injects code into a component's setup() that registers
|
|
93
|
+
// itself on ctx.modules. After the render, ctx.modules would contain all the
|
|
94
|
+
// components that have been instantiated during this render call.
|
|
95
|
+
ssrContext._meta.endingHeadTags += this._renderModulesPreload_zova(cast(ssrContext).modules, { ssrContext });
|
|
96
|
+
ssrContext._meta.endingHeadTags += this._renderModulesPreload(cast(ssrContext).modules, { ssrContext });
|
|
97
|
+
|
|
98
|
+
const html = renderTemplate(ssrContext);
|
|
99
|
+
|
|
100
|
+
// transferCache
|
|
101
|
+
await this._renderTransferCache(options, route);
|
|
102
|
+
|
|
103
|
+
// todo: ssg
|
|
104
|
+
|
|
105
|
+
return html;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public async ensureReady(handlerNonce: number) {
|
|
109
|
+
if (!this._handlerInstance) {
|
|
110
|
+
if (!this._handlerPromise) {
|
|
111
|
+
this._handlerPromise = this._prepareHandler(handlerNonce);
|
|
112
|
+
}
|
|
113
|
+
this._handlerInstance = await this._handlerPromise;
|
|
114
|
+
}
|
|
115
|
+
return this._handlerInstance;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private async _prepareHandler(handlerNonce: number) {
|
|
119
|
+
// handler
|
|
120
|
+
const fileHandler = path.join(this._siteAssetDir, 'handler.js');
|
|
121
|
+
const fileUrl = `${pathToHref(fileHandler)}?${handlerNonce}`;
|
|
122
|
+
const handlerInstance = await import(/* @vite-ignore */ fileUrl);
|
|
123
|
+
// clientManifest
|
|
124
|
+
const fileManifest = path.join(this._siteAssetDir, 'quasar.manifest.json');
|
|
125
|
+
const contentManifest = await fse.readFile(fileManifest, { encoding: 'utf-8' });
|
|
126
|
+
this._clientManifest = JSON.parse(contentManifest);
|
|
127
|
+
// ok
|
|
128
|
+
return handlerInstance;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private _renderModulesPreload(modules, opts) {
|
|
132
|
+
let links = '';
|
|
133
|
+
const seen = new Set();
|
|
134
|
+
|
|
135
|
+
modules.forEach(id => {
|
|
136
|
+
const files = this._clientManifest[id];
|
|
137
|
+
if (files === void 0) return;
|
|
138
|
+
|
|
139
|
+
files.forEach(file => {
|
|
140
|
+
if (seen.has(file) === true) return;
|
|
141
|
+
|
|
142
|
+
seen.add(file);
|
|
143
|
+
const filename = basename(file);
|
|
144
|
+
|
|
145
|
+
if (this._clientManifest[filename] !== void 0) {
|
|
146
|
+
for (const depFile of this._clientManifest[filename]) {
|
|
147
|
+
if (seen.has(depFile) === false) {
|
|
148
|
+
links += this._renderPreloadTag(depFile, opts);
|
|
149
|
+
seen.add(depFile);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
links += this._renderPreloadTag(file, opts);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
return links;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private _renderModulesPreload_zova(modules2, opts) {
|
|
162
|
+
let links = '';
|
|
163
|
+
const seen = /* @__PURE__ */ new Set();
|
|
164
|
+
modules2.forEach(id => {
|
|
165
|
+
if (!id.startsWith('@@')) return;
|
|
166
|
+
if (seen.has(id) === true) return;
|
|
167
|
+
let cache = this._ssrModulesZovaCache[id];
|
|
168
|
+
if (!cache) {
|
|
169
|
+
for (const key in this._clientManifest) {
|
|
170
|
+
const prefix = `${id.substring(2)}-`;
|
|
171
|
+
const postfix = '.js';
|
|
172
|
+
if (key.startsWith(prefix) && key.endsWith(postfix)) {
|
|
173
|
+
cache = this._resolveUrlPath(`/assets/${key}`);
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
this._ssrModulesZovaCache[id] = cache;
|
|
178
|
+
}
|
|
179
|
+
links += this._renderPreloadTag(cache, opts);
|
|
180
|
+
seen.add(id);
|
|
181
|
+
});
|
|
182
|
+
return links;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private _resolveUrlPath(url: string) {
|
|
186
|
+
let publicPath = this.sys.env.APP_PUBLIC_PATH;
|
|
187
|
+
if (publicPath) publicPath = `/${publicPath}`;
|
|
188
|
+
return publicPath + url;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private _renderPreloadTag(file: string, _opts) {
|
|
192
|
+
file = this.sys.util.getAbsoluteUrlFromPagePath(file, false, true);
|
|
193
|
+
if (jsRE.test(file) === true) {
|
|
194
|
+
return `<link rel="modulepreload" href="${file}" crossorigin>`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (cssRE.test(file) === true) {
|
|
198
|
+
return `<link rel="stylesheet" href="${file}" crossorigin>`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (woffRE.test(file) === true) {
|
|
202
|
+
return `<link rel="preload" href="${file}" as="font" type="font/woff" crossorigin>`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (woff2RE.test(file) === true) {
|
|
206
|
+
return `<link rel="preload" href="${file}" as="font" type="font/woff2" crossorigin>`;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (gifRE.test(file) === true) {
|
|
210
|
+
return `<link rel="preload" href="${file}" as="image" type="image/gif" crossorigin>`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (jpgRE.test(file) === true) {
|
|
214
|
+
return `<link rel="preload" href="${file}" as="image" type="image/jpeg" crossorigin>`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (pngRE.test(file) === true) {
|
|
218
|
+
return `<link rel="preload" href="${file}" as="image" type="image/png" crossorigin>`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return '';
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private async _renderTransferCache(options: ISsrHandlerRenderOptionsInner, route: RouteLocationResolvedGeneric) {
|
|
225
|
+
const { res } = options;
|
|
226
|
+
const transferCache = route.meta.transferCache ?? this.$$scopeSsr.config.transferCache;
|
|
227
|
+
if (transferCache === false) return;
|
|
228
|
+
// expires
|
|
229
|
+
const transferCacheExpires = transferCache.expires ?? 0;
|
|
230
|
+
const expires = typeof transferCacheExpires === 'string' ? ms(transferCacheExpires) / 1000 : transferCacheExpires;
|
|
231
|
+
if (expires === 0) {
|
|
232
|
+
res.appendHeader('cache-control', 'no-cache, no-store, must-revalidate');
|
|
233
|
+
} else {
|
|
234
|
+
res.appendHeader('cache-control', `public, max-age=${expires}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function pathToHref(fileName: string): string {
|
|
240
|
+
return pathToFileURL(fileName).href;
|
|
241
|
+
// return Path.sep === '\\' ? pathToFileURL(fileName).href : fileName;
|
|
242
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ssr.js';
|
package/src/types/ssr.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type http from 'node:http';
|
|
2
|
+
import type { SSRContextState, TypeSsrSitePerformAction } from 'zova-module-a-ssr';
|
|
3
|
+
|
|
4
|
+
import type { ServiceSsrHandler } from '../service/ssrHandler.js';
|
|
5
|
+
import 'zova';
|
|
6
|
+
|
|
7
|
+
export type TypeEventResolvePathResult = string | true | undefined;
|
|
8
|
+
|
|
9
|
+
export interface ISsrHandlerRenderOptionsInner {
|
|
10
|
+
req: http.IncomingMessage;
|
|
11
|
+
res: http.ServerResponse<http.IncomingMessage>;
|
|
12
|
+
performAction?: TypeSsrSitePerformAction;
|
|
13
|
+
state?: SSRContextState;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare module 'zova' {
|
|
17
|
+
export interface SysMeta {
|
|
18
|
+
$getSsrHandler(siteAssetDir: string): Promise<ServiceSsrHandler>;
|
|
19
|
+
}
|
|
20
|
+
}
|
package/README.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# @cabloy/set
|