diff --git a/examples/full/.testRun.ts b/examples/full/.testRun.ts
index 59deff3..72f5ec7 100644
--- a/examples/full/.testRun.ts
+++ b/examples/full/.testRun.ts
@@ -2,6 +2,7 @@ export { testRun };
import { test, expect, run, fetchHtml, page, getServerUrl, autoRetry, partRegex } from "@brillout/test-e2e";
import assert from "node:assert";
+const dataHk = partRegex`data-hk="${/[0-9-]+/}"`;
let isProd: boolean;
@@ -15,6 +16,7 @@ function testRun(cmd: `pnpm run ${"dev" | "preview"}`) {
title: "My Vike + Solid App",
text: "Rendered to HTML.",
counter: true,
+ image: true,
});
testUrl({
@@ -22,6 +24,7 @@ function testRun(cmd: `pnpm run ${"dev" | "preview"}`) {
title: "6 Star Wars Movies",
description: "All the 6 movies from the Star Wars franchise",
text: "A New Hope",
+ image: true,
});
testUrl({
@@ -41,6 +44,8 @@ function testRun(cmd: `pnpm run ${"dev" | "preview"}`) {
});
testNavigationBetweenWithSSRAndWithoutSSR();
+
+ testUseConfig();
}
function testNavigationBetweenWithSSRAndWithoutSSR() {
@@ -91,6 +96,7 @@ function testUrl({
text,
counter,
noSSR,
+ image,
}: {
url: string;
title: string;
@@ -98,6 +104,7 @@ function testUrl({
text: string;
counter?: true;
noSSR?: true;
+ image?: true;
}) {
test(url + " (HTML)", async () => {
const html = await fetchHtml(url);
@@ -105,13 +112,17 @@ function testUrl({
expect(html).toContain(text);
}
- const dataHkHash = /[0-9-]+/;
-
expect(getTitle(html)).toBe(title);
- expect(html).toMatch(partRegex``);
+ expect(html).toMatch(partRegex``);
if (!description) description = "Demo showcasing Vike + Solid";
expect(html).toMatch(partRegex``);
+
+ if (image) {
+ expect(html).toMatch(partRegex``);
+ } else {
+ expect(html).not.toContain("og:image");
+ }
});
test(url + " (Hydration)", async () => {
await page.goto(getServerUrl() + url);
@@ -123,6 +134,32 @@ function testUrl({
});
}
+function testUseConfig() {
+ test("useConfig() HTML", async () => {
+ const html = await fetchHtml("/images");
+ expect(html).toMatch(
+ partRegex``,
+ );
+ expect(html).toMatch(
+ partRegex``,
+ );
+ });
+ test("useConfig() hydration", async () => {
+ await page.goto(getServerUrl() + "/");
+ await testCounter();
+ ensureWasClientSideRouted("/pages/index");
+ await page.click('a:has-text("useConfig()")');
+ await testCounter();
+ ensureWasClientSideRouted("/pages/index");
+ await page.goto(getServerUrl() + "/images");
+ await testCounter();
+ });
+}
+
function getTitle(html: string) {
const title = html.match(/
(.*?)<\/title>/i)?.[1];
return title;
diff --git a/examples/full/pages/+Layout.tsx b/examples/full/pages/+Layout.tsx
index a5f676c..150717b 100644
--- a/examples/full/pages/+Layout.tsx
+++ b/examples/full/pages/+Layout.tsx
@@ -19,6 +19,7 @@ export function Layout(props: { children?: JSX.Element }) {
Without SSR
Nested Layout 1
Nested Layout 2
+ useConfig()
{props.children}
diff --git a/examples/full/pages/images/+Page.tsx b/examples/full/pages/images/+Page.tsx
new file mode 100644
index 0000000..d7cd5cc
--- /dev/null
+++ b/examples/full/pages/images/+Page.tsx
@@ -0,0 +1,47 @@
+export { Page };
+
+import { Head } from "vike-solid/Head";
+import logoOld from "../../assets/logo.svg";
+import logoNew from "../../assets/logo-new.svg";
+import { Counter } from "../../components/Counter";
+
+function Page() {
+ return (
+ <>
+
+ Page showcasing an <Image>
component that adds/teleports structured data (
+ <script type="application/ld+json">
) to <head>
, see HTML.
+
+
+ New logo:
+
+
+
+ Old logo:
+
+
+
+ >
+ );
+}
+
+function Image({ src, author }: { src: string; author: string }) {
+ return (
+ <>
+
+
+
+
+ >
+ );
+}
diff --git a/examples/full/pages/index/+Page.tsx b/examples/full/pages/index/+Page.tsx
index da91dff..7232688 100644
--- a/examples/full/pages/index/+Page.tsx
+++ b/examples/full/pages/index/+Page.tsx
@@ -1,4 +1,8 @@
+export { Page };
+
import { clientOnly } from "vike-solid/clientOnly";
+import { Config } from "vike-solid/Config";
+import image from "../../assets/logo-new.svg";
const ClientOnlyCounter = clientOnly(() => import("./Counter"));
const ClientOnlyCounterSlow = clientOnly(async () => {
@@ -8,9 +12,10 @@ const ClientOnlyCounterSlow = clientOnly(async () => {
return import("./Counter");
});
-export default function Page() {
+function Page() {
return (
<>
+
My Vike + Solid App
This page is:
diff --git a/examples/full/pages/star-wars/index/+data.ts b/examples/full/pages/star-wars/index/+data.ts
index c3bbeb2..4ab76e0 100644
--- a/examples/full/pages/star-wars/index/+data.ts
+++ b/examples/full/pages/star-wars/index/+data.ts
@@ -3,14 +3,27 @@ export { data };
export type Data = Awaited>;
import fetch from "node-fetch";
+import { useConfig } from "vike-solid/useConfig";
import type { Movie, MovieDetails } from "../types.js";
+import image from "../../../assets/logo-new.svg";
const data = async () => {
+ const config = useConfig();
+
const response = await fetch("https://brillout.github.io/star-wars/api/films.json");
const moviesData = (await response.json()) as MovieDetails[];
+
+ const n = moviesData.length;
+ config({
+ title: `${n} Star Wars Movies`, //
+ description: `All the ${n} movies from the Star Wars franchise`, //
+ image, //
+ });
+
// We remove data we don't need because the data is passed to the client; we should
// minimize what is sent over the network.
const movies = minimize(moviesData);
+
return movies;
};
diff --git a/examples/full/pages/star-wars/index/+description.ts b/examples/full/pages/star-wars/index/+description.ts
deleted file mode 100644
index e2be061..0000000
--- a/examples/full/pages/star-wars/index/+description.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export { description };
-
-import type { Data } from "./+data.js";
-import type { PageContext } from "vike/types";
-
-function description(pageContext: PageContext) {
- const movies = pageContext.data;
- return `All the ${movies.length} movies from the Star Wars franchise`;
-}
diff --git a/examples/full/pages/star-wars/index/+title.ts b/examples/full/pages/star-wars/index/+title.ts
deleted file mode 100644
index f410050..0000000
--- a/examples/full/pages/star-wars/index/+title.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export { title };
-
-import type { Data } from "./+data.js";
-import type { PageContext } from "vike/types";
-
-function title(pageContext: PageContext) {
- const movies = pageContext.data;
- return `${movies.length} Star Wars Movies`;
-}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e05c241..aa2a18c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -69,7 +69,7 @@ importers:
dependencies:
vite-plugin-solid:
specifier: ^2.10.2
- version: 2.10.2(solid-js@1.8.18)(vite@5.4.0(@types/node@18.17.4))
+ version: 2.10.2(solid-js@1.8.18)(vite@5.4.0(@types/node@20.14.11))
devDependencies:
'@babel/core':
specifier: ^7.24.9
@@ -90,8 +90,8 @@ importers:
specifier: ^15.2.3
version: 15.2.3(rollup@4.19.0)
'@types/node':
- specifier: ^18.17.4
- version: 18.17.4
+ specifier: ^20.14.11
+ version: 20.14.11
babel-preset-solid:
specifier: ^1.8.18
version: 1.8.18(@babel/core@7.24.9)
@@ -115,10 +115,10 @@ importers:
version: 5.5.3
vike:
specifier: ^0.4.183
- version: 0.4.183(vite@5.4.0(@types/node@18.17.4))
+ version: 0.4.183(vite@5.4.0(@types/node@20.14.11))
vite:
specifier: ^5.4.0
- version: 5.4.0(@types/node@18.17.4)
+ version: 5.4.0(@types/node@20.14.11)
packages:
@@ -1390,9 +1390,6 @@ packages:
'@types/estree@1.0.5':
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
- '@types/node@18.17.4':
- resolution: {integrity: sha512-ATL4WLgr7/W40+Sp1WnNTSKbgVn6Pvhc/2RHAdt8fl6NsQyp4oPCi2eKcGOvA494bwf1K/W6nGgZ9TwDqvpjdw==}
-
'@types/node@20.14.11':
resolution: {integrity: sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==}
@@ -3706,12 +3703,9 @@ snapshots:
'@types/estree@1.0.5': {}
- '@types/node@18.17.4': {}
-
'@types/node@20.14.11':
dependencies:
undici-types: 5.26.5
- optional: true
'@types/normalize-package-data@2.4.4': {}
@@ -4686,8 +4680,7 @@ snapshots:
uglify-js@3.18.0:
optional: true
- undici-types@5.26.5:
- optional: true
+ undici-types@5.26.5: {}
unicode-canonical-property-names-ecmascript@2.0.0: {}
@@ -4713,23 +4706,6 @@ snapshots:
spdx-correct: 3.2.0
spdx-expression-parse: 3.0.1
- vike@0.4.183(vite@5.4.0(@types/node@18.17.4)):
- dependencies:
- '@brillout/import': 0.2.3
- '@brillout/json-serializer': 0.5.13
- '@brillout/picocolors': 1.0.14
- '@brillout/require-shim': 0.1.2
- '@brillout/vite-plugin-server-entry': 0.4.7
- acorn: 8.12.1
- cac: 6.7.14
- es-module-lexer: 1.5.4
- esbuild: 0.19.12
- fast-glob: 3.3.2
- semver: 7.6.3
- sirv: 2.0.4
- source-map-support: 0.5.21
- vite: 5.4.0(@types/node@18.17.4)
-
vike@0.4.183(vite@5.4.0(@types/node@20.14.11)):
dependencies:
'@brillout/import': 0.2.3
@@ -4747,7 +4723,7 @@ snapshots:
source-map-support: 0.5.21
vite: 5.4.0(@types/node@20.14.11)
- vite-plugin-solid@2.10.2(solid-js@1.8.18)(vite@5.4.0(@types/node@18.17.4)):
+ vite-plugin-solid@2.10.2(solid-js@1.8.18)(vite@5.4.0(@types/node@20.14.11)):
dependencies:
'@babel/core': 7.24.9
'@types/babel__core': 7.20.5
@@ -4755,20 +4731,11 @@ snapshots:
merge-anything: 5.1.7
solid-js: 1.8.18
solid-refresh: 0.6.3(solid-js@1.8.18)
- vite: 5.4.0(@types/node@18.17.4)
- vitefu: 0.2.5(vite@5.4.0(@types/node@18.17.4))
+ vite: 5.4.0(@types/node@20.14.11)
+ vitefu: 0.2.5(vite@5.4.0(@types/node@20.14.11))
transitivePeerDependencies:
- supports-color
- vite@5.4.0(@types/node@18.17.4):
- dependencies:
- esbuild: 0.21.5
- postcss: 8.4.41
- rollup: 4.19.0
- optionalDependencies:
- '@types/node': 18.17.4
- fsevents: 2.3.3
-
vite@5.4.0(@types/node@20.14.11):
dependencies:
esbuild: 0.21.5
@@ -4778,9 +4745,9 @@ snapshots:
'@types/node': 20.14.11
fsevents: 2.3.3
- vitefu@0.2.5(vite@5.4.0(@types/node@18.17.4)):
+ vitefu@0.2.5(vite@5.4.0(@types/node@20.14.11)):
optionalDependencies:
- vite: 5.4.0(@types/node@18.17.4)
+ vite: 5.4.0(@types/node@20.14.11)
web-streams-polyfill@3.2.1: {}
diff --git a/vike-solid/components/Config/Config-client.ts b/vike-solid/components/Config/Config-client.ts
new file mode 100644
index 0000000..edb4074
--- /dev/null
+++ b/vike-solid/components/Config/Config-client.ts
@@ -0,0 +1,17 @@
+export { Config };
+
+// Same as ./Config-server.ts but importing useConfig-client.js
+
+import { useConfig } from "../../hooks/useConfig/useConfig-client.js";
+import type { ConfigFromHook } from "../../types/Config.js";
+
+/**
+ * Set configurations inside React components.
+ *
+ * https://vike.dev/useConfig
+ */
+function Config(props: ConfigFromHook): null {
+ const config = useConfig();
+ config(props);
+ return null;
+}
diff --git a/vike-solid/components/Config/Config-server.ts b/vike-solid/components/Config/Config-server.ts
new file mode 100644
index 0000000..b2f331d
--- /dev/null
+++ b/vike-solid/components/Config/Config-server.ts
@@ -0,0 +1,17 @@
+export { Config };
+
+// Same as ./Config-client.ts but importing useConfig-server.js
+
+import { useConfig } from "../../hooks/useConfig/useConfig-server.js";
+import type { ConfigFromHook } from "../../types/Config.js";
+
+/**
+ * Set configurations inside React components.
+ *
+ * https://vike.dev/useConfig
+ */
+function Config(props: ConfigFromHook): null {
+ const config = useConfig();
+ config(props);
+ return null;
+}
diff --git a/vike-solid/components/Head/Head-client.ts b/vike-solid/components/Head/Head-client.ts
new file mode 100644
index 0000000..1e79b7d
--- /dev/null
+++ b/vike-solid/components/Head/Head-client.ts
@@ -0,0 +1,4 @@
+// https://vike.dev/Head#only-html
+export function Head(): null {
+ return null;
+}
diff --git a/vike-solid/components/Head/Head-server.ts b/vike-solid/components/Head/Head-server.ts
new file mode 100644
index 0000000..6713efe
--- /dev/null
+++ b/vike-solid/components/Head/Head-server.ts
@@ -0,0 +1,17 @@
+export { Head };
+
+import { useConfig } from "../../hooks/useConfig/useConfig-server.js";
+import type { JSX } from "solid-js/jsx-runtime";
+
+/**
+ * Add arbitrary `` tags.
+ *
+ * (The children are teleported to ``.)
+ *
+ * https://vike.dev/Head
+ */
+function Head({ children }: { children: JSX.Element }): null {
+ const config = useConfig();
+ config({ Head: children });
+ return null;
+}
diff --git a/vike-solid/package.json b/vike-solid/package.json
index 95d08fc..07c1c07 100644
--- a/vike-solid/package.json
+++ b/vike-solid/package.json
@@ -24,7 +24,7 @@
"@brillout/release-me": "^0.3.9",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-node-resolve": "^15.2.3",
- "@types/node": "^18.17.4",
+ "@types/node": "^20.14.11",
"babel-preset-solid": "^1.8.18",
"bumpp": "^9.4.1",
"rollup": "^4.19.0",
@@ -44,6 +44,14 @@
"browser": "./dist/hooks/useConfig/useConfig-client.js",
"default": "./dist/hooks/useConfig/useConfig-server.js"
},
+ "./Config": {
+ "browser": "./dist/components/Config/Config-client.js",
+ "default": "./dist/components/Config/Config-server.js"
+ },
+ "./Head": {
+ "browser": "./dist/components/Head/Head-client.js",
+ "default": "./dist/components/Head/Head-server.js"
+ },
"./clientOnly": "./dist/helpers/clientOnly.js",
"./renderer/onRenderHtml": "./dist/renderer/onRenderHtml.js",
"./renderer/onRenderClient": "./dist/renderer/onRenderClient.js",
@@ -71,6 +79,12 @@
"useConfig": [
"dist/hooks/useConfig/useConfig-server.d.ts"
],
+ "Config": [
+ "./dist/components/Config/Config-server.d.ts"
+ ],
+ "Head": [
+ "./dist/components/Head/Head-server.d.ts"
+ ],
"clientOnly": [
"dist/helpers/clientOnly.d.ts"
]
diff --git a/vike-solid/rollup.config.js b/vike-solid/rollup.config.js
index 0fe5c68..9c6ca68 100644
--- a/vike-solid/rollup.config.js
+++ b/vike-solid/rollup.config.js
@@ -11,6 +11,10 @@ export default [
"hooks/useData": "./hooks/useData.tsx",
"hooks/useConfig/useConfig-client": "./hooks/useConfig/useConfig-client.ts",
"hooks/useConfig/useConfig-server": "./hooks/useConfig/useConfig-server.ts",
+ "components/Config/Config-client": "./components/Config/Config-client.ts",
+ "components/Config/Config-server": "./components/Config/Config-server.ts",
+ "components/Head/Head-client": "./components/Head/Head-client.ts",
+ "components/Head/Head-server": "./components/Head/Head-server.ts",
"helpers/clientOnly": "./helpers/clientOnly.tsx",
},
ssr: true,
@@ -23,6 +27,10 @@ export default [
"hooks/useData": "./hooks/useData.tsx",
"hooks/useConfig/useConfig-client": "./hooks/useConfig/useConfig-client.ts",
"hooks/useConfig/useConfig-server": "./hooks/useConfig/useConfig-server.ts",
+ "components/Config/Config-client": "./components/Config/Config-client.ts",
+ "components/Config/Config-server": "./components/Config/Config-server.ts",
+ "components/Head/Head-client": "./components/Head/Head-client.ts",
+ "components/Head/Head-server": "./components/Head/Head-server.ts",
},
ssr: false,
external: ["vike/server", "vike/plugin", "vike/getPageContext"],
@@ -34,6 +42,10 @@ export default [
"./hooks/useData.tsx",
"./hooks/useConfig/useConfig-client.ts",
"./hooks/useConfig/useConfig-server.ts",
+ "./components/Config/Config-client.ts",
+ "./components/Config/Config-server.ts",
+ "./components/Head/Head-client.ts",
+ "./components/Head/Head-server.ts",
"./helpers/clientOnly.tsx",
"./vite-plugin-vike-solid.ts",
],