From 0dd0d8d8ddba199e0ba924ee0221d06e6d7ff519 Mon Sep 17 00:00:00 2001 From: Katsuhiro Ueno Date: Mon, 22 Jul 2024 14:32:07 +0900 Subject: [PATCH] add partial hydration support for Solid.js BREAKING CHANGE: where to specify `?hydrate` is changed. --- example/multilib/index.html.js | 2 +- packages/render-solid/README.md | 9 +++ packages/render-solid/package.json | 2 +- packages/render-solid/src/index.ts | 89 +++++++++++++++--------------- pnpm-lock.yaml | 2 +- template/solid/src/Root.jsx | 1 - template/solid/src/index.html.jsx | 5 +- template/solid/vite.config.js | 5 +- 8 files changed, 59 insertions(+), 56 deletions(-) diff --git a/example/multilib/index.html.js b/example/multilib/index.html.js index 5c39162..c4d68e6 100644 --- a/example/multilib/index.html.js +++ b/example/multilib/index.html.js @@ -12,7 +12,7 @@ export const main = () => ({ main: () => import('../../template/react/src/browser.html.jsx?render') }, 'solid/index.html': { - main: () => import('../../template/solid/src/index.html.jsx?hydrate&render') + main: () => import('../../template/solid/src/index.html.jsx?render') }, 'solid/browser.html': { main: () => import('../../template/solid/src/browser.html.jsx?render') diff --git a/packages/render-solid/README.md b/packages/render-solid/README.md index cf6d0c6..4e2654b 100644 --- a/packages/render-solid/README.md +++ b/packages/render-solid/README.md @@ -4,6 +4,15 @@ A renderer plugin for combining [Minissg] with [Solid.js]. See [Minissg's README] for details. +## `hydrate` query arguments + +`?hydrate` query may have a `without-script` parameter. +To enable hydration, Solid.js requires to insert a hydration script into +a webpage. +@minissg/render-solid do so. +If you would like to avoid this insertion for some reason, +write `?hydrate=without-script` instead of `?hydrate`. + ## License MIT diff --git a/packages/render-solid/package.json b/packages/render-solid/package.json index 05f3558..44a5dc3 100644 --- a/packages/render-solid/package.json +++ b/packages/render-solid/package.json @@ -39,7 +39,7 @@ "solid" ], "peerDependencies": { - "solid-js": "^1", + "solid-js": "^1.6", "vite-plugin-minissg": "^1 || ^3 || ^4" } } diff --git a/packages/render-solid/src/index.ts b/packages/render-solid/src/index.ts index 937d8c8..3851aac 100644 --- a/packages/render-solid/src/index.ts +++ b/packages/render-solid/src/index.ts @@ -4,56 +4,55 @@ import { js } from '../../vite-plugin-minissg/src/util' const renderer: Renderer = { render: { server: () => js` - import { renderToStringAsync, createComponent } from 'solid-js/web' + import { createComponent } from 'solid-js' + import { renderToStringAsync, NoHydration } from 'solid-js/web' export default async function render(Component) { - return await renderToStringAsync(() => createComponent(Component, {})) + return await renderToStringAsync(() => { + return createComponent(NoHydration, { + get children() { + return createComponent(Component, {}) + } + }) + }) }` }, hydrate: { - // Note that solid does not support partial hydration. To enable - // hydration, the entire HTML document is made from a solid component. - // As an unstable experiment, if an element name is given as a parameter - // of ?hydrate query, it generates a serialized portion of HTML document. - server: ({ id, moduleId, parameter: div }) => { - if (div === '') { - return js` - export { default } from ${moduleId} - export * from ${moduleId}` - } else { - return js` - import { renderToString, HydrationScript } from 'solid-js/web' - import Component from ${moduleId} - export * from ${moduleId} - export default function(props) { - const args = JSON.stringify(props) - return renderToString(() => ( - <> - - <${div} data-hydrate={${id}} data-hydrate-args={args}> - - - - }) - }` - } + server: ({ id, moduleId, parameter }) => { + const params = parameter.split(',').filter(i => i !== '') + const withoutScript = params.findIndex(i => i === 'without-script') + if (withoutScript >= 0) params.splice(withoutScript, 1) + return js` + import { Dynamic, Hydration, HydrationScript } from 'solid-js/web' + import Component from ${moduleId} + export * from ${moduleId} + export default props => ( + + + + + {${withoutScript < 0} && } + + )` }, - client: ({ id, moduleId, parameter: div }) => { - if (div === '') { - return js` - import { createComponent, hydrate } from 'solid-js/web' - import Component from ${moduleId} - hydrate(() => createComponent(Component, {}), document)` - } else { - return js` - import { createComponent, hydrate } from 'solid-js/web' - import Component from ${moduleId} - const selector = ${`${div}[data-hydrate=${JSON.stringify(id)}]`} - const elem = document.querySelector(selector) - if (elem != null) { - const props = JSON.parse(elem.dataset.hydrateArgs) - hydrate(() => createComponent(Component, props), elem) - }` - } + client: ({ id, moduleId, parameter }) => { + const params = parameter.split(',').filter(i => i !== '') + const div = params[0] ?? 'div' + const selector = `${div}[data-hydrate=${JSON.stringify(id)}]` + return js` + import { createComponent, hydrate } from 'solid-js/web' + import Component from ${moduleId} + Array.from(document.querySelectorAll(${selector}), elem => { + const props = JSON.parse(elem.dataset.hydrateArgs) + // I'm not sure whether this is correct, but it works anyway + const hk = elem.querySelector('[data-hk]')?.dataset.hk + const renderId = hk?.replace(/0-0$/, '') + const options = renderId ? { renderId } : {} + hydrate(() => createComponent(Component, props), elem, options) + })` } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0ae9b3b..542a380 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -283,7 +283,7 @@ importers: packages/render-solid: dependencies: solid-js: - specifier: ^1 + specifier: ^1.6 version: 1.8.18 vite-plugin-minissg: specifier: workspace:^ diff --git a/template/solid/src/Root.jsx b/template/solid/src/Root.jsx index 334d463..27eff95 100644 --- a/template/solid/src/Root.jsx +++ b/template/solid/src/Root.jsx @@ -10,7 +10,6 @@ export default function Root(props) { Vite + MiniSSG + Solid - {props.head} {props.children} diff --git a/template/solid/src/index.html.jsx b/template/solid/src/index.html.jsx index 1a5eaf7..30f222e 100644 --- a/template/solid/src/index.html.jsx +++ b/template/solid/src/index.html.jsx @@ -1,10 +1,9 @@ -import { HydrationScript } from 'solid-js/web' import Root from './Root' -import App from './App' +import App from './App?hydrate' export default function IndexHtml() { return ( - }> + ) diff --git a/template/solid/vite.config.js b/template/solid/vite.config.js index 28b51c8..1542a87 100644 --- a/template/solid/vite.config.js +++ b/template/solid/vite.config.js @@ -6,10 +6,7 @@ import minissgSolid from '@minissg/render-solid' export default defineConfig({ build: { rollupOptions: { - input: [ - './src/index.html.jsx?hydrate&render', - './src/browser.html.jsx?render' - ] + input: ['./src/index.html.jsx?render', './src/browser.html.jsx?render'] } }, plugins: [