vike-react 0.4.17 → 0.4.18-commit-5c88c7b
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/+config.d.ts +7 -0
- package/dist/+config.js +4 -0
- package/dist/components/Loading.d.ts +6 -0
- package/dist/components/Loading.js +16 -0
- package/dist/renderer/getPageElement.js +12 -4
- package/dist/renderer/onRenderClient.d.ts +1 -0
- package/dist/renderer/onRenderClient.js +29 -24
- package/dist/renderer/onRenderHtml.js +40 -31
- package/dist/renderer/ssrEffect.js +13 -10
- package/dist/renderer/styles.css +6 -0
- package/dist/types/Config.d.ts +26 -10
- package/dist/types/PageContext.d.ts +0 -2
- package/dist/utils/assert.d.ts +1 -0
- package/dist/utils/assert.js +5 -0
- package/package.json +7 -5
package/dist/+config.d.ts
CHANGED
@@ -5,6 +5,7 @@ declare const _default: {
|
|
5
5
|
require: {
|
6
6
|
vike: string;
|
7
7
|
};
|
8
|
+
Loading: "import:vike-react/components/Loading:default";
|
8
9
|
onRenderHtml: "import:vike-react/renderer/onRenderHtml:onRenderHtml";
|
9
10
|
onRenderClient: "import:vike-react/renderer/onRenderClient:onRenderClient";
|
10
11
|
passToClient: string[];
|
@@ -90,6 +91,12 @@ declare const _default: {
|
|
90
91
|
server: true;
|
91
92
|
};
|
92
93
|
};
|
94
|
+
Loading: {
|
95
|
+
env: {
|
96
|
+
server: true;
|
97
|
+
client: true;
|
98
|
+
};
|
99
|
+
};
|
93
100
|
};
|
94
101
|
};
|
95
102
|
export default _default;
|
package/dist/+config.js
CHANGED
@@ -7,6 +7,7 @@ export default {
|
|
7
7
|
require: {
|
8
8
|
vike: '>=0.4.178'
|
9
9
|
},
|
10
|
+
Loading: 'import:vike-react/components/Loading:default',
|
10
11
|
// https://vike.dev/onRenderHtml
|
11
12
|
onRenderHtml: 'import:vike-react/renderer/onRenderHtml:onRenderHtml',
|
12
13
|
// https://vike.dev/onRenderClient
|
@@ -66,6 +67,9 @@ export default {
|
|
66
67
|
},
|
67
68
|
reactStrictMode: {
|
68
69
|
env: { client: true, server: true }
|
70
|
+
},
|
71
|
+
Loading: {
|
72
|
+
env: { server: true, client: true }
|
69
73
|
}
|
70
74
|
}
|
71
75
|
};
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
export default {
|
3
|
+
component: LoadingComponent
|
4
|
+
};
|
5
|
+
function LoadingComponent() {
|
6
|
+
return (React.createElement("div", { style: {
|
7
|
+
width: '100%',
|
8
|
+
height: '100%',
|
9
|
+
maxHeight: '100%',
|
10
|
+
background: 'linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%)',
|
11
|
+
borderRadius: '5px',
|
12
|
+
backgroundSize: '200% 100%',
|
13
|
+
animation: '1.3s vike-react-shine linear infinite',
|
14
|
+
aspectRatio: '2.5/1'
|
15
|
+
} }));
|
16
|
+
}
|
@@ -1,16 +1,24 @@
|
|
1
1
|
export { getPageElement };
|
2
|
-
import React from 'react';
|
2
|
+
import React, { Suspense } from 'react';
|
3
3
|
import { PageContextProvider } from '../hooks/usePageContext.js';
|
4
4
|
function getPageElement(pageContext) {
|
5
|
-
const { Page } = pageContext;
|
5
|
+
const { Page, config: { Loading } } = pageContext;
|
6
6
|
let page = Page ? React.createElement(Page, null) : null;
|
7
|
+
// Wrapping
|
8
|
+
const addSuspense = (el) => {
|
9
|
+
if (!Loading?.layout)
|
10
|
+
return el;
|
11
|
+
return React.createElement(Suspense, { fallback: React.createElement(Loading.layout, null) }, page);
|
12
|
+
};
|
13
|
+
page = addSuspense(page);
|
7
14
|
[
|
8
15
|
// Inner wrapping
|
9
16
|
...(pageContext.config.Layout || []),
|
10
17
|
// Outer wrapping
|
11
18
|
...(pageContext.config.Wrapper || [])
|
12
|
-
].forEach((
|
13
|
-
page = React.createElement(
|
19
|
+
].forEach((Wrap) => {
|
20
|
+
page = React.createElement(Wrap, null, page);
|
21
|
+
page = addSuspense(page);
|
14
22
|
});
|
15
23
|
page = React.createElement(PageContextProvider, { pageContext: pageContext }, page);
|
16
24
|
if (pageContext.config.reactStrictMode !== false) {
|
@@ -3,23 +3,23 @@ export { onRenderClient };
|
|
3
3
|
import ReactDOM from 'react-dom/client';
|
4
4
|
import { getHeadSetting } from './getHeadSetting.js';
|
5
5
|
import { getPageElement } from './getPageElement.js';
|
6
|
+
import './styles.css';
|
6
7
|
let root;
|
7
8
|
const onRenderClient = (pageContext) => {
|
8
9
|
// Use case:
|
9
10
|
// - Store hydration https://github.com/vikejs/vike-react/issues/110
|
10
11
|
pageContext.config.onBeforeRenderClient?.(pageContext);
|
11
12
|
const page = getPageElement(pageContext);
|
13
|
+
pageContext.page = page;
|
12
14
|
// TODO: implement this? So that, upon errors, onRenderClient() throws an error and Vike can render the error. As of April 2024 it isn't released yet.
|
13
15
|
// - https://react-dev-git-fork-rickhanlonii-rh-root-options-fbopensource.vercel.app/reference/react-dom/client/createRoot#show-a-dialog-for-uncaught-errors
|
14
16
|
// - https://react-dev-git-fork-rickhanlonii-rh-root-options-fbopensource.vercel.app/reference/react-dom/client/hydrateRoot#show-a-dialog-for-uncaught-errors
|
15
17
|
const onUncaughtError = (_error, _errorInfo) => { };
|
16
18
|
const container = document.getElementById('root');
|
17
|
-
if (
|
18
|
-
|
19
|
-
|
20
|
-
//
|
21
|
-
pageContext.isHydration) {
|
22
|
-
// Hydration
|
19
|
+
if (pageContext.isHydration &&
|
20
|
+
// Whether the page was [Server-Side Rendered](https://vike.dev/ssr).
|
21
|
+
container.innerHTML !== '') {
|
22
|
+
// First render while using SSR, i.e. [hydration](https://vike.dev/hydration)
|
23
23
|
root = ReactDOM.hydrateRoot(container, page, {
|
24
24
|
// @ts-expect-error
|
25
25
|
onUncaughtError
|
@@ -27,34 +27,39 @@ const onRenderClient = (pageContext) => {
|
|
27
27
|
}
|
28
28
|
else {
|
29
29
|
if (!root) {
|
30
|
-
// First render
|
30
|
+
// First render without SSR
|
31
31
|
root = ReactDOM.createRoot(container, {
|
32
32
|
// @ts-expect-error
|
33
33
|
onUncaughtError
|
34
34
|
});
|
35
35
|
}
|
36
|
-
else {
|
37
|
-
// Client-side navigation
|
38
|
-
const title = getHeadSetting('title', pageContext) || '';
|
39
|
-
const lang = getHeadSetting('lang', pageContext) || 'en';
|
40
|
-
const favicon = getHeadSetting('favicon', pageContext);
|
41
|
-
// We skip if the value is undefined because we shouldn't remove values set in HTML (by the Head setting).
|
42
|
-
// - This also means that previous values will leak: upon client-side navigation, the title set by the previous page won't be removed if the next page doesn't override it. But that's okay because usually pages always have a favicon and title, which means that previous values are always overriden. Also, as a workaround, the user can set the value to `null` to ensure that previous values are overriden.
|
43
|
-
if (title !== undefined)
|
44
|
-
document.title = title;
|
45
|
-
if (lang !== undefined)
|
46
|
-
document.documentElement.lang = lang;
|
47
|
-
if (favicon !== undefined)
|
48
|
-
setFavicon(favicon);
|
49
|
-
}
|
50
36
|
root.render(page);
|
51
37
|
}
|
52
|
-
pageContext.page = page;
|
53
38
|
pageContext.root = root;
|
54
|
-
|
55
|
-
|
39
|
+
if (!pageContext.isHydration) {
|
40
|
+
// E.g. document.title
|
41
|
+
updateDocument(pageContext);
|
42
|
+
}
|
43
|
+
// Use cases:
|
44
|
+
// - User-land document settings (e.g. user-land implementation of `config.favicon`)
|
45
|
+
// - Testing tools https://github.com/vikejs/vike-react/issues/95
|
56
46
|
pageContext.config.onAfterRenderClient?.(pageContext);
|
57
47
|
};
|
48
|
+
function updateDocument(pageContext) {
|
49
|
+
const title = getHeadSetting('title', pageContext);
|
50
|
+
const lang = getHeadSetting('lang', pageContext);
|
51
|
+
const favicon = getHeadSetting('favicon', pageContext);
|
52
|
+
// - We skip if `undefined` as we shouldn't remove values set by the Head setting.
|
53
|
+
// - Setting a default prevents the previous value to be leaked: upon client-side navigation, the value set by the previous page won't be removed if the next page doesn't override it.
|
54
|
+
// - Most of the time, the user sets a default himself (i.e. a value defined at /pages/+config.js)
|
55
|
+
if (title !== undefined)
|
56
|
+
document.title = title || '';
|
57
|
+
if (lang !== undefined)
|
58
|
+
document.documentElement.lang = lang || 'en';
|
59
|
+
if (favicon !== undefined)
|
60
|
+
setFavicon(favicon);
|
61
|
+
}
|
62
|
+
// TODO/next-major-release: remove config.favicon and, instead, add docs showcasing how to implement a favicon setting on the user-land.
|
58
63
|
// https://stackoverflow.com/questions/260857/changing-website-favicon-dynamically/260876#260876
|
59
64
|
function setFavicon(faviconUrl) {
|
60
65
|
let link = document.querySelector("link[rel~='icon']");
|
@@ -10,53 +10,62 @@ import { getPageElement } from './getPageElement.js';
|
|
10
10
|
checkVikeVersion();
|
11
11
|
addEcosystemStamp();
|
12
12
|
const onRenderHtml = async (pageContext) => {
|
13
|
-
const
|
14
|
-
const
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
let pageView;
|
13
|
+
const pageHtml = await getPageHtml(pageContext);
|
14
|
+
const { headHtml, lang } = getHeadHtml(pageContext);
|
15
|
+
return escapeInject `<!DOCTYPE html>
|
16
|
+
<html lang='${lang}'>
|
17
|
+
<head>${headHtml}</head>
|
18
|
+
<body>
|
19
|
+
<div id="root">${pageHtml}</div>
|
20
|
+
</body>
|
21
|
+
</html>`;
|
22
|
+
};
|
23
|
+
async function getPageHtml(pageContext) {
|
24
|
+
let pageHtml;
|
26
25
|
if (!pageContext.Page) {
|
27
|
-
|
26
|
+
pageHtml = '';
|
28
27
|
}
|
29
28
|
else {
|
30
29
|
const page = getPageElement(pageContext);
|
31
30
|
const { stream, streamIsRequired } = pageContext.config;
|
32
31
|
if (!stream && !streamIsRequired) {
|
33
|
-
|
32
|
+
pageHtml = dangerouslySkipEscape(renderToString(page));
|
34
33
|
}
|
35
34
|
else {
|
36
35
|
const disable = stream === false ? true : undefined;
|
37
|
-
|
36
|
+
pageHtml = await renderToStream(page, {
|
38
37
|
webStream: typeof stream === 'string' ? stream === 'web' : undefined,
|
39
38
|
userAgent: pageContext.headers?.['user-agent'] ||
|
40
|
-
//
|
39
|
+
// TODO/eventually: remove old way of acccessing the User Agent header.
|
40
|
+
// @ts-ignore
|
41
41
|
pageContext.userAgent,
|
42
42
|
disable
|
43
43
|
});
|
44
44
|
}
|
45
45
|
}
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
}
|
46
|
+
return pageHtml;
|
47
|
+
}
|
48
|
+
function getHeadHtml(pageContext) {
|
49
|
+
const title = getHeadSetting('title', pageContext);
|
50
|
+
const favicon = getHeadSetting('favicon', pageContext);
|
51
|
+
const lang = getHeadSetting('lang', pageContext) || 'en';
|
52
|
+
const titleTags = !title ? '' : escapeInject `<title>${title}</title><meta property="og:title" content="${title}" />`;
|
53
|
+
const faviconTag = !favicon ? '' : escapeInject `<link rel="icon" href="${favicon}" />`;
|
54
|
+
const Head = pageContext.config.Head || (() => React.createElement(React.Fragment, null));
|
55
|
+
let headElement = (React.createElement(PageContextProvider, { pageContext: pageContext },
|
56
|
+
React.createElement(Head, null)));
|
57
|
+
if (pageContext.config.reactStrictMode !== false) {
|
58
|
+
headElement = React.createElement(React.StrictMode, null, headElement);
|
59
|
+
}
|
60
|
+
const headElementHtml = dangerouslySkipEscape(renderToString(headElement));
|
61
|
+
const headHtml = escapeInject `
|
62
|
+
<meta charset="UTF-8" />
|
63
|
+
${titleTags}
|
64
|
+
${headElementHtml}
|
65
|
+
${faviconTag}
|
66
|
+
`;
|
67
|
+
return { headHtml, lang };
|
68
|
+
}
|
60
69
|
// We don't need this anymore starting from vike@0.4.173 which added the `require` setting.
|
61
70
|
// TODO/eventually: remove this once <=0.4.172 versions become rare.
|
62
71
|
function checkVikeVersion() {
|
@@ -2,18 +2,21 @@ export { ssrEffect };
|
|
2
2
|
function ssrEffect({ configDefinedAt, configValue }) {
|
3
3
|
if (typeof configValue !== 'boolean')
|
4
4
|
throw new Error(`${configDefinedAt} should be a boolean`);
|
5
|
+
const env = {
|
6
|
+
// Always load on the client-side.
|
7
|
+
client: true,
|
8
|
+
// When the SSR flag is false, we want to render the page only on the client-side.
|
9
|
+
// We achieve this by loading `Page` only on the client-side: when onRenderHtml()
|
10
|
+
// gets a `Page` value that is undefined it skip server-side rendering.
|
11
|
+
server: configValue !== false
|
12
|
+
};
|
5
13
|
return {
|
6
14
|
meta: {
|
7
|
-
Page: {
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
// We achieve this by loading `Page` only on the client-side: when onRenderHtml()
|
13
|
-
// gets a `Page` value that is undefined it skip server-side rendering.
|
14
|
-
server: configValue !== false
|
15
|
-
}
|
16
|
-
}
|
15
|
+
Page: { env },
|
16
|
+
/* We don't do this to enable wraping <Head> with <Wrapper>
|
17
|
+
Wrapper: { env }, */
|
18
|
+
Layout: { env },
|
19
|
+
Loading: { env }
|
17
20
|
}
|
18
21
|
};
|
19
22
|
}
|
package/dist/types/Config.d.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import type { ImportString, PageContextClient } from 'vike/types';
|
1
|
+
import type { ImportString, PageContextClient, PageContext } from 'vike/types';
|
2
2
|
declare global {
|
3
3
|
namespace Vike {
|
4
4
|
interface Config {
|
@@ -20,16 +20,26 @@ declare global {
|
|
20
20
|
* https://vike.dev/Wrapper
|
21
21
|
*/
|
22
22
|
Wrapper?: Wrapper | ImportString;
|
23
|
-
/**
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
23
|
+
/**
|
24
|
+
* ```js
|
25
|
+
* <title>${title}</title>
|
26
|
+
* <meta property="og:title" content="${title}" />
|
27
|
+
* ```
|
28
|
+
*/
|
29
|
+
title?: PlainOrGetter<string>;
|
30
|
+
/**
|
31
|
+
* ```js
|
32
|
+
* <link rel="icon" href="${favicon}" />
|
33
|
+
* ```
|
34
|
+
*/
|
35
|
+
favicon?: PlainOrGetter<string>;
|
36
|
+
/**
|
37
|
+
* ```js
|
38
|
+
* <html lang="${lang}">
|
39
|
+
* ```
|
40
|
+
* @default 'en'
|
31
41
|
*/
|
32
|
-
lang?: string
|
42
|
+
lang?: PlainOrGetter<string>;
|
33
43
|
/**
|
34
44
|
* If `true`, the page is rendered twice: on the server-side (to HTML) and on the client-side (hydration).
|
35
45
|
*
|
@@ -82,6 +92,7 @@ declare global {
|
|
82
92
|
* https://vike.dev/onAfterRenderClient
|
83
93
|
*/
|
84
94
|
onAfterRenderClient?: (pageContext: PageContextClient) => void;
|
95
|
+
Loading?: Loading | ImportString;
|
85
96
|
}
|
86
97
|
interface ConfigResolved {
|
87
98
|
Wrapper?: Wrapper[];
|
@@ -89,8 +100,13 @@ declare global {
|
|
89
100
|
}
|
90
101
|
}
|
91
102
|
}
|
103
|
+
type PlainOrGetter<T> = T | ((pageContext: PageContext) => T);
|
92
104
|
type Wrapper = (props: {
|
93
105
|
children: React.ReactNode;
|
94
106
|
}) => React.ReactNode;
|
95
107
|
type Layout = Wrapper;
|
108
|
+
type Loading = {
|
109
|
+
component?: () => React.ReactNode;
|
110
|
+
layout?: () => React.ReactNode;
|
111
|
+
};
|
96
112
|
export {};
|
@@ -6,8 +6,6 @@ declare global {
|
|
6
6
|
interface PageContext {
|
7
7
|
/** The root React component of the page */
|
8
8
|
Page?: () => React.ReactNode;
|
9
|
-
/** The user agent string of the user's browser */
|
10
|
-
userAgent?: string;
|
11
9
|
/** The root React element of the page */
|
12
10
|
page?: JSX.Element;
|
13
11
|
/** The React root DOM container */
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare function assert(condition: unknown): asserts condition;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "vike-react",
|
3
|
-
"version": "0.4.
|
3
|
+
"version": "0.4.18-commit-5c88c7b",
|
4
4
|
"type": "module",
|
5
5
|
"main": "./dist/index.js",
|
6
6
|
"types": "./dist/index.d.ts",
|
@@ -12,11 +12,13 @@
|
|
12
12
|
".": "./dist/index.js",
|
13
13
|
"./config": "./dist/+config.js",
|
14
14
|
"./renderer/onRenderHtml": "./dist/renderer/onRenderHtml.js",
|
15
|
-
"./renderer/onRenderClient": "./dist/renderer/onRenderClient.js"
|
15
|
+
"./renderer/onRenderClient": "./dist/renderer/onRenderClient.js",
|
16
|
+
"./components/Loading": "./dist/components/Loading.js"
|
16
17
|
},
|
17
18
|
"scripts": {
|
18
19
|
"dev": "tsc --watch",
|
19
|
-
"build": "rimraf dist/ && tsc",
|
20
|
+
"build": "rimraf dist/ && tsc && pnpm run build:css",
|
21
|
+
"build:css": "cp src/renderer/styles.css dist/renderer/styles.css",
|
20
22
|
"release": "release-me patch",
|
21
23
|
"release:minor": "release-me minor",
|
22
24
|
"release:commit": "release-me commit"
|
@@ -36,11 +38,11 @@
|
|
36
38
|
"react": "^18.2.0",
|
37
39
|
"react-dom": "^18.2.0",
|
38
40
|
"rimraf": "^5.0.5",
|
39
|
-
"typescript": "^5.
|
41
|
+
"typescript": "^5.5.3",
|
40
42
|
"vike": "^0.4.178"
|
41
43
|
},
|
42
44
|
"dependencies": {
|
43
|
-
"react-streaming": "^0.3.
|
45
|
+
"react-streaming": "^0.3.42"
|
44
46
|
},
|
45
47
|
"typesVersions": {
|
46
48
|
"*": {
|