vike-react 0.3.8 → 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 CHANGED
@@ -7,6 +7,5 @@
7
7
 
8
8
  React integration for [Vike](https://vike.dev).
9
9
 
10
- > For integrations with Vue and Solid, see the other [`vike-*` packages](https://vike.dev/vike-packages).
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,19 +22,16 @@ 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: {
37
32
  env: {
38
33
  server: true;
34
+ client: true;
39
35
  };
40
36
  };
41
37
  ssr: {
@@ -55,6 +51,12 @@ declare const _default: {
55
51
  server: true;
56
52
  };
57
53
  };
54
+ Wrapper: {
55
+ env: {
56
+ client: true;
57
+ server: true;
58
+ };
59
+ };
58
60
  };
59
61
  };
60
62
  export default _default;
@@ -70,8 +72,6 @@ declare global {
70
72
  Layout?: Component;
71
73
  /** <title>${title}</title> */
72
74
  title?: string;
73
- /** <meta name="description" content="${description}" /> */
74
- description?: string;
75
75
  /** <link rel="icon" href="${favicon}" /> */
76
76
  favicon?: string;
77
77
  /** <html lang="${lang}">
@@ -100,6 +100,7 @@ declare global {
100
100
  */
101
101
  stream?: boolean;
102
102
  VikeReactQueryWrapper?: Component;
103
+ Wrapper?: Component;
103
104
  }
104
105
  }
105
106
  }
@@ -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#modify-existing-configurations
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
- // TODO/next-major-release: remove pageProps (i.e. tell users to use data() instead of onBeforeRender() to fetch data)
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'],
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,14 +37,11 @@ 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
- env: { server: true }
44
+ env: { server: true, client: true }
53
45
  },
54
46
  ssr: {
55
47
  env: { config: 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
- const { Context } = getGlobalObject('PageContextProvider.ts', {
7
- Context: React.createContext(undefined)
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
- if (!pageContext)
11
- throw new Error('Argument pageContext missing');
12
- return React.createElement(Context.Provider, { value: pageContext }, children);
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 pageContext = useContext(Context);
17
- if (!pageContext)
18
- throw new Error('<PageContextProvider> is needed for being able to use usePageContext()');
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,3 @@
1
+ export { getHeadSetting };
2
+ import type { PageContext } from 'vike/types';
3
+ declare function getHeadSetting(headSetting: 'title' | 'favicon' | 'lang', pageContext: PageContext): undefined | null | string;
@@ -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
- // TODO/next-major-release: remove pageProps (i.e. tell users to use data() instead of onBeforeRender() to fetch data)
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, { ...pageProps }) : null))))));
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 { OnRenderClientAsync } from 'vike/types';
3
- declare const onRenderClient: OnRenderClientAsync;
2
+ import type { OnRenderClientSync } from 'vike/types';
3
+ declare const onRenderClient: OnRenderClientSync;
@@ -1,30 +1,50 @@
1
1
  // https://vike.dev/onRenderClient
2
2
  export { onRenderClient };
3
3
  import ReactDOM from 'react-dom/client';
4
- import { getTitle } from './getTitle.js';
4
+ import { getHeadSetting } from './getHeadSetting.js';
5
5
  import { getPageElement } from './getPageElement.js';
6
6
  let root;
