@finesoft/front 0.1.18 → 0.1.20
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-Z3EFLAP2.js +10 -0
- package/dist/browser.cjs +7 -5
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +12 -2
- package/dist/browser.d.ts +12 -2
- package/dist/browser.js +2 -2
- package/dist/{chunk-2VETWTN5.js → chunk-5ZECBECP.js} +18 -3
- package/dist/chunk-5ZECBECP.js.map +1 -0
- package/dist/{chunk-RVKDILGM.js → chunk-BUYWNNNQ.js} +8 -6
- package/dist/chunk-BUYWNNNQ.js.map +1 -0
- package/dist/{chunk-T2AQHAYK.js → chunk-H3RNYNSD.js} +2 -2
- package/dist/{chunk-Z4MHYAS3.js → chunk-SN3OO3DU.js} +15 -3
- package/dist/chunk-SN3OO3DU.js.map +1 -0
- package/dist/index.cjs +296 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -2
- package/dist/index.d.ts +29 -2
- package/dist/index.js +267 -29
- package/dist/index.js.map +1 -1
- package/dist/{src-7D236CLJ.js → src-KUWACLPD.js} +5 -3
- package/package.json +1 -1
- package/dist/app-MYBG3TGV.js +0 -10
- package/dist/chunk-2VETWTN5.js.map +0 -1
- package/dist/chunk-RVKDILGM.js.map +0 -1
- package/dist/chunk-Z4MHYAS3.js.map +0 -1
- /package/dist/{app-MYBG3TGV.js.map → app-Z3EFLAP2.js.map} +0 -0
- /package/dist/{chunk-T2AQHAYK.js.map → chunk-H3RNYNSD.js.map} +0 -0
- /package/dist/{src-7D236CLJ.js.map → src-KUWACLPD.js.map} +0 -0
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FrameworkConfig, Framework, BasePage, PrefetchedIntent } from './browser.cjs';
|
|
2
|
-
export { ACTION_KINDS, Action, ActionDispatcher, ActionHandler, ActionHandlerDependencies, AsyncMapper, BaseController, BaseItem, BaseLogger, BaseShelf, BrowserAppConfig, CompositeLogger, CompositeLoggerFactory, CompoundAction, ConsoleLogger, ConsoleLoggerFactory, Container, DEP_KEYS, ExternalUrlAction, ExternalUrlDependencies, FeatureFlags, FlowAction, FlowActionCallbacks, FlowActionDependencies, History, HttpClient, HttpClientConfig, HttpError, Intent, IntentController, IntentDispatcher, Locale, Logger, LoggerFactory, Logger as LoggerInterface, LruMap, Mapper, MetricsRecorder, Net, None, Optional, PrefetchedIntents, RouteDefinition, RouteMatch, Router, Storage, buildUrl, createPrefetchedIntentsFromDom, defineRoutes, deserializeServerData, generateUuid, getBaseUrl, isCompoundAction, isExternalUrlAction, isFlowAction, isNone, isSome, makeDependencies, makeExternalUrlAction, makeFlowAction, mapEach, pipe, pipeAsync, registerActionHandlers, registerExternalUrlHandler, registerFlowActionHandler, removeHost, removeQueryParams, removeScheme, resetFilterCache, shouldLog, stableStringify, startBrowserApp, tryScroll } from './browser.cjs';
|
|
2
|
+
export { ACTION_KINDS, Action, ActionDispatcher, ActionHandler, ActionHandlerDependencies, AsyncMapper, BaseController, BaseItem, BaseLogger, BaseShelf, BrowserAppConfig, CompositeLogger, CompositeLoggerFactory, CompoundAction, ConsoleLogger, ConsoleLoggerFactory, Container, DEP_KEYS, ExternalUrlAction, ExternalUrlDependencies, FeatureFlags, FlowAction, FlowActionCallbacks, FlowActionDependencies, History, HttpClient, HttpClientConfig, HttpError, Intent, IntentController, IntentDispatcher, Locale, Logger, LoggerFactory, Logger as LoggerInterface, LruMap, Mapper, MetricsRecorder, Net, None, Optional, PrefetchedIntents, RenderMode, RouteDefinition, RouteMatch, Router, Storage, buildUrl, createPrefetchedIntentsFromDom, defineRoutes, deserializeServerData, generateUuid, getBaseUrl, isCompoundAction, isExternalUrlAction, isFlowAction, isNone, isSome, makeDependencies, makeExternalUrlAction, makeFlowAction, mapEach, pipe, pipeAsync, registerActionHandlers, registerExternalUrlHandler, registerFlowActionHandler, removeHost, removeQueryParams, removeScheme, resetFilterCache, shouldLog, stableStringify, startBrowserApp, tryScroll } from './browser.cjs';
|
|
3
3
|
import * as node_fs from 'node:fs';
|
|
4
4
|
import { Hono } from 'hono';
|
|
5
5
|
import { ViteDevServer } from 'vite';
|
|
@@ -35,6 +35,8 @@ interface SSRRenderResult {
|
|
|
35
35
|
head: string;
|
|
36
36
|
css: string;
|
|
37
37
|
serverData: PrefetchedIntent[];
|
|
38
|
+
/** 该路由的渲染模式(由 Router 返回) */
|
|
39
|
+
renderMode?: string;
|
|
38
40
|
}
|
|
39
41
|
declare function ssrRender(options: SSRRenderOptions): Promise<SSRRenderResult>;
|
|
40
42
|
|
|
@@ -88,6 +90,11 @@ interface InjectSSROptions {
|
|
|
88
90
|
serializedData: string;
|
|
89
91
|
}
|
|
90
92
|
declare function injectSSRContent(options: InjectSSROptions): string;
|
|
93
|
+
/**
|
|
94
|
+
* CSR 空壳注入 — 只替换 lang,清空 body/head/data 占位符
|
|
95
|
+
* 用于 renderMode === "csr" 的路由
|
|
96
|
+
*/
|
|
97
|
+
declare function injectCSRShell(template: string, locale: string): string;
|
|
91
98
|
|
|
92
99
|
/**
|
|
93
100
|
* serializeServerData — 将 PrefetchedIntents 数据序列化为安全的 JSON
|
|
@@ -121,6 +128,11 @@ interface AdapterContext {
|
|
|
121
128
|
resolvedResolve: unknown;
|
|
122
129
|
/** Vite 的 css 配置 */
|
|
123
130
|
resolvedCss: unknown;
|
|
131
|
+
/**
|
|
132
|
+
* 按路由覆盖的渲染模式。
|
|
133
|
+
* key: 精确路径或 glob 模式,value: "ssr" | "csr" | "prerender"
|
|
134
|
+
*/
|
|
135
|
+
renderModes?: Record<string, string>;
|
|
124
136
|
vite: any;
|
|
125
137
|
fs: typeof node_fs;
|
|
126
138
|
path: {
|
|
@@ -264,6 +276,7 @@ interface SSRModule {
|
|
|
264
276
|
head: string;
|
|
265
277
|
css: string;
|
|
266
278
|
serverData: unknown;
|
|
279
|
+
renderMode?: string;
|
|
267
280
|
}>;
|
|
268
281
|
serializeServerData: (data: unknown) => string;
|
|
269
282
|
}
|
|
@@ -413,6 +426,20 @@ interface FinesoftFrontViteOptions {
|
|
|
413
426
|
* - 不设置则不生成部署产物
|
|
414
427
|
*/
|
|
415
428
|
adapter?: string | Adapter;
|
|
429
|
+
/**
|
|
430
|
+
* 按路由覆盖渲染模式(优先级高于 RouteDefinition.renderMode)。
|
|
431
|
+
* key: 精确路径或 glob 模式,如 "/search" 或 "/blog/*"
|
|
432
|
+
* value: "ssr" | "csr" | "prerender"
|
|
433
|
+
*
|
|
434
|
+
* @example
|
|
435
|
+
* ```ts
|
|
436
|
+
* renderModes: {
|
|
437
|
+
* "/search": "csr",
|
|
438
|
+
* "/blog/*": "prerender",
|
|
439
|
+
* }
|
|
440
|
+
* ```
|
|
441
|
+
*/
|
|
442
|
+
renderModes?: Record<string, "ssr" | "csr" | "prerender">;
|
|
416
443
|
}
|
|
417
444
|
declare function finesoftFrontViteConfig(options?: FinesoftFrontViteOptions): {
|
|
418
445
|
name: string;
|
|
@@ -425,4 +452,4 @@ declare function finesoftFrontViteConfig(options?: FinesoftFrontViteOptions): {
|
|
|
425
452
|
closeBundle(): Promise<void>;
|
|
426
453
|
};
|
|
427
454
|
|
|
428
|
-
export { type Adapter, type AdapterContext, BasePage, type FinesoftFrontViteOptions, Framework, FrameworkConfig, type InjectSSROptions, PrefetchedIntent, type RuntimeInfo, type SSRAppOptions, type SSRModule, type SSRRenderConfig, type SSRRenderOptions, type SSRRenderResult, SSR_PLACEHOLDERS, type ServerConfig, type ServerInstance, type StartServerOptions, autoAdapter, cloudflareAdapter, createSSRApp, createSSRRender, createServer, detectRuntime, finesoftFrontViteConfig, injectSSRContent, netlifyAdapter, nodeAdapter, parseAcceptLanguage, resolveAdapter, resolveRoot, serializeServerData, ssrRender, startServer, staticAdapter, vercelAdapter };
|
|
455
|
+
export { type Adapter, type AdapterContext, BasePage, type FinesoftFrontViteOptions, Framework, FrameworkConfig, type InjectSSROptions, PrefetchedIntent, type RuntimeInfo, type SSRAppOptions, type SSRModule, type SSRRenderConfig, type SSRRenderOptions, type SSRRenderResult, SSR_PLACEHOLDERS, type ServerConfig, type ServerInstance, type StartServerOptions, autoAdapter, cloudflareAdapter, createSSRApp, createSSRRender, createServer, detectRuntime, finesoftFrontViteConfig, injectCSRShell, injectSSRContent, netlifyAdapter, nodeAdapter, parseAcceptLanguage, resolveAdapter, resolveRoot, serializeServerData, ssrRender, startServer, staticAdapter, vercelAdapter };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FrameworkConfig, Framework, BasePage, PrefetchedIntent } from './browser.js';
|
|
2
|
-
export { ACTION_KINDS, Action, ActionDispatcher, ActionHandler, ActionHandlerDependencies, AsyncMapper, BaseController, BaseItem, BaseLogger, BaseShelf, BrowserAppConfig, CompositeLogger, CompositeLoggerFactory, CompoundAction, ConsoleLogger, ConsoleLoggerFactory, Container, DEP_KEYS, ExternalUrlAction, ExternalUrlDependencies, FeatureFlags, FlowAction, FlowActionCallbacks, FlowActionDependencies, History, HttpClient, HttpClientConfig, HttpError, Intent, IntentController, IntentDispatcher, Locale, Logger, LoggerFactory, Logger as LoggerInterface, LruMap, Mapper, MetricsRecorder, Net, None, Optional, PrefetchedIntents, RouteDefinition, RouteMatch, Router, Storage, buildUrl, createPrefetchedIntentsFromDom, defineRoutes, deserializeServerData, generateUuid, getBaseUrl, isCompoundAction, isExternalUrlAction, isFlowAction, isNone, isSome, makeDependencies, makeExternalUrlAction, makeFlowAction, mapEach, pipe, pipeAsync, registerActionHandlers, registerExternalUrlHandler, registerFlowActionHandler, removeHost, removeQueryParams, removeScheme, resetFilterCache, shouldLog, stableStringify, startBrowserApp, tryScroll } from './browser.js';
|
|
2
|
+
export { ACTION_KINDS, Action, ActionDispatcher, ActionHandler, ActionHandlerDependencies, AsyncMapper, BaseController, BaseItem, BaseLogger, BaseShelf, BrowserAppConfig, CompositeLogger, CompositeLoggerFactory, CompoundAction, ConsoleLogger, ConsoleLoggerFactory, Container, DEP_KEYS, ExternalUrlAction, ExternalUrlDependencies, FeatureFlags, FlowAction, FlowActionCallbacks, FlowActionDependencies, History, HttpClient, HttpClientConfig, HttpError, Intent, IntentController, IntentDispatcher, Locale, Logger, LoggerFactory, Logger as LoggerInterface, LruMap, Mapper, MetricsRecorder, Net, None, Optional, PrefetchedIntents, RenderMode, RouteDefinition, RouteMatch, Router, Storage, buildUrl, createPrefetchedIntentsFromDom, defineRoutes, deserializeServerData, generateUuid, getBaseUrl, isCompoundAction, isExternalUrlAction, isFlowAction, isNone, isSome, makeDependencies, makeExternalUrlAction, makeFlowAction, mapEach, pipe, pipeAsync, registerActionHandlers, registerExternalUrlHandler, registerFlowActionHandler, removeHost, removeQueryParams, removeScheme, resetFilterCache, shouldLog, stableStringify, startBrowserApp, tryScroll } from './browser.js';
|
|
3
3
|
import * as node_fs from 'node:fs';
|
|
4
4
|
import { Hono } from 'hono';
|
|
5
5
|
import { ViteDevServer } from 'vite';
|
|
@@ -35,6 +35,8 @@ interface SSRRenderResult {
|
|
|
35
35
|
head: string;
|
|
36
36
|
css: string;
|
|
37
37
|
serverData: PrefetchedIntent[];
|
|
38
|
+
/** 该路由的渲染模式(由 Router 返回) */
|
|
39
|
+
renderMode?: string;
|
|
38
40
|
}
|
|
39
41
|
declare function ssrRender(options: SSRRenderOptions): Promise<SSRRenderResult>;
|
|
40
42
|
|
|
@@ -88,6 +90,11 @@ interface InjectSSROptions {
|
|
|
88
90
|
serializedData: string;
|
|
89
91
|
}
|
|
90
92
|
declare function injectSSRContent(options: InjectSSROptions): string;
|
|
93
|
+
/**
|
|
94
|
+
* CSR 空壳注入 — 只替换 lang,清空 body/head/data 占位符
|
|
95
|
+
* 用于 renderMode === "csr" 的路由
|
|
96
|
+
*/
|
|
97
|
+
declare function injectCSRShell(template: string, locale: string): string;
|
|
91
98
|
|
|
92
99
|
/**
|
|
93
100
|
* serializeServerData — 将 PrefetchedIntents 数据序列化为安全的 JSON
|
|
@@ -121,6 +128,11 @@ interface AdapterContext {
|
|
|
121
128
|
resolvedResolve: unknown;
|
|
122
129
|
/** Vite 的 css 配置 */
|
|
123
130
|
resolvedCss: unknown;
|
|
131
|
+
/**
|
|
132
|
+
* 按路由覆盖的渲染模式。
|
|
133
|
+
* key: 精确路径或 glob 模式,value: "ssr" | "csr" | "prerender"
|
|
134
|
+
*/
|
|
135
|
+
renderModes?: Record<string, string>;
|
|
124
136
|
vite: any;
|
|
125
137
|
fs: typeof node_fs;
|
|
126
138
|
path: {
|
|
@@ -264,6 +276,7 @@ interface SSRModule {
|
|
|
264
276
|
head: string;
|
|
265
277
|
css: string;
|
|
266
278
|
serverData: unknown;
|
|
279
|
+
renderMode?: string;
|
|
267
280
|
}>;
|
|
268
281
|
serializeServerData: (data: unknown) => string;
|
|
269
282
|
}
|
|
@@ -413,6 +426,20 @@ interface FinesoftFrontViteOptions {
|
|
|
413
426
|
* - 不设置则不生成部署产物
|
|
414
427
|
*/
|
|
415
428
|
adapter?: string | Adapter;
|
|
429
|
+
/**
|
|
430
|
+
* 按路由覆盖渲染模式(优先级高于 RouteDefinition.renderMode)。
|
|
431
|
+
* key: 精确路径或 glob 模式,如 "/search" 或 "/blog/*"
|
|
432
|
+
* value: "ssr" | "csr" | "prerender"
|
|
433
|
+
*
|
|
434
|
+
* @example
|
|
435
|
+
* ```ts
|
|
436
|
+
* renderModes: {
|
|
437
|
+
* "/search": "csr",
|
|
438
|
+
* "/blog/*": "prerender",
|
|
439
|
+
* }
|
|
440
|
+
* ```
|
|
441
|
+
*/
|
|
442
|
+
renderModes?: Record<string, "ssr" | "csr" | "prerender">;
|
|
416
443
|
}
|
|
417
444
|
declare function finesoftFrontViteConfig(options?: FinesoftFrontViteOptions): {
|
|
418
445
|
name: string;
|
|
@@ -425,4 +452,4 @@ declare function finesoftFrontViteConfig(options?: FinesoftFrontViteOptions): {
|
|
|
425
452
|
closeBundle(): Promise<void>;
|
|
426
453
|
};
|
|
427
454
|
|
|
428
|
-
export { type Adapter, type AdapterContext, BasePage, type FinesoftFrontViteOptions, Framework, FrameworkConfig, type InjectSSROptions, PrefetchedIntent, type RuntimeInfo, type SSRAppOptions, type SSRModule, type SSRRenderConfig, type SSRRenderOptions, type SSRRenderResult, SSR_PLACEHOLDERS, type ServerConfig, type ServerInstance, type StartServerOptions, autoAdapter, cloudflareAdapter, createSSRApp, createSSRRender, createServer, detectRuntime, finesoftFrontViteConfig, injectSSRContent, netlifyAdapter, nodeAdapter, parseAcceptLanguage, resolveAdapter, resolveRoot, serializeServerData, ssrRender, startServer, staticAdapter, vercelAdapter };
|
|
455
|
+
export { type Adapter, type AdapterContext, BasePage, type FinesoftFrontViteOptions, Framework, FrameworkConfig, type InjectSSROptions, PrefetchedIntent, type RuntimeInfo, type SSRAppOptions, type SSRModule, type SSRRenderConfig, type SSRRenderOptions, type SSRRenderResult, SSR_PLACEHOLDERS, type ServerConfig, type ServerInstance, type StartServerOptions, autoAdapter, cloudflareAdapter, createSSRApp, createSSRRender, createServer, detectRuntime, finesoftFrontViteConfig, injectCSRShell, injectSSRContent, netlifyAdapter, nodeAdapter, parseAcceptLanguage, resolveAdapter, resolveRoot, serializeServerData, ssrRender, startServer, staticAdapter, vercelAdapter };
|
package/dist/index.js
CHANGED
|
@@ -7,17 +7,18 @@ import {
|
|
|
7
7
|
registerFlowActionHandler,
|
|
8
8
|
startBrowserApp,
|
|
9
9
|
tryScroll
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-H3RNYNSD.js";
|
|
11
11
|
import {
|
|
12
12
|
createSSRApp
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-SN3OO3DU.js";
|
|
14
14
|
import {
|
|
15
15
|
SSR_PLACEHOLDERS,
|
|
16
16
|
createSSRRender,
|
|
17
|
+
injectCSRShell,
|
|
17
18
|
injectSSRContent,
|
|
18
19
|
serializeServerData,
|
|
19
20
|
ssrRender
|
|
20
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-5ZECBECP.js";
|
|
21
22
|
import {
|
|
22
23
|
ACTION_KINDS,
|
|
23
24
|
ActionDispatcher,
|
|
@@ -57,7 +58,7 @@ import {
|
|
|
57
58
|
resetFilterCache,
|
|
58
59
|
shouldLog,
|
|
59
60
|
stableStringify
|
|
60
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-BUYWNNNQ.js";
|
|
61
62
|
import {
|
|
62
63
|
parseAcceptLanguage
|
|
63
64
|
} from "./chunk-FYP2ZYYV.js";
|
|
@@ -75,6 +76,7 @@ function generateSSREntry(ctx, opts) {
|
|
|
75
76
|
const setupCall = ctx.setupPath ? `if (typeof _setupDefault === "function") await _setupDefault(app);` : ``;
|
|
76
77
|
const locales = JSON.stringify(ctx.locales);
|
|
77
78
|
const defaultLocale = JSON.stringify(ctx.defaultLocale);
|
|
79
|
+
const renderModes = JSON.stringify(ctx.renderModes ?? {});
|
|
78
80
|
return `
|
|
79
81
|
import { Hono } from "hono";
|
|
80
82
|
${opts.platformImport}
|
|
@@ -84,6 +86,7 @@ ${setupImport}
|
|
|
84
86
|
const TEMPLATE = ${JSON.stringify(ctx.templateHtml)};
|
|
85
87
|
const LOCALES = ${locales};
|
|
86
88
|
const DEFAULT_LOCALE = ${defaultLocale};
|
|
89
|
+
const RENDER_MODES = ${renderModes};
|
|
87
90
|
|
|
88
91
|
function parseAcceptLanguage(header) {
|
|
89
92
|
if (!header) return DEFAULT_LOCALE;
|
|
@@ -106,6 +109,28 @@ function injectSSR(t, locale, head, css, html, data) {
|
|
|
106
109
|
.replace("<!--ssr-data-->", '<script id="serialized-server-data" type="application/json">' + data + "</script>");
|
|
107
110
|
}
|
|
108
111
|
|
|
112
|
+
function injectCSRShell(t, locale) {
|
|
113
|
+
return t
|
|
114
|
+
.replace("<!--ssr-lang-->", locale)
|
|
115
|
+
.replace("<!--ssr-head-->", "")
|
|
116
|
+
.replace("<!--ssr-body-->", "")
|
|
117
|
+
.replace("<!--ssr-data-->", "");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function matchRenderMode(url) {
|
|
121
|
+
const path = url.split("?")[0];
|
|
122
|
+
if (RENDER_MODES[path]) return RENDER_MODES[path];
|
|
123
|
+
for (const [pattern, mode] of Object.entries(RENDER_MODES)) {
|
|
124
|
+
if (pattern.includes("*")) {
|
|
125
|
+
const re = new RegExp("^" + pattern.replace(/\\*/g, ".*") + "$");
|
|
126
|
+
if (re.test(path)) return mode;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const isrCache = new Map();
|
|
133
|
+
|
|
109
134
|
const app = new Hono();
|
|
110
135
|
${setupCall}
|
|
111
136
|
|
|
@@ -113,9 +138,33 @@ app.get("*", async (c) => {
|
|
|
113
138
|
const url = c.req.path + (c.req.url.includes("?") ? "?" + c.req.url.split("?")[1] : "");
|
|
114
139
|
try {
|
|
115
140
|
const locale = parseAcceptLanguage(c.req.header("accept-language"));
|
|
116
|
-
|
|
141
|
+
|
|
142
|
+
// Vite \u914D\u7F6E\u7EA7\u522B\u8986\u76D6: CSR \u76F4\u63A5\u8FD4\u56DE\u7A7A\u58F3
|
|
143
|
+
const overrideMode = matchRenderMode(url);
|
|
144
|
+
if (overrideMode === "csr") {
|
|
145
|
+
return c.html(injectCSRShell(TEMPLATE, locale));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ISR \u7F13\u5B58\u547D\u4E2D
|
|
149
|
+
const cached = isrCache.get(url);
|
|
150
|
+
if (cached) return c.html(cached);
|
|
151
|
+
|
|
152
|
+
const { html: appHtml, head, css, serverData, renderMode } = await render(url, locale);
|
|
153
|
+
|
|
154
|
+
// \u8DEF\u7531\u7EA7 CSR
|
|
155
|
+
if (renderMode === "csr") {
|
|
156
|
+
return c.html(injectCSRShell(TEMPLATE, locale));
|
|
157
|
+
}
|
|
158
|
+
|
|
117
159
|
const serializedData = serializeServerData(serverData);
|
|
118
|
-
|
|
160
|
+
const finalHtml = injectSSR(TEMPLATE, locale, head, css, appHtml, serializedData);
|
|
161
|
+
|
|
162
|
+
// Prerender ISR \u7F13\u5B58\uFF08\u5305\u62EC Vite \u914D\u7F6E\u8986\u76D6\u548C\u8DEF\u7531\u7EA7\uFF09
|
|
163
|
+
if (renderMode === "prerender" || overrideMode === "prerender") {
|
|
164
|
+
isrCache.set(url, finalHtml);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return c.html(finalHtml);
|
|
119
168
|
} catch (e) {
|
|
120
169
|
console.error("[SSR Error]", e);
|
|
121
170
|
return c.text("Internal Server Error", 500);
|
|
@@ -154,6 +203,89 @@ function copyStaticAssets(ctx, destDir, opts) {
|
|
|
154
203
|
fs.rmSync(path.join(destDir, "index.html"), { force: true });
|
|
155
204
|
}
|
|
156
205
|
}
|
|
206
|
+
async function prerenderRoutes(ctx, routesExport = "src/lib/bootstrap.ts") {
|
|
207
|
+
const { fs, path, root, vite } = ctx;
|
|
208
|
+
const { pathToFileURL } = await import(
|
|
209
|
+
/* @vite-ignore */
|
|
210
|
+
"url"
|
|
211
|
+
);
|
|
212
|
+
await vite.build({
|
|
213
|
+
root,
|
|
214
|
+
build: {
|
|
215
|
+
ssr: routesExport,
|
|
216
|
+
outDir: path.resolve(root, "dist/server"),
|
|
217
|
+
emptyOutDir: false,
|
|
218
|
+
rollupOptions: {
|
|
219
|
+
output: { entryFileNames: "_routes_prerender.mjs" }
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
resolve: ctx.resolvedResolve
|
|
223
|
+
});
|
|
224
|
+
const routesPath = pathToFileURL(
|
|
225
|
+
path.resolve(root, "dist/server/_routes_prerender.mjs")
|
|
226
|
+
).href;
|
|
227
|
+
const routesMod = await import(
|
|
228
|
+
/* @vite-ignore */
|
|
229
|
+
routesPath
|
|
230
|
+
);
|
|
231
|
+
const routes = routesMod.routes ?? routesMod.default ?? [];
|
|
232
|
+
fs.rmSync(path.resolve(root, "dist/server/_routes_prerender.mjs"), {
|
|
233
|
+
force: true
|
|
234
|
+
});
|
|
235
|
+
const prerenderPaths = /* @__PURE__ */ new Set();
|
|
236
|
+
for (const r of routes) {
|
|
237
|
+
if (r.renderMode === "prerender" && r.path && !r.path.includes(":")) {
|
|
238
|
+
prerenderPaths.add(r.path);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (ctx.renderModes) {
|
|
242
|
+
for (const [pattern, mode] of Object.entries(ctx.renderModes)) {
|
|
243
|
+
if (mode === "prerender" && !pattern.includes("*") && !pattern.includes(":")) {
|
|
244
|
+
prerenderPaths.add(pattern);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (prerenderPaths.size === 0) return [];
|
|
249
|
+
const ssrPath = pathToFileURL(
|
|
250
|
+
path.resolve(root, "dist/server/ssr.js")
|
|
251
|
+
).href;
|
|
252
|
+
const ssrModule = await import(
|
|
253
|
+
/* @vite-ignore */
|
|
254
|
+
ssrPath
|
|
255
|
+
);
|
|
256
|
+
const results = [];
|
|
257
|
+
for (const routePath of prerenderPaths) {
|
|
258
|
+
for (const locale of ctx.locales) {
|
|
259
|
+
const url = locale === ctx.defaultLocale ? routePath : `/${locale}${routePath === "/" ? "" : routePath}`;
|
|
260
|
+
try {
|
|
261
|
+
const {
|
|
262
|
+
html: appHtml,
|
|
263
|
+
head,
|
|
264
|
+
css,
|
|
265
|
+
serverData
|
|
266
|
+
} = await ssrModule.render(url, locale);
|
|
267
|
+
const serializedData = ssrModule.serializeServerData(serverData);
|
|
268
|
+
const finalHtml = ctx.templateHtml.replace("<!--ssr-lang-->", locale).replace(
|
|
269
|
+
"<!--ssr-head-->",
|
|
270
|
+
head + "\n<style>" + css + "</style>"
|
|
271
|
+
).replace("<!--ssr-body-->", appHtml).replace(
|
|
272
|
+
"<!--ssr-data-->",
|
|
273
|
+
'<script id="serialized-server-data" type="application/json">' + serializedData + "</script>"
|
|
274
|
+
);
|
|
275
|
+
results.push({ url, html: finalHtml });
|
|
276
|
+
} catch (e) {
|
|
277
|
+
console.warn(` [prerender] Failed to render ${url}:`, e);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (results.length > 0) {
|
|
282
|
+
console.log(
|
|
283
|
+
` Pre-rendered ${results.length} pages (${prerenderPaths.size} routes \xD7 ${ctx.locales.length} locales)
|
|
284
|
+
`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
return results;
|
|
288
|
+
}
|
|
157
289
|
|
|
158
290
|
// ../server/src/adapters/cloudflare.ts
|
|
159
291
|
function cloudflareAdapter() {
|
|
@@ -179,6 +311,14 @@ function cloudflareAdapter() {
|
|
|
179
311
|
// 这些构建工具运行时不需要,且 fsevents 是 macOS .node 原生二进制无法打包
|
|
180
312
|
});
|
|
181
313
|
copyStaticAssets(ctx, path.resolve(outputDir, "assets"));
|
|
314
|
+
const prerendered = await prerenderRoutes(ctx);
|
|
315
|
+
for (const { url, html } of prerendered) {
|
|
316
|
+
const filePath = url === "/" ? path.join(outputDir, "assets", "index.html") : path.join(outputDir, "assets", url, "index.html");
|
|
317
|
+
fs.mkdirSync(path.resolve(filePath, ".."), {
|
|
318
|
+
recursive: true
|
|
319
|
+
});
|
|
320
|
+
fs.writeFileSync(filePath, html);
|
|
321
|
+
}
|
|
182
322
|
} finally {
|
|
183
323
|
fs.rmSync(tempEntry, { force: true });
|
|
184
324
|
}
|
|
@@ -222,6 +362,13 @@ function netlifyAdapter() {
|
|
|
222
362
|
path.resolve(root, "dist/client/_redirects"),
|
|
223
363
|
redirects
|
|
224
364
|
);
|
|
365
|
+
const prerendered = await prerenderRoutes(ctx);
|
|
366
|
+
const clientDir = path.resolve(root, "dist/client");
|
|
367
|
+
for (const { url, html } of prerendered) {
|
|
368
|
+
const filePath = url === "/" ? path.join(clientDir, "index.html") : path.join(clientDir, url, "index.html");
|
|
369
|
+
fs.mkdirSync(path.resolve(filePath, ".."), { recursive: true });
|
|
370
|
+
fs.writeFileSync(filePath, html);
|
|
371
|
+
}
|
|
225
372
|
console.log(
|
|
226
373
|
" Netlify output \u2192 .netlify/functions-internal/ssr/\n Publish dir: dist/client/\n"
|
|
227
374
|
);
|
|
@@ -255,6 +402,18 @@ serve({ fetch: app.fetch, port }, (info) => {
|
|
|
255
402
|
} finally {
|
|
256
403
|
fs.rmSync(tempEntry, { force: true });
|
|
257
404
|
}
|
|
405
|
+
const prerendered = await prerenderRoutes(ctx);
|
|
406
|
+
if (prerendered.length > 0) {
|
|
407
|
+
const prerenderDir = path.resolve(root, "dist/prerender");
|
|
408
|
+
fs.mkdirSync(prerenderDir, { recursive: true });
|
|
409
|
+
for (const { url, html } of prerendered) {
|
|
410
|
+
const filePath = url === "/" ? path.join(prerenderDir, "index.html") : path.join(prerenderDir, url, "index.html");
|
|
411
|
+
fs.mkdirSync(path.resolve(filePath, ".."), {
|
|
412
|
+
recursive: true
|
|
413
|
+
});
|
|
414
|
+
fs.writeFileSync(filePath, html);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
258
417
|
console.log(
|
|
259
418
|
" Node output \u2192 dist/server/index.mjs\n Run: node dist/server/index.mjs\n"
|
|
260
419
|
);
|
|
@@ -283,7 +442,7 @@ function staticAdapter(opts = {}) {
|
|
|
283
442
|
ssrPath
|
|
284
443
|
);
|
|
285
444
|
ctx.copyStaticAssets(outputDir, { excludeHtml: true });
|
|
286
|
-
const routePaths = await
|
|
445
|
+
const { paths: routePaths, defs: routeDefs } = await extractRoutesWithModes(ctx, opts);
|
|
287
446
|
const allUrls = [];
|
|
288
447
|
for (const routePath of routePaths) {
|
|
289
448
|
for (const locale of ctx.locales) {
|
|
@@ -302,21 +461,37 @@ function staticAdapter(opts = {}) {
|
|
|
302
461
|
ctx.locales,
|
|
303
462
|
ctx.defaultLocale
|
|
304
463
|
);
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const finalHtml = injectSSRForStatic(
|
|
313
|
-
ctx.templateHtml,
|
|
314
|
-
locale,
|
|
315
|
-
head,
|
|
316
|
-
css,
|
|
317
|
-
appHtml,
|
|
318
|
-
serializedData
|
|
464
|
+
const routeDef = routeDefs.find(
|
|
465
|
+
(r) => r.path === stripLocalePrefix(url, ctx.locales)
|
|
466
|
+
);
|
|
467
|
+
const mode = resolveRenderMode(
|
|
468
|
+
stripLocalePrefix(url, ctx.locales),
|
|
469
|
+
routeDef?.renderMode,
|
|
470
|
+
ctx.renderModes
|
|
319
471
|
);
|
|
472
|
+
let finalHtml;
|
|
473
|
+
if (mode === "csr") {
|
|
474
|
+
finalHtml = injectCSRShellForStatic(
|
|
475
|
+
ctx.templateHtml,
|
|
476
|
+
locale
|
|
477
|
+
);
|
|
478
|
+
} else {
|
|
479
|
+
const {
|
|
480
|
+
html: appHtml,
|
|
481
|
+
head,
|
|
482
|
+
css,
|
|
483
|
+
serverData
|
|
484
|
+
} = await ssrModule.render(url, locale);
|
|
485
|
+
const serializedData = ssrModule.serializeServerData(serverData);
|
|
486
|
+
finalHtml = injectSSRForStatic(
|
|
487
|
+
ctx.templateHtml,
|
|
488
|
+
locale,
|
|
489
|
+
head,
|
|
490
|
+
css,
|
|
491
|
+
appHtml,
|
|
492
|
+
serializedData
|
|
493
|
+
);
|
|
494
|
+
}
|
|
320
495
|
const filePath = url === "/" ? path.join(outputDir, "index.html") : path.join(outputDir, url, "index.html");
|
|
321
496
|
fs.mkdirSync(path.resolve(filePath, ".."), {
|
|
322
497
|
recursive: true
|
|
@@ -331,9 +506,10 @@ function staticAdapter(opts = {}) {
|
|
|
331
506
|
}
|
|
332
507
|
};
|
|
333
508
|
}
|
|
334
|
-
async function
|
|
509
|
+
async function extractRoutesWithModes(ctx, opts) {
|
|
335
510
|
const routesFile = opts.routesExport ?? "src/lib/bootstrap.ts";
|
|
336
511
|
const paths = [];
|
|
512
|
+
const defs = [];
|
|
337
513
|
try {
|
|
338
514
|
const { pathToFileURL } = await import(
|
|
339
515
|
/* @vite-ignore */
|
|
@@ -363,6 +539,7 @@ async function extractRoutes(ctx, opts) {
|
|
|
363
539
|
for (const r of routes) {
|
|
364
540
|
if (r.path && !r.path.includes(":")) {
|
|
365
541
|
paths.push(r.path);
|
|
542
|
+
defs.push({ path: r.path, renderMode: r.renderMode });
|
|
366
543
|
}
|
|
367
544
|
}
|
|
368
545
|
}
|
|
@@ -382,7 +559,7 @@ async function extractRoutes(ctx, opts) {
|
|
|
382
559
|
}
|
|
383
560
|
}
|
|
384
561
|
if (paths.length === 0) paths.push("/");
|
|
385
|
-
return paths;
|
|
562
|
+
return { paths, defs };
|
|
386
563
|
}
|
|
387
564
|
function inferLocale(url, locales, defaultLocale) {
|
|
388
565
|
const segments = url.split("/").filter(Boolean);
|
|
@@ -397,6 +574,29 @@ function injectSSRForStatic(template, locale, head, css, html, serializedData) {
|
|
|
397
574
|
'<script id="serialized-server-data" type="application/json">' + serializedData + "</script>"
|
|
398
575
|
);
|
|
399
576
|
}
|
|
577
|
+
function injectCSRShellForStatic(template, locale) {
|
|
578
|
+
return template.replace("<!--ssr-lang-->", locale).replace("<!--ssr-head-->", "").replace("<!--ssr-body-->", "").replace("<!--ssr-data-->", "");
|
|
579
|
+
}
|
|
580
|
+
function stripLocalePrefix(url, locales) {
|
|
581
|
+
const segments = url.split("/").filter(Boolean);
|
|
582
|
+
if (segments.length > 0 && locales.includes(segments[0])) {
|
|
583
|
+
const rest = segments.slice(1).join("/");
|
|
584
|
+
return rest ? `/${rest}` : "/";
|
|
585
|
+
}
|
|
586
|
+
return url;
|
|
587
|
+
}
|
|
588
|
+
function resolveRenderMode(routePath, routeRenderMode, renderModes) {
|
|
589
|
+
if (renderModes) {
|
|
590
|
+
if (renderModes[routePath]) return renderModes[routePath];
|
|
591
|
+
for (const [pattern, mode] of Object.entries(renderModes)) {
|
|
592
|
+
if (pattern.includes("*")) {
|
|
593
|
+
const re = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
594
|
+
if (re.test(routePath)) return mode;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
return routeRenderMode ?? "ssr";
|
|
599
|
+
}
|
|
400
600
|
|
|
401
601
|
// ../server/src/adapters/vercel.ts
|
|
402
602
|
function vercelAdapter() {
|
|
@@ -407,8 +607,8 @@ function vercelAdapter() {
|
|
|
407
607
|
const outputDir = path.resolve(root, ".vercel/output");
|
|
408
608
|
fs.rmSync(outputDir, { recursive: true, force: true });
|
|
409
609
|
const entrySource = generateSSREntry(ctx, {
|
|
410
|
-
platformImport: `import {
|
|
411
|
-
platformExport: `export default
|
|
610
|
+
platformImport: `import { getRequestListener } from "@hono/node-server";`,
|
|
611
|
+
platformExport: `export default getRequestListener(app.fetch);`
|
|
412
612
|
});
|
|
413
613
|
const tempEntry = path.resolve(root, ".vercel-entry.tmp.mjs");
|
|
414
614
|
fs.writeFileSync(tempEntry, entrySource);
|
|
@@ -455,6 +655,13 @@ function vercelAdapter() {
|
|
|
455
655
|
} finally {
|
|
456
656
|
fs.rmSync(tempEntry, { force: true });
|
|
457
657
|
}
|
|
658
|
+
const prerendered = await prerenderRoutes(ctx);
|
|
659
|
+
const staticDir = path.resolve(root, ".vercel/output/static");
|
|
660
|
+
for (const { url, html } of prerendered) {
|
|
661
|
+
const filePath = url === "/" ? path.join(staticDir, "index.html") : path.join(staticDir, url, "index.html");
|
|
662
|
+
fs.mkdirSync(path.resolve(filePath, ".."), { recursive: true });
|
|
663
|
+
fs.writeFileSync(filePath, html);
|
|
664
|
+
}
|
|
458
665
|
console.log(" Vercel output \u2192 .vercel/output/\n");
|
|
459
666
|
}
|
|
460
667
|
};
|
|
@@ -720,6 +927,18 @@ function resolveSetupFn(mod) {
|
|
|
720
927
|
const first = Object.values(mod).find((v) => typeof v === "function");
|
|
721
928
|
return first ?? null;
|
|
722
929
|
}
|
|
930
|
+
function matchRenderModeConfig(url, renderModes) {
|
|
931
|
+
if (!renderModes) return null;
|
|
932
|
+
const path = url.split("?")[0];
|
|
933
|
+
if (renderModes[path]) return renderModes[path];
|
|
934
|
+
for (const [pattern, mode] of Object.entries(renderModes)) {
|
|
935
|
+
if (pattern.includes("*")) {
|
|
936
|
+
const re = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
937
|
+
if (re.test(path)) return mode;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
return null;
|
|
941
|
+
}
|
|
723
942
|
function finesoftFrontViteConfig(options = {}) {
|
|
724
943
|
const ssrEntry = options.ssr?.entry ?? "src/ssr.ts";
|
|
725
944
|
let root = process.cwd();
|
|
@@ -752,7 +971,7 @@ function finesoftFrontViteConfig(options = {}) {
|
|
|
752
971
|
/* @vite-ignore */
|
|
753
972
|
"hono"
|
|
754
973
|
);
|
|
755
|
-
const { createSSRApp: createSSRApp2 } = await import("./app-
|
|
974
|
+
const { createSSRApp: createSSRApp2 } = await import("./app-Z3EFLAP2.js");
|
|
756
975
|
const { getRequestListener } = await import(
|
|
757
976
|
/* @vite-ignore */
|
|
758
977
|
"@hono/node-server"
|
|
@@ -799,9 +1018,9 @@ function finesoftFrontViteConfig(options = {}) {
|
|
|
799
1018
|
/* @vite-ignore */
|
|
800
1019
|
"hono"
|
|
801
1020
|
);
|
|
802
|
-
const { injectSSRContent: injectSSRContent2 } = await import(
|
|
1021
|
+
const { injectSSRContent: injectSSRContent2, injectCSRShell: injectCSRShell2 } = await import(
|
|
803
1022
|
/* @vite-ignore */
|
|
804
|
-
"./src-
|
|
1023
|
+
"./src-KUWACLPD.js"
|
|
805
1024
|
);
|
|
806
1025
|
const { parseAcceptLanguage: parseAcceptLanguage2 } = await import("./locale-YK3THSI6.js");
|
|
807
1026
|
const { getRequestListener } = await import(
|
|
@@ -809,6 +1028,7 @@ function finesoftFrontViteConfig(options = {}) {
|
|
|
809
1028
|
"@hono/node-server"
|
|
810
1029
|
);
|
|
811
1030
|
const app = new HonoClass();
|
|
1031
|
+
const isrCache = /* @__PURE__ */ new Map();
|
|
812
1032
|
if (typeof options.setup === "function") {
|
|
813
1033
|
await options.setup(app);
|
|
814
1034
|
} else if (typeof options.setup === "string") {
|
|
@@ -847,12 +1067,25 @@ function finesoftFrontViteConfig(options = {}) {
|
|
|
847
1067
|
options.locales,
|
|
848
1068
|
options.defaultLocale
|
|
849
1069
|
);
|
|
1070
|
+
const overrideMode = matchRenderModeConfig(
|
|
1071
|
+
url,
|
|
1072
|
+
options.renderModes
|
|
1073
|
+
);
|
|
1074
|
+
if (overrideMode === "csr") {
|
|
1075
|
+
return c.html(injectCSRShell2(template, locale));
|
|
1076
|
+
}
|
|
1077
|
+
const cached = isrCache.get(url);
|
|
1078
|
+
if (cached) return c.html(cached);
|
|
850
1079
|
const {
|
|
851
1080
|
html: appHtml,
|
|
852
1081
|
head,
|
|
853
1082
|
css,
|
|
854
|
-
serverData
|
|
1083
|
+
serverData,
|
|
1084
|
+
renderMode
|
|
855
1085
|
} = await ssrModule.render(url, locale);
|
|
1086
|
+
if (renderMode === "csr") {
|
|
1087
|
+
return c.html(injectCSRShell2(template, locale));
|
|
1088
|
+
}
|
|
856
1089
|
const serializedData = ssrModule.serializeServerData(serverData);
|
|
857
1090
|
const finalHtml = injectSSRContent2({
|
|
858
1091
|
template,
|
|
@@ -862,6 +1095,9 @@ function finesoftFrontViteConfig(options = {}) {
|
|
|
862
1095
|
html: appHtml,
|
|
863
1096
|
serializedData
|
|
864
1097
|
});
|
|
1098
|
+
if (renderMode === "prerender" || overrideMode === "prerender") {
|
|
1099
|
+
isrCache.set(url, finalHtml);
|
|
1100
|
+
}
|
|
865
1101
|
return c.html(finalHtml);
|
|
866
1102
|
} catch (e) {
|
|
867
1103
|
console.error("[SSR Preview Error]", e);
|
|
@@ -932,6 +1168,7 @@ function finesoftFrontViteConfig(options = {}) {
|
|
|
932
1168
|
locales,
|
|
933
1169
|
defaultLocale,
|
|
934
1170
|
templateHtml,
|
|
1171
|
+
renderModes: options.renderModes,
|
|
935
1172
|
resolvedResolve,
|
|
936
1173
|
resolvedCss,
|
|
937
1174
|
vite,
|
|
@@ -990,6 +1227,7 @@ export {
|
|
|
990
1227
|
finesoftFrontViteConfig,
|
|
991
1228
|
generateUuid,
|
|
992
1229
|
getBaseUrl,
|
|
1230
|
+
injectCSRShell,
|
|
993
1231
|
injectSSRContent,
|
|
994
1232
|
isCompoundAction,
|
|
995
1233
|
isExternalUrlAction,
|