vike-lite 1.12.0 → 1.13.1
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/client/router.mjs +4 -2
- package/dist/index.d.mts +5 -5
- package/dist/server-Cm1ZA_Dp.mjs +220 -0
- package/dist/server.mjs +1 -195
- package/dist/vite.mjs +13 -13
- package/package.json +3 -2
- package/dist/shared-DEpJkq09.mjs +0 -4
package/dist/client/router.mjs
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { t as BASE_URL } from "../shared-DEpJkq09.mjs";
|
|
2
1
|
//#region src/client/router.ts
|
|
3
2
|
/**
|
|
4
3
|
* Change page programmatically on the client without reloading the browser.
|
|
@@ -8,7 +7,10 @@ import { t as BASE_URL } from "../shared-DEpJkq09.mjs";
|
|
|
8
7
|
function navigate(url, options) {
|
|
9
8
|
if (typeof globalThis === "undefined") throw new Error("navigate() can only be called on the client side.");
|
|
10
9
|
let finalUrl = url;
|
|
11
|
-
if (finalUrl.startsWith("/"))
|
|
10
|
+
if (finalUrl.startsWith("/")) {
|
|
11
|
+
const { BASE_URL } = import.meta.env;
|
|
12
|
+
finalUrl = (BASE_URL.endsWith("/") ? BASE_URL.slice(0, -1) : BASE_URL) + (finalUrl === "/" ? "" : finalUrl);
|
|
13
|
+
}
|
|
12
14
|
globalThis.history.pushState({ triggeredBy: "vike-lite" }, "", finalUrl);
|
|
13
15
|
globalThis.dispatchEvent(new CustomEvent("vike-navigate", { detail: {
|
|
14
16
|
keepScrollPosition: options?.keepScrollPosition,
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/index.d.ts
|
|
2
|
-
type PageContextBase = {
|
|
2
|
+
type PageContextBase<Data = unknown> = {
|
|
3
3
|
routeParams: Record<string, string>;
|
|
4
4
|
urlOriginal: string;
|
|
5
5
|
urlPathname: string;
|
|
@@ -8,19 +8,19 @@ type PageContextBase = {
|
|
|
8
8
|
is404?: boolean;
|
|
9
9
|
is500?: boolean;
|
|
10
10
|
errorMessage?: string;
|
|
11
|
-
}
|
|
12
|
-
type PageContext<Data = unknown> = PageContextBase & (unknown extends Data ? {
|
|
11
|
+
} & (unknown extends Data ? {
|
|
13
12
|
data?: Data;
|
|
14
13
|
} : {
|
|
15
14
|
data: Data;
|
|
16
15
|
});
|
|
17
|
-
type PageContextServer<Data = unknown> =
|
|
16
|
+
type PageContextServer<Data = unknown> = PageContextBase<Data> & {
|
|
18
17
|
isClientSide: false;
|
|
19
18
|
nonce?: string;
|
|
20
19
|
};
|
|
21
|
-
type PageContextClient = PageContextBase & {
|
|
20
|
+
type PageContextClient<Data = unknown> = PageContextBase<Data> & {
|
|
22
21
|
isClientSide: true;
|
|
23
22
|
isHydration?: boolean;
|
|
24
23
|
};
|
|
24
|
+
type PageContext<Data = unknown> = PageContextServer<Data> | PageContextClient<Data>;
|
|
25
25
|
//#endregion
|
|
26
26
|
export { PageContext, PageContextClient, PageContextServer };
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { t as matchRoute } from "./matchRoute-nTNPxoMq.mjs";
|
|
2
|
+
import { AbortRedirect, AbortRender } from "./server/abort.mjs";
|
|
3
|
+
import { n as store } from "./store-DPJQje-K.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
|
+
const { BASE_URL } = import.meta.env;
|
|
21
|
+
return `${BASE_URL === "/" ? "" : BASE_URL.endsWith("/") ? BASE_URL.slice(0, -1) : BASE_URL}${file.startsWith("/") ? file : `/${file}`}`;
|
|
22
|
+
}
|
|
23
|
+
const assetsCache = /* @__PURE__ */ new WeakMap();
|
|
24
|
+
function getAssets(route, nonce) {
|
|
25
|
+
if (!isProd) return computeAssets(route, nonce);
|
|
26
|
+
const nonceKey = nonce || "";
|
|
27
|
+
let routeCache = assetsCache.get(route);
|
|
28
|
+
if (!routeCache) {
|
|
29
|
+
routeCache = /* @__PURE__ */ new Map();
|
|
30
|
+
assetsCache.set(route, routeCache);
|
|
31
|
+
}
|
|
32
|
+
let assets = routeCache.get(nonceKey);
|
|
33
|
+
if (!assets) {
|
|
34
|
+
assets = computeAssets(route, nonce);
|
|
35
|
+
routeCache.set(nonceKey, assets);
|
|
36
|
+
}
|
|
37
|
+
return assets;
|
|
38
|
+
}
|
|
39
|
+
function computeAssets(route, nonce) {
|
|
40
|
+
if (!isProd) return {
|
|
41
|
+
cssLinks: "",
|
|
42
|
+
jsPreloads: "",
|
|
43
|
+
entryClient: withBase("@id/virtual:entry-client")
|
|
44
|
+
};
|
|
45
|
+
const cssFiles = /* @__PURE__ */ new Set();
|
|
46
|
+
const jsFiles = /* @__PURE__ */ new Map();
|
|
47
|
+
const { manifest } = store;
|
|
48
|
+
function getVirtualEntryClientKey() {
|
|
49
|
+
for (const key in manifest) if (manifest[key].isEntry) return key;
|
|
50
|
+
throw new Error("entry-client not found in manifest");
|
|
51
|
+
}
|
|
52
|
+
function collectAssets(key, isCritical) {
|
|
53
|
+
const chunk = manifest[key];
|
|
54
|
+
if (!chunk) return;
|
|
55
|
+
const current = jsFiles.get(chunk.file);
|
|
56
|
+
if (current === true) return;
|
|
57
|
+
if (current === false && !isCritical) return;
|
|
58
|
+
jsFiles.set(chunk.file, isCritical);
|
|
59
|
+
if (chunk.css) for (const css of chunk.css) cssFiles.add(css);
|
|
60
|
+
if (chunk.imports) for (const imp of chunk.imports) collectAssets(imp, false);
|
|
61
|
+
}
|
|
62
|
+
const virtualEntryClientKey = getVirtualEntryClientKey();
|
|
63
|
+
collectAssets(virtualEntryClientKey, true);
|
|
64
|
+
const { page, layout, head } = route;
|
|
65
|
+
collectAssets(page, true);
|
|
66
|
+
if (head) collectAssets(head, true);
|
|
67
|
+
if (layout) collectAssets(layout, true);
|
|
68
|
+
const criticalJs = [];
|
|
69
|
+
const sharedJs = [];
|
|
70
|
+
for (const [file, isCritical] of jsFiles) (isCritical ? criticalJs : sharedJs).push(file);
|
|
71
|
+
const nonceAttr = nonce ? ` nonce="${nonce}"` : "";
|
|
72
|
+
return {
|
|
73
|
+
cssLinks: [...cssFiles].map((href) => `<link rel="stylesheet" href="${withBase(href)}"${nonceAttr}>`).join(""),
|
|
74
|
+
jsPreloads: [...criticalJs.map((href) => `<link rel="modulepreload" href="${withBase(href)}" crossorigin fetchpriority="high"${nonceAttr}>`), ...sharedJs.map((href) => `<link rel="modulepreload" href="${withBase(href)}" crossorigin${nonceAttr}>`)].join(""),
|
|
75
|
+
entryClient: withBase(manifest[virtualEntryClientKey].file)
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async function buildPageContext(urlPathname, urlOriginal, isJsonRequest) {
|
|
79
|
+
const matched = matchRoute(urlPathname, store.routes);
|
|
80
|
+
if (!matched) return null;
|
|
81
|
+
const { route, routeParams } = matched;
|
|
82
|
+
const pageContext = {
|
|
83
|
+
routeParams,
|
|
84
|
+
urlOriginal,
|
|
85
|
+
urlPathname
|
|
86
|
+
};
|
|
87
|
+
const [dataMod, titleMod, PageModule, HeadModule, LayoutModule] = await Promise.all([
|
|
88
|
+
route.Data?.() ?? null,
|
|
89
|
+
route.Title?.() ?? null,
|
|
90
|
+
isJsonRequest ? null : route.Page(),
|
|
91
|
+
isJsonRequest ? null : route.Head?.() ?? null,
|
|
92
|
+
isJsonRequest ? null : route.Layout?.() ?? null
|
|
93
|
+
]);
|
|
94
|
+
if (dataMod) try {
|
|
95
|
+
pageContext.data = await (dataMod.data ?? dataMod.default)(pageContext);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error("+data hook failed at:", urlPathname);
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
if (titleMod) try {
|
|
101
|
+
const titleFn = titleMod.title ?? titleMod.default;
|
|
102
|
+
pageContext.title = typeof titleFn === "function" ? titleFn(pageContext) : titleFn;
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error("+title hook failed at:", urlPathname);
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
pageContext,
|
|
109
|
+
route,
|
|
110
|
+
PageModule,
|
|
111
|
+
HeadModule,
|
|
112
|
+
LayoutModule
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
async function renderErrorPage(req, status, urlPathname, error, nonce) {
|
|
116
|
+
let errorMessage;
|
|
117
|
+
let is500;
|
|
118
|
+
if (status === 500) {
|
|
119
|
+
console.error(`[vike-lite] Server Error:`, error);
|
|
120
|
+
errorMessage = isProd ? "Internal Server Error" : error instanceof Error ? error.message : "Unknown error";
|
|
121
|
+
is500 = true;
|
|
122
|
+
} else is500 = false;
|
|
123
|
+
if (!store.errorRoute) return new Response(status === 404 ? "Not Found" : "Internal Server Error", { status });
|
|
124
|
+
try {
|
|
125
|
+
const { default: onRenderHtml } = await store.config.onRenderHtml();
|
|
126
|
+
const [PageModule, HeadModule, LayoutModule] = await Promise.all([
|
|
127
|
+
store.errorRoute.Page(),
|
|
128
|
+
store.errorRoute.Head?.() ?? null,
|
|
129
|
+
store.errorRoute.Layout?.() ?? null
|
|
130
|
+
]);
|
|
131
|
+
const pageContext = {
|
|
132
|
+
urlOriginal: req.url,
|
|
133
|
+
urlPathname,
|
|
134
|
+
routeParams: {},
|
|
135
|
+
is404: status === 404,
|
|
136
|
+
is500,
|
|
137
|
+
errorMessage
|
|
138
|
+
};
|
|
139
|
+
const html = await onRenderHtml({
|
|
140
|
+
pageContext,
|
|
141
|
+
Page: PageModule.Page ?? PageModule.default,
|
|
142
|
+
Layout: LayoutModule ? LayoutModule.Layout ?? LayoutModule.default : void 0,
|
|
143
|
+
Head: HeadModule ? HeadModule.Head ?? HeadModule.default : void 0,
|
|
144
|
+
pageTitleTag: `<title>${status === 404 ? "Page Not Found" : "Server Error"}</title>`,
|
|
145
|
+
serializedContext: serializeContext(pageContext),
|
|
146
|
+
assets: getAssets(store.errorRoute, nonce),
|
|
147
|
+
nonce
|
|
148
|
+
});
|
|
149
|
+
return new Response(html, {
|
|
150
|
+
status,
|
|
151
|
+
headers: { "Content-Type": "text/html" }
|
|
152
|
+
});
|
|
153
|
+
} catch (renderError) {
|
|
154
|
+
console.error("[vike-lite] Error page render failed:", renderError);
|
|
155
|
+
return new Response(status === 404 ? "Not Found" : "Internal Server Error", { status });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async function renderPage(req, { nonce } = {}) {
|
|
159
|
+
let { pathname } = new URL(req.url);
|
|
160
|
+
const { BASE_URL } = import.meta.env;
|
|
161
|
+
if (BASE_URL !== "/") {
|
|
162
|
+
const baseSlashed = BASE_URL.endsWith("/") ? BASE_URL : BASE_URL + "/";
|
|
163
|
+
const baseNoSlash = baseSlashed.slice(0, -1);
|
|
164
|
+
pathname = pathname === baseNoSlash ? "/" : pathname.slice(baseSlashed.length - 1);
|
|
165
|
+
}
|
|
166
|
+
const isJsonRequest = pathname.endsWith(".pageContext.json");
|
|
167
|
+
let targetPathname = pathname;
|
|
168
|
+
if (isJsonRequest) {
|
|
169
|
+
targetPathname = targetPathname.replace(/\.pageContext\.json$/, "");
|
|
170
|
+
if (targetPathname === "/index") targetPathname = "/";
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
const resolved = await buildPageContext(targetPathname, req.url, isJsonRequest);
|
|
174
|
+
if (!resolved) {
|
|
175
|
+
if (isJsonRequest) return Response.json({ is404: true }, { status: 404 });
|
|
176
|
+
return renderErrorPage(req, 404, targetPathname, nonce);
|
|
177
|
+
}
|
|
178
|
+
const { pageContext, route, PageModule, HeadModule, LayoutModule } = resolved;
|
|
179
|
+
if (isJsonRequest) return Response.json(pageContext);
|
|
180
|
+
const { default: onRenderHtml } = await store.config.onRenderHtml();
|
|
181
|
+
const html = await onRenderHtml({
|
|
182
|
+
pageContext,
|
|
183
|
+
Page: PageModule.Page ?? PageModule.default,
|
|
184
|
+
Head: HeadModule ? HeadModule.Head ?? HeadModule.default : void 0,
|
|
185
|
+
Layout: LayoutModule ? LayoutModule.Layout ?? LayoutModule.default : void 0,
|
|
186
|
+
pageTitleTag: pageContext.title ? `<title>${pageContext.title}</title>` : "",
|
|
187
|
+
serializedContext: serializeContext(pageContext),
|
|
188
|
+
assets: getAssets(route, nonce),
|
|
189
|
+
nonce
|
|
190
|
+
});
|
|
191
|
+
return new Response(html, {
|
|
192
|
+
status: 200,
|
|
193
|
+
headers: { "Content-Type": "text/html" }
|
|
194
|
+
});
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (error instanceof AbortRedirect) {
|
|
197
|
+
let redirectUrl = error.url;
|
|
198
|
+
if (redirectUrl.startsWith("/")) redirectUrl = (BASE_URL.endsWith("/") ? BASE_URL.slice(0, -1) : BASE_URL) + (redirectUrl === "/" ? "" : redirectUrl);
|
|
199
|
+
if (isJsonRequest) return Response.json({ _redirect: redirectUrl }, { status: 200 });
|
|
200
|
+
return new Response(null, {
|
|
201
|
+
status: error.statusCode,
|
|
202
|
+
headers: { Location: redirectUrl }
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
if (error instanceof AbortRender) {
|
|
206
|
+
if (isJsonRequest) return Response.json({
|
|
207
|
+
is404: error.statusCode === 404,
|
|
208
|
+
is500: error.statusCode >= 500,
|
|
209
|
+
isError: true,
|
|
210
|
+
reason: error.reason
|
|
211
|
+
}, { status: error.statusCode });
|
|
212
|
+
return renderErrorPage(req, error.statusCode, targetPathname, error.reason, nonce);
|
|
213
|
+
}
|
|
214
|
+
console.error("Render Error:", error);
|
|
215
|
+
if (isJsonRequest) return Response.json({ is500: true }, { status: 500 });
|
|
216
|
+
return renderErrorPage(req, 500, targetPathname, error, nonce);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
//#endregion
|
|
220
|
+
export { renderPage as t };
|
package/dist/server.mjs
CHANGED
|
@@ -1,196 +1,2 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
import { t as matchRoute } from "./matchRoute-nTNPxoMq.mjs";
|
|
3
|
-
import { AbortRedirect, AbortRender } from "./server/abort.mjs";
|
|
4
|
-
import { n as store } from "./store-DPJQje-K.mjs";
|
|
5
|
-
//#region src/utils/serializeContext.ts
|
|
6
|
-
const ESCAPE_LOOKUP = {
|
|
7
|
-
"&": String.raw`\u0026`,
|
|
8
|
-
">": String.raw`\u003e`,
|
|
9
|
-
"<": String.raw`\u003c`,
|
|
10
|
-
"\u2028": String.raw`\u2028`,
|
|
11
|
-
"\u2029": String.raw`\u2029`
|
|
12
|
-
};
|
|
13
|
-
const ESCAPE_REGEX = /[&><\u{2028}\u{2029}]/gu;
|
|
14
|
-
function serializeContext(data) {
|
|
15
|
-
return JSON.stringify(data).replaceAll(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]);
|
|
16
|
-
}
|
|
17
|
-
//#endregion
|
|
18
|
-
//#region src/server/renderPage.ts
|
|
19
|
-
const isProd = process.env.NODE_ENV === "production";
|
|
20
|
-
function withBase(file) {
|
|
21
|
-
return `${BASE_URL.replace(/\/$/, "")}/${file.replace(/^\//, "")}`;
|
|
22
|
-
}
|
|
23
|
-
function getAssets(route, nonce) {
|
|
24
|
-
if (!isProd) return {
|
|
25
|
-
cssLinks: "",
|
|
26
|
-
jsPreloads: "",
|
|
27
|
-
entryClient: withBase("@id/virtual:entry-client")
|
|
28
|
-
};
|
|
29
|
-
const cssFiles = /* @__PURE__ */ new Set();
|
|
30
|
-
const jsFiles = /* @__PURE__ */ new Map();
|
|
31
|
-
const { manifest } = store;
|
|
32
|
-
function getVirtualEntryClientKey() {
|
|
33
|
-
for (const key in manifest) if (manifest[key].isEntry) return key;
|
|
34
|
-
throw new Error("entry-client not found in manifest");
|
|
35
|
-
}
|
|
36
|
-
function collectAssets(key, isCritical) {
|
|
37
|
-
const chunk = manifest[key];
|
|
38
|
-
if (!chunk) return;
|
|
39
|
-
const current = jsFiles.get(chunk.file);
|
|
40
|
-
if (current === true) return;
|
|
41
|
-
if (current === false && !isCritical) return;
|
|
42
|
-
jsFiles.set(chunk.file, isCritical);
|
|
43
|
-
if (chunk.css) for (const css of chunk.css) cssFiles.add(css);
|
|
44
|
-
if (chunk.imports) for (const imp of chunk.imports) collectAssets(imp, false);
|
|
45
|
-
}
|
|
46
|
-
const virtualEntryClientKey = getVirtualEntryClientKey();
|
|
47
|
-
collectAssets(virtualEntryClientKey, true);
|
|
48
|
-
const { page, layout, head } = route;
|
|
49
|
-
collectAssets(page, true);
|
|
50
|
-
if (head) collectAssets(head, true);
|
|
51
|
-
if (layout) collectAssets(layout, true);
|
|
52
|
-
const criticalJs = [];
|
|
53
|
-
const sharedJs = [];
|
|
54
|
-
for (const [file, isCritical] of jsFiles) (isCritical ? criticalJs : sharedJs).push(file);
|
|
55
|
-
const nonceAttr = nonce ? ` nonce="${nonce}"` : "";
|
|
56
|
-
return {
|
|
57
|
-
cssLinks: [...cssFiles].map((href) => `<link rel="stylesheet" href="${withBase(href)}"${nonceAttr}>`).join(""),
|
|
58
|
-
jsPreloads: [...criticalJs.map((href) => `<link rel="modulepreload" href="${withBase(href)}" crossorigin fetchpriority="high"${nonceAttr}>`), ...sharedJs.map((href) => `<link rel="modulepreload" href="${withBase(href)}" crossorigin${nonceAttr}>`)].join(""),
|
|
59
|
-
entryClient: withBase(manifest[virtualEntryClientKey].file)
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
async function buildPageContext(urlPathname, urlOriginal, isJsonRequest) {
|
|
63
|
-
const matched = matchRoute(urlPathname, store.routes);
|
|
64
|
-
if (!matched) return null;
|
|
65
|
-
const { route, routeParams } = matched;
|
|
66
|
-
const pageContext = {
|
|
67
|
-
routeParams,
|
|
68
|
-
urlOriginal,
|
|
69
|
-
urlPathname
|
|
70
|
-
};
|
|
71
|
-
const [dataMod, titleMod, PageModule, HeadModule, LayoutModule] = await Promise.all([
|
|
72
|
-
route.Data?.() ?? null,
|
|
73
|
-
route.Title?.() ?? null,
|
|
74
|
-
isJsonRequest ? null : route.Page(),
|
|
75
|
-
isJsonRequest ? null : route.Head?.() ?? null,
|
|
76
|
-
isJsonRequest ? null : route.Layout?.() ?? null
|
|
77
|
-
]);
|
|
78
|
-
if (dataMod) try {
|
|
79
|
-
pageContext.data = await (dataMod.data ?? dataMod.default)(pageContext);
|
|
80
|
-
} catch (error) {
|
|
81
|
-
console.error("+data hook failed at:", urlPathname);
|
|
82
|
-
throw error;
|
|
83
|
-
}
|
|
84
|
-
if (titleMod) try {
|
|
85
|
-
const titleFn = titleMod.title ?? titleMod.default;
|
|
86
|
-
pageContext.title = typeof titleFn === "function" ? titleFn(pageContext) : titleFn;
|
|
87
|
-
} catch (error) {
|
|
88
|
-
console.error("+title hook failed at:", urlPathname);
|
|
89
|
-
throw error;
|
|
90
|
-
}
|
|
91
|
-
return {
|
|
92
|
-
pageContext,
|
|
93
|
-
route,
|
|
94
|
-
PageModule,
|
|
95
|
-
HeadModule,
|
|
96
|
-
LayoutModule
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
async function renderErrorPage(req, status, urlPathname, error, nonce) {
|
|
100
|
-
if (!store.errorRoute) return new Response(status === 404 ? "Not Found" : "Internal Server Error", { status });
|
|
101
|
-
try {
|
|
102
|
-
const { default: onRenderHtml } = await store.config.onRenderHtml();
|
|
103
|
-
const [PageModule, HeadModule, LayoutModule] = await Promise.all([
|
|
104
|
-
store.errorRoute.Page(),
|
|
105
|
-
store.errorRoute.Head?.() ?? null,
|
|
106
|
-
store.errorRoute.Layout?.() ?? null
|
|
107
|
-
]);
|
|
108
|
-
const pageContext = {
|
|
109
|
-
urlOriginal: req.url,
|
|
110
|
-
urlPathname,
|
|
111
|
-
routeParams: {},
|
|
112
|
-
is404: status === 404,
|
|
113
|
-
is500: status === 500,
|
|
114
|
-
errorMessage: status === 500 && error instanceof Error ? error.message : void 0
|
|
115
|
-
};
|
|
116
|
-
const html = await onRenderHtml({
|
|
117
|
-
pageContext,
|
|
118
|
-
Page: PageModule.Page ?? PageModule.default,
|
|
119
|
-
Layout: LayoutModule ? LayoutModule.Layout ?? LayoutModule.default : void 0,
|
|
120
|
-
Head: HeadModule ? HeadModule.Head ?? HeadModule.default : void 0,
|
|
121
|
-
pageTitleTag: `<title>${status === 404 ? "Page Not Found" : "Server Error"}</title>`,
|
|
122
|
-
serializedContext: serializeContext(pageContext),
|
|
123
|
-
assets: getAssets(store.errorRoute, nonce),
|
|
124
|
-
nonce
|
|
125
|
-
});
|
|
126
|
-
return new Response(html, {
|
|
127
|
-
status,
|
|
128
|
-
headers: { "Content-Type": "text/html" }
|
|
129
|
-
});
|
|
130
|
-
} catch (renderError) {
|
|
131
|
-
console.error("Error page render failed:", renderError);
|
|
132
|
-
return new Response(status === 404 ? "Not Found" : "Internal Server Error", { status });
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
async function renderPage(req, { nonce } = {}) {
|
|
136
|
-
let { pathname } = new URL(req.url);
|
|
137
|
-
if (BASE_URL !== "/") {
|
|
138
|
-
const baseSlashed = BASE_URL.endsWith("/") ? BASE_URL : BASE_URL + "/";
|
|
139
|
-
const baseNoSlash = baseSlashed.slice(0, -1);
|
|
140
|
-
pathname = pathname === baseNoSlash ? "/" : pathname.slice(baseSlashed.length - 1);
|
|
141
|
-
}
|
|
142
|
-
const isJsonRequest = pathname.endsWith(".pageContext.json");
|
|
143
|
-
let targetPathname = pathname;
|
|
144
|
-
if (isJsonRequest) {
|
|
145
|
-
targetPathname = targetPathname.replace(/\.pageContext\.json$/, "");
|
|
146
|
-
if (targetPathname === "/index") targetPathname = "/";
|
|
147
|
-
}
|
|
148
|
-
try {
|
|
149
|
-
const resolved = await buildPageContext(targetPathname, req.url, isJsonRequest);
|
|
150
|
-
if (!resolved) {
|
|
151
|
-
if (isJsonRequest) return Response.json({ is404: true }, { status: 404 });
|
|
152
|
-
return renderErrorPage(req, 404, targetPathname, nonce);
|
|
153
|
-
}
|
|
154
|
-
const { pageContext, route, PageModule, HeadModule, LayoutModule } = resolved;
|
|
155
|
-
if (isJsonRequest) return Response.json(pageContext);
|
|
156
|
-
const { default: onRenderHtml } = await store.config.onRenderHtml();
|
|
157
|
-
const html = await onRenderHtml({
|
|
158
|
-
pageContext,
|
|
159
|
-
Page: PageModule.Page ?? PageModule.default,
|
|
160
|
-
Head: HeadModule ? HeadModule.Head ?? HeadModule.default : void 0,
|
|
161
|
-
Layout: LayoutModule ? LayoutModule.Layout ?? LayoutModule.default : void 0,
|
|
162
|
-
pageTitleTag: pageContext.title ? `<title>${pageContext.title}</title>` : "",
|
|
163
|
-
serializedContext: serializeContext(pageContext),
|
|
164
|
-
assets: getAssets(route, nonce),
|
|
165
|
-
nonce
|
|
166
|
-
});
|
|
167
|
-
return new Response(html, {
|
|
168
|
-
status: 200,
|
|
169
|
-
headers: { "Content-Type": "text/html" }
|
|
170
|
-
});
|
|
171
|
-
} catch (error) {
|
|
172
|
-
if (error instanceof AbortRedirect) {
|
|
173
|
-
let redirectUrl = error.url;
|
|
174
|
-
if (redirectUrl.startsWith("/")) redirectUrl = (BASE_URL.endsWith("/") ? BASE_URL.slice(0, -1) : BASE_URL) + (redirectUrl === "/" ? "" : redirectUrl);
|
|
175
|
-
if (isJsonRequest) return Response.json({ _redirect: redirectUrl }, { status: 200 });
|
|
176
|
-
return new Response(null, {
|
|
177
|
-
status: error.statusCode,
|
|
178
|
-
headers: { Location: redirectUrl }
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
if (error instanceof AbortRender) {
|
|
182
|
-
if (isJsonRequest) return Response.json({
|
|
183
|
-
is404: error.statusCode === 404,
|
|
184
|
-
is500: error.statusCode >= 500,
|
|
185
|
-
isError: true,
|
|
186
|
-
reason: error.reason
|
|
187
|
-
}, { status: error.statusCode });
|
|
188
|
-
return renderErrorPage(req, error.statusCode, targetPathname, error.reason, nonce);
|
|
189
|
-
}
|
|
190
|
-
console.error("Render Error:", error);
|
|
191
|
-
if (isJsonRequest) return Response.json({ is500: true }, { status: 500 });
|
|
192
|
-
return renderErrorPage(req, 500, targetPathname, error, nonce);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
//#endregion
|
|
1
|
+
import { t as renderPage } from "./server-Cm1ZA_Dp.mjs";
|
|
196
2
|
export { renderPage };
|
package/dist/vite.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t as renderPage } from "./server-Cm1ZA_Dp.mjs";
|
|
1
2
|
import fs from "node:fs";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { Readable } from "node:stream";
|
|
@@ -206,7 +207,7 @@ function routerPlugin({ pagesDir = "pages", serverEntry = "server/index", apiPre
|
|
|
206
207
|
};
|
|
207
208
|
},
|
|
208
209
|
configResolved(config) {
|
|
209
|
-
if (!config.plugins.some((plugin) => plugin.name?.startsWith("vike-lite-") && SUPPORTED_RENDERERS.includes(plugin.name.replace("vike-lite-", "")))) throw new Error(`No UI renderer plugin found in 'vite.config': please install and configure one of ${SUPPORTED_RENDERERS.map((r) => `vike-lite-${r}`).join(", ")}`);
|
|
210
|
+
if (!config.plugins.some((plugin) => plugin.name?.startsWith("vike-lite-") && SUPPORTED_RENDERERS.includes(plugin.name.replace("vike-lite-", "")))) throw new Error(`[vike-lite] No UI renderer plugin found in 'vite.config': please install and configure one of ${SUPPORTED_RENDERERS.map((r) => `vike-lite-${r}`).join(", ")}`);
|
|
210
211
|
},
|
|
211
212
|
resolveId(id) {
|
|
212
213
|
if (VIRTUAL_VALUES.has(id)) return "\0" + id;
|
|
@@ -259,7 +260,7 @@ function routerPlugin({ pagesDir = "pages", serverEntry = "server/index", apiPre
|
|
|
259
260
|
serverEntryPath = (basePath + ext).replaceAll("\\", "/");
|
|
260
261
|
break;
|
|
261
262
|
}
|
|
262
|
-
if (!serverEntryPath) throw new Error(`[vike-lite] serverEntry ${serverEntry} file not found
|
|
263
|
+
if (!serverEntryPath) throw new Error(`[vike-lite] serverEntry ${serverEntry} file not found`);
|
|
263
264
|
return `import '${VIRTUAL.setup}';export * from'${serverEntryPath}';export{default}from'${serverEntryPath}';`;
|
|
264
265
|
}
|
|
265
266
|
return `import '${VIRTUAL.setup}';import{renderPage}from'vike-lite/server';
|
|
@@ -360,7 +361,6 @@ if (process.env.NODE_ENV === 'production') {
|
|
|
360
361
|
if (!fs.existsSync(prerenderPath)) return;
|
|
361
362
|
const { routes } = await import(pathToFileURL(prerenderPath).href);
|
|
362
363
|
if (!routes) return;
|
|
363
|
-
const { renderPage } = await import("vike-lite/server");
|
|
364
364
|
const urlsToPrerender = /* @__PURE__ */ new Set();
|
|
365
365
|
for (const route of routes) {
|
|
366
366
|
let shouldPrerender = prerender;
|
|
@@ -386,29 +386,29 @@ if (process.env.NODE_ENV === 'production') {
|
|
|
386
386
|
}
|
|
387
387
|
}
|
|
388
388
|
if (urlsToPrerender.size === 0) return;
|
|
389
|
-
console.log("
|
|
389
|
+
console.log("[vike-lite] 📦 Starting Static Site Generation (SSG)…");
|
|
390
390
|
const { BASE_URL } = import.meta.env;
|
|
391
391
|
const baseNoSlash = BASE_URL.endsWith("/") ? BASE_URL.slice(0, -1) : BASE_URL;
|
|
392
392
|
let generatedCount = 0;
|
|
393
393
|
const clientDir = path.resolve(viteConfigRoot, outDir, "client");
|
|
394
394
|
for (const urlPath of urlsToPrerender) {
|
|
395
395
|
const htmlRes = await renderPage(new Request(`http://localhost${baseNoSlash}${urlPath}`));
|
|
396
|
-
if (htmlRes
|
|
396
|
+
if (htmlRes?.ok && htmlRes.headers.get("content-type")?.includes("text/html")) {
|
|
397
397
|
const outDirRoute = path.join(clientDir, urlPath === "/" ? "" : urlPath);
|
|
398
398
|
fs.mkdirSync(outDirRoute, { recursive: true });
|
|
399
399
|
fs.writeFileSync(path.join(outDirRoute, "index.html"), await htmlRes.text());
|
|
400
|
-
} else throw new Error(
|
|
400
|
+
} else throw new Error(`[vike-lite] ❌ SSG HTML Error for "${urlPath}"`);
|
|
401
401
|
const jsonTarget = urlPath === "/" ? "/index" : urlPath;
|
|
402
402
|
const jsonRes = await renderPage(new Request(`http://localhost${baseNoSlash}${jsonTarget}.pageContext.json`));
|
|
403
|
-
if (jsonRes
|
|
403
|
+
if (jsonRes?.ok) {
|
|
404
404
|
const jsonOutPath = path.join(clientDir, `${jsonTarget}.pageContext.json`);
|
|
405
405
|
fs.mkdirSync(path.dirname(jsonOutPath), { recursive: true });
|
|
406
406
|
fs.writeFileSync(jsonOutPath, await jsonRes.text());
|
|
407
|
-
} else throw new Error(
|
|
408
|
-
console.log(
|
|
407
|
+
} else throw new Error(`[vike-lite] ❌ SSG JSON Error for "${jsonTarget}"`);
|
|
408
|
+
console.log(` └─ ${urlPath}`);
|
|
409
409
|
generatedCount++;
|
|
410
410
|
}
|
|
411
|
-
console.log(
|
|
411
|
+
console.log(`[vike-lite] ✨ SSG Completed! Generated ${generatedCount} static routes`);
|
|
412
412
|
},
|
|
413
413
|
configureServer(server) {
|
|
414
414
|
return () => {
|
|
@@ -435,14 +435,14 @@ if (process.env.NODE_ENV === 'production') {
|
|
|
435
435
|
headers
|
|
436
436
|
};
|
|
437
437
|
if (req.url.startsWith(apiPrefix)) {
|
|
438
|
-
server.config.logger.info(
|
|
438
|
+
server.config.logger.info(`⚡ API: ${req.method} ${req.url}`, { timestamp: true });
|
|
439
439
|
requestInit.body = Readable.toWeb(req);
|
|
440
440
|
requestInit.duplex = "half";
|
|
441
|
-
} else if (req.url.endsWith(".pageContext.json")) server.config.logger.info(
|
|
441
|
+
} else if (req.url.endsWith(".pageContext.json")) server.config.logger.info(`🔄 SPA Navigation: ${req.url}`, { timestamp: true });
|
|
442
442
|
const response = await app.fetch(new Request(`http://${req.headers.host}${req.url}`, requestInit));
|
|
443
443
|
res.statusCode = response.status;
|
|
444
444
|
if (response.headers.get("content-type")?.includes("text/html")) {
|
|
445
|
-
server.config.logger.info(
|
|
445
|
+
server.config.logger.info(`📄 Page: ${req.url}`, { timestamp: true });
|
|
446
446
|
let html = await response.text();
|
|
447
447
|
html = await injectFOUCStyles(server, html);
|
|
448
448
|
html = await server.transformIndexHtml(req.url, html);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vike-lite",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -46,11 +46,12 @@
|
|
|
46
46
|
"dist"
|
|
47
47
|
],
|
|
48
48
|
"scripts": {
|
|
49
|
+
"tsc": "tsc --noEmit",
|
|
49
50
|
"build": "tsdown"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
|
52
53
|
"@types/node": "^26.1.1",
|
|
53
|
-
"tsdown": "^0.22.
|
|
54
|
+
"tsdown": "^0.22.5",
|
|
54
55
|
"vite": "^8.1.4"
|
|
55
56
|
},
|
|
56
57
|
"peerDependencies": {
|
package/dist/shared-DEpJkq09.mjs
DELETED