vike-lite 1.12.0 → 1.13.0

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.
@@ -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("/")) finalUrl = (BASE_URL.endsWith("/") ? BASE_URL.slice(0, -1) : BASE_URL) + (finalUrl === "/" ? "" : finalUrl);
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,
@@ -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 BASE_URL } from "./shared-DEpJkq09.mjs";
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";
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vike-lite",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "module": "./dist/index.mjs",
@@ -46,6 +46,7 @@
46
46
  "dist"
47
47
  ],
48
48
  "scripts": {
49
+ "tsc": "tsc --noEmit",
49
50
  "build": "tsdown"
50
51
  },
51
52
  "devDependencies": {
@@ -1,4 +0,0 @@
1
- //#region src/shared/index.ts
2
- const { BASE_URL } = import.meta.env;
3
- //#endregion
4
- export { BASE_URL as t };