vike-react 0.5.12 → 0.6.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.
@@ -8,4 +8,4 @@ declare function ClientOnly<T>({ load, children, fallback, deps, }: {
8
8
  children: (Component: React.ComponentType<T>) => ReactNode;
9
9
  fallback: ReactNode;
10
10
  deps?: Parameters<typeof useEffect>[1];
11
- }): string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null | undefined;
11
+ }): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | React.JSX.Element | null | undefined;
@@ -1,7 +1,7 @@
1
1
  export { ClientOnly };
2
2
  import React, { lazy, useEffect, useState, startTransition } from 'react';
3
3
  function ClientOnly({ load, children, fallback, deps = [], }) {
4
- // TODO/next-major-release: remove this file/export
4
+ // TODO/next-major: remove this file/export
5
5
  console.warn('[vike-react][warning] <ClientOnly> is deprecated: use clientOnly() instead https://vike.dev/clientOnly');
6
6
  const [Component, setComponent] = useState(null);
7
7
  useEffect(() => {
package/dist/config.d.ts CHANGED
@@ -145,6 +145,10 @@ declare const config: {
145
145
  client: true;
146
146
  };
147
147
  };
148
+ react: {
149
+ cumulative: true;
150
+ env: {};
151
+ };
148
152
  };
149
153
  };
150
154
  import './types/Config.js';
package/dist/config.js CHANGED
@@ -100,12 +100,17 @@ const config = {
100
100
  cumulative: true,
101
101
  env: { client: true, server: true },
102
102
  },
103
+ // TODO/next-major: move to +react.js > strictMode ?
103
104
  reactStrictMode: {
104
105
  env: { client: true, server: true },
105
106
  },
106
107
  Loading: {
107
108
  env: { server: true, client: true },
108
109
  },
110
+ react: {
111
+ cumulative: true,
112
+ env: {},
113
+ },
109
114
  },
110
115
  };
111
116
  // @eject-remove start
@@ -1,9 +1,10 @@
1
1
  export { useConfig };
2
2
  import { usePageContext } from '../usePageContext.js';
3
3
  import { getPageContext } from 'vike/getPageContext';
4
- import { useStream } from 'react-streaming';
4
+ import { useStreamOptional } from 'react-streaming';
5
5
  import { objectKeys } from '../../utils/objectKeys.js';
6
6
  import { includes } from '../../utils/includes.js';
7
+ import { assert } from '../../utils/assert.js';
7
8
  import { configsCumulative } from './configsCumulative.js';
