preactjs / preset-vite

Preset for using Preact with the vite bundler
https://npm.im/@preact/preset-vite
MIT License
260 stars 26 forks source link

Signals breaks SSR #61

Closed tornqvist closed 2 years ago

tornqvist commented 2 years ago

Using signals break vite SSR throwing the error:

Error: Objects are not valid as a child. Encountered an object with the keys {0,1,2,3,4,5}.

  in div (at [...]/test.jsx:7)
  in Hello (at [...]/test.jsx:11)

    at [...]/node_modules/preact/debug/dist/debug.js:1:6901
    at Array.forEach (<anonymous>)
    at n.options.diffed ([...]/node_modules/preact/debug/dist/debug.js:1:6796)
    at o.options.diffed ([...]/node_modules/preact/hooks/dist/hooks.js:1:1497)
    at [...]/node_modules/@preact/signals/dist/signals.js:1:1575
    at j ([...]/node_modules/preact-render-to-string/dist/commonjs.js:1:7571)
    at j ([...]/node_modules/preact-render-to-string/dist/commonjs.js:1:6493)
    at Proxy.k ([...]/node_modules/preact-render-to-string/dist/commonjs.js:1:5029)
    at render (test.jsx:11:24)
    at Immediate.<anonymous> (file://[...]/test.vite.js:21:29)

There's only an error if a signal is not the only child of an element, e.g. <span>{mySignal}</span> works but <span>Hi {mySignal}</span> doesn't.

Minimal code to reproduce error, running on Node.js v18.9.0.

// server.js

import { on } from 'events'
import { Server } from 'http'
import { createServer as createViteServer } from 'vite'

const server = new Server()
const vite = await createViteServer({
  server: { middlewareMode: true },
  appType: 'custom'
})

server.listen(3000, async function () {
  const res = await fetch('http://localhost:3000')
  console.log(res.status, res.statusText, '\n', await res.text())
})

for await (const [req, res] of on(server, 'request')) {
  vite.middlewares(req, res, async function () {
    try {
      const { render } = await vite.ssrLoadModule('./test.jsx')
      const appHtml = await render()
      res.statusCode = 200
      res.setHeader('Content-Type', 'text/html')
      res.end(appHtml)
    } catch (err) {
      vite.ssrFixStacktrace(err)
      console.error(err)
      res.statusCode = 500
      res.end()
    }
  })
}
// test.jsx

import { default as renderToString } from 'preact-render-to-string'
import { signal } from '@preact/signals'

const name = signal('world')

function Hello() {
  return <div>Hello {name}!</div>
}

export function render() {
  return renderToString(<Hello />)
}

I also made sure that the preact-render-to-string package is not the culprit by running the following code through rollup:

// test.cjs

const render = require('preact-render-to-string')
const { signal } = require('@preact/signals')
const { h } = require('preact')

const name = signal('world')

function Hello() {
  return <div>Hello {name}!</div>
}

console.log(render(<Hello />))
npx rollup -p 'jsx={ factory: "h" }' test.cjs | node --
JoviDeCroock commented 2 years ago

You need to update preact-render-to-string to atleast 5.2.3

tornqvist commented 2 years ago

I'm using preact-render-to-string@5.2.4

tornqvist commented 2 years ago

This miraculously resolved itself so I'm closing the issue. Thanks!