Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

uhtml/ssr - Added both easy SSR and worker.js target #114

Merged
merged 1 commit into from
Apr 13, 2024
Merged

Conversation

WebReflection
Copy link
Owner

This MR brings forward the hydro branch #113 as I got stuck but also I am not sure anymore I was doing the right thing ... but as I don't want to get stuck, it made sense to at least provide an uhtml/ssr export and fix some gotcha with the persistent fragment.

This MR also fixes #111 by removing TypeScirpt as a whole and provide just JSDoc TS for the provided files. I hope this won't break anyone out there but as types were buggy this can land as minor as it doesn't break anything, it adds a new uhtml/ssr export, and it should improve TS / types in general.

The hydro branch is on hold until I have both head and time to get that right.

@WebReflection WebReflection mentioned this pull request Apr 13, 2024
@WebReflection WebReflection merged commit 4b5d821 into main Apr 13, 2024
4 checks passed
@iacore
Copy link

iacore commented Apr 13, 2024

Thanks! It works well! Maybe you will be interested in the final product.

@iacore
Copy link

iacore commented Apr 14, 2024

I used uhtml/ssr a bit more, and... my code ended up like this:

/**
 * @function
 * @param {import('uhtml/keyed')} */
module.exports = ({ Hole, attr, html, htmlFor, render, svg, svgFor, document }) => ({
  root() {
    const body = html`
      <p>Hi!</p>
    `
    return html`<head><link rel="stylesheet" href="/all.css" /></head><body>${body}</body>`
  }
})

given how document is only available at run-time, this API is not .. great. Also, the return type of initSsr is inferred as any in vscode; not sure why.

Is it possible for a html`` template to exist without document?

@WebReflection
Copy link
Owner Author

@iacore I am not sure I am following ... see the test example: https://github.com/WebReflection/uhtml/blob/main/test/ssr.mjs

you can have one or more documents (one per page to render if you want) and I can't provide a global document as that's hostile.

I have removed types and used the suggested way to export things in there ... I don't know why TS doesn't understand that but I wonder if you are on latest TS.

As follow up questions:

  • what/how do you want to use the document if shared? it leads to weird concurrent cases if all pages share the same document
  • is the issue that you can't have globally available html out of a module? if that's the case you can always create your own .cjs file and do something like:
module.exports = typeof process === 'object' ?
  require('uhtml/ssr')('...') :
  { ...require('uhtml/keyed'), document };

This uhtml.cjs would give you same exports if you are either on Node/Bun or Browser.

Make it an .mjs or a .js with type: "module" if you're after ESM.

Would this improve the API?

@iacore
Copy link

iacore commented Apr 15, 2024

As follow up questions:

* what/how do you want to use the `document` if shared? it leads to weird concurrent cases if all pages share the same document

I don't want to use document if shared, although node.js is single-threaded so this would not be a problem if there is only one render call.

* is the issue that you can't have globally available `html` out of a module?

This is the issue. uhtml does not allow components to be defined with a generic html function; it has to be bound to a document.

In preact, the components are defined with JSX/hyperscript, so this is not a problem.

So my question is, can Hole live without document? Is this limitation of uhtml's design?


For the types, I will fix it.

@WebReflection
Copy link
Owner Author

you need to import html and render regardless of the SSR story, that's how modules work in JS and uhtml is just a module so I keep not being sure I follow the question.

The Hole is just a class and represents entries expected as holes within the render logic. Neither the render nor the uhtml or other exports are global but where do you have an issue with it? JSX requires transpilation (even in Bun) and it works with pragma functions that default to React namespace which is also global ... none of this is part of uhtml as the whole point is to use standards (ESM, template literal tags, no tools needed) but your files need to import those utilities and I've suggested a way to do so, out of other ways, if sharable components is what you are after.

@iacore
Copy link

iacore commented Apr 15, 2024

Here is a more concrete example.

Here's a file, abc.js.

import { html, render } from 'uhtml'

html`<h1>a</h1>`

If I run it with node, it doesn't work.

❯ node abc.js
file:///home/user/computing/lib/js/uhtml/esm/persistent-fragment.js:30
export class PersistentFragment extends custom(DocumentFragment) {
                                               ^

ReferenceError: DocumentFragment is not defined
    at file:///home/user/computing/lib/js/uhtml/esm/persistent-fragment.js:30:48
    at ModuleJob.run (node:internal/modules/esm/module_job:222:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:323:24)
    at async loadESM (node:internal/process/esm_loader:28:7)
    at async handleMainPromise (node:internal/modules/run_main:120:12)

Node.js v21.7.2

Can this limitation be overcome?

IMO, the definition of a component (even as simple as html`<h1>a</h1>`) should not depend on the DOM environment to exist. i.e. DOM would be needed only when the component is rendered, not when it is defined.

I think I will read the source code of uhtml to understand how it works.

@WebReflection
Copy link
Owner Author

WebReflection commented Apr 15, 2024

Can this limitation be overcome?

yes, that's why you want either uhtml/init or uhtml/ssr ... if you want that to work in both client or server you need to create your own file as explained in here #114 (comment) as I can't provide uhtml without a Document on NodeJS side and you don't want a shared document for multiple pages purpose unless you use a template for the <head> part and you only render different bodies, that works too, still you gotta provide a document.

If you are OK in polluting the global context then in node only:

import initSSR from 'uhtml/ssr';

const { document } = initSSR('...');
Object.assign(globalThis, {
  document,
  DocumentFragment: document.createDocumentFragment().constructor
});

Then you should be able to use that file and eventually serve document.toString() after render(document.body, () => htmlyour thing)

edit P.S. that entry point should run before the rest of your node app runs or use .cjs instead so that the resolution of each module is incremental.

@WebReflection
Copy link
Owner Author

@iacore another alternative is to use importMap so that on browser you get just that while on node you get your file that bootstrap the SSR and exports then everything it offers, after placing the document and the fragment to the global, then you should have a seamless exprience. I can't remember the current state of importMap in node though.

What I do in my tests is like this: https://github.com/WebReflection/uhtml/blob/main/test/modern.mjs#L9-L13

@iacore
Copy link

iacore commented Apr 15, 2024

What I do in my tests is like this: https://github.com/WebReflection/uhtml/blob/main/test/modern.mjs#L9-L13

Thanks! This is exactly what I'm looking for!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Type definitions are buggy
2 participants