ryansolid / dom-expressions

A Fine-Grained Runtime for Performant DOM Rendering
MIT License
887 stars 127 forks source link

SSR Runtime for `hyper` and `lit` #148

Open lxsmnsyc opened 2 years ago

lxsmnsyc commented 2 years ago

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

dragonwocky commented 2 years ago

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:

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/solid-js@1.4.7/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/solid-js@1.4.7/deno/html.js:2:3734)
    at v (https://esm.sh/v86/solid-js@1.4.7/deno/html.js:17:41)
    at App (file:///home/dragonwocky/local/tmp/deno_solid/main.tsx:21:14)
    at https://esm.sh/v86/solid-js@1.4.7/deno/solid-js.js:2:13610
    at O (https://esm.sh/v86/solid-js@1.4.7/deno/solid-js.js:2:4892)
    at Object.De [as createComponent] (https://esm.sh/v86/solid-js@1.4.7/deno/solid-js.js:2:13604)
    at m (https://esm.sh/v86/solid-js@1.4.7/deno/h.js:2:1490)
    at d (https://esm.sh/v86/solid-js@1.4.7/deno/h.js:2:290)
    at P (https://esm.sh/v86/solid-js@1.4.7/deno/web/dist/server.js:2:9446)
    at pe (https://esm.sh/v86/solid-js@1.4.7/deno/web/dist/server.js:2:5075)
ryansolid commented 2 years ago

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.