@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.cjs
CHANGED
|
@@ -395,10 +395,10 @@ var init_router = __esm({
|
|
|
395
395
|
Router = class {
|
|
396
396
|
routes = [];
|
|
397
397
|
/** 添加路由规则 */
|
|
398
|
-
add(pattern, intentId) {
|
|
398
|
+
add(pattern, intentId, renderMode) {
|
|
399
399
|
const paramNames = [];
|
|
400
400
|
const regexStr = pattern.replace(
|
|
401
|
-
/\/:(\w+)(\?)?/g,
|
|
401
|
+
/\/:([\w]+)(\?)?/g,
|
|
402
402
|
(_, name, optional) => {
|
|
403
403
|
paramNames.push(name);
|
|
404
404
|
return optional ? "(?:/([^/]+))?" : "/([^/]+)";
|
|
@@ -408,7 +408,8 @@ var init_router = __esm({
|
|
|
408
408
|
pattern,
|
|
409
409
|
intentId,
|
|
410
410
|
regex: new RegExp(`^${regexStr}/?$`),
|
|
411
|
-
paramNames
|
|
411
|
+
paramNames,
|
|
412
|
+
renderMode
|
|
412
413
|
});
|
|
413
414
|
return this;
|
|
414
415
|
}
|
|
@@ -429,7 +430,8 @@ var init_router = __esm({
|
|
|
429
430
|
}
|
|
430
431
|
return {
|
|
431
432
|
intent: { id: route.intentId, params },
|
|
432
|
-
action: makeFlowAction(urlOrPath)
|
|
433
|
+
action: makeFlowAction(urlOrPath),
|
|
434
|
+
renderMode: route.renderMode
|
|
433
435
|
};
|
|
434
436
|
}
|
|
435
437
|
}
|
|
@@ -817,7 +819,7 @@ function defineRoutes(framework, definitions) {
|
|
|
817
819
|
framework.registerIntent(def.controller);
|
|
818
820
|
registeredIntents.add(def.intentId);
|
|
819
821
|
}
|
|
820
|
-
framework.router.add(def.path, def.intentId);
|
|
822
|
+
framework.router.add(def.path, def.intentId, def.renderMode);
|
|
821
823
|
}
|
|
822
824
|
}
|
|
823
825
|
var init_define_routes = __esm({
|
|
@@ -973,6 +975,16 @@ async function ssrRender(options) {
|
|
|
973
975
|
const parsed = new URL(url, "http://localhost");
|
|
974
976
|
const fullPath = parsed.pathname + parsed.search;
|
|
975
977
|
const match = framework.routeUrl(fullPath);
|
|
978
|
+
if (match?.renderMode === "csr") {
|
|
979
|
+
framework.dispose();
|
|
980
|
+
return {
|
|
981
|
+
html: "",
|
|
982
|
+
head: "",
|
|
983
|
+
css: "",
|
|
984
|
+
serverData: [],
|
|
985
|
+
renderMode: "csr"
|
|
986
|
+
};
|
|
987
|
+
}
|
|
976
988
|
let page;
|
|
977
989
|
let serverData = [];
|
|
978
990
|
if (match) {
|
|
@@ -991,7 +1003,8 @@ async function ssrRender(options) {
|
|
|
991
1003
|
html: result.html,
|
|
992
1004
|
head: result.head,
|
|
993
1005
|
css: result.css,
|
|
994
|
-
serverData
|
|
1006
|
+
serverData,
|
|
1007
|
+
renderMode: match?.renderMode
|
|
995
1008
|
};
|
|
996
1009
|
}
|
|
997
1010
|
var init_render = __esm({
|
|
@@ -1029,6 +1042,9 @@ ${cssTag}`).replace(SSR_PLACEHOLDERS.BODY, html).replace(
|
|
|
1029
1042
|
`<script id="serialized-server-data" type="application/json">${serializedData}</script>`
|
|
1030
1043
|
);
|
|
1031
1044
|
}
|
|
1045
|
+
function injectCSRShell(template, locale) {
|
|
1046
|
+
return template.replace(SSR_PLACEHOLDERS.LANG, locale).replace(SSR_PLACEHOLDERS.HEAD, "").replace(SSR_PLACEHOLDERS.BODY, "").replace(SSR_PLACEHOLDERS.DATA, "");
|
|
1047
|
+
}
|
|
1032
1048
|
var SSR_PLACEHOLDERS;
|
|
1033
1049
|
var init_inject = __esm({
|
|
1034
1050
|
"../ssr/src/inject.ts"() {
|
|
@@ -1071,6 +1087,7 @@ __export(src_exports, {
|
|
|
1071
1087
|
Framework: () => Framework,
|
|
1072
1088
|
SSR_PLACEHOLDERS: () => SSR_PLACEHOLDERS,
|
|
1073
1089
|
createSSRRender: () => createSSRRender,
|
|
1090
|
+
injectCSRShell: () => injectCSRShell,
|
|
1074
1091
|
injectSSRContent: () => injectSSRContent,
|
|
1075
1092
|
serializeServerData: () => serializeServerData,
|
|
1076
1093
|
ssrRender: () => ssrRender
|
|
@@ -1132,6 +1149,7 @@ function createSSRApp(options) {
|
|
|
1132
1149
|
defaultLocale
|
|
1133
1150
|
} = options;
|
|
1134
1151
|
const app = new import_hono.Hono();
|
|
1152
|
+
const isrCache = /* @__PURE__ */ new Map();
|
|
1135
1153
|
async function readTemplate(url) {
|
|
1136
1154
|
if (!isProduction && vite) {
|
|
1137
1155
|
const { readFileSync: readFileSync2 } = await import(
|
|
@@ -1182,6 +1200,9 @@ function createSSRApp(options) {
|
|
|
1182
1200
|
app.get("*", async (c) => {
|
|
1183
1201
|
const url = c.req.path + (c.req.url.includes("?") ? "?" + c.req.url.split("?")[1] : "");
|
|
1184
1202
|
try {
|
|
1203
|
+
const cacheKey = url;
|
|
1204
|
+
const cached = isrCache.get(cacheKey);
|
|
1205
|
+
if (cached) return c.html(cached);
|
|
1185
1206
|
const template = await readTemplate(url);
|
|
1186
1207
|
const { render, serializeServerData: serializeServerData2 } = await loadSSRModule();
|
|
1187
1208
|
const locale = parseAcceptLanguage(
|
|
@@ -1193,8 +1214,12 @@ function createSSRApp(options) {
|
|
|
1193
1214
|
html: appHtml,
|
|
1194
1215
|
head,
|
|
1195
1216
|
css,
|
|
1196
|
-
serverData
|
|
1217
|
+
serverData,
|
|
1218
|
+
renderMode
|
|
1197
1219
|
} = await render(url, locale);
|
|
1220
|
+
if (renderMode === "csr") {
|
|
1221
|
+
return c.html(injectCSRShell(template, locale));
|
|
1222
|
+
}
|
|
1198
1223
|
const serializedData = serializeServerData2(serverData);
|
|
1199
1224
|
const finalHtml = injectSSRContent({
|
|
1200
1225
|
template,
|
|
@@ -1204,6 +1229,9 @@ function createSSRApp(options) {
|
|
|
1204
1229
|
html: appHtml,
|
|
1205
1230
|
serializedData
|
|
1206
1231
|
});
|
|
1232
|
+
if (renderMode === "prerender") {
|
|
1233
|
+
isrCache.set(cacheKey, finalHtml);
|
|
1234
|
+
}
|
|
1207
1235
|
return c.html(finalHtml);
|
|
1208
1236
|
} catch (e) {
|
|
1209
1237
|
if (!isProduction && vite) {
|
|
@@ -1261,6 +1289,7 @@ __export(index_exports, {
|
|
|
1261
1289
|
finesoftFrontViteConfig: () => finesoftFrontViteConfig,
|
|
1262
1290
|
generateUuid: () => generateUuid,
|
|
1263
1291
|
getBaseUrl: () => getBaseUrl,
|
|
1292
|
+
injectCSRShell: () => injectCSRShell,
|
|
1264
1293
|
injectSSRContent: () => injectSSRContent,
|
|
1265
1294
|
isCompoundAction: () => isCompoundAction,
|
|
1266
1295
|
isExternalUrlAction: () => isExternalUrlAction,
|
|
@@ -1645,6 +1674,7 @@ function generateSSREntry(ctx, opts) {
|
|
|
1645
1674
|
const setupCall = ctx.setupPath ? `if (typeof _setupDefault === "function") await _setupDefault(app);` : ``;
|
|
1646
1675
|
const locales = JSON.stringify(ctx.locales);
|
|
1647
1676
|
const defaultLocale = JSON.stringify(ctx.defaultLocale);
|
|
1677
|
+
const renderModes = JSON.stringify(ctx.renderModes ?? {});
|
|
1648
1678
|
return `
|
|
1649
1679
|
import { Hono } from "hono";
|
|
1650
1680
|
${opts.platformImport}
|
|
@@ -1654,6 +1684,7 @@ ${setupImport}
|
|
|
1654
1684
|
const TEMPLATE = ${JSON.stringify(ctx.templateHtml)};
|
|
1655
1685
|
const LOCALES = ${locales};
|
|
1656
1686
|
const DEFAULT_LOCALE = ${defaultLocale};
|
|
1687
|
+
const RENDER_MODES = ${renderModes};
|
|
1657
1688
|
|
|
1658
1689
|
function parseAcceptLanguage(header) {
|
|
1659
1690
|
if (!header) return DEFAULT_LOCALE;
|
|
@@ -1676,6 +1707,28 @@ function injectSSR(t, locale, head, css, html, data) {
|
|
|
1676
1707
|
.replace("<!--ssr-data-->", '<script id="serialized-server-data" type="application/json">' + data + "</script>");
|
|
1677
1708
|
}
|
|
1678
1709
|
|
|
1710
|
+
function injectCSRShell(t, locale) {
|
|
1711
|
+
return t
|
|
1712
|
+
.replace("<!--ssr-lang-->", locale)
|
|
1713
|
+
.replace("<!--ssr-head-->", "")
|
|
1714
|
+
.replace("<!--ssr-body-->", "")
|
|
1715
|
+
.replace("<!--ssr-data-->", "");
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
function matchRenderMode(url) {
|
|
1719
|
+
const path = url.split("?")[0];
|
|
1720
|
+
if (RENDER_MODES[path]) return RENDER_MODES[path];
|
|
1721
|
+
for (const [pattern, mode] of Object.entries(RENDER_MODES)) {
|
|
1722
|
+
if (pattern.includes("*")) {
|
|
1723
|
+
const re = new RegExp("^" + pattern.replace(/\\*/g, ".*") + "$");
|
|
1724
|
+
if (re.test(path)) return mode;
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
return null;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
const isrCache = new Map();
|
|
1731
|
+
|
|
1679
1732
|
const app = new Hono();
|
|
1680
1733
|
${setupCall}
|
|
1681
1734
|
|
|
@@ -1683,9 +1736,33 @@ app.get("*", async (c) => {
|
|
|
1683
1736
|
const url = c.req.path + (c.req.url.includes("?") ? "?" + c.req.url.split("?")[1] : "");
|
|
1684
1737
|
try {
|
|
1685
1738
|
const locale = parseAcceptLanguage(c.req.header("accept-language"));
|
|
1686
|
-
|
|
1739
|
+
|
|
1740
|
+
// Vite \u914D\u7F6E\u7EA7\u522B\u8986\u76D6: CSR \u76F4\u63A5\u8FD4\u56DE\u7A7A\u58F3
|
|
1741
|
+
const overrideMode = matchRenderMode(url);
|
|
1742
|
+
if (overrideMode === "csr") {
|
|
1743
|
+
return c.html(injectCSRShell(TEMPLATE, locale));
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
// ISR \u7F13\u5B58\u547D\u4E2D
|
|
1747
|
+
const cached = isrCache.get(url);
|
|
1748
|
+
if (cached) return c.html(cached);
|
|
1749
|
+
|
|
1750
|
+
const { html: appHtml, head, css, serverData, renderMode } = await render(url, locale);
|
|
1751
|
+
|
|
1752
|
+
// \u8DEF\u7531\u7EA7 CSR
|
|
1753
|
+
if (renderMode === "csr") {
|
|
1754
|
+
return c.html(injectCSRShell(TEMPLATE, locale));
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1687
1757
|
const serializedData = serializeServerData(serverData);
|
|
1688
|
-
|
|
1758
|
+
const finalHtml = injectSSR(TEMPLATE, locale, head, css, appHtml, serializedData);
|
|
1759
|
+
|
|
1760
|
+
// Prerender ISR \u7F13\u5B58\uFF08\u5305\u62EC Vite \u914D\u7F6E\u8986\u76D6\u548C\u8DEF\u7531\u7EA7\uFF09
|
|
1761
|
+
if (renderMode === "prerender" || overrideMode === "prerender") {
|
|
1762
|
+
isrCache.set(url, finalHtml);
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
return c.html(finalHtml);
|
|
1689
1766
|
} catch (e) {
|
|
1690
1767
|
console.error("[SSR Error]", e);
|
|
1691
1768
|
return c.text("Internal Server Error", 500);
|
|
@@ -1724,6 +1801,89 @@ function copyStaticAssets(ctx, destDir, opts) {
|
|
|
1724
1801
|
fs.rmSync(path.join(destDir, "index.html"), { force: true });
|
|
1725
1802
|
}
|
|
1726
1803
|
}
|
|
1804
|
+
async function prerenderRoutes(ctx, routesExport = "src/lib/bootstrap.ts") {
|
|
1805
|
+
const { fs, path, root, vite } = ctx;
|
|
1806
|
+
const { pathToFileURL } = await import(
|
|
1807
|
+
/* @vite-ignore */
|
|
1808
|
+
"url"
|
|
1809
|
+
);
|
|
1810
|
+
await vite.build({
|
|
1811
|
+
root,
|
|
1812
|
+
build: {
|
|
1813
|
+
ssr: routesExport,
|
|
1814
|
+
outDir: path.resolve(root, "dist/server"),
|
|
1815
|
+
emptyOutDir: false,
|
|
1816
|
+
rollupOptions: {
|
|
1817
|
+
output: { entryFileNames: "_routes_prerender.mjs" }
|
|
1818
|
+
}
|
|
1819
|
+
},
|
|
1820
|
+
resolve: ctx.resolvedResolve
|
|
1821
|
+
});
|
|
1822
|
+
const routesPath = pathToFileURL(
|
|
1823
|
+
path.resolve(root, "dist/server/_routes_prerender.mjs")
|
|
1824
|
+
).href;
|
|
1825
|
+
const routesMod = await import(
|
|
1826
|
+
/* @vite-ignore */
|
|
1827
|
+
routesPath
|
|
1828
|
+
);
|
|
1829
|
+
const routes = routesMod.routes ?? routesMod.default ?? [];
|
|
1830
|
+
fs.rmSync(path.resolve(root, "dist/server/_routes_prerender.mjs"), {
|
|
1831
|
+
force: true
|
|
1832
|
+
});
|
|
1833
|
+
const prerenderPaths = /* @__PURE__ */ new Set();
|
|
1834
|
+
for (const r of routes) {
|
|
1835
|
+
if (r.renderMode === "prerender" && r.path && !r.path.includes(":")) {
|
|
1836
|
+
prerenderPaths.add(r.path);
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
if (ctx.renderModes) {
|
|
1840
|
+
for (const [pattern, mode] of Object.entries(ctx.renderModes)) {
|
|
1841
|
+
if (mode === "prerender" && !pattern.includes("*") && !pattern.includes(":")) {
|
|
1842
|
+
prerenderPaths.add(pattern);
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
if (prerenderPaths.size === 0) return [];
|
|
1847
|
+
const ssrPath = pathToFileURL(
|
|
1848
|
+
path.resolve(root, "dist/server/ssr.js")
|
|
1849
|
+
).href;
|
|
1850
|
+
const ssrModule = await import(
|
|
1851
|
+
/* @vite-ignore */
|
|
1852
|
+
ssrPath
|
|
1853
|
+
);
|
|
1854
|
+
const results = [];
|
|
1855
|
+
for (const routePath of prerenderPaths) {
|
|
1856
|
+
for (const locale of ctx.locales) {
|
|
1857
|
+
const url = locale === ctx.defaultLocale ? routePath : `/${locale}${routePath === "/" ? "" : routePath}`;
|
|
1858
|
+
try {
|
|
1859
|
+
const {
|
|
1860
|
+
html: appHtml,
|
|
1861
|
+
head,
|
|
1862
|
+
css,
|
|
1863
|
+
serverData
|
|
1864
|
+
} = await ssrModule.render(url, locale);
|
|
1865
|
+
const serializedData = ssrModule.serializeServerData(serverData);
|
|
1866
|
+
const finalHtml = ctx.templateHtml.replace("<!--ssr-lang-->", locale).replace(
|
|
1867
|
+
"<!--ssr-head-->",
|
|
1868
|
+
head + "\n<style>" + css + "</style>"
|
|
1869
|
+
).replace("<!--ssr-body-->", appHtml).replace(
|
|
1870
|
+
"<!--ssr-data-->",
|
|
1871
|
+
'<script id="serialized-server-data" type="application/json">' + serializedData + "</script>"
|
|
1872
|
+
);
|
|
1873
|
+
results.push({ url, html: finalHtml });
|
|
1874
|
+
} catch (e) {
|
|
1875
|
+
console.warn(` [prerender] Failed to render ${url}:`, e);
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
if (results.length > 0) {
|
|
1880
|
+
console.log(
|
|
1881
|
+
` Pre-rendered ${results.length} pages (${prerenderPaths.size} routes \xD7 ${ctx.locales.length} locales)
|
|
1882
|
+
`
|
|
1883
|
+
);
|
|
1884
|
+
}
|
|
1885
|
+
return results;
|
|
1886
|
+
}
|
|
1727
1887
|
|
|
1728
1888
|
// ../server/src/adapters/cloudflare.ts
|
|
1729
1889
|
function cloudflareAdapter() {
|
|
@@ -1749,6 +1909,14 @@ function cloudflareAdapter() {
|
|
|
1749
1909
|
// 这些构建工具运行时不需要,且 fsevents 是 macOS .node 原生二进制无法打包
|
|
1750
1910
|
});
|
|
1751
1911
|
copyStaticAssets(ctx, path.resolve(outputDir, "assets"));
|
|
1912
|
+
const prerendered = await prerenderRoutes(ctx);
|
|
1913
|
+
for (const { url, html } of prerendered) {
|
|
1914
|
+
const filePath = url === "/" ? path.join(outputDir, "assets", "index.html") : path.join(outputDir, "assets", url, "index.html");
|
|
1915
|
+
fs.mkdirSync(path.resolve(filePath, ".."), {
|
|
1916
|
+
recursive: true
|
|
1917
|
+
});
|
|
1918
|
+
fs.writeFileSync(filePath, html);
|
|
1919
|
+
}
|
|
1752
1920
|
} finally {
|
|
1753
1921
|
fs.rmSync(tempEntry, { force: true });
|
|
1754
1922
|
}
|
|
@@ -1792,6 +1960,13 @@ function netlifyAdapter() {
|
|
|
1792
1960
|
path.resolve(root, "dist/client/_redirects"),
|
|
1793
1961
|
redirects
|
|
1794
1962
|
);
|
|
1963
|
+
const prerendered = await prerenderRoutes(ctx);
|
|
1964
|
+
const clientDir = path.resolve(root, "dist/client");
|
|
1965
|
+
for (const { url, html } of prerendered) {
|
|
1966
|
+
const filePath = url === "/" ? path.join(clientDir, "index.html") : path.join(clientDir, url, "index.html");
|
|
1967
|
+
fs.mkdirSync(path.resolve(filePath, ".."), { recursive: true });
|
|
1968
|
+
fs.writeFileSync(filePath, html);
|
|
1969
|
+
}
|
|
1795
1970
|
console.log(
|
|
1796
1971
|
" Netlify output \u2192 .netlify/functions-internal/ssr/\n Publish dir: dist/client/\n"
|
|
1797
1972
|
);
|
|
@@ -1825,6 +2000,18 @@ serve({ fetch: app.fetch, port }, (info) => {
|
|
|
1825
2000
|
} finally {
|
|
1826
2001
|
fs.rmSync(tempEntry, { force: true });
|
|
1827
2002
|
}
|
|
2003
|
+
const prerendered = await prerenderRoutes(ctx);
|
|
2004
|
+
if (prerendered.length > 0) {
|
|
2005
|
+
const prerenderDir = path.resolve(root, "dist/prerender");
|
|
2006
|
+
fs.mkdirSync(prerenderDir, { recursive: true });
|
|
2007
|
+
for (const { url, html } of prerendered) {
|
|
2008
|
+
const filePath = url === "/" ? path.join(prerenderDir, "index.html") : path.join(prerenderDir, url, "index.html");
|
|
2009
|
+
fs.mkdirSync(path.resolve(filePath, ".."), {
|
|
2010
|
+
recursive: true
|
|
2011
|
+
});
|
|
2012
|
+
fs.writeFileSync(filePath, html);
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
1828
2015
|
console.log(
|
|
1829
2016
|
" Node output \u2192 dist/server/index.mjs\n Run: node dist/server/index.mjs\n"
|
|
1830
2017
|
);
|
|
@@ -1853,7 +2040,7 @@ function staticAdapter(opts = {}) {
|
|
|
1853
2040
|
ssrPath
|
|
1854
2041
|
);
|
|
1855
2042
|
ctx.copyStaticAssets(outputDir, { excludeHtml: true });
|
|
1856
|
-
const routePaths = await
|
|
2043
|
+
const { paths: routePaths, defs: routeDefs } = await extractRoutesWithModes(ctx, opts);
|
|
1857
2044
|
const allUrls = [];
|
|
1858
2045
|
for (const routePath of routePaths) {
|
|
1859
2046
|
for (const locale of ctx.locales) {
|
|
@@ -1872,21 +2059,37 @@ function staticAdapter(opts = {}) {
|
|
|
1872
2059
|
ctx.locales,
|
|
1873
2060
|
ctx.defaultLocale
|
|
1874
2061
|
);
|
|
1875
|
-
const
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
const finalHtml = injectSSRForStatic(
|
|
1883
|
-
ctx.templateHtml,
|
|
1884
|
-
locale,
|
|
1885
|
-
head,
|
|
1886
|
-
css,
|
|
1887
|
-
appHtml,
|
|
1888
|
-
serializedData
|
|
2062
|
+
const routeDef = routeDefs.find(
|
|
2063
|
+
(r) => r.path === stripLocalePrefix(url, ctx.locales)
|
|
2064
|
+
);
|
|
2065
|
+
const mode = resolveRenderMode(
|
|
2066
|
+
stripLocalePrefix(url, ctx.locales),
|
|
2067
|
+
routeDef?.renderMode,
|
|
2068
|
+
ctx.renderModes
|
|
1889
2069
|
);
|
|
2070
|
+
let finalHtml;
|
|
2071
|
+
if (mode === "csr") {
|
|
2072
|
+
finalHtml = injectCSRShellForStatic(
|
|
2073
|
+
ctx.templateHtml,
|
|
2074
|
+
locale
|
|
2075
|
+
);
|
|
2076
|
+
} else {
|
|
2077
|
+
const {
|
|
2078
|
+
html: appHtml,
|
|
2079
|
+
head,
|
|
2080
|
+
css,
|
|
2081
|
+
serverData
|
|
2082
|
+
} = await ssrModule.render(url, locale);
|
|
2083
|
+
const serializedData = ssrModule.serializeServerData(serverData);
|
|
2084
|
+
finalHtml = injectSSRForStatic(
|
|
2085
|
+
ctx.templateHtml,
|
|
2086
|
+
locale,
|
|
2087
|
+
head,
|
|
2088
|
+
css,
|
|
2089
|
+
appHtml,
|
|
2090
|
+
serializedData
|
|
2091
|
+
);
|
|
2092
|
+
}
|
|
1890
2093
|
const filePath = url === "/" ? path.join(outputDir, "index.html") : path.join(outputDir, url, "index.html");
|
|
1891
2094
|
fs.mkdirSync(path.resolve(filePath, ".."), {
|
|
1892
2095
|
recursive: true
|
|
@@ -1901,9 +2104,10 @@ function staticAdapter(opts = {}) {
|
|
|
1901
2104
|
}
|
|
1902
2105
|
};
|
|
1903
2106
|
}
|
|
1904
|
-
async function
|
|
2107
|
+
async function extractRoutesWithModes(ctx, opts) {
|
|
1905
2108
|
const routesFile = opts.routesExport ?? "src/lib/bootstrap.ts";
|
|
1906
2109
|
const paths = [];
|
|
2110
|
+
const defs = [];
|
|
1907
2111
|
try {
|
|
1908
2112
|
const { pathToFileURL } = await import(
|
|
1909
2113
|
/* @vite-ignore */
|
|
@@ -1933,6 +2137,7 @@ async function extractRoutes(ctx, opts) {
|
|
|
1933
2137
|
for (const r of routes) {
|
|
1934
2138
|
if (r.path && !r.path.includes(":")) {
|
|
1935
2139
|
paths.push(r.path);
|
|
2140
|
+
defs.push({ path: r.path, renderMode: r.renderMode });
|
|
1936
2141
|
}
|
|
1937
2142
|
}
|
|
1938
2143
|
}
|
|
@@ -1952,7 +2157,7 @@ async function extractRoutes(ctx, opts) {
|
|
|
1952
2157
|
}
|
|
1953
2158
|
}
|
|
1954
2159
|
if (paths.length === 0) paths.push("/");
|
|
1955
|
-
return paths;
|
|
2160
|
+
return { paths, defs };
|
|
1956
2161
|
}
|
|
1957
2162
|
function inferLocale(url, locales, defaultLocale) {
|
|
1958
2163
|
const segments = url.split("/").filter(Boolean);
|
|
@@ -1967,6 +2172,29 @@ function injectSSRForStatic(template, locale, head, css, html, serializedData) {
|
|
|
1967
2172
|
'<script id="serialized-server-data" type="application/json">' + serializedData + "</script>"
|
|
1968
2173
|
);
|
|
1969
2174
|
}
|
|
2175
|
+
function injectCSRShellForStatic(template, locale) {
|
|
2176
|
+
return template.replace("<!--ssr-lang-->", locale).replace("<!--ssr-head-->", "").replace("<!--ssr-body-->", "").replace("<!--ssr-data-->", "");
|
|
2177
|
+
}
|
|
2178
|
+
function stripLocalePrefix(url, locales) {
|
|
2179
|
+
const segments = url.split("/").filter(Boolean);
|
|
2180
|
+
if (segments.length > 0 && locales.includes(segments[0])) {
|
|
2181
|
+
const rest = segments.slice(1).join("/");
|
|
2182
|
+
return rest ? `/${rest}` : "/";
|
|
2183
|
+
}
|
|
2184
|
+
return url;
|
|
2185
|
+
}
|
|
2186
|
+
function resolveRenderMode(routePath, routeRenderMode, renderModes) {
|
|
2187
|
+
if (renderModes) {
|
|
2188
|
+
if (renderModes[routePath]) return renderModes[routePath];
|
|
2189
|
+
for (const [pattern, mode] of Object.entries(renderModes)) {
|
|
2190
|
+
if (pattern.includes("*")) {
|
|
2191
|
+
const re = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
2192
|
+
if (re.test(routePath)) return mode;
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
return routeRenderMode ?? "ssr";
|
|
2197
|
+
}
|
|
1970
2198
|
|
|
1971
2199
|
// ../server/src/adapters/vercel.ts
|
|
1972
2200
|
function vercelAdapter() {
|
|
@@ -1977,8 +2205,8 @@ function vercelAdapter() {
|
|
|
1977
2205
|
const outputDir = path.resolve(root, ".vercel/output");
|
|
1978
2206
|
fs.rmSync(outputDir, { recursive: true, force: true });
|
|
1979
2207
|
const entrySource = generateSSREntry(ctx, {
|
|
1980
|
-
platformImport: `import {
|
|
1981
|
-
platformExport: `export default
|
|
2208
|
+
platformImport: `import { getRequestListener } from "@hono/node-server";`,
|
|
2209
|
+
platformExport: `export default getRequestListener(app.fetch);`
|
|
1982
2210
|
});
|
|
1983
2211
|
const tempEntry = path.resolve(root, ".vercel-entry.tmp.mjs");
|
|
1984
2212
|
fs.writeFileSync(tempEntry, entrySource);
|
|
@@ -2025,6 +2253,13 @@ function vercelAdapter() {
|
|
|
2025
2253
|
} finally {
|
|
2026
2254
|
fs.rmSync(tempEntry, { force: true });
|
|
2027
2255
|
}
|
|
2256
|
+
const prerendered = await prerenderRoutes(ctx);
|
|
2257
|
+
const staticDir = path.resolve(root, ".vercel/output/static");
|
|
2258
|
+
for (const { url, html } of prerendered) {
|
|
2259
|
+
const filePath = url === "/" ? path.join(staticDir, "index.html") : path.join(staticDir, url, "index.html");
|
|
2260
|
+
fs.mkdirSync(path.resolve(filePath, ".."), { recursive: true });
|
|
2261
|
+
fs.writeFileSync(filePath, html);
|
|
2262
|
+
}
|
|
2028
2263
|
console.log(" Vercel output \u2192 .vercel/output/\n");
|
|
2029
2264
|
}
|
|
2030
2265
|
};
|
|
@@ -2297,6 +2532,18 @@ function resolveSetupFn(mod) {
|
|
|
2297
2532
|
const first = Object.values(mod).find((v) => typeof v === "function");
|
|
2298
2533
|
return first ?? null;
|
|
2299
2534
|
}
|
|
2535
|
+
function matchRenderModeConfig(url, renderModes) {
|
|
2536
|
+
if (!renderModes) return null;
|
|
2537
|
+
const path = url.split("?")[0];
|
|
2538
|
+
if (renderModes[path]) return renderModes[path];
|
|
2539
|
+
for (const [pattern, mode] of Object.entries(renderModes)) {
|
|
2540
|
+
if (pattern.includes("*")) {
|
|
2541
|
+
const re = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
2542
|
+
if (re.test(path)) return mode;
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
return null;
|
|
2546
|
+
}
|
|
2300
2547
|
function finesoftFrontViteConfig(options = {}) {
|
|
2301
2548
|
const ssrEntry = options.ssr?.entry ?? "src/ssr.ts";
|
|
2302
2549
|
let root = process.cwd();
|
|
@@ -2376,13 +2623,14 @@ function finesoftFrontViteConfig(options = {}) {
|
|
|
2376
2623
|
/* @vite-ignore */
|
|
2377
2624
|
"hono"
|
|
2378
2625
|
);
|
|
2379
|
-
const { injectSSRContent: injectSSRContent2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
|
|
2626
|
+
const { injectSSRContent: injectSSRContent2, injectCSRShell: injectCSRShell2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
|
|
2380
2627
|
const { parseAcceptLanguage: parseAcceptLanguage2 } = await Promise.resolve().then(() => (init_locale(), locale_exports));
|
|
2381
2628
|
const { getRequestListener } = await import(
|
|
2382
2629
|
/* @vite-ignore */
|
|
2383
2630
|
"@hono/node-server"
|
|
2384
2631
|
);
|
|
2385
2632
|
const app = new HonoClass();
|
|
2633
|
+
const isrCache = /* @__PURE__ */ new Map();
|
|
2386
2634
|
if (typeof options.setup === "function") {
|
|
2387
2635
|
await options.setup(app);
|
|
2388
2636
|
} else if (typeof options.setup === "string") {
|
|
@@ -2421,12 +2669,25 @@ function finesoftFrontViteConfig(options = {}) {
|
|
|
2421
2669
|
options.locales,
|
|
2422
2670
|
options.defaultLocale
|
|
2423
2671
|
);
|
|
2672
|
+
const overrideMode = matchRenderModeConfig(
|
|
2673
|
+
url,
|
|
2674
|
+
options.renderModes
|
|
2675
|
+
);
|
|
2676
|
+
if (overrideMode === "csr") {
|
|
2677
|
+
return c.html(injectCSRShell2(template, locale));
|
|
2678
|
+
}
|
|
2679
|
+
const cached = isrCache.get(url);
|
|
2680
|
+
if (cached) return c.html(cached);
|
|
2424
2681
|
const {
|
|
2425
2682
|
html: appHtml,
|
|
2426
2683
|
head,
|
|
2427
2684
|
css,
|
|
2428
|
-
serverData
|
|
2685
|
+
serverData,
|
|
2686
|
+
renderMode
|
|
2429
2687
|
} = await ssrModule.render(url, locale);
|
|
2688
|
+
if (renderMode === "csr") {
|
|
2689
|
+
return c.html(injectCSRShell2(template, locale));
|
|
2690
|
+
}
|
|
2430
2691
|
const serializedData = ssrModule.serializeServerData(serverData);
|
|
2431
2692
|
const finalHtml = injectSSRContent2({
|
|
2432
2693
|
template,
|
|
@@ -2436,6 +2697,9 @@ function finesoftFrontViteConfig(options = {}) {
|
|
|
2436
2697
|
html: appHtml,
|
|
2437
2698
|
serializedData
|
|
2438
2699
|
});
|
|
2700
|
+
if (renderMode === "prerender" || overrideMode === "prerender") {
|
|
2701
|
+
isrCache.set(url, finalHtml);
|
|
2702
|
+
}
|
|
2439
2703
|
return c.html(finalHtml);
|
|
2440
2704
|
} catch (e) {
|
|
2441
2705
|
console.error("[SSR Preview Error]", e);
|
|
@@ -2506,6 +2770,7 @@ function finesoftFrontViteConfig(options = {}) {
|
|
|
2506
2770
|
locales,
|
|
2507
2771
|
defaultLocale,
|
|
2508
2772
|
templateHtml,
|
|
2773
|
+
renderModes: options.renderModes,
|
|
2509
2774
|
resolvedResolve,
|
|
2510
2775
|
resolvedCss,
|
|
2511
2776
|
vite,
|
|
@@ -2565,6 +2830,7 @@ function finesoftFrontViteConfig(options = {}) {
|
|
|
2565
2830
|
finesoftFrontViteConfig,
|
|
2566
2831
|
generateUuid,
|
|
2567
2832
|
getBaseUrl,
|
|
2833
|
+
injectCSRShell,
|
|
2568
2834
|
injectSSRContent,
|
|
2569
2835
|
isCompoundAction,
|
|
2570
2836
|
isExternalUrlAction,
|