8
9
  /**
9
10
  * Set configurations inside components and Vike hooks.
@@ -17,12 +18,13 @@ function useConfig() {
17
18
  return (config) => setPageContextConfigFromHook(config, pageContext);
18
19
  // Component
19
20
  pageContext = usePageContext();
20
- const stream = useStream();
21
+ const stream = useStreamOptional();
21
22
  return (config) => {
22
23
  if (!pageContext._headAlreadySet) {
23
24
  setPageContextConfigFromHook(config, pageContext);
24
25
  }
25
26
  else {
27
+ assert(stream);
26
28
  // <head> already sent to the browser => send DOM-manipulating scripts during HTML streaming
27
29
  apply(config, stream);
28
30
  }
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- // TODO/next-major-release: remove this file/export
1
+ // TODO/next-major: remove this file/export
2
2
  console.warn("[vike-react][warning][deprecation] Replace `import vikeReact from 'vike-react'` with `import vikeReact from 'vike-react/config'` (typically in your /pages/+config.js)");
3
3
  export { default } from './config.js';
@@ -6,6 +6,7 @@ import { getPageElement } from './getPageElement.js';
6
6
  import './styles.css';
7
7
  import { callCumulativeHooks } from '../utils/callCumulativeHooks.js';
8
8
  import { applyHeadSettings } from './applyHeadSettings.js';
9
+ import { resolveReactOptions } from './resolveReactOptions.js';
9
10
  let root;
10
11
  const onRenderClient = async (pageContext) => {
11
12
  pageContext._headAlreadySet = pageContext.isHydration;
@@ -14,27 +15,22 @@ const onRenderClient = async (pageContext) => {
14
15
  await callCumulativeHooks(pageContext.config.onBeforeRenderClient, pageContext);
15
16
  const { page, renderPromise } = getPageElement(pageContext);
16
17
  pageContext.page = page;
17
- // 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.
18
+ // TODO: implement this? So that, upon errors, onRenderClient() throws an error and Vike can render the error page. As of April 2024 it isn't released yet.
18
19
  // - https://react-dev-git-fork-rickhanlonii-rh-root-options-fbopensource.vercel.app/reference/react-dom/client/createRoot#show-a-dialog-for-uncaught-errors
19
20
  // - https://react-dev-git-fork-rickhanlonii-rh-root-options-fbopensource.vercel.app/reference/react-dom/client/hydrateRoot#show-a-dialog-for-uncaught-errors
20
21
  const onUncaughtError = (_error, _errorInfo) => { };
21
22
  const container = document.getElementById('root');
23
+ const { hydrateRootOptions, createRootOptions } = resolveReactOptions(pageContext);
22
24
  if (pageContext.isHydration &&
23
25
  // Whether the page was [Server-Side Rendered](https://vike.dev/ssr).
24
26
  container.innerHTML !== '') {
25
27
  // First render while using SSR, i.e. [hydration](https://vike.dev/hydration)
26
- root = ReactDOM.hydrateRoot(container, page, {
27
- // @ts-expect-error
28
- onUncaughtError,
29
- });
28
+ root = ReactDOM.hydrateRoot(container, page, hydrateRootOptions);
30
29
  }
31
30
  else {
32
31
  if (!root) {
33
32
  // First render without SSR
34
- root = ReactDOM.createRoot(container, {
35
- // @ts-expect-error
36
- onUncaughtError,
37
- });
33
+ root = ReactDOM.createRoot(container, createRootOptions);
38
34
  }
39
35
  root.render(page);
40
36
  }
@@ -10,6 +10,7 @@ import { getPageElement } from './getPageElement.js';
10
10
  import { isReactElement } from '../utils/isReactElement.js';
11
11
  import { getTagAttributesString } from '../utils/getTagAttributesString.js';
12
12
  import { callCumulativeHooks } from '../utils/callCumulativeHooks.js';
13
+ import { resolveReactOptions } from './resolveReactOptions.js';
13
14
  addEcosystemStamp();
14
15
  const onRenderHtml = async (pageContext) => {
15
16
  const pageHtml = await getPageHtml(pageContext);
@@ -36,11 +37,12 @@ async function getPageHtml(pageContext) {
36
37
  pageContext.page = getPageElement(pageContext).page;
37
38
  // https://github.com/vikejs/vike-react/issues/87#issuecomment-2488742744
38
39
  await callCumulativeHooks(pageContext.config.onBeforeRenderHtml, pageContext);
40
+ const { renderToStringOptions } = resolveReactOptions(pageContext);
39
41
  let pageHtml = '';
40
42
  if (pageContext.page) {
41
43
  const { stream, streamIsRequired } = pageContext.config;
42
44
  if (!stream && !streamIsRequired) {
43
- const pageHtmlString = renderToString(pageContext.page);
45
+ const pageHtmlString = renderToString(pageContext.page, renderToStringOptions);
44
46
  pageContext.pageHtmlString = pageHtmlString;
45
47
  pageHtml = dangerouslySkipEscape(pageHtmlString);
46
48
  }
@@ -0,0 +1,4 @@
1
+ export { resolveReactOptions };
2
+ import type { PageContext } from 'vike/types';
3
+ import type { ReactOptions } from '../types/Config.js';
4
+ declare function resolveReactOptions(pageContext: PageContext): ReactOptions;
@@ -0,0 +1,32 @@
1
+ export { resolveReactOptions };
2
+ import { isCallable } from '../utils/isCallable.js';
3
+ import { objectEntries } from '../utils/objectEntries.js';
4
+ function resolveReactOptions(pageContext) {
5
+ const optionsAcc = {};
6
+ (pageContext.config.react ?? []).forEach((valUnresolved) => {
7
+ const optionList = isCallable(valUnresolved) ? valUnresolved(pageContext) : valUnresolved;
8
+ if (!optionList)
9
+ return;
10
+ objectEntries(optionList).forEach(([fnName, options]) => {
11
+ if (!options)
12
+ return;
13
+ optionsAcc[fnName] ?? (optionsAcc[fnName] = {});
14
+ objectEntries(options).forEach(([key, val]) => {
15
+ var _a;
16
+ if (!isCallable(val)) {
17
+ // @ts-ignore
18
+ (_a = optionsAcc[fnName])[key] ?? (_a[key] = val);
19
+ }
20
+ else {
21
+ const valPrevious = optionsAcc[fnName][key];
22
+ // @ts-ignore
23
+ optionsAcc[fnName][key] = (...args) => {
24
+ valPrevious?.(...args);
25
+ val(...args);
26
+ };
27
+ }
28
+ });
29
+ });
30
+ });
31
+ return optionsAcc;
32
+ }
@@ -3,6 +3,8 @@ import type { TagAttributes } from '../utils/getTagAttributesString.js';
3
3
  import type { Viewport } from '../integration/onRenderHtml.js';
4
4
  import type { ConfigsCumulative } from '../hooks/useConfig/configsCumulative.js';
5
5
  import type React from 'react';
6
+ import type { HydrationOptions, RootOptions } from 'react-dom/client';
7
+ import type { ServerOptions } from 'react-dom/server';
6
8
  declare global {
7
9
  namespace Vike {
8
10
  interface Config {
@@ -187,6 +189,12 @@ declare global {
187
189
  * https://vike.dev/Loading
188
190
  */
189
191
  Loading?: Loading | ImportString;
