@finesoft/front 0.1.38 → 0.1.41
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-re7jUCuM.mjs +178 -0
- package/dist/app-re7jUCuM.mjs.map +1 -0
- package/dist/index.d.mts +1276 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2749 -0
- package/dist/index.mjs.map +1 -0
- package/dist/locale-D2Bu7w47.mjs +27 -0
- package/dist/locale-D2Bu7w47.mjs.map +1 -0
- package/dist/rolldown-runtime-wcPFST8Q.mjs +13 -0
- package/dist/server-peer-modules-BSSsxBaF.d.mts +41 -0
- package/dist/server-peer-modules-BSSsxBaF.d.mts.map +1 -0
- package/package.json +21 -32
- package/README.md +0 -1066
- package/dist/app-D4K35MX3.js +0 -10
- package/dist/app-D4K35MX3.js.map +0 -1
- package/dist/browser.cjs +0 -1265
- package/dist/browser.cjs.map +0 -1
- package/dist/browser.d.cts +0 -771
- package/dist/browser.d.ts +0 -771
- package/dist/browser.js +0 -99
- package/dist/browser.js.map +0 -1
- package/dist/chunk-AYO3UUQC.js +0 -118
- package/dist/chunk-AYO3UUQC.js.map +0 -1
- package/dist/chunk-OXKFPW4U.js +0 -824
- package/dist/chunk-OXKFPW4U.js.map +0 -1
- package/dist/chunk-PHDR7PIL.js +0 -382
- package/dist/chunk-PHDR7PIL.js.map +0 -1
- package/dist/chunk-PSPVIVC2.js +0 -25
- package/dist/chunk-PSPVIVC2.js.map +0 -1
- package/dist/chunk-SFGR32K6.js +0 -188
- package/dist/chunk-SFGR32K6.js.map +0 -1
- package/dist/index.cjs +0 -3369
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -570
- package/dist/index.d.ts +0 -570
- package/dist/index.js +0 -1632
- package/dist/index.js.map +0 -1
- package/dist/locale-CAZ4INCX.js +0 -7
- package/dist/locale-CAZ4INCX.js.map +0 -1
- package/dist/src-MVACJWBF.js +0 -21
- package/dist/src-MVACJWBF.js.map +0 -1
package/dist/index.js
DELETED
|
@@ -1,1632 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
History,
|
|
3
|
-
createPrefetchedIntentsFromDom,
|
|
4
|
-
deserializeServerData,
|
|
5
|
-
registerActionHandlers,
|
|
6
|
-
registerExternalUrlHandler,
|
|
7
|
-
registerFlowActionHandler,
|
|
8
|
-
startBrowserApp,
|
|
9
|
-
tryScroll
|
|
10
|
-
} from "./chunk-PHDR7PIL.js";
|
|
11
|
-
import {
|
|
12
|
-
MAX_SSR_DEPTH,
|
|
13
|
-
SSR_DEPTH_HEADER,
|
|
14
|
-
createInternalFetch,
|
|
15
|
-
createSSRApp
|
|
16
|
-
} from "./chunk-SFGR32K6.js";
|
|
17
|
-
import {
|
|
18
|
-
SSR_PLACEHOLDERS,
|
|
19
|
-
createSSRRender,
|
|
20
|
-
injectCSRShell,
|
|
21
|
-
injectSSRContent,
|
|
22
|
-
serializeServerData,
|
|
23
|
-
ssrRender
|
|
24
|
-
} from "./chunk-AYO3UUQC.js";
|
|
25
|
-
import {
|
|
26
|
-
ACTION_KINDS,
|
|
27
|
-
ActionDispatcher,
|
|
28
|
-
BaseController,
|
|
29
|
-
BaseLogger,
|
|
30
|
-
CompositeLogger,
|
|
31
|
-
CompositeLoggerFactory,
|
|
32
|
-
ConsoleLogger,
|
|
33
|
-
ConsoleLoggerFactory,
|
|
34
|
-
Container,
|
|
35
|
-
DEP_KEYS,
|
|
36
|
-
Framework,
|
|
37
|
-
HttpClient,
|
|
38
|
-
HttpError,
|
|
39
|
-
IntentDispatcher,
|
|
40
|
-
LruMap,
|
|
41
|
-
PrefetchedIntents,
|
|
42
|
-
Router,
|
|
43
|
-
buildUrl,
|
|
44
|
-
defineRoutes,
|
|
45
|
-
generateUuid,
|
|
46
|
-
getBaseUrl,
|
|
47
|
-
isCompoundAction,
|
|
48
|
-
isExternalUrlAction,
|
|
49
|
-
isFlowAction,
|
|
50
|
-
isNone,
|
|
51
|
-
isSome,
|
|
52
|
-
makeDependencies,
|
|
53
|
-
makeExternalUrlAction,
|
|
54
|
-
makeFlowAction,
|
|
55
|
-
mapEach,
|
|
56
|
-
pipe,
|
|
57
|
-
pipeAsync,
|
|
58
|
-
removeHost,
|
|
59
|
-
removeQueryParams,
|
|
60
|
-
removeScheme,
|
|
61
|
-
resetFilterCache,
|
|
62
|
-
shouldLog,
|
|
63
|
-
stableStringify
|
|
64
|
-
} from "./chunk-OXKFPW4U.js";
|
|
65
|
-
import {
|
|
66
|
-
parseAcceptLanguage
|
|
67
|
-
} from "./chunk-PSPVIVC2.js";
|
|
68
|
-
|
|
69
|
-
// ../server/src/proxy.ts
|
|
70
|
-
function sanitizeProxyPath(raw) {
|
|
71
|
-
if (raw.startsWith("//")) return null;
|
|
72
|
-
return raw.startsWith("/") ? raw : `/${raw}`;
|
|
73
|
-
}
|
|
74
|
-
function validateConfig(config) {
|
|
75
|
-
if (!config.prefix.startsWith("/")) {
|
|
76
|
-
throw new Error(
|
|
77
|
-
`[proxy] prefix must start with "/": "${config.prefix}"`
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
if (!config.target.startsWith("https://")) {
|
|
81
|
-
throw new Error(`[proxy] target must use HTTPS: "${config.target}"`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
function registerProxyRoutes(app, configs) {
|
|
85
|
-
for (const config of configs) {
|
|
86
|
-
validateConfig(config);
|
|
87
|
-
const methods = config.methods ?? ["all"];
|
|
88
|
-
const pattern = `${config.prefix}/*`;
|
|
89
|
-
const handler = async (c) => {
|
|
90
|
-
const subPath = sanitizeProxyPath(
|
|
91
|
-
c.req.path.replace(config.prefix, "")
|
|
92
|
-
);
|
|
93
|
-
if (!subPath) return c.text("Invalid path", 400);
|
|
94
|
-
const targetUrl = new URL(subPath, config.target);
|
|
95
|
-
const reqUrl = new URL(c.req.url);
|
|
96
|
-
reqUrl.searchParams.forEach(
|
|
97
|
-
(v, k) => targetUrl.searchParams.set(k, v)
|
|
98
|
-
);
|
|
99
|
-
const headers = { ...config.headers };
|
|
100
|
-
if (config.auth) {
|
|
101
|
-
const token = process.env[config.auth.envKey] ?? "";
|
|
102
|
-
if (token) {
|
|
103
|
-
headers.Authorization = config.auth.type === "bearer" ? `Bearer ${token}` : `Basic ${token}`;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
try {
|
|
107
|
-
const resp = await fetch(targetUrl.toString(), {
|
|
108
|
-
headers,
|
|
109
|
-
redirect: config.followRedirects ? "follow" : "manual"
|
|
110
|
-
});
|
|
111
|
-
const body = await resp.text();
|
|
112
|
-
const respHeaders = {
|
|
113
|
-
"Content-Type": resp.headers.get("Content-Type") ?? "application/json"
|
|
114
|
-
};
|
|
115
|
-
if (config.cache) {
|
|
116
|
-
respHeaders["Cache-Control"] = config.cache;
|
|
117
|
-
}
|
|
118
|
-
return c.newResponse(body, resp.status, respHeaders);
|
|
119
|
-
} catch (e) {
|
|
120
|
-
console.error(`[Proxy ${config.prefix}]`, e);
|
|
121
|
-
return c.json({ error: "Proxy request failed" }, 502);
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
for (const method of methods) {
|
|
125
|
-
app[method](pattern, handler);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
function generateProxyCode(configs) {
|
|
130
|
-
if (!configs || configs.length === 0) return "";
|
|
131
|
-
for (const config of configs) {
|
|
132
|
-
validateConfig(config);
|
|
133
|
-
}
|
|
134
|
-
const blocks = [];
|
|
135
|
-
blocks.push(`
|
|
136
|
-
// \u2500\u2500\u2500 \u6846\u67B6\u58F0\u660E\u5F0F\u4EE3\u7406\u8DEF\u7531 \u2500\u2500\u2500
|
|
137
|
-
function _sanitizeProxyPath(raw) {
|
|
138
|
-
if (raw.startsWith("//")) return null;
|
|
139
|
-
return raw.startsWith("/") ? raw : "/" + raw;
|
|
140
|
-
}
|
|
141
|
-
`);
|
|
142
|
-
for (const config of configs) {
|
|
143
|
-
const methods = config.methods ?? ["all"];
|
|
144
|
-
const pattern = `"${config.prefix}/*"`;
|
|
145
|
-
const headersJson = JSON.stringify(config.headers ?? {});
|
|
146
|
-
const cacheStr = config.cache ? JSON.stringify(config.cache) : "null";
|
|
147
|
-
const redirect = config.followRedirects ? '"follow"' : '"manual"';
|
|
148
|
-
let authCode = "";
|
|
149
|
-
if (config.auth) {
|
|
150
|
-
const envKey = JSON.stringify(config.auth.envKey);
|
|
151
|
-
const prefix = config.auth.type === "bearer" ? "Bearer " : "Basic ";
|
|
152
|
-
authCode = `
|
|
153
|
-
const _token = (typeof process !== "undefined" && process.env && process.env[${envKey}]) || "";
|
|
154
|
-
if (_token) _headers.Authorization = "${prefix}" + _token;`;
|
|
155
|
-
}
|
|
156
|
-
const handlerCode = `async (c) => {
|
|
157
|
-
const _sub = _sanitizeProxyPath(c.req.path.replace(${JSON.stringify(
|
|
158
|
-
config.prefix
|
|
159
|
-
)}, ""));
|
|
160
|
-
if (!_sub) return c.text("Invalid path", 400);
|
|
161
|
-
const _target = new URL(_sub, ${JSON.stringify(config.target)});
|
|
162
|
-
const _reqUrl = new URL(c.req.url);
|
|
163
|
-
_reqUrl.searchParams.forEach((v, k) => _target.searchParams.set(k, v));
|
|
164
|
-
const _headers = ${headersJson};${authCode}
|
|
165
|
-
try {
|
|
166
|
-
const _resp = await fetch(_target.toString(), { headers: _headers, redirect: ${redirect} });
|
|
167
|
-
const _body = await _resp.text();
|
|
168
|
-
const _rh = { "Content-Type": _resp.headers.get("Content-Type") || "application/json" };
|
|
169
|
-
if (${cacheStr}) _rh["Cache-Control"] = ${cacheStr};
|
|
170
|
-
return c.newResponse(_body, _resp.status, _rh);
|
|
171
|
-
} catch (_e) {
|
|
172
|
-
console.error("[Proxy ${config.prefix}]", _e);
|
|
173
|
-
return c.json({ error: "Proxy request failed" }, 502);
|
|
174
|
-
}
|
|
175
|
-
}`;
|
|
176
|
-
for (const method of methods) {
|
|
177
|
-
blocks.push(`app.${method}(${pattern}, ${handlerCode});`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
return blocks.join("\n");
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// ../server/src/adapters/shared.ts
|
|
184
|
-
var BUILD_TOOL_EXTERNALS = [
|
|
185
|
-
"vite",
|
|
186
|
-
"esbuild",
|
|
187
|
-
"rollup",
|
|
188
|
-
"fsevents",
|
|
189
|
-
"lightningcss"
|
|
190
|
-
];
|
|
191
|
-
function generateSSREntry(ctx, opts) {
|
|
192
|
-
const setupImport = ctx.setupPath ? `import _setupDefault from "./${ctx.setupPath}";` : ``;
|
|
193
|
-
const setupCall = ctx.setupPath ? `if (typeof _setupDefault === "function") await _setupDefault(app);` : ``;
|
|
194
|
-
const locales = JSON.stringify(ctx.locales);
|
|
195
|
-
const defaultLocale = JSON.stringify(ctx.defaultLocale);
|
|
196
|
-
const renderModes = JSON.stringify(ctx.renderModes ?? {});
|
|
197
|
-
const cacheImpl = opts.platformCache ? opts.platformCache : `
|
|
198
|
-
const ISR_CACHE_MAX = 1000;
|
|
199
|
-
const _isrMap = new Map();
|
|
200
|
-
async function platformCacheGet(url) {
|
|
201
|
-
return _isrMap.get(url) ?? null;
|
|
202
|
-
}
|
|
203
|
-
async function platformCacheSet(url, html) {
|
|
204
|
-
if (_isrMap.size >= ISR_CACHE_MAX) {
|
|
205
|
-
const first = _isrMap.keys().next().value;
|
|
206
|
-
_isrMap.delete(first);
|
|
207
|
-
}
|
|
208
|
-
_isrMap.set(url, html);
|
|
209
|
-
}`;
|
|
210
|
-
return `
|
|
211
|
-
import { Hono } from "hono";
|
|
212
|
-
${opts.platformImport}
|
|
213
|
-
import { render, serializeServerData } from "./${ctx.ssrEntry}";
|
|
214
|
-
${setupImport}
|
|
215
|
-
|
|
216
|
-
const TEMPLATE = ${JSON.stringify(ctx.templateHtml)};
|
|
217
|
-
const LOCALES = ${locales};
|
|
218
|
-
const DEFAULT_LOCALE = ${defaultLocale};
|
|
219
|
-
const RENDER_MODES = ${renderModes};
|
|
220
|
-
${cacheImpl}
|
|
221
|
-
|
|
222
|
-
function parseAcceptLanguage(header) {
|
|
223
|
-
if (!header) return DEFAULT_LOCALE;
|
|
224
|
-
const langs = header.split(",").map(p => {
|
|
225
|
-
const [l, q] = p.trim().split(";q=");
|
|
226
|
-
return { l: l.trim().toLowerCase(), q: q ? (+q || 0) : 1 };
|
|
227
|
-
}).sort((a, b) => b.q - a.q);
|
|
228
|
-
for (const { l } of langs) {
|
|
229
|
-
const prefix = l.split("-")[0];
|
|
230
|
-
if (LOCALES.includes(prefix)) return prefix;
|
|
231
|
-
}
|
|
232
|
-
return DEFAULT_LOCALE;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function injectSSR(t, locale, head, css, html, data) {
|
|
236
|
-
return t
|
|
237
|
-
.replace("<!--ssr-lang-->", locale)
|
|
238
|
-
.replace("<!--ssr-head-->", head + "\\n<style>" + css + "</style>")
|
|
239
|
-
.replace("<!--ssr-body-->", html)
|
|
240
|
-
.replace("<!--ssr-data-->", '<script id="serialized-server-data" type="application/json">' + data + "</script>");
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function injectCSRShell(t, locale) {
|
|
244
|
-
return t
|
|
245
|
-
.replace("<!--ssr-lang-->", locale)
|
|
246
|
-
.replace("<!--ssr-head-->", "")
|
|
247
|
-
.replace("<!--ssr-body-->", "")
|
|
248
|
-
.replace("<!--ssr-data-->", "");
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
function matchRenderMode(url) {
|
|
252
|
-
const path = url.split("?")[0];
|
|
253
|
-
if (RENDER_MODES[path]) return RENDER_MODES[path];
|
|
254
|
-
for (const [pattern, mode] of Object.entries(RENDER_MODES)) {
|
|
255
|
-
if (pattern.includes("*")) {
|
|
256
|
-
const escaped = pattern.replace(/[.+?^\${}()|[\\]\\\\]/g, "\\\\$&");
|
|
257
|
-
const re = new RegExp("^" + escaped.replace(/\\*/g, ".*") + "$");
|
|
258
|
-
if (re.test(path)) return mode;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
return null;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const app = new Hono();
|
|
265
|
-
${generateProxyCode(ctx.proxies ?? [])}
|
|
266
|
-
${setupCall}
|
|
267
|
-
${opts.platformMiddleware ?? ""}
|
|
268
|
-
|
|
269
|
-
// \u5185\u90E8 fetch \u56DE\u73AF\uFF1ASSR \u63A7\u5236\u5668\u7684 fetch \u8BF7\u6C42\u76F4\u63A5\u8D70 Hono \u5185\u5B58\u8DEF\u7531
|
|
270
|
-
// \u6DF1\u5EA6\u901A\u8FC7\u8BF7\u6C42\u5934\u4F20\u9012\uFF0C\u5E76\u53D1\u5B89\u5168\u4E14\u80FD\u8DE8\u6E32\u67D3\u6B63\u786E\u8FFD\u8E2A\u9012\u5F52
|
|
271
|
-
const _SSR_DEPTH_HEADER = "x-ssr-depth";
|
|
272
|
-
const _MAX_SSR_DEPTH = 5;
|
|
273
|
-
|
|
274
|
-
function _createInternalFetch(depth) {
|
|
275
|
-
return function(input, init) {
|
|
276
|
-
if (typeof input === "string" && input.startsWith("/")) {
|
|
277
|
-
const req = new Request("http://localhost" + input, init);
|
|
278
|
-
req.headers.set(_SSR_DEPTH_HEADER, String(depth));
|
|
279
|
-
return app.fetch(req);
|
|
280
|
-
}
|
|
281
|
-
return globalThis.fetch(input, init);
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
app.get("*", async (c) => {
|
|
286
|
-
// \u9012\u5F52\u6DF1\u5EA6\u4FDD\u62A4\uFF1A\u4ECE\u8BF7\u6C42\u5934\u8BFB\u53D6 SSR \u6DF1\u5EA6
|
|
287
|
-
const _ssrDepth = parseInt(c.req.header(_SSR_DEPTH_HEADER) || "0", 10);
|
|
288
|
-
if (_ssrDepth >= _MAX_SSR_DEPTH) {
|
|
289
|
-
return c.text("SSR recursion loop detected", 508);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const url = c.req.path + (c.req.url.includes("?") ? "?" + c.req.url.split("?")[1] : "");
|
|
293
|
-
try {
|
|
294
|
-
const locale = parseAcceptLanguage(c.req.header("accept-language"));
|
|
295
|
-
|
|
296
|
-
// Vite \u914D\u7F6E\u7EA7\u522B\u8986\u76D6: CSR \u76F4\u63A5\u8FD4\u56DE\u7A7A\u58F3
|
|
297
|
-
const overrideMode = matchRenderMode(url);
|
|
298
|
-
if (overrideMode === "csr") {
|
|
299
|
-
return c.html(injectCSRShell(TEMPLATE, locale));
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// ISR \u7F13\u5B58\u547D\u4E2D\uFF08key \u542B locale\uFF0C\u907F\u514D\u8DE8\u8BED\u8A00\u7F13\u5B58\u6C61\u67D3\uFF09
|
|
303
|
-
const _cacheKey = locale + ":" + url;
|
|
304
|
-
const cached = await platformCacheGet(_cacheKey);
|
|
305
|
-
if (cached) return c.html(cached);
|
|
306
|
-
|
|
307
|
-
const { html: appHtml, head, css, serverData, renderMode } = await render(url, locale, { fetch: _createInternalFetch(_ssrDepth + 1) });
|
|
308
|
-
|
|
309
|
-
// \u8DEF\u7531\u7EA7 CSR
|
|
310
|
-
if (renderMode === "csr") {
|
|
311
|
-
return c.html(injectCSRShell(TEMPLATE, locale));
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
const serializedData = serializeServerData(serverData);
|
|
315
|
-
const finalHtml = injectSSR(TEMPLATE, locale, head, css, appHtml, serializedData);
|
|
316
|
-
|
|
317
|
-
// Prerender ISR \u7F13\u5B58\uFF08\u5305\u62EC Vite \u914D\u7F6E\u8986\u76D6\u548C\u8DEF\u7531\u7EA7\uFF09
|
|
318
|
-
if (renderMode === "prerender" || overrideMode === "prerender") {
|
|
319
|
-
await platformCacheSet(_cacheKey, finalHtml);
|
|
320
|
-
${opts.platformPrerenderResponseHook ?? ""}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
return c.html(finalHtml);
|
|
324
|
-
} catch (e) {
|
|
325
|
-
console.error("[SSR Error]", e);
|
|
326
|
-
return c.text("Internal Server Error", 500);
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
${opts.platformExport}
|
|
331
|
-
`;
|
|
332
|
-
}
|
|
333
|
-
async function buildBundle(ctx, opts) {
|
|
334
|
-
await ctx.vite.build({
|
|
335
|
-
root: ctx.root,
|
|
336
|
-
build: {
|
|
337
|
-
ssr: opts.entry,
|
|
338
|
-
outDir: opts.outDir,
|
|
339
|
-
emptyOutDir: true,
|
|
340
|
-
target: opts.target ?? "node18",
|
|
341
|
-
rollupOptions: {
|
|
342
|
-
output: { entryFileNames: opts.fileName ?? "index.mjs" }
|
|
343
|
-
}
|
|
344
|
-
},
|
|
345
|
-
ssr: {
|
|
346
|
-
noExternal: opts.noExternal !== false,
|
|
347
|
-
external: opts.external ?? BUILD_TOOL_EXTERNALS
|
|
348
|
-
},
|
|
349
|
-
resolve: ctx.resolvedResolve,
|
|
350
|
-
css: ctx.resolvedCss
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
function copyStaticAssets(ctx, destDir, opts) {
|
|
354
|
-
const { fs, path } = ctx;
|
|
355
|
-
fs.cpSync(path.resolve(ctx.root, "dist/client"), destDir, {
|
|
356
|
-
recursive: true
|
|
357
|
-
});
|
|
358
|
-
if (opts?.excludeHtml !== false) {
|
|
359
|
-
fs.rmSync(path.join(destDir, "index.html"), { force: true });
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
async function prerenderRoutes(ctx) {
|
|
363
|
-
const { fs, path, root, vite } = ctx;
|
|
364
|
-
const { pathToFileURL } = await import(
|
|
365
|
-
/* @vite-ignore */
|
|
366
|
-
"url"
|
|
367
|
-
);
|
|
368
|
-
const routesExport = ctx.bootstrapEntry ?? "src/lib/bootstrap.ts";
|
|
369
|
-
let routes = [];
|
|
370
|
-
const routesFileExists = fs.existsSync(path.resolve(root, routesExport));
|
|
371
|
-
if (routesFileExists) {
|
|
372
|
-
await vite.build({
|
|
373
|
-
root,
|
|
374
|
-
build: {
|
|
375
|
-
ssr: routesExport,
|
|
376
|
-
outDir: path.resolve(root, "dist/server"),
|
|
377
|
-
emptyOutDir: false,
|
|
378
|
-
rollupOptions: {
|
|
379
|
-
output: { entryFileNames: "_routes_prerender.mjs" }
|
|
380
|
-
}
|
|
381
|
-
},
|
|
382
|
-
resolve: ctx.resolvedResolve
|
|
383
|
-
});
|
|
384
|
-
const routesPath = pathToFileURL(
|
|
385
|
-
path.resolve(root, "dist/server/_routes_prerender.mjs")
|
|
386
|
-
).href;
|
|
387
|
-
const routesMod = await import(
|
|
388
|
-
/* @vite-ignore */
|
|
389
|
-
routesPath
|
|
390
|
-
);
|
|
391
|
-
routes = routesMod.routes ?? routesMod.default ?? [];
|
|
392
|
-
fs.rmSync(path.resolve(root, "dist/server/_routes_prerender.mjs"), {
|
|
393
|
-
force: true
|
|
394
|
-
});
|
|
395
|
-
}
|
|
396
|
-
const prerenderPaths = /* @__PURE__ */ new Set();
|
|
397
|
-
for (const r of routes) {
|
|
398
|
-
if (r.renderMode === "prerender" && r.path && !r.path.includes(":")) {
|
|
399
|
-
prerenderPaths.add(r.path);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
if (ctx.renderModes) {
|
|
403
|
-
for (const [pattern, mode] of Object.entries(ctx.renderModes)) {
|
|
404
|
-
if (mode === "prerender" && !pattern.includes("*") && !pattern.includes(":")) {
|
|
405
|
-
prerenderPaths.add(pattern);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
if (prerenderPaths.size === 0) return [];
|
|
410
|
-
const ssrPath = pathToFileURL(
|
|
411
|
-
path.resolve(root, "dist/server/ssr.js")
|
|
412
|
-
).href;
|
|
413
|
-
const ssrModule = await import(
|
|
414
|
-
/* @vite-ignore */
|
|
415
|
-
ssrPath
|
|
416
|
-
);
|
|
417
|
-
const results = [];
|
|
418
|
-
for (const routePath of prerenderPaths) {
|
|
419
|
-
for (const locale of ctx.locales) {
|
|
420
|
-
const url = locale === ctx.defaultLocale ? routePath : `/${locale}${routePath === "/" ? "" : routePath}`;
|
|
421
|
-
try {
|
|
422
|
-
const {
|
|
423
|
-
html: appHtml,
|
|
424
|
-
head,
|
|
425
|
-
css,
|
|
426
|
-
serverData
|
|
427
|
-
} = await ssrModule.render(url, locale);
|
|
428
|
-
const serializedData = ssrModule.serializeServerData(serverData);
|
|
429
|
-
const finalHtml = ctx.templateHtml.replace("<!--ssr-lang-->", locale).replace(
|
|
430
|
-
"<!--ssr-head-->",
|
|
431
|
-
head + "\n<style>" + css + "</style>"
|
|
432
|
-
).replace("<!--ssr-body-->", appHtml).replace(
|
|
433
|
-
"<!--ssr-data-->",
|
|
434
|
-
'<script id="serialized-server-data" type="application/json">' + serializedData + "</script>"
|
|
435
|
-
);
|
|
436
|
-
results.push({ url, html: finalHtml });
|
|
437
|
-
} catch (e) {
|
|
438
|
-
console.warn(` [prerender] Failed to render ${url}:`, e);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
if (results.length > 0) {
|
|
443
|
-
console.log(
|
|
444
|
-
` Pre-rendered ${results.length} pages (${prerenderPaths.size} routes \xD7 ${ctx.locales.length} locales)
|
|
445
|
-
`
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
return results;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// ../server/src/adapters/cloudflare.ts
|
|
452
|
-
function cloudflareAdapter() {
|
|
453
|
-
return {
|
|
454
|
-
name: "cloudflare",
|
|
455
|
-
async build(ctx) {
|
|
456
|
-
const { fs, path, root } = ctx;
|
|
457
|
-
const outputDir = path.resolve(root, "dist/cloudflare");
|
|
458
|
-
fs.rmSync(outputDir, { recursive: true, force: true });
|
|
459
|
-
const entrySource = generateSSREntry(ctx, {
|
|
460
|
-
platformImport: ``,
|
|
461
|
-
platformExport: `export default app;`,
|
|
462
|
-
// Cloudflare Cache API — 持久化 ISR 缓存到 CDN 边缘节点
|
|
463
|
-
platformCache: `
|
|
464
|
-
const ISR_CACHE_TTL = 3600; // 1 hour
|
|
465
|
-
async function platformCacheGet(url) {
|
|
466
|
-
try {
|
|
467
|
-
const cache = caches.default;
|
|
468
|
-
const cacheKey = new Request("https://isr-cache/" + encodeURIComponent(url));
|
|
469
|
-
const resp = await cache.match(cacheKey);
|
|
470
|
-
if (resp) return await resp.text();
|
|
471
|
-
} catch {}
|
|
472
|
-
return null;
|
|
473
|
-
}
|
|
474
|
-
async function platformCacheSet(url, html) {
|
|
475
|
-
try {
|
|
476
|
-
const cache = caches.default;
|
|
477
|
-
const cacheKey = new Request("https://isr-cache/" + encodeURIComponent(url));
|
|
478
|
-
const resp = new Response(html, {
|
|
479
|
-
headers: { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "public, max-age=" + ISR_CACHE_TTL },
|
|
480
|
-
});
|
|
481
|
-
await cache.put(cacheKey, resp);
|
|
482
|
-
} catch {}
|
|
483
|
-
}`
|
|
484
|
-
});
|
|
485
|
-
const tempEntry = path.resolve(root, ".cf-entry.tmp.mjs");
|
|
486
|
-
fs.writeFileSync(tempEntry, entrySource);
|
|
487
|
-
try {
|
|
488
|
-
await buildBundle(ctx, {
|
|
489
|
-
entry: ".cf-entry.tmp.mjs",
|
|
490
|
-
outDir: outputDir,
|
|
491
|
-
target: "es2022",
|
|
492
|
-
fileName: "_worker.js"
|
|
493
|
-
// 使用默认 external(vite/esbuild/rollup/fsevents/lightningcss)
|
|
494
|
-
// 这些构建工具运行时不需要,且 fsevents 是 macOS .node 原生二进制无法打包
|
|
495
|
-
});
|
|
496
|
-
copyStaticAssets(ctx, path.resolve(outputDir, "assets"));
|
|
497
|
-
const prerendered = await prerenderRoutes(ctx);
|
|
498
|
-
for (const { url, html } of prerendered) {
|
|
499
|
-
const filePath = url === "/" ? path.join(outputDir, "assets", "index.html") : path.join(outputDir, "assets", url, "index.html");
|
|
500
|
-
fs.mkdirSync(path.resolve(filePath, ".."), {
|
|
501
|
-
recursive: true
|
|
502
|
-
});
|
|
503
|
-
fs.writeFileSync(filePath, html);
|
|
504
|
-
}
|
|
505
|
-
} finally {
|
|
506
|
-
fs.rmSync(tempEntry, { force: true });
|
|
507
|
-
}
|
|
508
|
-
console.log(" Cloudflare output \u2192 dist/cloudflare/\n");
|
|
509
|
-
}
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// ../server/src/adapters/netlify.ts
|
|
514
|
-
function netlifyAdapter() {
|
|
515
|
-
return {
|
|
516
|
-
name: "netlify",
|
|
517
|
-
async build(ctx) {
|
|
518
|
-
const { fs, path, root } = ctx;
|
|
519
|
-
const funcDir = path.resolve(
|
|
520
|
-
root,
|
|
521
|
-
".netlify/functions-internal/ssr"
|
|
522
|
-
);
|
|
523
|
-
fs.rmSync(path.resolve(root, ".netlify"), {
|
|
524
|
-
recursive: true,
|
|
525
|
-
force: true
|
|
526
|
-
});
|
|
527
|
-
const entrySource = generateSSREntry(ctx, {
|
|
528
|
-
platformImport: `import { handle } from "hono/netlify";`,
|
|
529
|
-
platformExport: `export default handle(app);
|
|
530
|
-
export const config = { path: "/*", preferStatic: true };`,
|
|
531
|
-
// Netlify CDN 缓存 — 使用 Netlify-CDN-Cache-Control 头做 ISR
|
|
532
|
-
platformCache: `
|
|
533
|
-
const ISR_SWR_TTL = 3600;
|
|
534
|
-
const ISR_CACHE_MAX = 1000;
|
|
535
|
-
const _isrMap = new Map();
|
|
536
|
-
async function platformCacheGet(url) {
|
|
537
|
-
return _isrMap.get(url) ?? null;
|
|
538
|
-
}
|
|
539
|
-
async function platformCacheSet(url, html) {
|
|
540
|
-
if (_isrMap.size >= ISR_CACHE_MAX) {
|
|
541
|
-
const first = _isrMap.keys().next().value;
|
|
542
|
-
_isrMap.delete(first);
|
|
543
|
-
}
|
|
544
|
-
_isrMap.set(url, html);
|
|
545
|
-
}`,
|
|
546
|
-
platformPrerenderResponseHook: `c.header("Cache-Control", "public, max-age=0, must-revalidate");
|
|
547
|
-
c.header("Netlify-CDN-Cache-Control", "public, max-age=" + ISR_SWR_TTL + ", stale-while-revalidate=" + ISR_SWR_TTL + ", durable");`
|
|
548
|
-
});
|
|
549
|
-
const tempEntry = path.resolve(root, ".netlify-entry.tmp.mjs");
|
|
550
|
-
fs.writeFileSync(tempEntry, entrySource);
|
|
551
|
-
try {
|
|
552
|
-
await buildBundle(ctx, {
|
|
553
|
-
entry: ".netlify-entry.tmp.mjs",
|
|
554
|
-
outDir: funcDir,
|
|
555
|
-
target: "node18"
|
|
556
|
-
});
|
|
557
|
-
} finally {
|
|
558
|
-
fs.rmSync(tempEntry, { force: true });
|
|
559
|
-
}
|
|
560
|
-
const redirects = `/* /.netlify/functions/ssr 200
|
|
561
|
-
`;
|
|
562
|
-
fs.writeFileSync(
|
|
563
|
-
path.resolve(root, "dist/client/_redirects"),
|
|
564
|
-
redirects
|
|
565
|
-
);
|
|
566
|
-
const prerendered = await prerenderRoutes(ctx);
|
|
567
|
-
const clientDir = path.resolve(root, "dist/client");
|
|
568
|
-
for (const { url, html } of prerendered) {
|
|
569
|
-
const filePath = url === "/" ? path.join(clientDir, "index.html") : path.join(clientDir, url, "index.html");
|
|
570
|
-
fs.mkdirSync(path.resolve(filePath, ".."), { recursive: true });
|
|
571
|
-
fs.writeFileSync(filePath, html);
|
|
572
|
-
}
|
|
573
|
-
console.log(
|
|
574
|
-
" Netlify output \u2192 .netlify/functions-internal/ssr/\n Publish dir: dist/client/\n"
|
|
575
|
-
);
|
|
576
|
-
}
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// ../server/src/adapters/node.ts
|
|
581
|
-
function nodeAdapter() {
|
|
582
|
-
return {
|
|
583
|
-
name: "node",
|
|
584
|
-
async build(ctx) {
|
|
585
|
-
const { fs, path, root } = ctx;
|
|
586
|
-
const entrySource = generateSSREntry(ctx, {
|
|
587
|
-
platformImport: `import { serve } from "@hono/node-server";
|
|
588
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
589
|
-
import { resolve, dirname } from "node:path";
|
|
590
|
-
import { fileURLToPath } from "node:url";`,
|
|
591
|
-
platformMiddleware: `
|
|
592
|
-
// \u9884\u6E32\u67D3\u6587\u4EF6\u4E2D\u95F4\u4EF6\uFF1A\u68C0\u67E5 dist/prerender/ \u4E0B\u662F\u5426\u6709\u5BF9\u5E94\u7684\u9759\u6001 HTML
|
|
593
|
-
const __entry_dirname = dirname(fileURLToPath(import.meta.url));
|
|
594
|
-
const prerenderDir = resolve(__entry_dirname, "../prerender");
|
|
595
|
-
|
|
596
|
-
app.use("*", async (c, next) => {
|
|
597
|
-
const urlPath = c.req.path;
|
|
598
|
-
const candidates = [
|
|
599
|
-
resolve(prerenderDir, "." + urlPath, "index.html"),
|
|
600
|
-
resolve(prerenderDir, "." + urlPath + ".html"),
|
|
601
|
-
];
|
|
602
|
-
if (urlPath === "/") candidates.unshift(resolve(prerenderDir, "index.html"));
|
|
603
|
-
for (const f of candidates) {
|
|
604
|
-
if (existsSync(f)) {
|
|
605
|
-
const html = readFileSync(f, "utf-8");
|
|
606
|
-
return c.html(html);
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
await next();
|
|
610
|
-
});
|
|
611
|
-
`,
|
|
612
|
-
platformExport: `
|
|
613
|
-
const port = +(process.env.PORT || 3000);
|
|
614
|
-
serve({ fetch: app.fetch, port }, (info) => {
|
|
615
|
-
console.log(\`Server running at http://localhost:\${info.port}\`);
|
|
616
|
-
});
|
|
617
|
-
`
|
|
618
|
-
});
|
|
619
|
-
const tempEntry = path.resolve(root, ".node-entry.tmp.mjs");
|
|
620
|
-
fs.writeFileSync(tempEntry, entrySource);
|
|
621
|
-
try {
|
|
622
|
-
await buildBundle(ctx, {
|
|
623
|
-
entry: ".node-entry.tmp.mjs",
|
|
624
|
-
outDir: path.resolve(root, "dist/server"),
|
|
625
|
-
target: "node18"
|
|
626
|
-
});
|
|
627
|
-
} finally {
|
|
628
|
-
fs.rmSync(tempEntry, { force: true });
|
|
629
|
-
}
|
|
630
|
-
const prerendered = await prerenderRoutes(ctx);
|
|
631
|
-
if (prerendered.length > 0) {
|
|
632
|
-
const prerenderDir = path.resolve(root, "dist/prerender");
|
|
633
|
-
fs.mkdirSync(prerenderDir, { recursive: true });
|
|
634
|
-
for (const { url, html } of prerendered) {
|
|
635
|
-
const filePath = url === "/" ? path.join(prerenderDir, "index.html") : path.join(prerenderDir, url, "index.html");
|
|
636
|
-
fs.mkdirSync(path.resolve(filePath, ".."), {
|
|
637
|
-
recursive: true
|
|
638
|
-
});
|
|
639
|
-
fs.writeFileSync(filePath, html);
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
console.log(
|
|
643
|
-
" Node output \u2192 dist/server/index.mjs\n Run: node dist/server/index.mjs\n"
|
|
644
|
-
);
|
|
645
|
-
}
|
|
646
|
-
};
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
// ../server/src/adapters/static.ts
|
|
650
|
-
function staticAdapter(opts = {}) {
|
|
651
|
-
return {
|
|
652
|
-
name: "static",
|
|
653
|
-
async build(ctx) {
|
|
654
|
-
const { fs, path, root, vite } = ctx;
|
|
655
|
-
const outputDir = path.resolve(root, "dist/static");
|
|
656
|
-
fs.rmSync(outputDir, { recursive: true, force: true });
|
|
657
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
658
|
-
const { pathToFileURL } = await import(
|
|
659
|
-
/* @vite-ignore */
|
|
660
|
-
"url"
|
|
661
|
-
);
|
|
662
|
-
const ssrPath = pathToFileURL(
|
|
663
|
-
path.resolve(root, "dist/server/ssr.js")
|
|
664
|
-
).href;
|
|
665
|
-
const ssrModule = await import(
|
|
666
|
-
/* @vite-ignore */
|
|
667
|
-
ssrPath
|
|
668
|
-
);
|
|
669
|
-
ctx.copyStaticAssets(outputDir, { excludeHtml: true });
|
|
670
|
-
const { paths: routePaths, defs: routeDefs } = await extractRoutesWithModes(ctx, opts);
|
|
671
|
-
const allUrls = [];
|
|
672
|
-
for (const routePath of routePaths) {
|
|
673
|
-
for (const locale of ctx.locales) {
|
|
674
|
-
const url = locale === ctx.defaultLocale ? routePath : `/${locale}${routePath === "/" ? "" : routePath}`;
|
|
675
|
-
allUrls.push(url);
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
console.log(
|
|
679
|
-
` Pre-rendering ${allUrls.length} pages (${routePaths.length} routes \xD7 ${ctx.locales.length} locales)...
|
|
680
|
-
`
|
|
681
|
-
);
|
|
682
|
-
for (const url of allUrls) {
|
|
683
|
-
try {
|
|
684
|
-
const locale = inferLocale(
|
|
685
|
-
url,
|
|
686
|
-
ctx.locales,
|
|
687
|
-
ctx.defaultLocale
|
|
688
|
-
);
|
|
689
|
-
const routeDef = routeDefs.find(
|
|
690
|
-
(r) => r.path === stripLocalePrefix(url, ctx.locales)
|
|
691
|
-
);
|
|
692
|
-
const mode = resolveRenderMode(
|
|
693
|
-
stripLocalePrefix(url, ctx.locales),
|
|
694
|
-
routeDef?.renderMode,
|
|
695
|
-
ctx.renderModes
|
|
696
|
-
);
|
|
697
|
-
let finalHtml;
|
|
698
|
-
if (mode === "csr") {
|
|
699
|
-
finalHtml = injectCSRShellForStatic(
|
|
700
|
-
ctx.templateHtml,
|
|
701
|
-
locale
|
|
702
|
-
);
|
|
703
|
-
} else {
|
|
704
|
-
const {
|
|
705
|
-
html: appHtml,
|
|
706
|
-
head,
|
|
707
|
-
css,
|
|
708
|
-
serverData
|
|
709
|
-
} = await ssrModule.render(url, locale);
|
|
710
|
-
const serializedData = ssrModule.serializeServerData(serverData);
|
|
711
|
-
finalHtml = injectSSRForStatic(
|
|
712
|
-
ctx.templateHtml,
|
|
713
|
-
locale,
|
|
714
|
-
head,
|
|
715
|
-
css,
|
|
716
|
-
appHtml,
|
|
717
|
-
serializedData
|
|
718
|
-
);
|
|
719
|
-
}
|
|
720
|
-
const filePath = url === "/" ? path.join(outputDir, "index.html") : path.join(outputDir, url, "index.html");
|
|
721
|
-
fs.mkdirSync(path.resolve(filePath, ".."), {
|
|
722
|
-
recursive: true
|
|
723
|
-
});
|
|
724
|
-
fs.writeFileSync(filePath, finalHtml);
|
|
725
|
-
} catch (e) {
|
|
726
|
-
console.warn(` [static] Failed to render ${url}:`, e);
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
console.log(` Static output \u2192 dist/static/
|
|
730
|
-
`);
|
|
731
|
-
}
|
|
732
|
-
};
|
|
733
|
-
}
|
|
734
|
-
async function extractRoutesWithModes(ctx, opts) {
|
|
735
|
-
const routesFile = opts.routesExport ?? "src/lib/bootstrap.ts";
|
|
736
|
-
const paths = [];
|
|
737
|
-
const defs = [];
|
|
738
|
-
try {
|
|
739
|
-
const { pathToFileURL } = await import(
|
|
740
|
-
/* @vite-ignore */
|
|
741
|
-
"url"
|
|
742
|
-
);
|
|
743
|
-
await ctx.vite.build({
|
|
744
|
-
root: ctx.root,
|
|
745
|
-
build: {
|
|
746
|
-
ssr: routesFile,
|
|
747
|
-
outDir: ctx.path.resolve(ctx.root, "dist/server"),
|
|
748
|
-
emptyOutDir: false,
|
|
749
|
-
rollupOptions: {
|
|
750
|
-
output: { entryFileNames: "_routes.mjs" }
|
|
751
|
-
}
|
|
752
|
-
},
|
|
753
|
-
resolve: ctx.resolvedResolve
|
|
754
|
-
});
|
|
755
|
-
const routesPath = pathToFileURL(
|
|
756
|
-
ctx.path.resolve(ctx.root, "dist/server/_routes.mjs")
|
|
757
|
-
).href;
|
|
758
|
-
const routesMod = await import(
|
|
759
|
-
/* @vite-ignore */
|
|
760
|
-
routesPath
|
|
761
|
-
);
|
|
762
|
-
const routes = routesMod.routes ?? routesMod.default;
|
|
763
|
-
if (Array.isArray(routes)) {
|
|
764
|
-
for (const r of routes) {
|
|
765
|
-
if (r.path && !r.path.includes(":")) {
|
|
766
|
-
paths.push(r.path);
|
|
767
|
-
defs.push({ path: r.path, renderMode: r.renderMode });
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
ctx.fs.rmSync(ctx.path.resolve(ctx.root, "dist/server/_routes.mjs"), {
|
|
772
|
-
force: true
|
|
773
|
-
});
|
|
774
|
-
} catch (e) {
|
|
775
|
-
console.warn(
|
|
776
|
-
` [static] Could not load routes from "${routesFile}". Using "/" only.`,
|
|
777
|
-
e
|
|
778
|
-
);
|
|
779
|
-
if (paths.length === 0) paths.push("/");
|
|
780
|
-
}
|
|
781
|
-
if (opts.dynamicRoutes) {
|
|
782
|
-
for (const r of opts.dynamicRoutes) {
|
|
783
|
-
if (!paths.includes(r)) paths.push(r);
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
if (paths.length === 0) paths.push("/");
|
|
787
|
-
return { paths, defs };
|
|
788
|
-
}
|
|
789
|
-
function inferLocale(url, locales, defaultLocale) {
|
|
790
|
-
const segments = url.split("/").filter(Boolean);
|
|
791
|
-
if (segments.length > 0 && locales.includes(segments[0])) {
|
|
792
|
-
return segments[0];
|
|
793
|
-
}
|
|
794
|
-
return defaultLocale;
|
|
795
|
-
}
|
|
796
|
-
function injectSSRForStatic(template, locale, head, css, html, serializedData) {
|
|
797
|
-
return template.replace("<!--ssr-lang-->", locale).replace("<!--ssr-head-->", head + "\n<style>" + css + "</style>").replace("<!--ssr-body-->", html).replace(
|
|
798
|
-
"<!--ssr-data-->",
|
|
799
|
-
'<script id="serialized-server-data" type="application/json">' + serializedData + "</script>"
|
|
800
|
-
);
|
|
801
|
-
}
|
|
802
|
-
function injectCSRShellForStatic(template, locale) {
|
|
803
|
-
return template.replace("<!--ssr-lang-->", locale).replace("<!--ssr-head-->", "").replace("<!--ssr-body-->", "").replace("<!--ssr-data-->", "");
|
|
804
|
-
}
|
|
805
|
-
function stripLocalePrefix(url, locales) {
|
|
806
|
-
const segments = url.split("/").filter(Boolean);
|
|
807
|
-
if (segments.length > 0 && locales.includes(segments[0])) {
|
|
808
|
-
const rest = segments.slice(1).join("/");
|
|
809
|
-
return rest ? `/${rest}` : "/";
|
|
810
|
-
}
|
|
811
|
-
return url;
|
|
812
|
-
}
|
|
813
|
-
function resolveRenderMode(routePath, routeRenderMode, renderModes) {
|
|
814
|
-
if (renderModes) {
|
|
815
|
-
if (renderModes[routePath]) return renderModes[routePath];
|
|
816
|
-
for (const [pattern, mode] of Object.entries(renderModes)) {
|
|
817
|
-
if (pattern.includes("*")) {
|
|
818
|
-
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
819
|
-
const re = new RegExp("^" + escaped.replace(/\*/g, ".*") + "$");
|
|
820
|
-
if (re.test(routePath)) return mode;
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
return routeRenderMode ?? "ssr";
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
// ../server/src/adapters/vercel.ts
|
|
828
|
-
function vercelAdapter() {
|
|
829
|
-
return {
|
|
830
|
-
name: "vercel",
|
|
831
|
-
async build(ctx) {
|
|
832
|
-
const { fs, path, root } = ctx;
|
|
833
|
-
const outputDir = path.resolve(root, ".vercel/output");
|
|
834
|
-
fs.rmSync(outputDir, { recursive: true, force: true });
|
|
835
|
-
const entrySource = generateSSREntry(ctx, {
|
|
836
|
-
platformImport: `import { getRequestListener } from "@hono/node-server";`,
|
|
837
|
-
platformExport: [
|
|
838
|
-
`const _listener = getRequestListener(app.fetch);`,
|
|
839
|
-
`export default (req, res) => {`,
|
|
840
|
-
` const m = req.headers["x-now-route-matches"];`,
|
|
841
|
-
` if (typeof m === "string") {`,
|
|
842
|
-
` try {`,
|
|
843
|
-
` const p = new URLSearchParams(m);`,
|
|
844
|
-
` const c = p.get("1");`,
|
|
845
|
-
` if (c != null) {`,
|
|
846
|
-
` const qi = (req.url || "").indexOf("?");`,
|
|
847
|
-
` const qs = qi !== -1 ? req.url.slice(qi) : "";`,
|
|
848
|
-
` req.url = "/" + decodeURIComponent(c) + qs;`,
|
|
849
|
-
` }`,
|
|
850
|
-
` } catch {}`,
|
|
851
|
-
` }`,
|
|
852
|
-
` return _listener(req, res);`,
|
|
853
|
-
`};`
|
|
854
|
-
].join("\n")
|
|
855
|
-
});
|
|
856
|
-
const tempEntry = path.resolve(root, ".vercel-entry.tmp.mjs");
|
|
857
|
-
fs.writeFileSync(tempEntry, entrySource);
|
|
858
|
-
try {
|
|
859
|
-
const funcDir = path.resolve(
|
|
860
|
-
root,
|
|
861
|
-
".vercel/output/functions/ssr.func"
|
|
862
|
-
);
|
|
863
|
-
await buildBundle(ctx, {
|
|
864
|
-
entry: ".vercel-entry.tmp.mjs",
|
|
865
|
-
outDir: funcDir,
|
|
866
|
-
target: "node18"
|
|
867
|
-
});
|
|
868
|
-
fs.writeFileSync(
|
|
869
|
-
path.resolve(funcDir, ".vc-config.json"),
|
|
870
|
-
JSON.stringify(
|
|
871
|
-
{
|
|
872
|
-
runtime: "nodejs20.x",
|
|
873
|
-
handler: "index.mjs",
|
|
874
|
-
launcherType: "Nodejs"
|
|
875
|
-
},
|
|
876
|
-
null,
|
|
877
|
-
2
|
|
878
|
-
)
|
|
879
|
-
);
|
|
880
|
-
copyStaticAssets(
|
|
881
|
-
ctx,
|
|
882
|
-
path.resolve(root, ".vercel/output/static")
|
|
883
|
-
);
|
|
884
|
-
fs.writeFileSync(
|
|
885
|
-
path.resolve(root, ".vercel/output/config.json"),
|
|
886
|
-
JSON.stringify(
|
|
887
|
-
{
|
|
888
|
-
version: 3,
|
|
889
|
-
routes: [
|
|
890
|
-
{ handle: "filesystem" },
|
|
891
|
-
{ src: "/(.*)", dest: "/ssr" }
|
|
892
|
-
]
|
|
893
|
-
},
|
|
894
|
-
null,
|
|
895
|
-
2
|
|
896
|
-
)
|
|
897
|
-
);
|
|
898
|
-
} finally {
|
|
899
|
-
fs.rmSync(tempEntry, { force: true });
|
|
900
|
-
}
|
|
901
|
-
const prerendered = await prerenderRoutes(ctx);
|
|
902
|
-
const staticDir = path.resolve(root, ".vercel/output/static");
|
|
903
|
-
for (const { url, html } of prerendered) {
|
|
904
|
-
const filePath = url === "/" ? path.join(staticDir, "index.html") : path.join(staticDir, url, "index.html");
|
|
905
|
-
fs.mkdirSync(path.resolve(filePath, ".."), { recursive: true });
|
|
906
|
-
fs.writeFileSync(filePath, html);
|
|
907
|
-
}
|
|
908
|
-
if (prerendered.length > 0) {
|
|
909
|
-
const configPath = path.resolve(
|
|
910
|
-
root,
|
|
911
|
-
".vercel/output/config.json"
|
|
912
|
-
);
|
|
913
|
-
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
914
|
-
config.overrides = config.overrides ?? {};
|
|
915
|
-
for (const { url } of prerendered) {
|
|
916
|
-
const key = url === "/" ? "index.html" : `${url.replace(/^\//, "")}/index.html`;
|
|
917
|
-
config.overrides[key] = {
|
|
918
|
-
path: url === "/" ? "/" : url,
|
|
919
|
-
contentType: "text/html; charset=utf-8"
|
|
920
|
-
};
|
|
921
|
-
}
|
|
922
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
923
|
-
}
|
|
924
|
-
console.log(" Vercel output \u2192 .vercel/output/\n");
|
|
925
|
-
}
|
|
926
|
-
};
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
// ../server/src/adapters/resolve.ts
|
|
930
|
-
function resolveAdapter(value) {
|
|
931
|
-
if (typeof value !== "string") return value;
|
|
932
|
-
switch (value) {
|
|
933
|
-
case "vercel":
|
|
934
|
-
return vercelAdapter();
|
|
935
|
-
case "cloudflare":
|
|
936
|
-
return cloudflareAdapter();
|
|
937
|
-
case "netlify":
|
|
938
|
-
return netlifyAdapter();
|
|
939
|
-
case "node":
|
|
940
|
-
return nodeAdapter();
|
|
941
|
-
case "static":
|
|
942
|
-
return staticAdapter();
|
|
943
|
-
case "auto":
|
|
944
|
-
return autoAdapter();
|
|
945
|
-
default:
|
|
946
|
-
throw new Error(
|
|
947
|
-
`[finesoft] Unknown adapter: "${value}". Available: vercel, cloudflare, netlify, node, static, auto`
|
|
948
|
-
);
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
// ../server/src/adapters/auto.ts
|
|
953
|
-
function autoAdapter() {
|
|
954
|
-
return {
|
|
955
|
-
name: "auto",
|
|
956
|
-
async build(ctx) {
|
|
957
|
-
const detected = detectPlatform();
|
|
958
|
-
console.log(` [auto] Detected platform: ${detected}
|
|
959
|
-
`);
|
|
960
|
-
const adapter = resolveAdapter(detected);
|
|
961
|
-
return adapter.build(ctx);
|
|
962
|
-
}
|
|
963
|
-
};
|
|
964
|
-
}
|
|
965
|
-
function detectPlatform() {
|
|
966
|
-
if (process.env.VERCEL) return "vercel";
|
|
967
|
-
if (process.env.CF_PAGES) return "cloudflare";
|
|
968
|
-
if (process.env.NETLIFY) return "netlify";
|
|
969
|
-
return "node";
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
// ../server/src/create-server.ts
|
|
973
|
-
import { Hono as Hono2 } from "hono";
|
|
974
|
-
|
|
975
|
-
// ../server/src/runtime.ts
|
|
976
|
-
function detectRuntime() {
|
|
977
|
-
return {
|
|
978
|
-
isDeno: typeof globalThis.Deno !== "undefined",
|
|
979
|
-
isBun: typeof globalThis.Bun !== "undefined",
|
|
980
|
-
isVercel: !!process.env.VERCEL,
|
|
981
|
-
isProduction: process.env.NODE_ENV === "production"
|
|
982
|
-
};
|
|
983
|
-
}
|
|
984
|
-
async function resolveRoot(importMetaUrl, levelsUp = 0) {
|
|
985
|
-
const isDeno = typeof globalThis.Deno !== "undefined";
|
|
986
|
-
if (isDeno) {
|
|
987
|
-
let url = new URL(importMetaUrl);
|
|
988
|
-
for (let i = 0; i < levelsUp; i++) {
|
|
989
|
-
url = new URL("..", url);
|
|
990
|
-
}
|
|
991
|
-
return url.pathname;
|
|
992
|
-
}
|
|
993
|
-
const { dirname, resolve, normalize } = await import(
|
|
994
|
-
/* @vite-ignore */
|
|
995
|
-
"path"
|
|
996
|
-
);
|
|
997
|
-
const { fileURLToPath } = await import(
|
|
998
|
-
/* @vite-ignore */
|
|
999
|
-
"url"
|
|
1000
|
-
);
|
|
1001
|
-
let dir = normalize(dirname(fileURLToPath(importMetaUrl)));
|
|
1002
|
-
for (let i = 0; i < levelsUp; i++) {
|
|
1003
|
-
dir = resolve(dir, "..");
|
|
1004
|
-
}
|
|
1005
|
-
return dir;
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
// ../server/src/start.ts
|
|
1009
|
-
import { Hono } from "hono";
|
|
1010
|
-
async function startServer(options) {
|
|
1011
|
-
const {
|
|
1012
|
-
app,
|
|
1013
|
-
root,
|
|
1014
|
-
port = 3e3,
|
|
1015
|
-
isProduction,
|
|
1016
|
-
vite,
|
|
1017
|
-
routes,
|
|
1018
|
-
locales,
|
|
1019
|
-
ssrEntryPath
|
|
1020
|
-
} = options;
|
|
1021
|
-
const { isDeno, isBun, isVercel } = options.runtime ?? detectRuntime();
|
|
1022
|
-
function printStartupBanner() {
|
|
1023
|
-
const lines = [
|
|
1024
|
-
`
|
|
1025
|
-
Server running at http://localhost:${port}
|
|
1026
|
-
`
|
|
1027
|
-
];
|
|
1028
|
-
if (routes && routes.length > 0) {
|
|
1029
|
-
lines.push(" Routes:");
|
|
1030
|
-
for (const r of routes) {
|
|
1031
|
-
lines.push(` ${r}`);
|
|
1032
|
-
}
|
|
1033
|
-
lines.push("");
|
|
1034
|
-
}
|
|
1035
|
-
if (locales && locales.length > 0) {
|
|
1036
|
-
lines.push(` Locales: ${locales.join(", ")}`);
|
|
1037
|
-
}
|
|
1038
|
-
if (ssrEntryPath) {
|
|
1039
|
-
lines.push(` SSR Entry: ${ssrEntryPath}`);
|
|
1040
|
-
}
|
|
1041
|
-
if (locales?.length || ssrEntryPath) {
|
|
1042
|
-
lines.push("");
|
|
1043
|
-
}
|
|
1044
|
-
console.log(lines.join("\n"));
|
|
1045
|
-
}
|
|
1046
|
-
if (isVercel) {
|
|
1047
|
-
return { vite };
|
|
1048
|
-
}
|
|
1049
|
-
if (!isProduction) {
|
|
1050
|
-
let devVite = vite;
|
|
1051
|
-
if (!devVite) {
|
|
1052
|
-
const { createServer: createViteServer } = await import(
|
|
1053
|
-
/* @vite-ignore */
|
|
1054
|
-
"vite"
|
|
1055
|
-
);
|
|
1056
|
-
devVite = await createViteServer({
|
|
1057
|
-
root,
|
|
1058
|
-
server: { middlewareMode: true },
|
|
1059
|
-
appType: "custom"
|
|
1060
|
-
});
|
|
1061
|
-
}
|
|
1062
|
-
const { getRequestListener } = await import(
|
|
1063
|
-
/* @vite-ignore */
|
|
1064
|
-
"@hono/node-server"
|
|
1065
|
-
);
|
|
1066
|
-
const { createServer: createServer2 } = await import(
|
|
1067
|
-
/* @vite-ignore */
|
|
1068
|
-
"http"
|
|
1069
|
-
);
|
|
1070
|
-
const listener = getRequestListener(app.fetch);
|
|
1071
|
-
const server = createServer2((req, res) => {
|
|
1072
|
-
devVite.middlewares(req, res, () => listener(req, res));
|
|
1073
|
-
});
|
|
1074
|
-
server.listen(port, () => {
|
|
1075
|
-
printStartupBanner();
|
|
1076
|
-
});
|
|
1077
|
-
return { vite: devVite };
|
|
1078
|
-
}
|
|
1079
|
-
if (isDeno) {
|
|
1080
|
-
globalThis.Deno.serve({ port }, app.fetch);
|
|
1081
|
-
} else if (isBun) {
|
|
1082
|
-
} else {
|
|
1083
|
-
const { serveStatic } = await import(
|
|
1084
|
-
/* @vite-ignore */
|
|
1085
|
-
"@hono/node-server/serve-static"
|
|
1086
|
-
);
|
|
1087
|
-
const { resolve } = await import(
|
|
1088
|
-
/* @vite-ignore */
|
|
1089
|
-
"path"
|
|
1090
|
-
);
|
|
1091
|
-
const prodApp = new Hono();
|
|
1092
|
-
const clientDir = resolve(root, "dist/client");
|
|
1093
|
-
prodApp.use(
|
|
1094
|
-
"/*",
|
|
1095
|
-
serveStatic({
|
|
1096
|
-
root: clientDir,
|
|
1097
|
-
// 禁止目录路径自动提供 index.html,让其 fall through 到 SSR
|
|
1098
|
-
rewriteRequestPath: (path) => path.endsWith("/") ? "/__nosuchfile__" : path
|
|
1099
|
-
})
|
|
1100
|
-
);
|
|
1101
|
-
prodApp.route("/", app);
|
|
1102
|
-
const { serve } = await import(
|
|
1103
|
-
/* @vite-ignore */
|
|
1104
|
-
"@hono/node-server"
|
|
1105
|
-
);
|
|
1106
|
-
serve({ fetch: prodApp.fetch, port }, () => {
|
|
1107
|
-
printStartupBanner();
|
|
1108
|
-
});
|
|
1109
|
-
}
|
|
1110
|
-
return { vite };
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
// ../server/src/create-server.ts
|
|
1114
|
-
async function createServer(config = {}) {
|
|
1115
|
-
const {
|
|
1116
|
-
root: rootOverride,
|
|
1117
|
-
locales,
|
|
1118
|
-
defaultLocale,
|
|
1119
|
-
port = Number(process.env.PORT) || 3e3,
|
|
1120
|
-
setup,
|
|
1121
|
-
proxies,
|
|
1122
|
-
ssr
|
|
1123
|
-
} = config;
|
|
1124
|
-
const root = rootOverride ?? process.cwd();
|
|
1125
|
-
const { existsSync } = await import(
|
|
1126
|
-
/* @vite-ignore */
|
|
1127
|
-
"fs"
|
|
1128
|
-
);
|
|
1129
|
-
const { resolve } = await import(
|
|
1130
|
-
/* @vite-ignore */
|
|
1131
|
-
"path"
|
|
1132
|
-
);
|
|
1133
|
-
const envPath = resolve(root, ".env");
|
|
1134
|
-
if (existsSync(envPath)) {
|
|
1135
|
-
try {
|
|
1136
|
-
const { config: dotenvConfig } = await import(
|
|
1137
|
-
/* @vite-ignore */
|
|
1138
|
-
"dotenv"
|
|
1139
|
-
);
|
|
1140
|
-
dotenvConfig({ path: envPath });
|
|
1141
|
-
} catch {
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
|
-
const runtime = detectRuntime();
|
|
1145
|
-
let vite;
|
|
1146
|
-
if (!runtime.isProduction && !runtime.isVercel) {
|
|
1147
|
-
const { createServer: createViteServer } = await import(
|
|
1148
|
-
/* @vite-ignore */
|
|
1149
|
-
"vite"
|
|
1150
|
-
);
|
|
1151
|
-
vite = await createViteServer({
|
|
1152
|
-
root,
|
|
1153
|
-
server: { middlewareMode: true },
|
|
1154
|
-
appType: "custom"
|
|
1155
|
-
});
|
|
1156
|
-
}
|
|
1157
|
-
const app = new Hono2();
|
|
1158
|
-
if (proxies?.length) {
|
|
1159
|
-
registerProxyRoutes(app, proxies);
|
|
1160
|
-
}
|
|
1161
|
-
if (setup) {
|
|
1162
|
-
await setup(app);
|
|
1163
|
-
}
|
|
1164
|
-
const ssrApp = createSSRApp({
|
|
1165
|
-
root,
|
|
1166
|
-
vite,
|
|
1167
|
-
isProduction: runtime.isProduction,
|
|
1168
|
-
supportedLocales: locales,
|
|
1169
|
-
defaultLocale,
|
|
1170
|
-
parentFetch: app.fetch.bind(app),
|
|
1171
|
-
...ssr
|
|
1172
|
-
});
|
|
1173
|
-
app.route("/", ssrApp);
|
|
1174
|
-
await startServer({
|
|
1175
|
-
app,
|
|
1176
|
-
root,
|
|
1177
|
-
port,
|
|
1178
|
-
isProduction: runtime.isProduction,
|
|
1179
|
-
vite,
|
|
1180
|
-
runtime,
|
|
1181
|
-
locales,
|
|
1182
|
-
ssrEntryPath: ssr?.ssrEntryPath
|
|
1183
|
-
});
|
|
1184
|
-
return { app, vite, runtime };
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
|
-
// ../server/src/vite-plugin.ts
|
|
1188
|
-
function resolveSetupFn(mod) {
|
|
1189
|
-
if (typeof mod.default === "function") return mod.default;
|
|
1190
|
-
if (typeof mod.setup === "function") return mod.setup;
|
|
1191
|
-
const first = Object.values(mod).find((v) => typeof v === "function");
|
|
1192
|
-
return first ?? null;
|
|
1193
|
-
}
|
|
1194
|
-
function matchRenderModeConfig(url, renderModes) {
|
|
1195
|
-
if (!renderModes) return null;
|
|
1196
|
-
const path = url.split("?")[0];
|
|
1197
|
-
if (renderModes[path]) return renderModes[path];
|
|
1198
|
-
for (const [pattern, mode] of Object.entries(renderModes)) {
|
|
1199
|
-
if (pattern.includes("*")) {
|
|
1200
|
-
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
1201
|
-
const re = new RegExp("^" + escaped.replace(/\*/g, ".*") + "$");
|
|
1202
|
-
if (re.test(path)) return mode;
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
return null;
|
|
1206
|
-
}
|
|
1207
|
-
function finesoftFrontViteConfig(options = {}) {
|
|
1208
|
-
const ssrEntry = options.ssr?.entry ?? "src/ssr.ts";
|
|
1209
|
-
let root = process.cwd();
|
|
1210
|
-
let resolvedCommand;
|
|
1211
|
-
let resolvedResolve;
|
|
1212
|
-
let resolvedCss;
|
|
1213
|
-
const CSS_EXTENSIONS = /\.(css|scss|less|sass|styl|stylus|pcss|postcss)($|\?)/;
|
|
1214
|
-
return {
|
|
1215
|
-
name: "finesoft-front",
|
|
1216
|
-
config(userConfig, env) {
|
|
1217
|
-
const overrides = {
|
|
1218
|
-
appType: "custom"
|
|
1219
|
-
};
|
|
1220
|
-
if (env.command === "build" && !process.env.__FINESOFT_SUB_BUILD__) {
|
|
1221
|
-
overrides.build = {
|
|
1222
|
-
outDir: userConfig.build?.outDir ?? "dist/client"
|
|
1223
|
-
};
|
|
1224
|
-
}
|
|
1225
|
-
return overrides;
|
|
1226
|
-
},
|
|
1227
|
-
configResolved(config) {
|
|
1228
|
-
resolvedCommand = config.command;
|
|
1229
|
-
resolvedResolve = config.resolve;
|
|
1230
|
-
resolvedCss = config.css;
|
|
1231
|
-
root = config.root;
|
|
1232
|
-
},
|
|
1233
|
-
/**
|
|
1234
|
-
* Dev 模式 CSS 内联 — 消除 SSR 首屏布局抖动
|
|
1235
|
-
*
|
|
1236
|
-
* Vite dev 模式下,global.scss 等非组件 CSS 通过 JS 模块系统异步加载,
|
|
1237
|
-
* 导致 SSR HTML 初次渲染缺少布局关键样式(box-sizing、flex 布局、padding-top 等)。
|
|
1238
|
-
*
|
|
1239
|
-
* 此 hook 在 HTML 模板变换阶段(SSR 渲染之前):
|
|
1240
|
-
* 1. 找到浏览器入口脚本(排除 /@vite/client 等内部脚本)
|
|
1241
|
-
* 2. 编译入口脚本,填充 Vite 模块图
|
|
1242
|
-
* 3. 遍历模块图收集所有 CSS 依赖(排除 .svelte 组件 CSS,由 SSR 渲染自行处理)
|
|
1243
|
-
* 4. 通过 ssrLoadModule 获取编译后 CSS(SCSS→CSS)
|
|
1244
|
-
* 5. 注入 <style data-vite-dev-id> 标签到 <head>
|
|
1245
|
-
*
|
|
1246
|
-
* data-vite-dev-id 确保 Vite HMR 客户端复用已有标签,避免重复注入。
|
|
1247
|
-
*/
|
|
1248
|
-
transformIndexHtml: {
|
|
1249
|
-
order: "pre",
|
|
1250
|
-
async handler(html, ctx) {
|
|
1251
|
-
const server = ctx.server;
|
|
1252
|
-
if (!server) return;
|
|
1253
|
-
const urlPath = (ctx.originalUrl || ctx.path || "").split(
|
|
1254
|
-
"?"
|
|
1255
|
-
)[0];
|
|
1256
|
-
if (/\.\w+$/.test(urlPath) && !urlPath.endsWith(".html")) {
|
|
1257
|
-
return;
|
|
1258
|
-
}
|
|
1259
|
-
const scripts = [
|
|
1260
|
-
...html.matchAll(
|
|
1261
|
-
/<script\b[^>]*\bsrc=["']([^"']+)["'][^>]*>/g
|
|
1262
|
-
)
|
|
1263
|
-
];
|
|
1264
|
-
const appEntry = scripts.find((m) => !m[1].startsWith("/@"));
|
|
1265
|
-
if (!appEntry) return;
|
|
1266
|
-
const browserEntry = appEntry[1];
|
|
1267
|
-
try {
|
|
1268
|
-
await server.transformRequest(browserEntry);
|
|
1269
|
-
} catch {
|
|
1270
|
-
return;
|
|
1271
|
-
}
|
|
1272
|
-
const cssUrls = [];
|
|
1273
|
-
const visited = /* @__PURE__ */ new Set();
|
|
1274
|
-
function walk(mod) {
|
|
1275
|
-
if (!mod?.url || visited.has(mod.url)) return;
|
|
1276
|
-
visited.add(mod.url);
|
|
1277
|
-
if (CSS_EXTENSIONS.test(mod.url) && !mod.url.includes(".svelte")) {
|
|
1278
|
-
cssUrls.push(mod.url);
|
|
1279
|
-
}
|
|
1280
|
-
if (mod.importedModules) {
|
|
1281
|
-
for (const imported of mod.importedModules) {
|
|
1282
|
-
walk(imported);
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
const mg = server.moduleGraph;
|
|
1287
|
-
const browserMod = await mg.getModuleByUrl(browserEntry);
|
|
1288
|
-
if (browserMod) walk(browserMod);
|
|
1289
|
-
if (cssUrls.length === 0) return;
|
|
1290
|
-
const tags = [];
|
|
1291
|
-
for (const url of cssUrls) {
|
|
1292
|
-
try {
|
|
1293
|
-
const mod = await server.ssrLoadModule(url);
|
|
1294
|
-
const css = mod?.default;
|
|
1295
|
-
if (typeof css === "string" && css.length > 0) {
|
|
1296
|
-
tags.push({
|
|
1297
|
-
tag: "style",
|
|
1298
|
-
attrs: { "data-vite-dev-id": url },
|
|
1299
|
-
children: css,
|
|
1300
|
-
injectTo: "head"
|
|
1301
|
-
});
|
|
1302
|
-
}
|
|
1303
|
-
} catch {
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
return tags;
|
|
1307
|
-
}
|
|
1308
|
-
},
|
|
1309
|
-
// ─── Dev ───────────────────────────────────────────────
|
|
1310
|
-
configureServer(server) {
|
|
1311
|
-
return async () => {
|
|
1312
|
-
const { Hono: HonoClass } = await import(
|
|
1313
|
-
/* @vite-ignore */
|
|
1314
|
-
"hono"
|
|
1315
|
-
);
|
|
1316
|
-
const { createSSRApp: createSSRApp2 } = await import("./app-D4K35MX3.js");
|
|
1317
|
-
const { getRequestListener } = await import(
|
|
1318
|
-
/* @vite-ignore */
|
|
1319
|
-
"@hono/node-server"
|
|
1320
|
-
);
|
|
1321
|
-
const app = new HonoClass();
|
|
1322
|
-
if (options.proxies?.length) {
|
|
1323
|
-
registerProxyRoutes(app, options.proxies);
|
|
1324
|
-
}
|
|
1325
|
-
if (typeof options.setup === "function") {
|
|
1326
|
-
await options.setup(app);
|
|
1327
|
-
} else if (typeof options.setup === "string") {
|
|
1328
|
-
const mod = await server.ssrLoadModule("/" + options.setup);
|
|
1329
|
-
const fn = resolveSetupFn(mod);
|
|
1330
|
-
if (fn) await fn(app);
|
|
1331
|
-
}
|
|
1332
|
-
const ssrApp = createSSRApp2({
|
|
1333
|
-
root,
|
|
1334
|
-
vite: server,
|
|
1335
|
-
isProduction: false,
|
|
1336
|
-
ssrEntryPath: "/" + ssrEntry,
|
|
1337
|
-
supportedLocales: options.locales,
|
|
1338
|
-
defaultLocale: options.defaultLocale,
|
|
1339
|
-
parentFetch: app.fetch.bind(app),
|
|
1340
|
-
renderModes: options.renderModes
|
|
1341
|
-
});
|
|
1342
|
-
app.route("/", ssrApp);
|
|
1343
|
-
const listener = getRequestListener(app.fetch);
|
|
1344
|
-
server.middlewares.use((req, res) => {
|
|
1345
|
-
listener(req, res);
|
|
1346
|
-
});
|
|
1347
|
-
};
|
|
1348
|
-
},
|
|
1349
|
-
// ─── Preview ───────────────────────────────────────────
|
|
1350
|
-
configurePreviewServer(server) {
|
|
1351
|
-
return async () => {
|
|
1352
|
-
const { readFileSync } = await import(
|
|
1353
|
-
/* @vite-ignore */
|
|
1354
|
-
"fs"
|
|
1355
|
-
);
|
|
1356
|
-
const { resolve } = await import(
|
|
1357
|
-
/* @vite-ignore */
|
|
1358
|
-
"path"
|
|
1359
|
-
);
|
|
1360
|
-
const { pathToFileURL } = await import(
|
|
1361
|
-
/* @vite-ignore */
|
|
1362
|
-
"url"
|
|
1363
|
-
);
|
|
1364
|
-
const { Hono: HonoClass } = await import(
|
|
1365
|
-
/* @vite-ignore */
|
|
1366
|
-
"hono"
|
|
1367
|
-
);
|
|
1368
|
-
const { injectSSRContent: injectSSRContent2, injectCSRShell: injectCSRShell2 } = await import(
|
|
1369
|
-
/* @vite-ignore */
|
|
1370
|
-
"./src-MVACJWBF.js"
|
|
1371
|
-
);
|
|
1372
|
-
const { parseAcceptLanguage: parseAcceptLanguage2 } = await import("./locale-CAZ4INCX.js");
|
|
1373
|
-
const { getRequestListener } = await import(
|
|
1374
|
-
/* @vite-ignore */
|
|
1375
|
-
"@hono/node-server"
|
|
1376
|
-
);
|
|
1377
|
-
const app = new HonoClass();
|
|
1378
|
-
const isrCache = /* @__PURE__ */ new Map();
|
|
1379
|
-
if (options.proxies?.length) {
|
|
1380
|
-
registerProxyRoutes(app, options.proxies);
|
|
1381
|
-
}
|
|
1382
|
-
if (typeof options.setup === "function") {
|
|
1383
|
-
await options.setup(app);
|
|
1384
|
-
} else if (typeof options.setup === "string") {
|
|
1385
|
-
try {
|
|
1386
|
-
const setupPath = pathToFileURL(
|
|
1387
|
-
resolve(root, "dist/server/setup.mjs")
|
|
1388
|
-
).href;
|
|
1389
|
-
const mod = await import(
|
|
1390
|
-
/* @vite-ignore */
|
|
1391
|
-
setupPath
|
|
1392
|
-
);
|
|
1393
|
-
const fn = resolveSetupFn(
|
|
1394
|
-
mod
|
|
1395
|
-
);
|
|
1396
|
-
if (fn) await fn(app);
|
|
1397
|
-
} catch {
|
|
1398
|
-
console.warn(
|
|
1399
|
-
"[finesoft] Could not load setup module for preview. API routes disabled."
|
|
1400
|
-
);
|
|
1401
|
-
}
|
|
1402
|
-
}
|
|
1403
|
-
const templatePath = resolve(root, "dist/client/index.html");
|
|
1404
|
-
const template = readFileSync(templatePath, "utf-8");
|
|
1405
|
-
const ssrPath = pathToFileURL(
|
|
1406
|
-
resolve(root, "dist/server/ssr.js")
|
|
1407
|
-
).href;
|
|
1408
|
-
const ssrModule = await import(
|
|
1409
|
-
/* @vite-ignore */
|
|
1410
|
-
ssrPath
|
|
1411
|
-
);
|
|
1412
|
-
app.get("*", async (c) => {
|
|
1413
|
-
const ssrDepth = parseInt(
|
|
1414
|
-
c.req.header(SSR_DEPTH_HEADER) ?? "0",
|
|
1415
|
-
10
|
|
1416
|
-
);
|
|
1417
|
-
if (ssrDepth >= MAX_SSR_DEPTH) {
|
|
1418
|
-
return c.text("SSR recursion loop detected", 508);
|
|
1419
|
-
}
|
|
1420
|
-
const url = c.req.path + (c.req.url.includes("?") ? "?" + c.req.url.split("?")[1] : "");
|
|
1421
|
-
try {
|
|
1422
|
-
const locale = parseAcceptLanguage2(
|
|
1423
|
-
c.req.header("accept-language"),
|
|
1424
|
-
options.locales,
|
|
1425
|
-
options.defaultLocale
|
|
1426
|
-
);
|
|
1427
|
-
const overrideMode = matchRenderModeConfig(
|
|
1428
|
-
url,
|
|
1429
|
-
options.renderModes
|
|
1430
|
-
);
|
|
1431
|
-
if (overrideMode === "csr") {
|
|
1432
|
-
return c.html(injectCSRShell2(template, locale));
|
|
1433
|
-
}
|
|
1434
|
-
const cacheKey = `${locale}:${url}`;
|
|
1435
|
-
const cached = isrCache.get(cacheKey);
|
|
1436
|
-
if (cached) return c.html(cached);
|
|
1437
|
-
const {
|
|
1438
|
-
html: appHtml,
|
|
1439
|
-
head,
|
|
1440
|
-
css,
|
|
1441
|
-
serverData,
|
|
1442
|
-
renderMode
|
|
1443
|
-
} = await ssrModule.render(url, locale, {
|
|
1444
|
-
fetch: createInternalFetch(
|
|
1445
|
-
app.fetch.bind(app),
|
|
1446
|
-
ssrDepth + 1
|
|
1447
|
-
)
|
|
1448
|
-
});
|
|
1449
|
-
if (renderMode === "csr") {
|
|
1450
|
-
return c.html(injectCSRShell2(template, locale));
|
|
1451
|
-
}
|
|
1452
|
-
const serializedData = ssrModule.serializeServerData(serverData);
|
|
1453
|
-
const finalHtml = injectSSRContent2({
|
|
1454
|
-
template,
|
|
1455
|
-
locale,
|
|
1456
|
-
head,
|
|
1457
|
-
css,
|
|
1458
|
-
html: appHtml,
|
|
1459
|
-
serializedData
|
|
1460
|
-
});
|
|
1461
|
-
if (renderMode === "prerender" || overrideMode === "prerender") {
|
|
1462
|
-
isrCache.set(cacheKey, finalHtml);
|
|
1463
|
-
}
|
|
1464
|
-
return c.html(finalHtml);
|
|
1465
|
-
} catch (e) {
|
|
1466
|
-
console.error("[SSR Preview Error]", e);
|
|
1467
|
-
return c.text("Internal Server Error", 500);
|
|
1468
|
-
}
|
|
1469
|
-
});
|
|
1470
|
-
const listener = getRequestListener(app.fetch);
|
|
1471
|
-
server.middlewares.use((req, res) => {
|
|
1472
|
-
listener(req, res);
|
|
1473
|
-
});
|
|
1474
|
-
};
|
|
1475
|
-
},
|
|
1476
|
-
// ─── Build ─────────────────────────────────────────────
|
|
1477
|
-
async closeBundle() {
|
|
1478
|
-
if (process.env.__FINESOFT_SUB_BUILD__) return;
|
|
1479
|
-
if (resolvedCommand !== "build") return;
|
|
1480
|
-
process.env.__FINESOFT_SUB_BUILD__ = "1";
|
|
1481
|
-
try {
|
|
1482
|
-
const vite = await import(
|
|
1483
|
-
/* @vite-ignore */
|
|
1484
|
-
"vite"
|
|
1485
|
-
);
|
|
1486
|
-
const fs = await import(
|
|
1487
|
-
/* @vite-ignore */
|
|
1488
|
-
"fs"
|
|
1489
|
-
);
|
|
1490
|
-
const path = await import(
|
|
1491
|
-
/* @vite-ignore */
|
|
1492
|
-
"path"
|
|
1493
|
-
);
|
|
1494
|
-
console.log("\n Building SSR bundle...\n");
|
|
1495
|
-
await vite.build({
|
|
1496
|
-
root,
|
|
1497
|
-
build: {
|
|
1498
|
-
ssr: ssrEntry,
|
|
1499
|
-
outDir: "dist/server"
|
|
1500
|
-
},
|
|
1501
|
-
resolve: resolvedResolve,
|
|
1502
|
-
css: resolvedCss
|
|
1503
|
-
});
|
|
1504
|
-
if (typeof options.setup === "string") {
|
|
1505
|
-
console.log(" Building setup module...\n");
|
|
1506
|
-
await vite.build({
|
|
1507
|
-
root,
|
|
1508
|
-
build: {
|
|
1509
|
-
ssr: options.setup,
|
|
1510
|
-
outDir: "dist/server",
|
|
1511
|
-
emptyOutDir: false,
|
|
1512
|
-
rollupOptions: {
|
|
1513
|
-
output: { entryFileNames: "setup.mjs" }
|
|
1514
|
-
}
|
|
1515
|
-
},
|
|
1516
|
-
resolve: resolvedResolve
|
|
1517
|
-
});
|
|
1518
|
-
}
|
|
1519
|
-
if (options.adapter) {
|
|
1520
|
-
const adapter = resolveAdapter(options.adapter);
|
|
1521
|
-
const locales = options.locales ?? ["zh", "en"];
|
|
1522
|
-
const defaultLocale = options.defaultLocale ?? locales[0] ?? "en";
|
|
1523
|
-
const templateHtml = fs.readFileSync(
|
|
1524
|
-
path.resolve(root, "dist/client/index.html"),
|
|
1525
|
-
"utf-8"
|
|
1526
|
-
);
|
|
1527
|
-
const ctx = {
|
|
1528
|
-
root,
|
|
1529
|
-
ssrEntry,
|
|
1530
|
-
setupPath: typeof options.setup === "string" ? options.setup : void 0,
|
|
1531
|
-
bootstrapEntry: options.bootstrapEntry,
|
|
1532
|
-
locales,
|
|
1533
|
-
defaultLocale,
|
|
1534
|
-
templateHtml,
|
|
1535
|
-
renderModes: options.renderModes,
|
|
1536
|
-
proxies: options.proxies,
|
|
1537
|
-
resolvedResolve,
|
|
1538
|
-
resolvedCss,
|
|
1539
|
-
vite,
|
|
1540
|
-
fs,
|
|
1541
|
-
path,
|
|
1542
|
-
generateSSREntry(opts) {
|
|
1543
|
-
return generateSSREntry(ctx, opts);
|
|
1544
|
-
},
|
|
1545
|
-
buildBundle(opts) {
|
|
1546
|
-
return buildBundle(ctx, opts);
|
|
1547
|
-
},
|
|
1548
|
-
copyStaticAssets(destDir, opts) {
|
|
1549
|
-
return copyStaticAssets(ctx, destDir, opts);
|
|
1550
|
-
}
|
|
1551
|
-
};
|
|
1552
|
-
console.log(` Running adapter: ${adapter.name}...
|
|
1553
|
-
`);
|
|
1554
|
-
await adapter.build(ctx);
|
|
1555
|
-
}
|
|
1556
|
-
} finally {
|
|
1557
|
-
delete process.env.__FINESOFT_SUB_BUILD__;
|
|
1558
|
-
}
|
|
1559
|
-
}
|
|
1560
|
-
};
|
|
1561
|
-
}
|
|
1562
|
-
export {
|
|
1563
|
-
ACTION_KINDS,
|
|
1564
|
-
ActionDispatcher,
|
|
1565
|
-
BaseController,
|
|
1566
|
-
BaseLogger,
|
|
1567
|
-
CompositeLogger,
|
|
1568
|
-
CompositeLoggerFactory,
|
|
1569
|
-
ConsoleLogger,
|
|
1570
|
-
ConsoleLoggerFactory,
|
|
1571
|
-
Container,
|
|
1572
|
-
DEP_KEYS,
|
|
1573
|
-
Framework,
|
|
1574
|
-
History,
|
|
1575
|
-
HttpClient,
|
|
1576
|
-
HttpError,
|
|
1577
|
-
IntentDispatcher,
|
|
1578
|
-
LruMap,
|
|
1579
|
-
PrefetchedIntents,
|
|
1580
|
-
Router,
|
|
1581
|
-
SSR_PLACEHOLDERS,
|
|
1582
|
-
autoAdapter,
|
|
1583
|
-
buildUrl,
|
|
1584
|
-
cloudflareAdapter,
|
|
1585
|
-
createPrefetchedIntentsFromDom,
|
|
1586
|
-
createSSRApp,
|
|
1587
|
-
createSSRRender,
|
|
1588
|
-
createServer,
|
|
1589
|
-
defineRoutes,
|
|
1590
|
-
deserializeServerData,
|
|
1591
|
-
detectRuntime,
|
|
1592
|
-
finesoftFrontViteConfig,
|
|
1593
|
-
generateProxyCode,
|
|
1594
|
-
generateUuid,
|
|
1595
|
-
getBaseUrl,
|
|
1596
|
-
injectCSRShell,
|
|
1597
|
-
injectSSRContent,
|
|
1598
|
-
isCompoundAction,
|
|
1599
|
-
isExternalUrlAction,
|
|
1600
|
-
isFlowAction,
|
|
1601
|
-
isNone,
|
|
1602
|
-
isSome,
|
|
1603
|
-
makeDependencies,
|
|
1604
|
-
makeExternalUrlAction,
|
|
1605
|
-
makeFlowAction,
|
|
1606
|
-
mapEach,
|
|
1607
|
-
netlifyAdapter,
|
|
1608
|
-
nodeAdapter,
|
|
1609
|
-
parseAcceptLanguage,
|
|
1610
|
-
pipe,
|
|
1611
|
-
pipeAsync,
|
|
1612
|
-
registerActionHandlers,
|
|
1613
|
-
registerExternalUrlHandler,
|
|
1614
|
-
registerFlowActionHandler,
|
|
1615
|
-
registerProxyRoutes,
|
|
1616
|
-
removeHost,
|
|
1617
|
-
removeQueryParams,
|
|
1618
|
-
removeScheme,
|
|
1619
|
-
resetFilterCache,
|
|
1620
|
-
resolveAdapter,
|
|
1621
|
-
resolveRoot,
|
|
1622
|
-
serializeServerData,
|
|
1623
|
-
shouldLog,
|
|
1624
|
-
ssrRender,
|
|
1625
|
-
stableStringify,
|
|
1626
|
-
startBrowserApp,
|
|
1627
|
-
startServer,
|
|
1628
|
-
staticAdapter,
|
|
1629
|
-
tryScroll,
|
|
1630
|
-
vercelAdapter
|
|
1631
|
-
};
|
|
1632
|
-
//# sourceMappingURL=index.js.map
|