7
- const onRenderClient = async (pageContext) => {
7
+ const onRenderClient = (pageContext) => {
8
8
  const page = getPageElement(pageContext);
9
9
  const container = document.getElementById('page-view');
10
10
  if (container.innerHTML !== '' && pageContext.isHydration) {
11
- // Hydration
11
+ // First render (hydration)
12
12
  root = ReactDOM.hydrateRoot(container, page);
13
13
  }
14
14
  else {
15
15
  if (!root) {
16
- // First rendering
16
+ // First render (not hydration)
17
17
  root = ReactDOM.createRoot(container);
18
18
  }
19
19
  else {
20
- // Client routing
21
- // See https://vike.dev/server-routing-vs-client-routing
22
- // Get the page's `title` config value, which may be different from the
23
- // previous page. It can even be null, in which case we should unset the
24
- // document title.
25
- const title = getTitle(pageContext);
26
- document.title = title || '';
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);
27
32
  }
28
33
  root.render(page);
29
34
  }
30
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,18 +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 { getTitle } from './getTitle.js';
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
10
  checkVikeVersion();
11
11
  const onRenderHtml = async (pageContext) => {
12
- const lang = pageContext.config.lang || 'en';
13
- const { stream, favicon, description } = pageContext.config;
14
- const faviconTag = !favicon ? '' : escapeInject `<link rel="icon" href="${favicon}" />`;
15
- const descriptionTag = !description ? '' : escapeInject `<meta name="description" content="${description}" />`;
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>`;
16
+ const faviconTag = !favicon ? '' : escapeInject `<link rel="icon" href="${favicon}" />`;
18
17
  const Head = pageContext.config.Head || (() => React.createElement(React.Fragment, null));
19
18
  const head = (React.createElement(React.StrictMode, null,
20
19
  React.createElement(PageContextProvider, { pageContext: pageContext },
@@ -26,7 +25,7 @@ const onRenderHtml = async (pageContext) => {
26
25
  }
27
26
  else {
28
27
  const page = getPageElement(pageContext);
29
- pageView = !stream
28
+ pageView = !pageContext.config.stream
30
29
  ? dangerouslySkipEscape(renderToString(page))
31
30
  : await renderToStream(page, { userAgent: pageContext.userAgent });
32
31
  }
@@ -34,10 +33,9 @@ const onRenderHtml = async (pageContext) => {
34
33
  <html lang='${lang}'>
35
34
  <head>
36
35
  <meta charset="UTF-8" />
37
- ${faviconTag}
38
36
  ${titleTag}
39
- ${descriptionTag}
40
37
  ${headHtml}
38
+ ${faviconTag}
41
39
  </head>
42
40
  <body>
43
41
  <div id="page-view">${pageView}</div>
@@ -56,5 +54,5 @@ function checkVikeVersion() {
56
54
  if (versionParts[2] >= 147)
57
55
  return;
58
56
  }
59
- throw new Error('Update Vike to its latest version (or vike@0.4.147 and any version above)');
57
+ throw new Error('Update Vike to 0.4.147 or above');
60
58
  }
@@ -0,0 +1 @@
1
+ export declare function assert(condition: unknown): asserts condition;
@@ -0,0 +1,5 @@
1
+ export function assert(condition) {
2
+ if (condition)
3
+ return;
4
+ throw new Error('You stumbled upon a vike-react bug. Go to https://github.com/vikejs/vike-react/issues/new and copy-paste this error. A maintainer will fix the bug (usually under 24 hours).');
5
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vike-react",
3
- "version": "0.3.8",
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,26 +16,27 @@
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": {
22
23
  "react": "18.x.x",
23
24
  "react-dom": "18.x.x",
24
25
  "vike": "^0.4.151",
25
- "vite": "^4.3.8"
26
+ "vite": "^4.3.8 || ^5.0.10"
26
27
  },
27
28
  "devDependencies": {
28
- "@brillout/release-me": "^0.1.13",
29
- "@types/node": "^18.19.3",
30
- "@types/react": "^18.2.45",
29
+ "@brillout/release-me": "^0.1.14",
30
+ "@types/node": "^20.11.4",
31
+ "@types/react": "^18.2.48",
31
32
  "@types/react-dom": "^18.2.18",
32
33
  "react": "^18.2.0",
33
34
  "react-dom": "^18.2.0",
34
35
  "typescript": "^5.3.3",
35
- "vike": "^0.4.151"
36
+ "vike": "^0.4.159"
36
37
  },
37
38
  "dependencies": {
38
- "react-streaming": "^0.3.18"
39
+ "react-streaming": "^0.3.19"
39
40
  },
40
41
  "typesVersions": {
41
42
  "*": {
@@ -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
- }