192
+ /**
193
+ * Options passed to React functions such as `createRoot()` or `hydrateRoot()`.
194
+ *
195
+ * https://vike.dev/react-setting
196
+ */
197
+ react?: ReactOptions | ((pageContext: PageContext) => ReactOptions) | ImportString;
190
198
  }
191
199
  interface ConfigResolved {
192
200
  Wrapper?: Wrapper[];
@@ -200,6 +208,7 @@ declare global {
200
208
  onAfterRenderHtml?: Function[];
201
209
  onBeforeRenderClient?: Function[];
202
210
  onAfterRenderClient?: Function[];
211
+ react?: Exclude<Config['react'], ImportString>[];
203
212
  }
204
213
  }
205
214
  }
@@ -219,4 +228,9 @@ type PickWithoutGetter<T, K extends keyof T> = {
219
228
  };
220
229
  export type ConfigFromHook = PickWithoutGetter<Vike.Config, 'Head' | 'title' | 'description' | 'image' | 'favicon' | 'lang' | 'viewport' | 'bodyAttributes' | 'htmlAttributes'>;
221
230
  export type ConfigFromHookResolved = Omit<ConfigFromHook, ConfigsCumulative> & Pick<Vike.ConfigResolved, ConfigsCumulative>;
231
+ export type ReactOptions = {
232
+ hydrateRootOptions?: HydrationOptions;
233
+ createRootOptions?: RootOptions;
234
+ renderToStringOptions?: ServerOptions;
235
+ };
222
236
  export {};
@@ -1,2 +1,2 @@
1
1
  export { callCumulativeHooks };
2
- declare function callCumulativeHooks(values: undefined | unknown[], pageContext: Record<string, any>): Promise<unknown[]>;
2
+ declare function callCumulativeHooks<T>(values: undefined | T[], pageContext: Record<string, any>): Promise<(undefined | null | Exclude<T, Function>)[]>;
@@ -1,10 +1,11 @@
1
1
  export { callCumulativeHooks };
2
2
  import { providePageContext } from 'vike/getPageContext';
3
+ import { isCallable } from './isCallable.js';
3
4
  async function callCumulativeHooks(values, pageContext) {
4
5
  if (!values)
5
6
  return [];
6
7
  const valuesPromises = values.map((val) => {
7
- if (typeof val === 'function') {
8
+ if (isCallable(val)) {
8
9
  providePageContext(pageContext);
9
10
  // Hook
10
11
  return val(pageContext);
@@ -1 +1 @@
1
- export declare function isCallable<T extends (...args: unknown[]) => unknown>(thing: T | unknown): thing is T;
1
+ export declare function isCallable<T extends (...args: any[]) => any>(thing: T | unknown): thing is T;
@@ -0,0 +1,2 @@
1
+ /** Same as Object.entries() but with type inference */
2
+ export declare function objectEntries<T extends object>(obj: T): [keyof T, T[keyof T]][];
@@ -0,0 +1,5 @@
1
+ // https://stackoverflow.com/questions/60141960/typescript-key-value-relation-preserving-object-entries-type/75337277#75337277
2
+ /** Same as Object.entries() but with type inference */
3
+ export function objectEntries(obj) {
4
+ return Object.entries(obj);
5
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vike-react",
3
- "version": "0.5.12",
3
+ "version": "0.6.0",
4
4
  "repository": "https://github.com/vikejs/vike-react",
5
5
  "type": "module",
6
6
  "exports": {
@@ -27,26 +27,25 @@
27
27
  "./__internal/integration/Loading": "./dist/integration/Loading.js"
28
28
  },
29
29
  "dependencies": {
30
- "react-streaming": "^0.3.47"
30
+ "react-streaming": "^0.4.2"
31
31
  },
32
32
  "peerDependencies": {
33
- "react": ">=18.0.0",
34
- "react-dom": ">=18.0.0",
33
+ "react": ">=19",
34
+ "react-dom": ">=19",
35
35
  "vike": ">=0.4.182"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@biomejs/biome": "^1.6.4",
39
39
  "@brillout/release-me": "^0.4.2",
40
40
  "@types/node": "^20.11.17",
41
- "@types/react": "^18.2.55",
42
- "@types/react-dom": "^18.2.19",
43
- "react": "^18.3.1",
44
- "react-dom": "^18.3.1",
45
- "react-streaming": "^0.3.47",
41
+ "@types/react": "^19.0.10",
42
+ "@types/react-dom": "^19.0.4",
43
+ "react": "^19.0.0",
44
+ "react-dom": "^19.0.0",
46
45
  "rimraf": "^5.0.5",
47
- "typescript": "^5.5.4",
48
- "vike": "^0.4.211",
49
- "vite": "^5.4.0"
46
+ "typescript": "^5.7.3",
47
+ "vike": "^0.4.223",
48
+ "vite": "^6.1.0"
50
49
  },
51
50
  "typesVersions": {
52
51
  "*": {