vike-lite 1.15.9 → 1.15.10
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/server.mjs +215 -1
- package/dist/vite.mjs +8 -10
- package/package.json +1 -1
- package/dist/server-CRcgzWMc.mjs +0 -216
package/dist/server.mjs
CHANGED
|
@@ -1,2 +1,216 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BASE_URL, matchRoute, stripBase } from "./__internal/shared.mjs";
|
|
2
|
+
import { AbortRedirect, AbortRender } from "./server/abort.mjs";
|
|
3
|
+
import { n as store } from "./store-BCs8gL_o.mjs";
|
|
4
|
+
//#region src/utils/serializeContext.ts
|
|
5
|
+
const ESCAPE_LOOKUP = {
|
|
6
|
+
"&": String.raw`\u0026`,
|
|
7
|
+
">": String.raw`\u003e`,
|
|
8
|
+
"<": String.raw`\u003c`,
|
|
9
|
+
"\u2028": String.raw`\u2028`,
|
|
10
|
+
"\u2029": String.raw`\u2029`
|
|
11
|
+
};
|
|
12
|
+
const ESCAPE_REGEX = /[&><\u{2028}\u{2029}]/gu;
|
|
13
|
+
function serializeContext(data) {
|
|
14
|
+
return JSON.stringify(data).replaceAll(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]);
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/server/renderPage.ts
|
|
18
|
+
const isProd = process.env.NODE_ENV === "production";
|
|
19
|
+
function withBase(file) {
|
|
20
|
+
return `${BASE_URL === "" ? "" : BASE_URL}${file.startsWith("/") ? file : `/${file}`}`;
|
|
21
|
+
}
|
|
22
|
+
const assetsCache = /* @__PURE__ */ new WeakMap();
|
|
23
|
+
function computeAssetFiles(route) {
|
|
24
|
+
const cssFiles = /* @__PURE__ */ new Set();
|
|
25
|
+
const jsFiles = /* @__PURE__ */ new Map();
|
|
26
|
+
const { manifest } = store;
|
|
27
|
+
function getVirtualEntryClientKey() {
|
|
28
|
+
for (const key in manifest) if (manifest[key].isEntry) return key;
|
|
29
|
+
throw new Error("entry-client not found in manifest");
|
|
30
|
+
}
|
|
31
|
+
function collectAssets(key, isCritical) {
|
|
32
|
+
const chunk = manifest[key];
|
|
33
|
+
if (!chunk) return;
|
|
34
|
+
const current = jsFiles.get(chunk.file);
|
|
35
|
+
if (current === true) return;
|
|
36
|
+
if (current === false && !isCritical) return;
|
|
37
|
+
jsFiles.set(chunk.file, isCritical);
|
|
38
|
+
if (chunk.css) for (const css of chunk.css) cssFiles.add(css);
|
|
39
|
+
if (chunk.imports) for (const imp of chunk.imports) collectAssets(imp, false);
|
|
40
|
+
}
|
|
41
|
+
const virtualEntryClientKey = getVirtualEntryClientKey();
|
|
42
|
+
collectAssets(virtualEntryClientKey, true);
|
|
43
|
+
const { page, layout, head } = route;
|
|
44
|
+
collectAssets(page, true);
|
|
45
|
+
if (head) collectAssets(head, true);
|
|
46
|
+
if (layout) collectAssets(layout, true);
|
|
47
|
+
const criticalJs = [];
|
|
48
|
+
const sharedJs = [];
|
|
49
|
+
for (const [file, isCritical] of jsFiles) (isCritical ? criticalJs : sharedJs).push(file);
|
|
50
|
+
return {
|
|
51
|
+
cssFiles: [...cssFiles],
|
|
52
|
+
criticalJs,
|
|
53
|
+
sharedJs,
|
|
54
|
+
entryClientFile: manifest[virtualEntryClientKey].file
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function getAssets(route, nonce) {
|
|
58
|
+
if (!isProd) return {
|
|
59
|
+
cssLinks: "",
|
|
60
|
+
jsPreloads: "",
|
|
61
|
+
entryClient: withBase("@id/virtual:vike-lite/entry-client")
|
|
62
|
+
};
|
|
63
|
+
let files = assetsCache.get(route);
|
|
64
|
+
if (!files) {
|
|
65
|
+
files = computeAssetFiles(route);
|
|
66
|
+
assetsCache.set(route, files);
|
|
67
|
+
}
|
|
68
|
+
const nonceAttr = nonce ? ` nonce="${nonce}"` : "";
|
|
69
|
+
return {
|
|
70
|
+
cssLinks: files.cssFiles.map((href) => `<link rel="stylesheet" href="${withBase(href)}"${nonceAttr}>`).join(""),
|
|
71
|
+
jsPreloads: [...files.criticalJs.map((href) => `<link rel="modulepreload" href="${withBase(href)}" crossorigin fetchpriority="high"${nonceAttr}>`), ...files.sharedJs.map((href) => `<link rel="modulepreload" href="${withBase(href)}" crossorigin${nonceAttr}>`)].join(""),
|
|
72
|
+
entryClient: withBase(files.entryClientFile)
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
async function buildPageContext(urlPathname, urlOriginal, isJsonRequest) {
|
|
76
|
+
const matched = matchRoute(urlPathname, store.routes);
|
|
77
|
+
if (!matched) return null;
|
|
78
|
+
const { route, routeParams } = matched;
|
|
79
|
+
const pageContext = {
|
|
80
|
+
routeParams,
|
|
81
|
+
urlOriginal,
|
|
82
|
+
urlPathname
|
|
83
|
+
};
|
|
84
|
+
const [dataMod, titleMod, PageModule, HeadModule, LayoutModule] = await Promise.all([
|
|
85
|
+
route.Data?.() ?? null,
|
|
86
|
+
route.Title?.() ?? null,
|
|
87
|
+
isJsonRequest ? null : route.Page(),
|
|
88
|
+
isJsonRequest ? null : route.Head?.() ?? null,
|
|
89
|
+
isJsonRequest ? null : route.Layout?.() ?? null
|
|
90
|
+
]);
|
|
91
|
+
if (dataMod) try {
|
|
92
|
+
pageContext.data = await (dataMod.data ?? dataMod.default)(pageContext);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (!(error instanceof AbortRedirect || error instanceof AbortRender)) console.error("+data hook failed at:", urlPathname);
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
if (titleMod) try {
|
|
98
|
+
const titleFn = titleMod.title ?? titleMod.default;
|
|
99
|
+
pageContext.title = typeof titleFn === "function" ? titleFn(pageContext) : titleFn;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (!(error instanceof AbortRedirect || error instanceof AbortRender)) console.error("+title hook failed at:", urlPathname);
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
pageContext,
|
|
106
|
+
route,
|
|
107
|
+
PageModule,
|
|
108
|
+
HeadModule,
|
|
109
|
+
LayoutModule
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
async function renderErrorPage(req, status, urlPathname, error, nonce) {
|
|
113
|
+
let errorMessage;
|
|
114
|
+
let is500;
|
|
115
|
+
if (status === 500) {
|
|
116
|
+
errorMessage = isProd ? "Internal Server Error" : error instanceof Error ? error.message : "Unknown error";
|
|
117
|
+
is500 = true;
|
|
118
|
+
} else is500 = false;
|
|
119
|
+
const fallbackText = status === 404 ? "Not Found" : "Internal Server Error";
|
|
120
|
+
if (!store.errorRoute) return new Response(fallbackText, {
|
|
121
|
+
status,
|
|
122
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
123
|
+
});
|
|
124
|
+
try {
|
|
125
|
+
const [PageModule, HeadModule, LayoutModule] = await Promise.all([
|
|
126
|
+
store.errorRoute.Page(),
|
|
127
|
+
store.errorRoute.Head?.() ?? null,
|
|
128
|
+
store.errorRoute.Layout?.() ?? null
|
|
129
|
+
]);
|
|
130
|
+
const pageContext = {
|
|
131
|
+
urlOriginal: req.url,
|
|
132
|
+
urlPathname,
|
|
133
|
+
routeParams: {},
|
|
134
|
+
is404: status === 404,
|
|
135
|
+
is500,
|
|
136
|
+
errorMessage
|
|
137
|
+
};
|
|
138
|
+
const html = await store.config.onRenderHtml({
|
|
139
|
+
pageContext,
|
|
140
|
+
Page: PageModule.Page ?? PageModule.default,
|
|
141
|
+
Layout: LayoutModule ? LayoutModule.Layout ?? LayoutModule.default : void 0,
|
|
142
|
+
Head: HeadModule ? HeadModule.Head ?? HeadModule.default : void 0,
|
|
143
|
+
pageTitleTag: `<title>${status === 404 ? "Page Not Found" : "Server Error"}</title>`,
|
|
144
|
+
serializedContext: serializeContext(pageContext),
|
|
145
|
+
assets: getAssets(store.errorRoute, nonce),
|
|
146
|
+
nonce
|
|
147
|
+
});
|
|
148
|
+
return new Response(html, {
|
|
149
|
+
status,
|
|
150
|
+
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
151
|
+
});
|
|
152
|
+
} catch (renderError) {
|
|
153
|
+
console.error("[vike-lite] Error page render failed:", renderError);
|
|
154
|
+
return new Response(fallbackText, {
|
|
155
|
+
status,
|
|
156
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async function renderPage(req, { nonce } = {}) {
|
|
161
|
+
let { pathname } = new URL(req.url);
|
|
162
|
+
pathname = stripBase(pathname);
|
|
163
|
+
const isJsonRequest = pathname.endsWith(".pageContext.json");
|
|
164
|
+
let targetPathname = pathname;
|
|
165
|
+
if (isJsonRequest) {
|
|
166
|
+
targetPathname = targetPathname.replace(/\.pageContext\.json$/, "");
|
|
167
|
+
if (targetPathname === "/index") targetPathname = "/";
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
const resolved = await buildPageContext(targetPathname, req.url, isJsonRequest);
|
|
171
|
+
if (!resolved) {
|
|
172
|
+
if (isJsonRequest) return Response.json({ is404: true }, { status: 404 });
|
|
173
|
+
return renderErrorPage(req, 404, targetPathname, nonce);
|
|
174
|
+
}
|
|
175
|
+
const { pageContext, route, PageModule, HeadModule, LayoutModule } = resolved;
|
|
176
|
+
if (isJsonRequest) return Response.json(pageContext);
|
|
177
|
+
const html = await store.config.onRenderHtml({
|
|
178
|
+
pageContext,
|
|
179
|
+
Page: PageModule.Page ?? PageModule.default,
|
|
180
|
+
Head: HeadModule ? HeadModule.Head ?? HeadModule.default : void 0,
|
|
181
|
+
Layout: LayoutModule ? LayoutModule.Layout ?? LayoutModule.default : void 0,
|
|
182
|
+
pageTitleTag: pageContext.title ? `<title>${pageContext.title}</title>` : "",
|
|
183
|
+
serializedContext: serializeContext(pageContext),
|
|
184
|
+
assets: getAssets(route, nonce),
|
|
185
|
+
nonce
|
|
186
|
+
});
|
|
187
|
+
return new Response(html, {
|
|
188
|
+
status: 200,
|
|
189
|
+
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
190
|
+
});
|
|
191
|
+
} catch (error) {
|
|
192
|
+
if (error instanceof AbortRedirect) {
|
|
193
|
+
let redirectUrl = error.url;
|
|
194
|
+
if (redirectUrl.startsWith("/")) redirectUrl = BASE_URL + (redirectUrl === "/" ? "" : redirectUrl);
|
|
195
|
+
if (isJsonRequest) return Response.json({ _redirect: redirectUrl }, { status: 200 });
|
|
196
|
+
return new Response(null, {
|
|
197
|
+
status: error.statusCode,
|
|
198
|
+
headers: { Location: redirectUrl }
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
if (error instanceof AbortRender) {
|
|
202
|
+
if (isJsonRequest) return Response.json({
|
|
203
|
+
is404: error.statusCode === 404,
|
|
204
|
+
is500: error.statusCode >= 500,
|
|
205
|
+
isError: true,
|
|
206
|
+
reason: error.reason
|
|
207
|
+
}, { status: error.statusCode });
|
|
208
|
+
return renderErrorPage(req, error.statusCode, targetPathname, error.reason, nonce);
|
|
209
|
+
}
|
|
210
|
+
console.error("Render Error:", error);
|
|
211
|
+
if (isJsonRequest) return Response.json({ is500: true }, { status: 500 });
|
|
212
|
+
return renderErrorPage(req, 500, targetPathname, error, nonce);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
//#endregion
|
|
2
216
|
export { renderPage };
|
package/dist/vite.mjs
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { BASE_URL } from "./__internal/shared.mjs";
|
|
2
|
-
import { t as renderPage } from "./server-CRcgzWMc.mjs";
|
|
3
1
|
import fs from "node:fs";
|
|
4
2
|
import path from "node:path";
|
|
5
3
|
import { Readable } from "node:stream";
|
|
@@ -113,6 +111,7 @@ function vikeLite({ pagesDir = "pages", apiPrefix = "/api", prerender = false, s
|
|
|
113
111
|
let viteConfigRoot;
|
|
114
112
|
let outDir;
|
|
115
113
|
let hasAnyPrerender;
|
|
114
|
+
let baseUrl;
|
|
116
115
|
const VIRTUAL = {
|
|
117
116
|
routes: "virtual:vike-lite/routes",
|
|
118
117
|
manifest: "virtual:vike-lite/client-manifest",
|
|
@@ -129,6 +128,8 @@ function vikeLite({ pagesDir = "pages", apiPrefix = "/api", prerender = false, s
|
|
|
129
128
|
return {
|
|
130
129
|
name: "vike-lite",
|
|
131
130
|
config(config, { mode }) {
|
|
131
|
+
const rawBase = config.base || "/";
|
|
132
|
+
baseUrl = rawBase === "/" ? "" : rawBase.endsWith("/") ? rawBase.slice(0, -1) : rawBase;
|
|
132
133
|
const envVariables = loadEnv(mode, config.envDir || process.cwd(), "");
|
|
133
134
|
for (const key in envVariables) if (process.env[key] === void 0) process.env[key] = envVariables[key];
|
|
134
135
|
outDir = config.build?.outDir ?? "dist";
|
|
@@ -288,15 +289,12 @@ function vikeLite({ pagesDir = "pages", apiPrefix = "/api", prerender = false, s
|
|
|
288
289
|
const defaultServerEntryContent = isProd ? fs.readFileSync(path.resolve("vike-lite/__internal/vite/defaultServerEntry.mjs"), "utf8") : `import{renderPage}from'vike-lite/server';`;
|
|
289
290
|
return importSetup + defaultServerEntryContent + "export default{fetch:renderPage};";
|
|
290
291
|
}
|
|
291
|
-
if (id === RESOLVED.entryPrerender) return importSetup + `export{routes}from'${VIRTUAL.routes}';`;
|
|
292
|
+
if (id === RESOLVED.entryPrerender) return importSetup + `export{routes}from'${VIRTUAL.routes}';export{renderPage}from'vike-lite/server';`;
|
|
292
293
|
},
|
|
293
294
|
async closeBundle() {
|
|
294
|
-
if (!isProd || this.environment.name !== "ssr") return;
|
|
295
|
+
if (!isProd || !hasAnyPrerender || this.environment.name !== "ssr") return;
|
|
295
296
|
const { pathToFileURL } = await import("node:url");
|
|
296
|
-
const
|
|
297
|
-
if (!fs.existsSync(prerenderPath)) return;
|
|
298
|
-
const { routes } = await import(pathToFileURL(prerenderPath).href);
|
|
299
|
-
if (!routes) return;
|
|
297
|
+
const { routes, renderPage } = await import(pathToFileURL(path.join(viteConfigRoot, outDir, "server/prerender.mjs")).href);
|
|
300
298
|
const urlsToPrerender = /* @__PURE__ */ new Set();
|
|
301
299
|
for (const route of routes) {
|
|
302
300
|
let shouldPrerender = prerender;
|
|
@@ -326,14 +324,14 @@ function vikeLite({ pagesDir = "pages", apiPrefix = "/api", prerender = false, s
|
|
|
326
324
|
let generatedCount = 0;
|
|
327
325
|
const clientDir = path.join(viteConfigRoot, outDir, "client");
|
|
328
326
|
for (const urlPath of urlsToPrerender) {
|
|
329
|
-
const htmlRes = await renderPage(new Request(`http://localhost${
|
|
327
|
+
const htmlRes = await renderPage(new Request(`http://localhost${baseUrl}${urlPath}`));
|
|
330
328
|
if (htmlRes?.ok && htmlRes.headers.get("content-type")?.includes("text/html")) {
|
|
331
329
|
const outDirRoute = path.join(clientDir, urlPath === "/" ? "" : urlPath);
|
|
332
330
|
fs.mkdirSync(outDirRoute, { recursive: true });
|
|
333
331
|
fs.writeFileSync(path.join(outDirRoute, "index.html"), await htmlRes.text());
|
|
334
332
|
} else throw new Error(`[vike-lite] ❌ SSG HTML Error for "${urlPath}"`);
|
|
335
333
|
const jsonTarget = urlPath === "/" ? "/index" : urlPath;
|
|
336
|
-
const jsonRes = await renderPage(new Request(`http://localhost${
|
|
334
|
+
const jsonRes = await renderPage(new Request(`http://localhost${baseUrl}${jsonTarget}.pageContext.json`));
|
|
337
335
|
if (jsonRes?.ok) {
|
|
338
336
|
const jsonOutPath = path.join(clientDir, `${jsonTarget}.pageContext.json`);
|
|
339
337
|
fs.mkdirSync(path.dirname(jsonOutPath), { recursive: true });
|
package/package.json
CHANGED
package/dist/server-CRcgzWMc.mjs
DELETED
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
import { BASE_URL, matchRoute, stripBase } from "./__internal/shared.mjs";
|
|
2
|
-
import { AbortRedirect, AbortRender } from "./server/abort.mjs";
|
|
3
|
-
import { n as store } from "./store-BCs8gL_o.mjs";
|
|
4
|
-
//#region src/utils/serializeContext.ts
|
|
5
|
-
const ESCAPE_LOOKUP = {
|
|
6
|
-
"&": String.raw`\u0026`,
|
|
7
|
-
">": String.raw`\u003e`,
|
|
8
|
-
"<": String.raw`\u003c`,
|
|
9
|
-
"\u2028": String.raw`\u2028`,
|
|
10
|
-
"\u2029": String.raw`\u2029`
|
|
11
|
-
};
|
|
12
|
-
const ESCAPE_REGEX = /[&><\u{2028}\u{2029}]/gu;
|
|
13
|
-
function serializeContext(data) {
|
|
14
|
-
return JSON.stringify(data).replaceAll(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]);
|
|
15
|
-
}
|
|
16
|
-
//#endregion
|
|
17
|
-
//#region src/server/renderPage.ts
|
|
18
|
-
const isProd = process.env.NODE_ENV === "production";
|
|
19
|
-
function withBase(file) {
|
|
20
|
-
return `${BASE_URL === "" ? "" : BASE_URL}${file.startsWith("/") ? file : `/${file}`}`;
|
|
21
|
-
}
|
|
22
|
-
const assetsCache = /* @__PURE__ */ new WeakMap();
|
|
23
|
-
function computeAssetFiles(route) {
|
|
24
|
-
const cssFiles = /* @__PURE__ */ new Set();
|
|
25
|
-
const jsFiles = /* @__PURE__ */ new Map();
|
|
26
|
-
const { manifest } = store;
|
|
27
|
-
function getVirtualEntryClientKey() {
|
|
28
|
-
for (const key in manifest) if (manifest[key].isEntry) return key;
|
|
29
|
-
throw new Error("entry-client not found in manifest");
|
|
30
|
-
}
|
|
31
|
-
function collectAssets(key, isCritical) {
|
|
32
|
-
const chunk = manifest[key];
|
|
33
|
-
if (!chunk) return;
|
|
34
|
-
const current = jsFiles.get(chunk.file);
|
|
35
|
-
if (current === true) return;
|
|
36
|
-
if (current === false && !isCritical) return;
|
|
37
|
-
jsFiles.set(chunk.file, isCritical);
|
|
38
|
-
if (chunk.css) for (const css of chunk.css) cssFiles.add(css);
|
|
39
|
-
if (chunk.imports) for (const imp of chunk.imports) collectAssets(imp, false);
|
|
40
|
-
}
|
|
41
|
-
const virtualEntryClientKey = getVirtualEntryClientKey();
|
|
42
|
-
collectAssets(virtualEntryClientKey, true);
|
|
43
|
-
const { page, layout, head } = route;
|
|
44
|
-
collectAssets(page, true);
|
|
45
|
-
if (head) collectAssets(head, true);
|
|
46
|
-
if (layout) collectAssets(layout, true);
|
|
47
|
-
const criticalJs = [];
|
|
48
|
-
const sharedJs = [];
|
|
49
|
-
for (const [file, isCritical] of jsFiles) (isCritical ? criticalJs : sharedJs).push(file);
|
|
50
|
-
return {
|
|
51
|
-
cssFiles: [...cssFiles],
|
|
52
|
-
criticalJs,
|
|
53
|
-
sharedJs,
|
|
54
|
-
entryClientFile: manifest[virtualEntryClientKey].file
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
function getAssets(route, nonce) {
|
|
58
|
-
if (!isProd) return {
|
|
59
|
-
cssLinks: "",
|
|
60
|
-
jsPreloads: "",
|
|
61
|
-
entryClient: withBase("@id/virtual:vike-lite/entry-client")
|
|
62
|
-
};
|
|
63
|
-
let files = assetsCache.get(route);
|
|
64
|
-
if (!files) {
|
|
65
|
-
files = computeAssetFiles(route);
|
|
66
|
-
assetsCache.set(route, files);
|
|
67
|
-
}
|
|
68
|
-
const nonceAttr = nonce ? ` nonce="${nonce}"` : "";
|
|
69
|
-
return {
|
|
70
|
-
cssLinks: files.cssFiles.map((href) => `<link rel="stylesheet" href="${withBase(href)}"${nonceAttr}>`).join(""),
|
|
71
|
-
jsPreloads: [...files.criticalJs.map((href) => `<link rel="modulepreload" href="${withBase(href)}" crossorigin fetchpriority="high"${nonceAttr}>`), ...files.sharedJs.map((href) => `<link rel="modulepreload" href="${withBase(href)}" crossorigin${nonceAttr}>`)].join(""),
|
|
72
|
-
entryClient: withBase(files.entryClientFile)
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
async function buildPageContext(urlPathname, urlOriginal, isJsonRequest) {
|
|
76
|
-
const matched = matchRoute(urlPathname, store.routes);
|
|
77
|
-
if (!matched) return null;
|
|
78
|
-
const { route, routeParams } = matched;
|
|
79
|
-
const pageContext = {
|
|
80
|
-
routeParams,
|
|
81
|
-
urlOriginal,
|
|
82
|
-
urlPathname
|
|
83
|
-
};
|
|
84
|
-
const [dataMod, titleMod, PageModule, HeadModule, LayoutModule] = await Promise.all([
|
|
85
|
-
route.Data?.() ?? null,
|
|
86
|
-
route.Title?.() ?? null,
|
|
87
|
-
isJsonRequest ? null : route.Page(),
|
|
88
|
-
isJsonRequest ? null : route.Head?.() ?? null,
|
|
89
|
-
isJsonRequest ? null : route.Layout?.() ?? null
|
|
90
|
-
]);
|
|
91
|
-
if (dataMod) try {
|
|
92
|
-
pageContext.data = await (dataMod.data ?? dataMod.default)(pageContext);
|
|
93
|
-
} catch (error) {
|
|
94
|
-
if (!(error instanceof AbortRedirect || error instanceof AbortRender)) console.error("+data hook failed at:", urlPathname);
|
|
95
|
-
throw error;
|
|
96
|
-
}
|
|
97
|
-
if (titleMod) try {
|
|
98
|
-
const titleFn = titleMod.title ?? titleMod.default;
|
|
99
|
-
pageContext.title = typeof titleFn === "function" ? titleFn(pageContext) : titleFn;
|
|
100
|
-
} catch (error) {
|
|
101
|
-
if (!(error instanceof AbortRedirect || error instanceof AbortRender)) console.error("+title hook failed at:", urlPathname);
|
|
102
|
-
throw error;
|
|
103
|
-
}
|
|
104
|
-
return {
|
|
105
|
-
pageContext,
|
|
106
|
-
route,
|
|
107
|
-
PageModule,
|
|
108
|
-
HeadModule,
|
|
109
|
-
LayoutModule
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
async function renderErrorPage(req, status, urlPathname, error, nonce) {
|
|
113
|
-
let errorMessage;
|
|
114
|
-
let is500;
|
|
115
|
-
if (status === 500) {
|
|
116
|
-
errorMessage = isProd ? "Internal Server Error" : error instanceof Error ? error.message : "Unknown error";
|
|
117
|
-
is500 = true;
|
|
118
|
-
} else is500 = false;
|
|
119
|
-
const fallbackText = status === 404 ? "Not Found" : "Internal Server Error";
|
|
120
|
-
if (!store.errorRoute) return new Response(fallbackText, {
|
|
121
|
-
status,
|
|
122
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
123
|
-
});
|
|
124
|
-
try {
|
|
125
|
-
const [PageModule, HeadModule, LayoutModule] = await Promise.all([
|
|
126
|
-
store.errorRoute.Page(),
|
|
127
|
-
store.errorRoute.Head?.() ?? null,
|
|
128
|
-
store.errorRoute.Layout?.() ?? null
|
|
129
|
-
]);
|
|
130
|
-
const pageContext = {
|
|
131
|
-
urlOriginal: req.url,
|
|
132
|
-
urlPathname,
|
|
133
|
-
routeParams: {},
|
|
134
|
-
is404: status === 404,
|
|
135
|
-
is500,
|
|
136
|
-
errorMessage
|
|
137
|
-
};
|
|
138
|
-
const html = await store.config.onRenderHtml({
|
|
139
|
-
pageContext,
|
|
140
|
-
Page: PageModule.Page ?? PageModule.default,
|
|
141
|
-
Layout: LayoutModule ? LayoutModule.Layout ?? LayoutModule.default : void 0,
|
|
142
|
-
Head: HeadModule ? HeadModule.Head ?? HeadModule.default : void 0,
|
|
143
|
-
pageTitleTag: `<title>${status === 404 ? "Page Not Found" : "Server Error"}</title>`,
|
|
144
|
-
serializedContext: serializeContext(pageContext),
|
|
145
|
-
assets: getAssets(store.errorRoute, nonce),
|
|
146
|
-
nonce
|
|
147
|
-
});
|
|
148
|
-
return new Response(html, {
|
|
149
|
-
status,
|
|
150
|
-
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
151
|
-
});
|
|
152
|
-
} catch (renderError) {
|
|
153
|
-
console.error("[vike-lite] Error page render failed:", renderError);
|
|
154
|
-
return new Response(fallbackText, {
|
|
155
|
-
status,
|
|
156
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
async function renderPage(req, { nonce } = {}) {
|
|
161
|
-
let { pathname } = new URL(req.url);
|
|
162
|
-
pathname = stripBase(pathname);
|
|
163
|
-
const isJsonRequest = pathname.endsWith(".pageContext.json");
|
|
164
|
-
let targetPathname = pathname;
|
|
165
|
-
if (isJsonRequest) {
|
|
166
|
-
targetPathname = targetPathname.replace(/\.pageContext\.json$/, "");
|
|
167
|
-
if (targetPathname === "/index") targetPathname = "/";
|
|
168
|
-
}
|
|
169
|
-
try {
|
|
170
|
-
const resolved = await buildPageContext(targetPathname, req.url, isJsonRequest);
|
|
171
|
-
if (!resolved) {
|
|
172
|
-
if (isJsonRequest) return Response.json({ is404: true }, { status: 404 });
|
|
173
|
-
return renderErrorPage(req, 404, targetPathname, nonce);
|
|
174
|
-
}
|
|
175
|
-
const { pageContext, route, PageModule, HeadModule, LayoutModule } = resolved;
|
|
176
|
-
if (isJsonRequest) return Response.json(pageContext);
|
|
177
|
-
const html = await store.config.onRenderHtml({
|
|
178
|
-
pageContext,
|
|
179
|
-
Page: PageModule.Page ?? PageModule.default,
|
|
180
|
-
Head: HeadModule ? HeadModule.Head ?? HeadModule.default : void 0,
|
|
181
|
-
Layout: LayoutModule ? LayoutModule.Layout ?? LayoutModule.default : void 0,
|
|
182
|
-
pageTitleTag: pageContext.title ? `<title>${pageContext.title}</title>` : "",
|
|
183
|
-
serializedContext: serializeContext(pageContext),
|
|
184
|
-
assets: getAssets(route, nonce),
|
|
185
|
-
nonce
|
|
186
|
-
});
|
|
187
|
-
return new Response(html, {
|
|
188
|
-
status: 200,
|
|
189
|
-
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
190
|
-
});
|
|
191
|
-
} catch (error) {
|
|
192
|
-
if (error instanceof AbortRedirect) {
|
|
193
|
-
let redirectUrl = error.url;
|
|
194
|
-
if (redirectUrl.startsWith("/")) redirectUrl = BASE_URL + (redirectUrl === "/" ? "" : redirectUrl);
|
|
195
|
-
if (isJsonRequest) return Response.json({ _redirect: redirectUrl }, { status: 200 });
|
|
196
|
-
return new Response(null, {
|
|
197
|
-
status: error.statusCode,
|
|
198
|
-
headers: { Location: redirectUrl }
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
if (error instanceof AbortRender) {
|
|
202
|
-
if (isJsonRequest) return Response.json({
|
|
203
|
-
is404: error.statusCode === 404,
|
|
204
|
-
is500: error.statusCode >= 500,
|
|
205
|
-
isError: true,
|
|
206
|
-
reason: error.reason
|
|
207
|
-
}, { status: error.statusCode });
|
|
208
|
-
return renderErrorPage(req, error.statusCode, targetPathname, error.reason, nonce);
|
|
209
|
-
}
|
|
210
|
-
console.error("Render Error:", error);
|
|
211
|
-
if (isJsonRequest) return Response.json({ is500: true }, { status: 500 });
|
|
212
|
-
return renderErrorPage(req, 500, targetPathname, error, nonce);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
//#endregion
|
|
216
|
-
export { renderPage as t };
|