dom-expressions icon indicating copy to clipboard operation
dom-expressions copied to clipboard

SSR Runtime for `hyper` and `lit`

Open lxsmnsyc opened this issue 1 year ago • 2 comments

As the title says. Currently hyper and lit only run on browser runtime, so if one were to use these on server-side, users aren't able to use SSR API from dom-expressions

lxsmnsyc avatar Jul 10 '22 12:07 lxsmnsyc

The particular use-case that brought this up was attempting to perform just-in-time server-side rendering in Deno with Solid.

Ideally, I'd like to use a proper dom-expressions transform. Unfortunately:

  • Deno's built-in JSX transform assumes HyperScript.
  • This would require some sort of pre-compilation via Babel, which either a) would not be JIT or b) would not work if running on e.g. Deno Deploy where there's no filesystem write access or eval().
  • dom-expressions expects a DOM.

My next best bet was to try using hyper-dom-expressions or lit-dom-expressions for server-side rendering, then serve Babel-transformed files to the client to do partial hydration, but renderToString and renderToStringAsync are both logging empty strings:

/** @jsxImportSource https://esm.sh/[email protected]/h */

import {
  Document,
  Element,
  Node,
} from "https://deno.land/x/deno_dom/deno-dom-wasm.ts";
import { renderToString } from "https://esm.sh/solid-js/web/dist/server";

// @ts-ignore
globalThis.document = new Document();
// @ts-ignore
globalThis.Element = Element;
// @ts-ignore
globalThis.SVGElement = Element;
// @ts-ignore
globalThis.Node = Node;

const App = () => {
  return <div>Hello world!</div>;
};

console.log(renderToString(() => <App />));

Given also that all three of dom-expressions's runtime renderers apparently require DOM interfaces, I am a little confused on how server-side rendering is meant to be possible. I'm assuming this is just because the server builds of hyper-dom-expressions or lit-dom-expressions are missing?

Edit: replacing hyper-dom-expressions with lit-dom-expressions in the example above doesn't work either. It throws an error before the renderToString() call is reached that I'm not entirely sure how to fix:

error: Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'querySelectorAll')
    at m (https://esm.sh/v86/[email protected]/deno/html.js:2:3734)
    at v (https://esm.sh/v86/[email protected]/deno/html.js:17:41)
    at App (file:///home/dragonwocky/local/tmp/deno_solid/main.tsx:21:14)
    at https://esm.sh/v86/[email protected]/deno/solid-js.js:2:13610
    at O (https://esm.sh/v86/[email protected]/deno/solid-js.js:2:4892)
    at Object.De [as createComponent] (https://esm.sh/v86/[email protected]/deno/solid-js.js:2:13604)
    at m (https://esm.sh/v86/[email protected]/deno/h.js:2:1490)
    at d (https://esm.sh/v86/[email protected]/deno/h.js:2:290)
    at P (https://esm.sh/v86/[email protected]/deno/web/dist/server.js:2:9446)
    at pe (https://esm.sh/v86/[email protected]/deno/web/dist/server.js:2:5075)

dragonwocky avatar Jul 10 '22 13:07 dragonwocky

SSR and even hydratable client both use different transforms than the standard client and leverage additional runtime behaviors. Hydration for a framework like Solid has been pretty tricky to figure out. I suspect the Tagged Template literals would be possible but not sure the HyperScript is.

I'm soon embarking into research to do more complex transformation for both the SSR and Hydration cases to support more performant approaches.

In any case good issue to track. But any runtime that cares about the combination of performance and ergonomics should consider supporting custom transforms or they will be leaving a ton on the table, both in server runtime perf and most importantly the ability for these solutions to do optimal hydration.

ryansolid avatar Jul 10 '22 15:07 ryansolid