vike-react 0.5.1 → 0.5.2

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.
@@ -1,11 +1 @@
1
- export { Head };
2
- /**
3
- * Add arbitrary `<head>` tags.
4
- *
5
- * (The children are teleported to `<head>`.)
6
- *
7
- * https://vike.dev/Head
8
- */
9
- declare function Head({ children }: {
10
- children: React.ReactNode;
11
- }): null;
1
+ export declare function Head(): null;
@@ -1,15 +1,4 @@
1
- export { Head };
2
- // Same as ./Head-server.ts but importing useConfig-client.js
3
- import { useConfig } from '../../hooks/useConfig/useConfig-client.js';
4
- /**
5
- * Add arbitrary `<head>` tags.
6
- *
7
- * (The children are teleported to `<head>`.)
8
- *
9
- * https://vike.dev/Head
10
- */
11
- function Head({ children }) {
12
- const config = useConfig();
13
- config({ Head: children });
1
+ // https://vike.dev/Head#only-html
2
+ export function Head() {
14
3
  return null;
15
4
  }
@@ -1,5 +1,4 @@
1
1
  export { Head };
2
- // Same as ./Head-client.ts but importing useConfig-server.js
3
2
  import { useConfig } from '../../hooks/useConfig/useConfig-server.js';
4
3
  /**
5
4
  * Add arbitrary `<head>` tags.
@@ -7,35 +7,27 @@ import { getPageContext } from 'vike/getPageContext';
7
7
  * https://vike.dev/useConfig
8
8
  */
9
9
  function useConfig() {
10
- const configSetter = (config) => setConfigOverPageContext(config, pageContext);
11
10
  // Vike hook
12
11
  let pageContext = getPageContext();
13
12
  if (pageContext)
14
- return configSetter;
13
+ return (config) => setPageContextConfigFromHook(config, pageContext);
15
14
  // React component
16
15
  pageContext = usePageContext();
17
16
  return (config) => {
18
17
  if (!('_headAlreadySet' in pageContext)) {
19
- configSetter(config);
18
+ setPageContextConfigFromHook(config, pageContext);
20
19
  }
21
20
  else {
22
- sideEffect(config);
21
+ apply(config);
23
22
  }
24
23
  };
25
24
  }
26
- const configsClientSide = ['title'];
27
- function setConfigOverPageContext(config, pageContext) {
25
+ function setPageContextConfigFromHook(config, pageContext) {
28
26
  pageContext._configFromHook ?? (pageContext._configFromHook = {});
29
- configsClientSide.forEach((configName) => {
30
- const configValue = config[configName];
31
- if (!configValue)
32
- return;
33
- pageContext._configFromHook[configName] = configValue;
34
- });
27
+ Object.assign(pageContext._configFromHook, config);
35
28
  }
36
- function sideEffect(config) {
29
+ function apply(config) {
37
30
  const { title } = config;
38
- if (title) {
31
+ if (title)
39
32
  window.document.title = title;
40
- }
41
33
  }
@@ -1,4 +1,5 @@
1
1
  export { useConfig };
2
+ export type ConfigFromHookCumulative = (typeof configsCumulative)[number];
2
3
  import type { ConfigFromHook } from '../../types/Config.js';
3
4
  /**
4
5
  * Set configurations inside React components and Vike hooks.
@@ -6,3 +7,4 @@ import type { ConfigFromHook } from '../../types/Config.js';
6
7
  * https://vike.dev/useConfig
7
8
  */
8
9
  declare function useConfig(): (config: ConfigFromHook) => void;
10
+ declare const configsCumulative: readonly ["Head", "bodyAttributes", "htmlAttributes"];
@@ -2,58 +2,58 @@ export { useConfig };
2
2
  import { usePageContext } from '../usePageContext.js';
3
3
  import { getPageContext } from 'vike/getPageContext';
4
4
  import { useStream } from 'react-streaming';
5
+ import { objectKeys } from '../../utils/objectKeys.js';
6
+ import { includes } from '../../utils/includes.js';
5
7
  /**
6
8
  * Set configurations inside React components and Vike hooks.
7
9
  *
8
10
  * https://vike.dev/useConfig
9
11
  */
10
12
  function useConfig() {
11
- const configSetter = (config) => setConfigOverPageContext(config, pageContext);
12
13
  // Vike hook
13
14
  let pageContext = getPageContext();
14
15
  if (pageContext)
15
- return configSetter;
16
+ return (config) => setPageContextConfigFromHook(config, pageContext);
16
17
  // React component
17
18
  pageContext = usePageContext();
18
19
  const stream = useStream();
19
20
  return (config) => {
20
21
  if (!pageContext._headAlreadySet) {
21
- configSetter(config);
22
+ setPageContextConfigFromHook(config, pageContext);
22
23
  }
23
24
  else {
24
25
  // <head> already sent to the browser => send DOM-manipulating scripts during HTML streaming
25
- sideEffect(config, stream);
26
+ apply(config, stream);
26
27
  }
27
28
  };
28
29
  }
29
- const configsHtmlOnly = ['Head', 'description', 'image'];
30
- const configsCumulative = ['Head'];
31
- const configsOverridable = ['title', 'description', 'image'];
32
- function setConfigOverPageContext(config, pageContext) {
30
+ const configsClientSide = ['title'];
31
+ const configsCumulative = ['Head', 'bodyAttributes', 'htmlAttributes'];
32
+ function setPageContextConfigFromHook(config, pageContext) {
33
33
  pageContext._configFromHook ?? (pageContext._configFromHook = {});
34
- if (pageContext.isClientSideNavigation) {
35
- // Remove HTML only configs which the client-side doesn't need (also avoiding serialization errors)
36
- for (const configName of configsHtmlOnly)
37
- delete config[configName];
38
- }
39
- // Cumulative values
40
- configsCumulative.forEach((configName) => {
34
+ objectKeys(config).forEach((configName) => {
41
35
  var _a;
42
- const configValue = config[configName];
43
- if (!configValue)
44
- return;
45
- (_a = pageContext._configFromHook)[configName] ?? (_a[configName] = []);
46
- pageContext._configFromHook[configName].push(configValue);
47
- });
48
- // Overridable values
49
- configsOverridable.forEach((configName) => {
50
- const configValue = config[configName];
51
- if (!configValue)
36
+ // Skip HTML only configs which the client-side doesn't need, saving KBs sent to the client as well as avoiding serialization errors.
37
+ if (pageContext.isClientSideNavigation && !configsClientSide.includes(configName))
52
38
  return;
53
- pageContext._configFromHook[configName] = configValue;
39
+ if (!includes(configsCumulative, configName)) {
40
+ // Overridable config
41
+ const configValue = config[configName];
42
+ if (configValue === undefined)
43
+ return;
44
+ pageContext._configFromHook[configName] = configValue;
45
+ }
46
+ else {
47
+ // Cumulative config
48
+ const configValue = config[configName];
49
+ if (!configValue)
50
+ return;
51
+ (_a = pageContext._configFromHook)[configName] ?? (_a[configName] = []);
52
+ pageContext._configFromHook[configName].push(configValue);
53
+ }
54
54
  });
55
55
  }
56
- function sideEffect(config, stream) {
56
+ function apply(config, stream) {
57
57
  const { title } = config;
58
58
  if (title) {
59
59
  const htmlSnippet = `<script>document.title = ${JSON.stringify(title)}</script>`;
@@ -1,5 +1,6 @@
1
1
  export { getHeadSetting };
2
2
  import type { PageContext } from 'vike/types';
3
3
  import type { PageContextInternal } from '../types/PageContext.js';
4
- type HeadSetting = 'favicon' | 'lang' | 'title' | 'description' | 'image';
5
- declare function getHeadSetting(headSetting: HeadSetting, pageContext: PageContext & PageContextInternal): undefined | null | string;
4
+ import type { ConfigFromHookResolved } from '../types/Config.js';
5
+ type HeadSetting = Exclude<keyof ConfigFromHookResolved, 'Head'>;
6
+ declare function getHeadSetting<T>(headSetting: HeadSetting, pageContext: PageContext & PageContextInternal): undefined | T;
@@ -1,27 +1,18 @@
1
1
  export { getHeadSetting };
2
2
  import { isCallable } from '../utils/isCallable.js';
3
3
  function getHeadSetting(headSetting, pageContext) {
4
+ // Set by useConfig()
4
5
  {
5
6
  const val = pageContext._configFromHook?.[headSetting];
6
7
  if (val !== undefined)
7
8
  return val;
8
9
  }
9
- const config = pageContext.configEntries[headSetting]?.[0];
10
- if (!config)
11
- return undefined;
12
- const val = config.configValue;
13
- if (typeof val === 'string')
14
- return val;
15
- if (!val)
16
- return null;
10
+ // Set by +configName.js
11
+ const val = pageContext.config[headSetting];
17
12
  if (isCallable(val)) {
18
- const valStr = val(pageContext);
19
- if (typeof valStr !== 'string') {
20
- throw new Error(config.configDefinedAt + ' should return a string');
21
- }
22
- return valStr;
13
+ return val(pageContext);
23
14
  }
24
15
  else {
25
- throw new Error(config.configDefinedAt + ' should be a string or a function returning a string');
16
+ return val;
26
17
  }
27
18
  }
@@ -64,6 +64,7 @@ function getHeadHtml(pageContext) {
64
64
  const imageTags = !image
65
65
  ? ''
66
66
  : escapeInject `<meta property="og:image" content="${image}"><meta name="twitter:card" content="summary_large_image">`;
67
+ const viewportTag = dangerouslySkipEscape(getViewportTag(getHeadSetting('viewport', pageContext)));
67
68
  const headElementsHtml = dangerouslySkipEscape([
68
69
  // Added by +Head
69
70
  ...(pageContext.config.Head ?? []),
@@ -75,7 +76,6 @@ function getHeadHtml(pageContext) {
75
76
  .join('\n'));
76
77
  // Not needed on the client-side, thus we remove it to save KBs sent to the client
77
78
  delete pageContext._configFromHook;
78
- const viewportTag = dangerouslySkipEscape(getViewportTag(pageContext.config.viewport));
79
79
  const headHtml = escapeInject `
80
80
  ${titleTags}
81
81
  ${viewportTag}
@@ -105,8 +105,8 @@ function getTagAttributes(pageContext) {
105
105
  // Don't set `lang` to its default value if it's `null` (so that users can set it to `null` in order to remove the default value)
106
106
  if (lang === undefined)
107
107
  lang = 'en';
108
- const bodyAttributes = mergeTagAttributesList(pageContext.config.bodyAttributes);
109
- const htmlAttributes = mergeTagAttributesList(pageContext.config.htmlAttributes);
108
+ const bodyAttributes = mergeTagAttributesList(getHeadSetting('bodyAttributes', pageContext));
109
+ const htmlAttributes = mergeTagAttributesList(getHeadSetting('htmlAttributes', pageContext));
110
110
  const bodyAttributesString = getTagAttributesString(bodyAttributes);
111
111
  const htmlAttributesString = getTagAttributesString({ ...htmlAttributes, lang: lang ?? htmlAttributes.lang });
112
112
  return { htmlAttributesString, bodyAttributesString };
@@ -1,6 +1,7 @@
1
1
  import type { ImportString, PageContextClient, PageContext as PageContext_, PageContextServer } from 'vike/types';
2
2
  import type { TagAttributes } from '../utils/getTagAttributesString.js';
3
3
  import type { Viewport } from '../renderer/onRenderHtml.js';
4
+ import type { ConfigFromHookCumulative } from '../hooks/useConfig/useConfig-server.js';
4
5
  declare global {
5
6
  namespace Vike {
6
7
  interface Config {
@@ -43,7 +44,7 @@ declare global {
43
44
  *
44
45
  * https://vike.dev/title
45
46
  */
46
- title?: string | ((pageContext: PageContext_) => string);
47
+ title?: string | null | ((pageContext: PageContext_) => string | null | undefined);
47
48
  /**
48
49
  * Set the page's description.
49
50
  *
@@ -57,7 +58,7 @@ declare global {
57
58
  *
58
59
  * https://vike.dev/description
59
60
  */
60
- description?: string | ((pageContext: PageContextServer) => string);
61
+ description?: string | null | ((pageContext: PageContextServer) => string | null | undefined);
61
62
  /**
62
63
  * Set the page's preview image upon URL sharing.
63
64
  *
@@ -71,7 +72,7 @@ declare global {
71
72
  *
72
73
  * https://vike.dev/image
73
74
  */
74
- image?: string | ((pageContext: PageContextServer) => string);
75
+ image?: string | null | ((pageContext: PageContextServer) => string | null | undefined);
75
76
  /**
76
77
  * Set the page's width shown to the user on mobile/tablet devices.
77
78
  *
@@ -79,7 +80,7 @@ declare global {
79
80
  *
80
81
  * https://vike.dev/viewport
81
82
  */
82
- viewport?: Viewport;
83
+ viewport?: Viewport | ((pageContext: PageContextServer) => Viewport | undefined);
83
84
  /**
84
85
  * Set the page's favicon.
85
86
  *
@@ -92,7 +93,7 @@ declare global {
92
93
  *
93
94
  * https://vike.dev/favicon
94
95
  */
95
- favicon?: string | ((pageContext: PageContextServer) => string);
96
+ favicon?: string | null | ((pageContext: PageContextServer) => string | null | undefined);
96
97
  /**
97
98
  * Set the page's language (`<html lang>`).
98
99
  *
@@ -100,19 +101,19 @@ declare global {
100
101
  *
101
102
  * https://vike.dev/lang
102
103
  */
103
- lang?: string | ((pageContext: PageContext_) => string);
104
+ lang?: string | null | ((pageContext: PageContext_) => string | null | undefined);
104
105
  /**
105
106
  * Add tag attributes such as `<html class="dark">`.
106
107
  *
107
108
  * https://vike.dev/htmlAttributes
108
109
  */
109
- htmlAttributes?: TagAttributes;
110
+ htmlAttributes?: TagAttributes | ((pageContext: PageContextServer) => TagAttributes | undefined);
110
111
  /**
111
112
  * Add tag attributes such as `<body class="dark">`.
112
113
  *
113
114
  * https://vike.dev/bodyAttributes
114
115
  */
115
- bodyAttributes?: TagAttributes;
116
+ bodyAttributes?: TagAttributes | ((pageContext: PageContextServer) => TagAttributes | undefined);
116
117
  /**
117
118
  * If `true`, the page is rendered twice: on the server-side (to HTML) and on the client-side (hydration).
118
119
  *
@@ -193,11 +194,6 @@ type Loading = {
193
194
  type PickWithoutGetter<T, K extends keyof T> = {
194
195
  [P in K]: Exclude<T[P], Function>;
195
196
  };
196
- export type ConfigFromHook = PickWithoutGetter<Vike.Config, 'Head' | 'title' | 'description' | 'image'>;
197
- export type ConfigFromHookResolved = {
198
- Head?: Head[];
199
- title?: string;
200
- description?: string;
201
- image?: string;
202
- };
197
+ export type ConfigFromHook = PickWithoutGetter<Vike.Config, 'Head' | 'title' | 'description' | 'image' | 'favicon' | 'lang' | 'viewport' | 'bodyAttributes' | 'htmlAttributes'>;
198
+ export type ConfigFromHookResolved = Omit<ConfigFromHook, ConfigFromHookCumulative> & Pick<Vike.ConfigResolved, ConfigFromHookCumulative>;
203
199
  export {};
@@ -0,0 +1,2 @@
1
+ /** Same as Array.prototype.includes() but with type inference */
2
+ export declare function includes<T>(values: readonly T[], x: unknown): x is T;
@@ -0,0 +1,10 @@
1
+ // https://stackoverflow.com/questions/56565528/typescript-const-assertions-how-to-use-array-prototype-includes/74213179#74213179
2
+ /** Same as Array.prototype.includes() but with type inference */
3
+ export function includes(values, x) {
4
+ return values.includes(x);
5
+ }
6
+ /*
7
+ export function includes<Arr extends any[] | readonly any[]>(arr: Arr, el: unknown): el is Arr[number] {
8
+ return arr.includes(el as any)
9
+ }
10
+ */
@@ -0,0 +1,2 @@
1
+ /** Same as Object.keys() but with type inference */
2
+ export declare function objectKeys<T extends object>(obj: T): (keyof T)[];
@@ -0,0 +1,6 @@
1
+ // https://stackoverflow.com/questions/52856496/typescript-object-keys-return-string
2
+ // https://github.com/sindresorhus/ts-extras/blob/main/source/object-keys.ts
3
+ /** Same as Object.keys() but with type inference */
4
+ export function objectKeys(obj) {
5
+ return Object.keys(obj);
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vike-react",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -11,15 +11,15 @@
11
11
  "browser": "./dist/hooks/useConfig/useConfig-client.js",
12
12
  "default": "./dist/hooks/useConfig/useConfig-server.js"
13
13
  },
14
- "./ClientOnly": "./dist/components/ClientOnly.js",
15
- "./Head": {
16
- "browser": "./dist/components/Head/Head-client.js",
17
- "default": "./dist/components/Head/Head-server.js"
18
- },
19
14
  "./Config": {
20
15
  "browser": "./dist/components/Config/Config-client.js",
21
16
  "default": "./dist/components/Config/Config-server.js"
22
17
  },
18
+ "./Head": {
19
+ "browser": "./dist/components/Head/Head-client.js",
20
+ "default": "./dist/components/Head/Head-server.js"
21
+ },
22
+ "./ClientOnly": "./dist/components/ClientOnly.js",
23
23
  "./clientOnly": "./dist/helpers/clientOnly.js",
24
24
  ".": "./dist/index.js",
25
25
  "./config": "./dist/+config.js",
@@ -38,8 +38,7 @@
38
38
  "peerDependencies": {
39
39
  "react": ">=18.0.0",
40
40
  "react-dom": ">=18.0.0",
41
- "vike": ">=0.4.182",
42
- "vite": ">=4.3.8"
41
+ "vike": ">=0.4.182"
43
42
  },
44
43
  "devDependencies": {
45
44
  "@biomejs/biome": "^1.6.4",
@@ -47,11 +46,13 @@
47
46
  "@types/node": "^20.11.17",
48
47
  "@types/react": "^18.2.55",
49
48
  "@types/react-dom": "^18.2.19",
50
- "react": "^18.2.0",
51
- "react-dom": "^18.2.0",
49
+ "react": "^18.3.1",
50
+ "react-dom": "^18.3.1",
51
+ "react-streaming": "^0.3.43",
52
52
  "rimraf": "^5.0.5",
53
- "typescript": "^5.5.3",
54
- "vike": "^0.4.182"
53
+ "typescript": "^5.5.4",
54
+ "vike": "^0.4.183",
55
+ "vite": "^5.4.0"
55
56
  },
56
57
  "dependencies": {
57
58
  "react-streaming": "^0.3.42"
@@ -67,12 +68,12 @@
67
68
  "useConfig": [
68
69
  "./dist/hooks/useConfig/useConfig-server.d.ts"
69
70
  ],
70
- "Head": [
71
- "./dist/components/Head/Head-server.d.ts"
72
- ],
73
71
  "Config": [
74
72
  "./dist/components/Config/Config-server.d.ts"
75
73
  ],
74
+ "Head": [
75
+ "./dist/components/Head/Head-server.d.ts"
76
+ ],
76
77
  "ClientOnly": [
77
78
  "./dist/components/ClientOnly.d.ts"
78
79
  ],