vike-lite 1.15.10 → 1.15.12
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/__internal/client.d.mts +51 -2
- package/dist/__internal/client.mjs +76 -4
- package/dist/__internal/shared.d.mts +10 -1
- package/dist/__internal/shared.mjs +12 -1
- package/dist/__internal/vite.d.mts +19 -0
- package/dist/__internal/vite.mjs +28 -0
- package/dist/client/router.mjs +2 -2
- package/package.json +5 -1
|
@@ -1,6 +1,55 @@
|
|
|
1
1
|
//#region src/__internal/client.d.ts
|
|
2
2
|
declare function createLinkClickHandler(onNavigate: (url: URL) => void): (e: MouseEvent) => void;
|
|
3
3
|
declare function createLinkPrefetchHandler(onPrefetch: (url: URL) => void): (e: Event) => void;
|
|
4
|
-
declare function finalizeNavigation(
|
|
4
|
+
declare function finalizeNavigation(scrollState: {
|
|
5
|
+
[key: string]: boolean;
|
|
6
|
+
}, key: string): void;
|
|
7
|
+
interface ViewComponents {
|
|
8
|
+
Page: any | null;
|
|
9
|
+
Layout: any | null;
|
|
10
|
+
Head: any | null;
|
|
11
|
+
}
|
|
12
|
+
interface RouteModuleLoaders {
|
|
13
|
+
Page: () => Promise<any>;
|
|
14
|
+
Layout?: () => Promise<any>;
|
|
15
|
+
Head?: () => Promise<any>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Load a route's Page/Layout/Head modules in parallel and resolve each
|
|
19
|
+
* module's export (named export first, falling back to the default export).
|
|
20
|
+
*/
|
|
21
|
+
declare function loadViewModules(route: RouteModuleLoaders): Promise<ViewComponents>;
|
|
22
|
+
/**
|
|
23
|
+
* Build the URL of the `+data`/`+title` JSON endpoint for a given pathname
|
|
24
|
+
* (e.g. base "/my-app" and path "/about" → "/my-app/about.pageContext.json").
|
|
25
|
+
*/
|
|
26
|
+
declare function buildPageContextJsonUrl(pathname: string, search: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Fetch and parse a `.pageContext.json` endpoint, throwing a descriptive error
|
|
29
|
+
* if a proxy/CDN intercepted the request with a non-JSON response.
|
|
30
|
+
*/
|
|
31
|
+
declare function fetchPageContextJson(jsonUrl: string, options: {
|
|
32
|
+
signal: AbortSignal;
|
|
33
|
+
cache?: RequestCache;
|
|
34
|
+
}): Promise<any>;
|
|
35
|
+
interface PrefetchableRoute {
|
|
36
|
+
page?: string;
|
|
37
|
+
layout?: string;
|
|
38
|
+
head?: string;
|
|
39
|
+
Page?: () => Promise<any>;
|
|
40
|
+
Layout?: () => Promise<any>;
|
|
41
|
+
Head?: () => Promise<any>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Create a per-router-instance prefetcher that loads a route's modules at most once
|
|
45
|
+
* (e.g. on link hover/focus), retrying later if the load failed.
|
|
46
|
+
*/
|
|
47
|
+
declare function createRoutePrefetcher(): (route: PrefetchableRoute) => void;
|
|
48
|
+
/**
|
|
49
|
+
* Detect a stale module graph error (dev server restarted / new build deployed) and
|
|
50
|
+
* force a full browser reload, guarded against reload loops via sessionStorage.
|
|
51
|
+
* Returns true if a reload was triggered — the caller should stop further error handling.
|
|
52
|
+
*/
|
|
53
|
+
declare function tryRecoverFromStaleModuleGraph(message: string, urlToReload: string): boolean;
|
|
5
54
|
//#endregion
|
|
6
|
-
export { createLinkClickHandler, createLinkPrefetchHandler, finalizeNavigation };
|
|
55
|
+
export { ViewComponents, buildPageContextJsonUrl, createLinkClickHandler, createLinkPrefetchHandler, createRoutePrefetcher, fetchPageContextJson, finalizeNavigation, loadViewModules, tryRecoverFromStaleModuleGraph };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BASE_URL } from "./shared.mjs";
|
|
1
2
|
//#region src/__internal/client.ts
|
|
2
3
|
function getClientSideUrl(target) {
|
|
3
4
|
if (!target?.href || target.target && target.target !== "_self" || target.hasAttribute("download") || target.hasAttribute("data-native") || target.getAttribute("rel")?.includes("external")) return null;
|
|
@@ -29,15 +30,86 @@ function createLinkPrefetchHandler(onPrefetch) {
|
|
|
29
30
|
onPrefetch(url);
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
|
-
function finalizeNavigation(
|
|
33
|
-
if (
|
|
33
|
+
function finalizeNavigation(scrollState, key) {
|
|
34
|
+
if (scrollState[key] === true) {
|
|
34
35
|
globalThis.scrollTo(0, 0);
|
|
35
|
-
|
|
36
|
+
scrollState[key] = false;
|
|
36
37
|
} else if (globalThis.location.hash) requestAnimationFrame(() => {
|
|
37
38
|
try {
|
|
38
39
|
document.querySelector(decodeURIComponent(globalThis.location.hash))?.scrollIntoView();
|
|
39
40
|
} catch {}
|
|
40
41
|
});
|
|
41
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Load a route's Page/Layout/Head modules in parallel and resolve each
|
|
45
|
+
* module's export (named export first, falling back to the default export).
|
|
46
|
+
*/
|
|
47
|
+
async function loadViewModules(route) {
|
|
48
|
+
const [PageMod, LayoutMod, HeadMod] = await Promise.all([
|
|
49
|
+
route.Page(),
|
|
50
|
+
route.Layout?.() ?? null,
|
|
51
|
+
route.Head?.() ?? null
|
|
52
|
+
]);
|
|
53
|
+
return {
|
|
54
|
+
Page: PageMod.Page ?? PageMod.default,
|
|
55
|
+
Layout: LayoutMod?.Layout ?? LayoutMod?.default ?? null,
|
|
56
|
+
Head: HeadMod?.Head ?? HeadMod?.default ?? null
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Build the URL of the `+data`/`+title` JSON endpoint for a given pathname
|
|
61
|
+
* (e.g. base "/my-app" and path "/about" → "/my-app/about.pageContext.json").
|
|
62
|
+
*/
|
|
63
|
+
function buildPageContextJsonUrl(pathname, search) {
|
|
64
|
+
return `${BASE_URL}${pathname === "/" ? "/index" : pathname}.pageContext.json${search}`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Fetch and parse a `.pageContext.json` endpoint, throwing a descriptive error
|
|
68
|
+
* if a proxy/CDN intercepted the request with a non-JSON response.
|
|
69
|
+
*/
|
|
70
|
+
async function fetchPageContextJson(jsonUrl, options) {
|
|
71
|
+
const res = await fetch(jsonUrl, options);
|
|
72
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
73
|
+
if (!contentType.includes("application/json")) throw new Error(`Expected JSON but got "${contentType}" for ${jsonUrl}. Check your proxy/CDN configuration.`);
|
|
74
|
+
return res.json();
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Create a per-router-instance prefetcher that loads a route's modules at most once
|
|
78
|
+
* (e.g. on link hover/focus), retrying later if the load failed.
|
|
79
|
+
*/
|
|
80
|
+
function createRoutePrefetcher() {
|
|
81
|
+
const prefetchedModules = /* @__PURE__ */ new Set();
|
|
82
|
+
return function prefetchRoute(route) {
|
|
83
|
+
const modules = [
|
|
84
|
+
[route.page, route.Page],
|
|
85
|
+
[route.layout, route.Layout],
|
|
86
|
+
[route.head, route.Head]
|
|
87
|
+
];
|
|
88
|
+
for (const [key, loader] of modules) {
|
|
89
|
+
if (!key || !loader || prefetchedModules.has(key)) continue;
|
|
90
|
+
prefetchedModules.add(key);
|
|
91
|
+
loader().catch(() => {
|
|
92
|
+
prefetchedModules.delete(key);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const STALE_MODULE_GRAPH_PATTERN = /dynamically imported module|importing a module script failed/i;
|
|
98
|
+
const RELOAD_GUARD_KEY = "vike-lite:reload-guard";
|
|
99
|
+
const RELOAD_GUARD_WINDOW_MS = 1e4;
|
|
100
|
+
/**
|
|
101
|
+
* Detect a stale module graph error (dev server restarted / new build deployed) and
|
|
102
|
+
* force a full browser reload, guarded against reload loops via sessionStorage.
|
|
103
|
+
* Returns true if a reload was triggered — the caller should stop further error handling.
|
|
104
|
+
*/
|
|
105
|
+
function tryRecoverFromStaleModuleGraph(message, urlToReload) {
|
|
106
|
+
if (!STALE_MODULE_GRAPH_PATTERN.test(message)) return false;
|
|
107
|
+
const last = Number(sessionStorage.getItem(RELOAD_GUARD_KEY) ?? 0);
|
|
108
|
+
if (Date.now() - last <= RELOAD_GUARD_WINDOW_MS) return false;
|
|
109
|
+
sessionStorage.setItem(RELOAD_GUARD_KEY, String(Date.now()));
|
|
110
|
+
console.warn("App update detected, forcing reload…");
|
|
111
|
+
globalThis.location.assign(urlToReload);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
42
114
|
//#endregion
|
|
43
|
-
export { createLinkClickHandler, createLinkPrefetchHandler, finalizeNavigation };
|
|
115
|
+
export { buildPageContextJsonUrl, createLinkClickHandler, createLinkPrefetchHandler, createRoutePrefetcher, fetchPageContextJson, finalizeNavigation, loadViewModules, tryRecoverFromStaleModuleGraph };
|
|
@@ -18,6 +18,15 @@ declare function matchRoute(urlPathname: string, routes: typeof import('virtual:
|
|
|
18
18
|
routeParams: Record<string, string>;
|
|
19
19
|
} | null;
|
|
20
20
|
declare const BASE_URL: string;
|
|
21
|
+
/**
|
|
22
|
+
* Remove the base path from a pathname (e.g. `location.pathname` on the client,
|
|
23
|
+
* or the incoming request pathname on the server).
|
|
24
|
+
* Returns the pathname unchanged if it doesn't match the base.
|
|
25
|
+
*/
|
|
21
26
|
declare function stripBase(pathname: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Prepend the base path to an absolute, root-relative path (e.g. '/about').
|
|
29
|
+
*/
|
|
30
|
+
declare function prependBase(pathname: string): string;
|
|
22
31
|
//#endregion
|
|
23
|
-
export { BASE_URL, RenderContext, matchRoute, stripBase };
|
|
32
|
+
export { BASE_URL, RenderContext, matchRoute, prependBase, stripBase };
|
|
@@ -44,11 +44,22 @@ const BASE_URL = (() => {
|
|
|
44
44
|
const { BASE_URL } = import.meta.env;
|
|
45
45
|
return BASE_URL.endsWith("/") ? BASE_URL.slice(0, -1) : BASE_URL;
|
|
46
46
|
})();
|
|
47
|
+
/**
|
|
48
|
+
* Remove the base path from a pathname (e.g. `location.pathname` on the client,
|
|
49
|
+
* or the incoming request pathname on the server).
|
|
50
|
+
* Returns the pathname unchanged if it doesn't match the base.
|
|
51
|
+
*/
|
|
47
52
|
function stripBase(pathname) {
|
|
48
53
|
if (BASE_URL === "") return pathname;
|
|
49
54
|
if (pathname === BASE_URL) return "/";
|
|
50
55
|
if (pathname.startsWith(BASE_URL + "/")) return pathname.slice(BASE_URL.length);
|
|
51
56
|
return pathname;
|
|
52
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Prepend the base path to an absolute, root-relative path (e.g. '/about').
|
|
60
|
+
*/
|
|
61
|
+
function prependBase(pathname) {
|
|
62
|
+
return BASE_URL + (pathname === "/" ? "" : pathname);
|
|
63
|
+
}
|
|
53
64
|
//#endregion
|
|
54
|
-
export { BASE_URL, matchRoute, stripBase };
|
|
65
|
+
export { BASE_URL, matchRoute, prependBase, stripBase };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Plugin } from "vite";
|
|
2
|
+
//#region src/__internal/vite.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Creates the Vite plugin that wires a UI framework adapter (react/vue/solid) into vike-lite,
|
|
5
|
+
* by providing the virtual `virtual:vike-lite/client` and `virtual:vike-lite/server` modules
|
|
6
|
+
* that vike-lite's core plugin reads to discover the framework's onRenderClient/onRenderHtml hooks.
|
|
7
|
+
*
|
|
8
|
+
* This centralizes logic that is otherwise identical across every `vike-lite-*` package.
|
|
9
|
+
*/
|
|
10
|
+
declare function createFrameworkAdapterPlugin({ packageName, hydration, wrapServerHydration }: {
|
|
11
|
+
/** The framework adapter's package name, e.g. 'vike-lite-react'. */
|
|
12
|
+
packageName: string;
|
|
13
|
+
/** @default true */
|
|
14
|
+
hydration?: boolean;
|
|
15
|
+
/** @default true */
|
|
16
|
+
wrapServerHydration?: boolean;
|
|
17
|
+
}): Plugin;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { createFrameworkAdapterPlugin };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/__internal/vite.ts
|
|
2
|
+
/**
|
|
3
|
+
* Creates the Vite plugin that wires a UI framework adapter (react/vue/solid) into vike-lite,
|
|
4
|
+
* by providing the virtual `virtual:vike-lite/client` and `virtual:vike-lite/server` modules
|
|
5
|
+
* that vike-lite's core plugin reads to discover the framework's onRenderClient/onRenderHtml hooks.
|
|
6
|
+
*
|
|
7
|
+
* This centralizes logic that is otherwise identical across every `vike-lite-*` package.
|
|
8
|
+
*/
|
|
9
|
+
function createFrameworkAdapterPlugin({ packageName, hydration = true, wrapServerHydration = true }) {
|
|
10
|
+
const virtualClientId = "virtual:vike-lite/client";
|
|
11
|
+
const virtualServerId = "virtual:vike-lite/server";
|
|
12
|
+
const resolvedVirtualClientId = "\0virtual:vike-lite/client";
|
|
13
|
+
const resolvedVirtualServerId = "\0virtual:vike-lite/server";
|
|
14
|
+
return {
|
|
15
|
+
name: packageName,
|
|
16
|
+
enforce: "pre",
|
|
17
|
+
resolveId(id) {
|
|
18
|
+
if (id === virtualClientId) return resolvedVirtualClientId;
|
|
19
|
+
if (id === virtualServerId) return resolvedVirtualServerId;
|
|
20
|
+
},
|
|
21
|
+
load(id) {
|
|
22
|
+
if (id === resolvedVirtualClientId) return `export const onRenderClient=async(options)=>(await import("${packageName}/__internal/client/onRenderClient")).onRenderClient({...options,hydration:${hydration}});`;
|
|
23
|
+
if (id === resolvedVirtualServerId) return wrapServerHydration ? `import { onRenderHtml as _onRenderHtml } from '${packageName}/__internal/server/onRenderHtml';export const onRenderHtml = (ctx) => _onRenderHtml({ ...ctx, hydration: ${hydration} });` : `export{onRenderHtml}from'${packageName}/__internal/server/onRenderHtml';`;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { createFrameworkAdapterPlugin };
|
package/dist/client/router.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { prependBase } from "../__internal/shared.mjs";
|
|
2
2
|
//#region src/client/router.ts
|
|
3
3
|
/**
|
|
4
4
|
* Change page programmatically on the client without reloading the browser.
|
|
@@ -8,7 +8,7 @@ import { BASE_URL } from "../__internal/shared.mjs";
|
|
|
8
8
|
function navigate(url, options) {
|
|
9
9
|
if (typeof globalThis === "undefined") throw new Error("navigate() can only be called on the client side.");
|
|
10
10
|
let finalUrl = url;
|
|
11
|
-
if (finalUrl.startsWith("/")) finalUrl =
|
|
11
|
+
if (finalUrl.startsWith("/")) finalUrl = prependBase(finalUrl);
|
|
12
12
|
globalThis.history.pushState({ triggeredBy: "vike-lite" }, "", finalUrl);
|
|
13
13
|
globalThis.dispatchEvent(new CustomEvent("vike-navigate", { detail: {
|
|
14
14
|
keepScrollPosition: options?.keepScrollPosition,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vike-lite",
|
|
3
|
-
"version": "1.15.
|
|
3
|
+
"version": "1.15.12",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -67,6 +67,10 @@
|
|
|
67
67
|
"./__internal/server": {
|
|
68
68
|
"types": "./dist/__internal/server.d.mts",
|
|
69
69
|
"import": "./dist/__internal/server.mjs"
|
|
70
|
+
},
|
|
71
|
+
"./__internal/vite": {
|
|
72
|
+
"types": "./dist/__internal/vite.d.mts",
|
|
73
|
+
"import": "./dist/__internal/vite.mjs"
|
|
70
74
|
}
|
|
71
75
|
},
|
|
72
76
|
"files": [
|