vike-lite-react 1.0.1 → 1.0.3
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/README.md +4 -3
- package/dist/PageContextProvider-Dec19bRR.mjs +9 -0
- package/dist/__internal/client/onRenderClient.d.mts +8 -0
- package/dist/__internal/client/onRenderClient.mjs +299 -0
- package/dist/__internal/server/onRenderHtml.d.mts +13 -0
- package/dist/__internal/server/onRenderHtml.mjs +33 -0
- package/dist/globalContext-BIY5-Fdk.mjs +8 -0
- package/dist/index.mjs +2 -7
- package/dist/vite.d.mts +1 -1
- package/dist/vite.mjs +3 -3
- package/package.json +32 -15
package/README.md
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Vike Lite React
|
|
2
|
+
<a href="https://npmjs.com/package/vike-lite-react"><img src="https://img.shields.io/npm/v/vike-lite-react.svg" alt="npm package"></a>
|
|
2
3
|
|
|
3
4
|
The official React integration for `vike-lite`. It provides seamless Server-Side Rendering (SSR), Static Site Generation (SSG), and client hydration out of the box, with a focus on minimalism and performance.
|
|
4
5
|
|
|
@@ -19,7 +20,7 @@ yarn add -D vike-lite-react @vitejs/plugin-react
|
|
|
19
20
|
yarn add react react-dom
|
|
20
21
|
```
|
|
21
22
|
|
|
22
|
-
###
|
|
23
|
+
### 🛠️ Vite Plugin
|
|
23
24
|
|
|
24
25
|
```ts
|
|
25
26
|
// vite.config.ts
|
|
@@ -141,7 +142,7 @@ Why choose `vike-lite`? It's built to be as minimal and fast as possible. Here a
|
|
|
141
142
|
| **Reactivity Architecture** | _Single Source of Truth_ | _Separation of Concerns_ | `vike-lite-react` keeps page data (`pageContext`) and the active UI (`view`: Page/Layout/Head) as two separate `useState` atoms, so a data update doesn't force React to re-resolve which components are mounted, and vice versa.
|
|
142
143
|
| **Accessibility (A11y)** | _Not_ handled by default | _Automatic_ handled | After a client-side navigation, `vike-lite-react` moves the focus to `#root`. This significantly improves UX for keyboard navigation and screen readers.
|
|
143
144
|
| `useData()` **Hook** | `getter` only | `[getter, setter]` | `vike-lite-react` allows you to mutate the route data locally without needing other state managers.
|
|
144
|
-
| URL parsed |
|
|
145
|
+
| URL parsed | [pageContext.urlParsed](https://vike.dev/pageContext#urlParsed) | [useUrl()](#useurl) | A dedicated hook, consistent with `vike-lite-react`
|
|
145
146
|
|
|
146
147
|
---
|
|
147
148
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { t as PageContextReactContext } from "./globalContext-BIY5-Fdk.mjs";
|
|
2
|
+
//#region src/hooks/PageContextProvider.tsx
|
|
3
|
+
function PageContextProvider({ value, children }) {
|
|
4
|
+
return <PageContextReactContext.Provider value={value}>
|
|
5
|
+
{children}
|
|
6
|
+
</PageContextReactContext.Provider>;
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { PageContextProvider as t };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { VikeState } from "vike-lite/__internal/server";
|
|
2
|
+
//#region src/__internal/client/onRenderClient.d.ts
|
|
3
|
+
declare function onRenderClient(clientOptions: {
|
|
4
|
+
routes: VikeState['routes'];
|
|
5
|
+
errorRoute: VikeState['errorRoute'];
|
|
6
|
+
}): Promise<void>;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { onRenderClient as default };
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { t as PageContextProvider } from "../../PageContextProvider-Dec19bRR.mjs";
|
|
2
|
+
import { Component, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { createRoot, hydrateRoot } from "react-dom/client";
|
|
4
|
+
import { matchRoute } from "vike-lite/__internal/shared";
|
|
5
|
+
import { hydration } from "virtual:vike-lite/config";
|
|
6
|
+
//#region src/__internal/shared/stripBase.ts
|
|
7
|
+
function stripBase(pathname) {
|
|
8
|
+
const { BASE_URL } = import.meta.env;
|
|
9
|
+
if (BASE_URL === "/" || !pathname.startsWith(BASE_URL)) return pathname;
|
|
10
|
+
const stripped = pathname.slice(BASE_URL.length);
|
|
11
|
+
return stripped.startsWith("/") ? stripped : "/" + stripped;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/__internal/client/onRenderClient.tsx
|
|
15
|
+
var RootErrorBoundary = class extends Component {
|
|
16
|
+
static getDerivedStateFromError(error) {
|
|
17
|
+
return { error };
|
|
18
|
+
}
|
|
19
|
+
state = { error: null };
|
|
20
|
+
render() {
|
|
21
|
+
if (this.state.error) return <div>Error: {this.state.error.message}</div>;
|
|
22
|
+
return this.props.children;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
function RouterApp(props) {
|
|
26
|
+
const [pageContext, setPageContextState] = useState(props.initialContext);
|
|
27
|
+
const [view, setView] = useState(props.initialView);
|
|
28
|
+
const [currentUrl, setCurrentUrl] = useState(props.initialUrl);
|
|
29
|
+
const [currentPathname, setCurrentPathname] = useState(props.initialContext.urlPathname);
|
|
30
|
+
const [reloadTick, setReloadTick] = useState(0);
|
|
31
|
+
const shouldScrollToTop = useRef(false);
|
|
32
|
+
const pendingContextOverride = useRef(null);
|
|
33
|
+
const reloadResolvers = useRef([]);
|
|
34
|
+
const isFirstRun = useRef(true);
|
|
35
|
+
const setPageContext = useCallback((updater) => {
|
|
36
|
+
setPageContextState(updater);
|
|
37
|
+
}, []);
|
|
38
|
+
const matchedRoute = useMemo(() => matchRoute(currentPathname, props.routes), [currentPathname, props.routes]);
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const handleLinkClick = (e) => {
|
|
41
|
+
const target = e.target.closest("a");
|
|
42
|
+
if (!target?.href || target.target === "_blank" || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey) return;
|
|
43
|
+
const url = new URL(target.href);
|
|
44
|
+
if (url.origin !== globalThis.location.origin) return;
|
|
45
|
+
if (url.pathname === globalThis.location.pathname && url.search === globalThis.location.search) return;
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
globalThis.history.pushState({ triggeredBy: "vike-lite" }, "", url.href);
|
|
48
|
+
if (!url.hash) shouldScrollToTop.current = true;
|
|
49
|
+
setCurrentUrl(url.href);
|
|
50
|
+
setCurrentPathname(stripBase(url.pathname));
|
|
51
|
+
};
|
|
52
|
+
const prefetchedModules = /* @__PURE__ */ new Set();
|
|
53
|
+
function prefetchRoute(route) {
|
|
54
|
+
const modules = [
|
|
55
|
+
[route.page, route.Page],
|
|
56
|
+
[route.layout, route.Layout],
|
|
57
|
+
[route.head, route.Head]
|
|
58
|
+
];
|
|
59
|
+
for (const [key, loader] of modules) {
|
|
60
|
+
if (!key || !loader || prefetchedModules.has(key)) continue;
|
|
61
|
+
prefetchedModules.add(key);
|
|
62
|
+
loader().catch(() => {
|
|
63
|
+
prefetchedModules.delete(key);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const handleLinkPrefetch = (e) => {
|
|
68
|
+
const target = e.target.closest("a");
|
|
69
|
+
if (!target?.href) return;
|
|
70
|
+
if (target.target && target.target !== "_self") return;
|
|
71
|
+
if (target.hasAttribute("download")) return;
|
|
72
|
+
const url = new URL(target.href);
|
|
73
|
+
if (url.origin !== globalThis.location.origin) return;
|
|
74
|
+
if (url.pathname === globalThis.location.pathname && url.search === globalThis.location.search) return;
|
|
75
|
+
const matched = matchRoute(stripBase(url.pathname), props.routes);
|
|
76
|
+
if (!matched) return;
|
|
77
|
+
prefetchRoute(matched.route);
|
|
78
|
+
};
|
|
79
|
+
const handlePopState = () => {
|
|
80
|
+
setCurrentUrl(globalThis.location.href);
|
|
81
|
+
setCurrentPathname(stripBase(globalThis.location.pathname));
|
|
82
|
+
};
|
|
83
|
+
const handleProgrammaticNavigate = (e) => {
|
|
84
|
+
const detail = e.detail || {};
|
|
85
|
+
if (!detail.keepScrollPosition) shouldScrollToTop.current = true;
|
|
86
|
+
if (detail.pageContext) pendingContextOverride.current = detail.pageContext;
|
|
87
|
+
setCurrentUrl(globalThis.location.href);
|
|
88
|
+
setCurrentPathname(stripBase(globalThis.location.pathname));
|
|
89
|
+
};
|
|
90
|
+
const handleProgrammaticReload = (e) => {
|
|
91
|
+
const resolve = e.detail?.resolve;
|
|
92
|
+
if (resolve) reloadResolvers.current.push(resolve);
|
|
93
|
+
setReloadTick((t) => t + 1);
|
|
94
|
+
};
|
|
95
|
+
document.addEventListener("click", handleLinkClick);
|
|
96
|
+
document.addEventListener("pointerenter", handleLinkPrefetch, { capture: true });
|
|
97
|
+
document.addEventListener("focusin", handleLinkPrefetch);
|
|
98
|
+
globalThis.addEventListener("popstate", handlePopState);
|
|
99
|
+
globalThis.addEventListener("vike-navigate", handleProgrammaticNavigate);
|
|
100
|
+
globalThis.addEventListener("vike-reload", handleProgrammaticReload);
|
|
101
|
+
return () => {
|
|
102
|
+
document.removeEventListener("click", handleLinkClick);
|
|
103
|
+
document.removeEventListener("pointerenter", handleLinkPrefetch, { capture: true });
|
|
104
|
+
document.removeEventListener("focusin", handleLinkPrefetch);
|
|
105
|
+
globalThis.removeEventListener("popstate", handlePopState);
|
|
106
|
+
globalThis.removeEventListener("vike-navigate", handleProgrammaticNavigate);
|
|
107
|
+
globalThis.removeEventListener("vike-reload", handleProgrammaticReload);
|
|
108
|
+
};
|
|
109
|
+
}, [props.routes]);
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
const pathname = currentPathname;
|
|
112
|
+
const isReload = reloadTick > 0;
|
|
113
|
+
if (isFirstRun.current && !isReload && globalThis.__PAGE_CONTEXT__?.urlPathname === pathname) {
|
|
114
|
+
isFirstRun.current = false;
|
|
115
|
+
globalThis.__PAGE_CONTEXT__.urlPathname = void 0;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
isFirstRun.current = false;
|
|
119
|
+
const controller = new AbortController();
|
|
120
|
+
const signal = controller.signal;
|
|
121
|
+
const urlFull = currentUrl;
|
|
122
|
+
const matched = matchedRoute;
|
|
123
|
+
const loadRoute = async () => {
|
|
124
|
+
const contextOverride = pendingContextOverride.current;
|
|
125
|
+
pendingContextOverride.current = null;
|
|
126
|
+
function finalizeNavigation() {
|
|
127
|
+
if (shouldScrollToTop.current) {
|
|
128
|
+
globalThis.scrollTo(0, 0);
|
|
129
|
+
shouldScrollToTop.current = false;
|
|
130
|
+
} else if (globalThis.location.hash) requestAnimationFrame(() => {
|
|
131
|
+
try {
|
|
132
|
+
document.querySelector(decodeURIComponent(globalThis.location.hash))?.scrollIntoView();
|
|
133
|
+
} catch {}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
const renderErrorPage = async (is404, message) => {
|
|
137
|
+
if (!props.errorRoute) return;
|
|
138
|
+
const [ErrorPageMod, ErrorLayoutMod, ErrorHeadMod] = await Promise.all([
|
|
139
|
+
props.errorRoute.Page(),
|
|
140
|
+
props.errorRoute.Layout?.() ?? null,
|
|
141
|
+
props.errorRoute.Head?.() ?? null
|
|
142
|
+
]);
|
|
143
|
+
if (signal.aborted) return;
|
|
144
|
+
setPageContext(() => ({
|
|
145
|
+
urlOriginal: urlFull,
|
|
146
|
+
urlPathname: pathname,
|
|
147
|
+
routeParams: {},
|
|
148
|
+
is404,
|
|
149
|
+
is500: !is404,
|
|
150
|
+
errorMessage: message,
|
|
151
|
+
isClientSide: true,
|
|
152
|
+
isHydration: false
|
|
153
|
+
}));
|
|
154
|
+
setView({
|
|
155
|
+
Page: ErrorPageMod.Page ?? ErrorPageMod.default,
|
|
156
|
+
Layout: ErrorLayoutMod?.Layout ?? ErrorLayoutMod?.default ?? null,
|
|
157
|
+
Head: ErrorHeadMod?.Head ?? ErrorHeadMod?.default ?? null
|
|
158
|
+
});
|
|
159
|
+
document.title = is404 ? "Not Found" : "Server Error";
|
|
160
|
+
finalizeNavigation();
|
|
161
|
+
};
|
|
162
|
+
if (!matched) return renderErrorPage(true);
|
|
163
|
+
const { route, routeParams } = matched;
|
|
164
|
+
try {
|
|
165
|
+
const urlObj = new URL(urlFull);
|
|
166
|
+
const jsonTarget = pathname === "/" ? "/index" : pathname;
|
|
167
|
+
const { BASE_URL } = import.meta.env;
|
|
168
|
+
const jsonUrl = `${BASE_URL.endsWith("/") ? BASE_URL.slice(0, -1) : BASE_URL}${jsonTarget}.pageContext.json${urlObj.search}`;
|
|
169
|
+
let ctx = null;
|
|
170
|
+
if (route.data || route.title) {
|
|
171
|
+
const res = await fetch(jsonUrl, {
|
|
172
|
+
signal,
|
|
173
|
+
cache: isReload ? "no-cache" : "default"
|
|
174
|
+
});
|
|
175
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
176
|
+
if (!contentType.includes("application/json")) throw new Error(`Expected JSON but got "${contentType}" for ${jsonUrl}. Check your proxy/CDN configuration.`);
|
|
177
|
+
ctx = await res.json();
|
|
178
|
+
}
|
|
179
|
+
if (signal.aborted) return;
|
|
180
|
+
if (ctx?._redirect) {
|
|
181
|
+
const urlObjRedirect = new URL(ctx._redirect, globalThis.location.origin);
|
|
182
|
+
if (urlObjRedirect.origin !== globalThis.location.origin) {
|
|
183
|
+
globalThis.location.assign(ctx._redirect);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
globalThis.history.pushState({ triggeredBy: "vike-lite" }, "", ctx._redirect);
|
|
187
|
+
shouldScrollToTop.current = true;
|
|
188
|
+
setCurrentUrl(urlObjRedirect.href);
|
|
189
|
+
setCurrentPathname(stripBase(urlObjRedirect.pathname));
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (ctx && (ctx.is404 || ctx.is500 || ctx.isError)) return renderErrorPage(ctx.is404, ctx.reason || "Server Error");
|
|
193
|
+
const [PageMod, LayoutMod, HeadMod] = await Promise.all([
|
|
194
|
+
route.Page(),
|
|
195
|
+
route.Layout?.() ?? null,
|
|
196
|
+
route.Head?.() ?? null
|
|
197
|
+
]);
|
|
198
|
+
if (signal.aborted) return;
|
|
199
|
+
setPageContext(() => ({
|
|
200
|
+
routeParams,
|
|
201
|
+
urlOriginal: urlObj.href,
|
|
202
|
+
urlPathname: pathname,
|
|
203
|
+
search: urlObj.search,
|
|
204
|
+
...ctx?.data && { data: ctx.data },
|
|
205
|
+
...ctx?.title && { title: ctx.title },
|
|
206
|
+
...contextOverride,
|
|
207
|
+
isClientSide: true,
|
|
208
|
+
isHydration: false
|
|
209
|
+
}));
|
|
210
|
+
setView({
|
|
211
|
+
Page: PageMod.Page ?? PageMod.default,
|
|
212
|
+
Layout: LayoutMod?.Layout ?? LayoutMod?.default ?? null,
|
|
213
|
+
Head: HeadMod?.Head ?? HeadMod?.default ?? null
|
|
214
|
+
});
|
|
215
|
+
if (ctx?.title) document.title = ctx.title;
|
|
216
|
+
requestAnimationFrame(() => {
|
|
217
|
+
if (globalThis.location.hash) return;
|
|
218
|
+
document.querySelector("#root")?.focus({ preventScroll: true });
|
|
219
|
+
});
|
|
220
|
+
finalizeNavigation();
|
|
221
|
+
} catch (error) {
|
|
222
|
+
if (error.name === "AbortError") return;
|
|
223
|
+
const message = error.message || "";
|
|
224
|
+
if (/dynamically imported module|importing a module script failed/i.test(message)) {
|
|
225
|
+
const GUARD_KEY = "vike-lite:reload-guard";
|
|
226
|
+
const last = Number(sessionStorage.getItem(GUARD_KEY) ?? 0);
|
|
227
|
+
if (Date.now() - last > 1e4) {
|
|
228
|
+
sessionStorage.setItem(GUARD_KEY, String(Date.now()));
|
|
229
|
+
console.warn("App update detected, forcing reload…");
|
|
230
|
+
globalThis.location.assign(urlFull);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
console.error("Router Error:", error);
|
|
235
|
+
renderErrorPage(false, message);
|
|
236
|
+
} finally {
|
|
237
|
+
if (!signal.aborted) {
|
|
238
|
+
for (const resolve of reloadResolvers.current) resolve();
|
|
239
|
+
reloadResolvers.current = [];
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
loadRoute();
|
|
244
|
+
return () => controller.abort();
|
|
245
|
+
}, [
|
|
246
|
+
currentPathname,
|
|
247
|
+
currentUrl,
|
|
248
|
+
matchedRoute,
|
|
249
|
+
reloadTick
|
|
250
|
+
]);
|
|
251
|
+
const { Page, Layout } = view;
|
|
252
|
+
const contextValue = useMemo(() => ({
|
|
253
|
+
pageContext,
|
|
254
|
+
setPageContext
|
|
255
|
+
}), [pageContext, setPageContext]);
|
|
256
|
+
return <RootErrorBoundary>
|
|
257
|
+
<PageContextProvider value={contextValue}>
|
|
258
|
+
{Layout ? <Layout><Page /></Layout> : <Page />}
|
|
259
|
+
</PageContextProvider>
|
|
260
|
+
</RootErrorBoundary>;
|
|
261
|
+
}
|
|
262
|
+
async function onRenderClient(clientOptions) {
|
|
263
|
+
const container = document.querySelector("#root");
|
|
264
|
+
const rawContext = globalThis.__PAGE_CONTEXT__ ?? {};
|
|
265
|
+
const isHydration = hydration && !!globalThis.__PAGE_CONTEXT__;
|
|
266
|
+
const initialContext = {
|
|
267
|
+
...rawContext,
|
|
268
|
+
isClientSide: true,
|
|
269
|
+
isHydration
|
|
270
|
+
};
|
|
271
|
+
let initialView = {
|
|
272
|
+
Page: null,
|
|
273
|
+
Layout: null,
|
|
274
|
+
Head: null
|
|
275
|
+
};
|
|
276
|
+
if (isHydration) {
|
|
277
|
+
const matched = matchRoute(initialContext.urlPathname ?? globalThis.location.pathname, clientOptions.routes);
|
|
278
|
+
if (matched) {
|
|
279
|
+
const [PageMod, LayoutMod, HeadMod] = await Promise.all([
|
|
280
|
+
matched.route.Page(),
|
|
281
|
+
matched.route.Layout?.() ?? null,
|
|
282
|
+
matched.route.Head?.() ?? null
|
|
283
|
+
]);
|
|
284
|
+
initialView = {
|
|
285
|
+
Page: PageMod.Page ?? PageMod.default,
|
|
286
|
+
Layout: LayoutMod?.Layout ?? LayoutMod?.default ?? null,
|
|
287
|
+
Head: HeadMod?.Head ?? HeadMod?.default ?? null
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const app = <RouterApp {...clientOptions} initialView={initialView} initialContext={initialContext} initialUrl={globalThis.location.href} />;
|
|
292
|
+
if (isHydration) hydrateRoot(container, app);
|
|
293
|
+
else {
|
|
294
|
+
container.replaceChildren();
|
|
295
|
+
createRoot(container).render(app);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
//#endregion
|
|
299
|
+
export { onRenderClient as default };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ComponentType, ReactNode } from "react";
|
|
2
|
+
import { RenderContext } from "vike-lite/__internal/shared";
|
|
3
|
+
//#region src/__internal/server/onRenderHtml.d.ts
|
|
4
|
+
interface ReactRenderContext extends RenderContext {
|
|
5
|
+
Page: ComponentType;
|
|
6
|
+
Head?: ComponentType;
|
|
7
|
+
Layout?: ComponentType<{
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
11
|
+
declare function onRenderHtml({ pageContext, Page, Head, Layout, pageTitleTag, serializedContext, assets }: ReactRenderContext): string;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { ReactRenderContext, onRenderHtml };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { t as PageContextProvider } from "../../PageContextProvider-Dec19bRR.mjs";
|
|
2
|
+
import { renderToString } from "react-dom/server";
|
|
3
|
+
import { hydration } from "virtual:vike-lite/renderer";
|
|
4
|
+
//#region src/__internal/server/onRenderHtml.tsx
|
|
5
|
+
function onRenderHtml({ pageContext, Page, Head, Layout, pageTitleTag, serializedContext, assets }) {
|
|
6
|
+
const { cssLinks, jsPreloads, entryClient } = assets;
|
|
7
|
+
const providerValue = {
|
|
8
|
+
pageContext,
|
|
9
|
+
setPageContext: () => {}
|
|
10
|
+
};
|
|
11
|
+
return `<!DOCTYPE html>
|
|
12
|
+
<html lang="en">
|
|
13
|
+
<head>
|
|
14
|
+
<meta charset="utf-8">
|
|
15
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
16
|
+
${pageTitleTag}
|
|
17
|
+
${cssLinks}
|
|
18
|
+
${jsPreloads}
|
|
19
|
+
${Head ? renderToString(<PageContextProvider value={providerValue}>
|
|
20
|
+
<Head />
|
|
21
|
+
</PageContextProvider>) : ""}
|
|
22
|
+
<script>window.__PAGE_CONTEXT__=${serializedContext}<\/script>
|
|
23
|
+
</head>
|
|
24
|
+
<body>
|
|
25
|
+
<div id="root" tabindex="-1">${hydration ? renderToString(<PageContextProvider value={providerValue}>
|
|
26
|
+
{Layout ? <Layout><Page /></Layout> : <Page />}
|
|
27
|
+
</PageContextProvider>) : ""}</div>
|
|
28
|
+
<script type="module" src="${entryClient}"><\/script>
|
|
29
|
+
</body>
|
|
30
|
+
</html>`;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { onRenderHtml };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createContext } from "react";
|
|
2
|
+
//#region src/hooks/globalContext.tsx
|
|
3
|
+
const KEY = "__vike_lite_react_context__";
|
|
4
|
+
const g = globalThis;
|
|
5
|
+
if (!Object.hasOwn(g, KEY)) g[KEY] = createContext(null);
|
|
6
|
+
const PageContextReactContext = g[KEY];
|
|
7
|
+
//#endregion
|
|
8
|
+
export { PageContextReactContext as t };
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
const KEY = "__vike_lite_react_context__";
|
|
4
|
-
const g = globalThis;
|
|
5
|
-
if (!Object.hasOwn(g, KEY)) g[KEY] = createContext(null);
|
|
6
|
-
const PageContextReactContext = g[KEY];
|
|
7
|
-
//#endregion
|
|
1
|
+
import { t as PageContextReactContext } from "./globalContext-BIY5-Fdk.mjs";
|
|
2
|
+
import { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
|
8
3
|
//#region src/hooks/useData.tsx
|
|
9
4
|
function useData() {
|
|
10
5
|
const ctx = useContext(PageContextReactContext);
|
package/dist/vite.d.mts
CHANGED
package/dist/vite.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import react from "@vitejs/plugin-react";
|
|
2
|
-
//#region src/vite
|
|
2
|
+
//#region src/vite.ts
|
|
3
3
|
function vikeLiteReact({ hydration = true, react: reactOptions } = {}) {
|
|
4
4
|
const virtualConfigId = "virtual:vike-lite/config";
|
|
5
5
|
const virtualClientId = "virtual:vike-lite/client";
|
|
@@ -16,9 +16,9 @@ function vikeLiteReact({ hydration = true, react: reactOptions } = {}) {
|
|
|
16
16
|
if (id === virtualServerId) return resolvedVirtualServerId;
|
|
17
17
|
},
|
|
18
18
|
load(id) {
|
|
19
|
-
if (id === resolvedVirtualConfigId) return `export const hydration
|
|
19
|
+
if (id === resolvedVirtualConfigId) return `export const hydration=${hydration};`;
|
|
20
20
|
if (id === resolvedVirtualClientId) return "export const onRenderClient=()=>import(\"vike-lite-react/__internal/client/onRenderClient\");";
|
|
21
|
-
if (id === resolvedVirtualServerId) return `export {
|
|
21
|
+
if (id === resolvedVirtualServerId) return `export {onRenderHtml}from'vike-lite-react/__internal/server/onRenderHtml';`;
|
|
22
22
|
}
|
|
23
23
|
};
|
|
24
24
|
return [...react(reactOptions), adapter];
|
package/package.json
CHANGED
|
@@ -1,7 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vike-lite-react",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/node-ecosystem/vike-lite.git"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/node-ecosystem/vike-lite#readme",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/node-ecosystem/vike-lite/issues"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"framework",
|
|
15
|
+
"lite",
|
|
16
|
+
"react",
|
|
17
|
+
"react-ssr",
|
|
18
|
+
"server-side-rendering",
|
|
19
|
+
"ssg",
|
|
20
|
+
"ssr",
|
|
21
|
+
"static-site-generator",
|
|
22
|
+
"vike",
|
|
23
|
+
"vike-lite",
|
|
24
|
+
"vite",
|
|
25
|
+
"vite-plugin",
|
|
26
|
+
"vite-ssr",
|
|
27
|
+
"vitejs",
|
|
28
|
+
"web-framework"
|
|
29
|
+
],
|
|
5
30
|
"type": "module",
|
|
6
31
|
"module": "./dist/index.mjs",
|
|
7
32
|
"types": "./dist/index.d.mts",
|
|
@@ -15,22 +40,14 @@
|
|
|
15
40
|
"import": "./dist/vite.mjs"
|
|
16
41
|
},
|
|
17
42
|
"./__internal/client/onRenderClient": {
|
|
18
|
-
"types": "./
|
|
19
|
-
"import": "./
|
|
43
|
+
"types": "./dist/__internal/client/onRenderClient.d.mts",
|
|
44
|
+
"import": "./dist/__internal/client/onRenderClient.mjs"
|
|
20
45
|
},
|
|
21
46
|
"./__internal/server/onRenderHtml": {
|
|
22
|
-
"types": "./
|
|
23
|
-
"import": "./
|
|
47
|
+
"types": "./dist/__internal/server/onRenderHtml.d.mts",
|
|
48
|
+
"import": "./dist/__internal/server/onRenderHtml.mjs"
|
|
24
49
|
}
|
|
25
50
|
},
|
|
26
|
-
"repository": {
|
|
27
|
-
"type": "git",
|
|
28
|
-
"url": "git+https://github.com/node-ecosystem/vike-lite-react.git"
|
|
29
|
-
},
|
|
30
|
-
"homepage": "https://github.com/node-ecosystem/vike-lite-react#readme",
|
|
31
|
-
"bugs": {
|
|
32
|
-
"url": "https://github.com/node-ecosystem/vike-lite-react/issues"
|
|
33
|
-
},
|
|
34
51
|
"files": [
|
|
35
52
|
"dist"
|
|
36
53
|
],
|
|
@@ -45,8 +62,8 @@
|
|
|
45
62
|
"@vitejs/plugin-react": "^6.0.3",
|
|
46
63
|
"react": "^19.2.7",
|
|
47
64
|
"react-dom": "^19.2.7",
|
|
48
|
-
"tsdown": "^0.22.
|
|
49
|
-
"vike-lite": "1.
|
|
65
|
+
"tsdown": "^0.22.8",
|
|
66
|
+
"vike-lite": "1.15.1",
|
|
50
67
|
"vite": "^8.1.4"
|
|
51
68
|
},
|
|
52
69
|
"peerDependencies": {
|