vike-react 0.3.9 → 0.4.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.
- package/README.md +2 -3
- package/dist/renderer/+config.d.ts +8 -8
- package/dist/renderer/+config.js +9 -14
- package/dist/renderer/PageContextProvider.js +12 -8
- package/dist/renderer/getHeadSetting.d.ts +3 -0
- package/dist/renderer/getHeadSetting.js +22 -0
- package/dist/renderer/getPageElement.js +3 -8
- package/dist/renderer/onRenderClient.d.ts +2 -2
- package/dist/renderer/onRenderClient.js +31 -14
- package/dist/renderer/onRenderHtml.js +8 -11
- package/dist/renderer/utils/assert.d.ts +1 -0
- package/dist/renderer/utils/assert.js +5 -0
- package/package.json +3 -2
- package/dist/renderer/getLang.d.ts +0 -7
- package/dist/renderer/getLang.js +0 -32
- package/dist/renderer/getTitle.d.ts +0 -7
- package/dist/renderer/getTitle.js +0 -37
package/README.md
CHANGED
@@ -7,6 +7,5 @@
|
|
7
7
|
|
8
8
|
React integration for [Vike](https://vike.dev).
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
See [examples/](https://github.com/vikejs/vike-react/tree/main/examples).
|
10
|
+
- [Examples](https://github.com/vikejs/vike-react/tree/main/examples)
|
11
|
+
- [Changelog](https://github.com/vikejs/vike-react/blob/main/packages/vike-react/CHANGELOG.md)
|
@@ -2,7 +2,6 @@ import type { ConfigEffect } from 'vike/types';
|
|
2
2
|
declare const _default: {
|
3
3
|
onRenderHtml: "import:vike-react/renderer/onRenderHtml:onRenderHtml";
|
4
4
|
onRenderClient: "import:vike-react/renderer/onRenderClient:onRenderClient";
|
5
|
-
passToClient: string[];
|
6
5
|
clientRouting: true;
|
7
6
|
hydrationCanBeAborted: true;
|
8
7
|
meta: {
|
@@ -23,14 +22,10 @@ declare const _default: {
|
|
23
22
|
client: true;
|
24
23
|
};
|
25
24
|
};
|
26
|
-
description: {
|
27
|
-
env: {
|
28
|
-
server: true;
|
29
|
-
};
|
30
|
-
};
|
31
25
|
favicon: {
|
32
26
|
env: {
|
33
27
|
server: true;
|
28
|
+
client: true;
|
34
29
|
};
|
35
30
|
};
|
36
31
|
lang: {
|
@@ -56,6 +51,12 @@ declare const _default: {
|
|
56
51
|
server: true;
|
57
52
|
};
|
58
53
|
};
|
54
|
+
Wrapper: {
|
55
|
+
env: {
|
56
|
+
client: true;
|
57
|
+
server: true;
|
58
|
+
};
|
59
|
+
};
|
59
60
|
};
|
60
61
|
};
|
61
62
|
export default _default;
|
@@ -71,8 +72,6 @@ declare global {
|
|
71
72
|
Layout?: Component;
|
72
73
|
/** <title>${title}</title> */
|
73
74
|
title?: string;
|
74
|
-
/** <meta name="description" content="${description}" /> */
|
75
|
-
description?: string;
|
76
75
|
/** <link rel="icon" href="${favicon}" /> */
|
77
76
|
favicon?: string;
|
78
77
|
/** <html lang="${lang}">
|
@@ -101,6 +100,7 @@ declare global {
|
|
101
100
|
*/
|
102
101
|
stream?: boolean;
|
103
102
|
VikeReactQueryWrapper?: Component;
|
103
|
+
Wrapper?: Component;
|
104
104
|
}
|
105
105
|
}
|
106
106
|
}
|
package/dist/renderer/+config.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
// Depending on the value of `config.meta.ssr`, set other config options' `env`
|
2
2
|
// accordingly.
|
3
|
-
// See https://vike.dev/meta
|
3
|
+
// See https://vike.dev/meta#:~:text=Modifying%20the%20environment%20of%20existing%20hooks
|
4
4
|
const toggleSsrRelatedConfig = ({ configDefinedAt, configValue }) => {
|
5
5
|
if (typeof configValue !== 'boolean') {
|
6
6
|
throw new Error(`${configDefinedAt} should be a boolean`);
|
@@ -19,19 +19,14 @@ const toggleSsrRelatedConfig = ({ configDefinedAt, configValue }) => {
|
|
19
19
|
};
|
20
20
|
};
|
21
21
|
export default {
|
22
|
+
// https://vike.dev/onRenderHtml
|
22
23
|
onRenderHtml: 'import:vike-react/renderer/onRenderHtml:onRenderHtml',
|
24
|
+
// https://vike.dev/onRenderClient
|
23
25
|
onRenderClient: 'import:vike-react/renderer/onRenderClient:onRenderClient',
|
24
|
-
//
|
25
|
-
// TODO/next-major-release: remove support for setting title over onBeforeRender()
|
26
|
-
// A page can define an onBeforeRender() hook to be run on the server, which
|
27
|
-
// can fetch data and return it as additional page context. Typically it will
|
28
|
-
// return the page's root React component's props and additional data that can
|
29
|
-
// be used by the renderers.
|
30
|
-
// It is a cumulative config option, so a web app using vike-react can extend
|
31
|
-
// this list.
|
32
|
-
passToClient: ['pageProps', 'title', 'lang'],
|
26
|
+
// https://vike.dev/clientRouting
|
33
27
|
clientRouting: true,
|
34
28
|
hydrationCanBeAborted: true,
|
29
|
+
// https://vike.dev/meta
|
35
30
|
meta: {
|
36
31
|
Head: {
|
37
32
|
env: { server: true }
|
@@ -42,11 +37,8 @@ export default {
|
|
42
37
|
title: {
|
43
38
|
env: { server: true, client: true }
|
44
39
|
},
|
45
|
-
description: {
|
46
|
-
env: { server: true }
|
47
|
-
},
|
48
40
|
favicon: {
|
49
|
-
env: { server: true }
|
41
|
+
env: { server: true, client: true }
|
50
42
|
},
|
51
43
|
lang: {
|
52
44
|
env: { server: true, client: true }
|
@@ -60,6 +52,9 @@ export default {
|
|
60
52
|
},
|
61
53
|
VikeReactQueryWrapper: {
|
62
54
|
env: { client: true, server: true }
|
55
|
+
},
|
56
|
+
Wrapper: {
|
57
|
+
env: { client: true, server: true }
|
63
58
|
}
|
64
59
|
}
|
65
60
|
};
|
@@ -3,19 +3,23 @@ export { usePageContext };
|
|
3
3
|
export { useData };
|
4
4
|
import React, { useContext } from 'react';
|
5
5
|
import { getGlobalObject } from './utils/getGlobalObject.js';
|
6
|
-
|
7
|
-
|
6
|
+
import { assert } from './utils/assert.js';
|
7
|
+
const globalObject = getGlobalObject('PageContextProvider.ts', {
|
8
|
+
reactContext: React.createContext(undefined)
|
8
9
|
});
|
9
10
|
function PageContextProvider({ pageContext, children }) {
|
10
|
-
|
11
|
-
|
12
|
-
return React.createElement(
|
11
|
+
assert(pageContext);
|
12
|
+
const { reactContext } = globalObject;
|
13
|
+
return React.createElement(reactContext.Provider, { value: pageContext }, children);
|
13
14
|
}
|
14
15
|
/** Access the pageContext from any React component */
|
15
16
|
function usePageContext() {
|
16
|
-
const
|
17
|
-
|
18
|
-
|
17
|
+
const { reactContext } = globalObject;
|
18
|
+
const pageContext = useContext(reactContext);
|
19
|
+
/* React throws an error upon wrong hook usage, so I guess a nice error message isn't needed? And I guess we can therefore assume and assert pageContext to have been provided? Let's see if users report back an assert() failure.
|
20
|
+
if (!pageContext) throw new Error('<PageContextProvider> is needed for being able to use usePageContext()')
|
21
|
+
*/
|
22
|
+
assert(pageContext);
|
19
23
|
return pageContext;
|
20
24
|
}
|
21
25
|
function useData() {
|
@@ -0,0 +1,22 @@
|
|
1
|
+
export { getHeadSetting };
|
2
|
+
import { isCallable } from './utils/isCallable.js';
|
3
|
+
function getHeadSetting(headSetting, pageContext) {
|
4
|
+
const config = pageContext.configEntries[headSetting]?.[0];
|
5
|
+
if (!config)
|
6
|
+
return undefined;
|
7
|
+
const val = config.configValue;
|
8
|
+
if (typeof val === 'string')
|
9
|
+
return val;
|
10
|
+
if (!val)
|
11
|
+
return null;
|
12
|
+
if (isCallable(val)) {
|
13
|
+
const valStr = val(pageContext);
|
14
|
+
if (typeof valStr !== 'string') {
|
15
|
+
throw new Error(config.configDefinedAt + ' should return a string');
|
16
|
+
}
|
17
|
+
return valStr;
|
18
|
+
}
|
19
|
+
else {
|
20
|
+
throw new Error(config.configDefinedAt + ' should be a string or a function returning a string');
|
21
|
+
}
|
22
|
+
}
|
@@ -3,19 +3,14 @@ import React from 'react';
|
|
3
3
|
import { PageContextProvider } from './PageContextProvider.js';
|
4
4
|
function getPageElement(pageContext) {
|
5
5
|
const Layout = pageContext.config.Layout ?? PassThrough;
|
6
|
-
const Wrapper =
|
7
|
-
/* Should we implement this? Enabling users to defined a wrapper that is used across all layouts.
|
8
|
-
pageContext.config.Wrapper ??
|
9
|
-
*/
|
10
|
-
PassThrough;
|
6
|
+
const Wrapper = pageContext.config.Wrapper ?? PassThrough;
|
11
7
|
const VikeReactQueryWrapper = pageContext.config.VikeReactQueryWrapper ?? PassThrough;
|
12
|
-
|
13
|
-
const { Page, pageProps } = pageContext;
|
8
|
+
const { Page } = pageContext;
|
14
9
|
const page = (React.createElement(React.StrictMode, null,
|
15
10
|
React.createElement(PageContextProvider, { pageContext: pageContext },
|
16
11
|
React.createElement(VikeReactQueryWrapper, { pageContext: pageContext },
|
17
12
|
React.createElement(Wrapper, null,
|
18
|
-
React.createElement(Layout, null, Page ? React.createElement(Page,
|
13
|
+
React.createElement(Layout, null, Page ? React.createElement(Page, null) : null))))));
|
19
14
|
return page;
|
20
15
|
}
|
21
16
|
function PassThrough({ children }) {
|
@@ -1,3 +1,3 @@
|
|
1
1
|
export { onRenderClient };
|
2
|
-
import type {
|
3
|
-
declare const onRenderClient:
|
2
|
+
import type { OnRenderClientSync } from 'vike/types';
|
3
|
+
declare const onRenderClient: OnRenderClientSync;
|
@@ -1,33 +1,50 @@
|
|
1
1
|
// https://vike.dev/onRenderClient
|
2
2
|
export { onRenderClient };
|
3
3
|
import ReactDOM from 'react-dom/client';
|
4
|
-
import {
|
4
|
+
import { getHeadSetting } from './getHeadSetting.js';
|
5
5
|
import { getPageElement } from './getPageElement.js';
|
6
|
-
import { getLang } from './getLang.js';
|
7
6
|
let root;
|
8
|
-
const onRenderClient =
|
7
|
+
const onRenderClient = (pageContext) => {
|
9
8
|
const page = getPageElement(pageContext);
|
10
9
|
const container = document.getElementById('page-view');
|
11
10
|
if (container.innerHTML !== '' && pageContext.isHydration) {
|
12
|
-
//
|
11
|
+
// First render (hydration)
|
13
12
|
root = ReactDOM.hydrateRoot(container, page);
|
14
13
|
}
|
15
14
|
else {
|
16
15
|
if (!root) {
|
17
|
-
// First
|
16
|
+
// First render (not hydration)
|
18
17
|
root = ReactDOM.createRoot(container);
|
19
18
|
}
|
20
19
|
else {
|
21
|
-
// Client
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
//
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
20
|
+
// Client-side navigation
|
21
|
+
const title = getHeadSetting('title', pageContext) || '';
|
22
|
+
const lang = getHeadSetting('lang', pageContext) || 'en';
|
23
|
+
const favicon = getHeadSetting('favicon', pageContext);
|
24
|
+
// We skip if the value is undefined because we shouldn't remove values set in HTML (by the Head setting).
|
25
|
+
// - 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.
|
26
|
+
if (title !== undefined)
|
27
|
+
document.title = title;
|
28
|
+
if (lang !== undefined)
|
29
|
+
document.documentElement.lang = lang;
|
30
|
+
if (favicon !== undefined)
|
31
|
+
setFavicon(favicon);
|
30
32
|
}
|
31
33
|
root.render(page);
|
32
34
|
}
|
33
35
|
};
|
36
|
+
// https://stackoverflow.com/questions/260857/changing-website-favicon-dynamically/260876#260876
|
37
|
+
function setFavicon(faviconUrl) {
|
38
|
+
let link = document.querySelector("link[rel~='icon']");
|
39
|
+
if (!faviconUrl) {
|
40
|
+
if (link)
|
41
|
+
document.head.removeChild(link);
|
42
|
+
return;
|
43
|
+
}
|
44
|
+
if (!link) {
|
45
|
+
link = document.createElement('link');
|
46
|
+
link.rel = 'icon';
|
47
|
+
document.head.appendChild(link);
|
48
|
+
}
|
49
|
+
link.href = faviconUrl;
|
50
|
+
}
|
@@ -3,19 +3,17 @@ export { onRenderHtml };
|
|
3
3
|
import { renderToString } from 'react-dom/server';
|
4
4
|
import { renderToStream } from 'react-streaming/server';
|
5
5
|
import { escapeInject, dangerouslySkipEscape, version } from 'vike/server';
|
6
|
-
import {
|
6
|
+
import { getHeadSetting } from './getHeadSetting.js';
|
7
7
|
import { getPageElement } from './getPageElement.js';
|
8
8
|
import { PageContextProvider } from './PageContextProvider.js';
|
9
9
|
import React from 'react';
|
10
|
-
import { getLang } from './getLang.js';
|
11
10
|
checkVikeVersion();
|
12
11
|
const onRenderHtml = async (pageContext) => {
|
13
|
-
const
|
14
|
-
const
|
15
|
-
const
|
16
|
-
const title = getTitle(pageContext);
|
12
|
+
const title = getHeadSetting('title', pageContext);
|
13
|
+
const favicon = getHeadSetting('favicon', pageContext);
|
14
|
+
const lang = getHeadSetting('lang', pageContext) || 'en';
|
17
15
|
const titleTag = !title ? '' : escapeInject `<title>${title}</title>`;
|
18
|
-
const
|
16
|
+
const faviconTag = !favicon ? '' : escapeInject `<link rel="icon" href="${favicon}" />`;
|
19
17
|
const Head = pageContext.config.Head || (() => React.createElement(React.Fragment, null));
|
20
18
|
const head = (React.createElement(React.StrictMode, null,
|
21
19
|
React.createElement(PageContextProvider, { pageContext: pageContext },
|
@@ -27,7 +25,7 @@ const onRenderHtml = async (pageContext) => {
|
|
27
25
|
}
|
28
26
|
else {
|
29
27
|
const page = getPageElement(pageContext);
|
30
|
-
pageView = !stream
|
28
|
+
pageView = !pageContext.config.stream
|
31
29
|
? dangerouslySkipEscape(renderToString(page))
|
32
30
|
: await renderToStream(page, { userAgent: pageContext.userAgent });
|
33
31
|
}
|
@@ -35,10 +33,9 @@ const onRenderHtml = async (pageContext) => {
|
|
35
33
|
<html lang='${lang}'>
|
36
34
|
<head>
|
37
35
|
<meta charset="UTF-8" />
|
38
|
-
${faviconTag}
|
39
36
|
${titleTag}
|
40
|
-
${descriptionTag}
|
41
37
|
${headHtml}
|
38
|
+
${faviconTag}
|
42
39
|
</head>
|
43
40
|
<body>
|
44
41
|
<div id="page-view">${pageView}</div>
|
@@ -57,5 +54,5 @@ function checkVikeVersion() {
|
|
57
54
|
if (versionParts[2] >= 147)
|
58
55
|
return;
|
59
56
|
}
|
60
|
-
throw new Error('Update Vike to
|
57
|
+
throw new Error('Update Vike to 0.4.147 or above');
|
61
58
|
}
|
@@ -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.
|
3
|
+
"version": "0.4.0",
|
4
4
|
"type": "module",
|
5
5
|
"main": "./dist/renderer/+config.js",
|
6
6
|
"types": "./dist/renderer/+config.d.ts",
|
@@ -16,6 +16,7 @@
|
|
16
16
|
"dev": "tsc --watch",
|
17
17
|
"build": "rm -rf dist/ && tsc",
|
18
18
|
"release": "release-me --git-prefix vike-react --changelog-dir packages/vike-react/ patch",
|
19
|
+
"release:minor": "release-me --git-prefix vike-react --changelog-dir packages/vike-react/ minor",
|
19
20
|
"release:commit": "release-me --git-prefix vike-react --changelog-dir packages/vike-react/ commit"
|
20
21
|
},
|
21
22
|
"peerDependencies": {
|
@@ -32,7 +33,7 @@
|
|
32
33
|
"react": "^18.2.0",
|
33
34
|
"react-dom": "^18.2.0",
|
34
35
|
"typescript": "^5.3.3",
|
35
|
-
"vike": "^0.4.
|
36
|
+
"vike": "^0.4.159"
|
36
37
|
},
|
37
38
|
"dependencies": {
|
38
39
|
"react-streaming": "^0.3.19"
|
@@ -1,7 +0,0 @@
|
|
1
|
-
export { getLang };
|
2
|
-
import type { PageContext } from 'vike/types';
|
3
|
-
/**
|
4
|
-
* Get the page's lang if defined, either from the config, the additional data fetched by
|
5
|
-
* the page's data() and onBeforeRender() hooks or from other hooks.
|
6
|
-
*/
|
7
|
-
declare function getLang(pageContext: PageContext): null | string;
|
package/dist/renderer/getLang.js
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
export { getLang };
|
2
|
-
import { isCallable } from './utils/isCallable.js';
|
3
|
-
/**
|
4
|
-
* Get the page's lang if defined, either from the config, the additional data fetched by
|
5
|
-
* the page's data() and onBeforeRender() hooks or from other hooks.
|
6
|
-
*/
|
7
|
-
function getLang(pageContext) {
|
8
|
-
// from onBeforeRoute() hook & other hooks, e.g. onPrerenderStart() hook
|
9
|
-
if (pageContext.lang !== undefined) {
|
10
|
-
return pageContext.lang;
|
11
|
-
}
|
12
|
-
const langConfig = pageContext.configEntries.lang?.[0];
|
13
|
-
if (!langConfig) {
|
14
|
-
return null;
|
15
|
-
}
|
16
|
-
const lang = langConfig.configValue;
|
17
|
-
if (typeof lang === 'string') {
|
18
|
-
return lang;
|
19
|
-
}
|
20
|
-
if (!lang) {
|
21
|
-
return null;
|
22
|
-
}
|
23
|
-
const { configDefinedAt } = langConfig;
|
24
|
-
if (isCallable(lang)) {
|
25
|
-
const val = lang(pageContext);
|
26
|
-
if (typeof val !== 'string') {
|
27
|
-
throw new Error(configDefinedAt + ' should return a string');
|
28
|
-
}
|
29
|
-
return val;
|
30
|
-
}
|
31
|
-
throw new Error(configDefinedAt + ' should be a string or a function returning a string');
|
32
|
-
}
|
@@ -1,7 +0,0 @@
|
|
1
|
-
export { getTitle };
|
2
|
-
import type { PageContext } from 'vike/types';
|
3
|
-
/**
|
4
|
-
* Get the page's title if defined, either from the additional data fetched by
|
5
|
-
* the page's data() and onBeforeRender() hooks or from the config.
|
6
|
-
*/
|
7
|
-
declare function getTitle(pageContext: PageContext): null | string;
|
@@ -1,37 +0,0 @@
|
|
1
|
-
export { getTitle };
|
2
|
-
import { isCallable } from './utils/isCallable.js';
|
3
|
-
/**
|
4
|
-
* Get the page's title if defined, either from the additional data fetched by
|
5
|
-
* the page's data() and onBeforeRender() hooks or from the config.
|
6
|
-
*/
|
7
|
-
function getTitle(pageContext) {
|
8
|
-
// from data() hook
|
9
|
-
if (pageContext.data?.title !== undefined) {
|
10
|
-
return pageContext.data.title;
|
11
|
-
}
|
12
|
-
// TODO/next-major-release: remove support for setting title over onBeforeRender()
|
13
|
-
// from onBeforeRender() hook
|
14
|
-
if (pageContext.title !== undefined) {
|
15
|
-
return pageContext.title;
|
16
|
-
}
|
17
|
-
const titleConfig = pageContext.configEntries.title?.[0];
|
18
|
-
if (!titleConfig) {
|
19
|
-
return null;
|
20
|
-
}
|
21
|
-
const title = titleConfig.configValue;
|
22
|
-
if (typeof title === 'string') {
|
23
|
-
return title;
|
24
|
-
}
|
25
|
-
if (!title) {
|
26
|
-
return null;
|
27
|
-
}
|
28
|
-
const { configDefinedAt } = titleConfig;
|
29
|
-
if (isCallable(title)) {
|
30
|
-
const val = title(pageContext);
|
31
|
-
if (typeof val !== 'string') {
|
32
|
-
throw new Error(configDefinedAt + ' should return a string');
|
33
|
-
}
|
34
|
-
return val;
|
35
|
-
}
|
36
|
-
throw new Error(configDefinedAt + ' should be a string or a function returning a string');
|
37
|
-
}
|