Skip to content

Commit

Permalink
feat: add partial hydration support for Solid.js
Browse files Browse the repository at this point in the history
  • Loading branch information
uenoB committed Jul 22, 2024
1 parent f094286 commit dd562d8
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 56 deletions.
2 changes: 1 addition & 1 deletion example/multilib/index.html.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
9 changes: 9 additions & 0 deletions packages/render-solid/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/render-solid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"solid"
],
"peerDependencies": {
"solid-js": "^1",
"solid-js": "^1.6",
"vite-plugin-minissg": "^1 || ^3 || ^4"
}
}
89 changes: 44 additions & 45 deletions packages/render-solid/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => (
<>
<HydrationScript />
<${div} data-hydrate={${id}} data-hydrate-args={args}>
<Component {...props} />
</${div}>
</>
})
}`
}
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 => (
<Dynamic
component={${params[0] ?? 'div'}}
data-hydrate={${id}}
data-hydrate-args={JSON.stringify(props)}
>
<Hydration>
<Component {...props} />
</Hydration>
{${withoutScript < 0} && <HydrationScript />}
</Dynamic>
)`
},
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)
})`
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion template/solid/src/Root.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export default function Root(props) {
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href={favicon} />
<title>Vite + MiniSSG + Solid</title>
{props.head}
</head>
<body>{props.children}</body>
</html>
Expand Down
5 changes: 2 additions & 3 deletions template/solid/src/index.html.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<Root head={<HydrationScript />}>
<Root>
<App />
</Root>
)
Expand Down
5 changes: 1 addition & 4 deletions template/solid/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand Down

0 comments on commit dd562d8

Please sign in to comment.