ryansolid / dom-expressions

A Fine-Grained Runtime for Performant DOM Rendering
MIT License
863 stars 125 forks source link

export client-side APIs from dom-expressions server.js, make them throw an error #345

Closed trusktr closed 5 days ago

trusktr commented 2 weeks ago

Fixes:

Are there any implications that I have overlooked? If no one is currently importing Aliases/etc on the server (because they currently can't) then adding the exports should be harmless.

This PR goes along with

trusktr commented 2 weeks ago

Still WIP, f.e. after this we get this downstream:

SyntaxError: The requested module 'solid-js/web' does not provide an export named 'classList'

I think for this we can do like:

export function classList(node, value, prev) {
  throw new Error("classList not supported on server side");
}

in server.js

For the type definition in server.d.ts we can use @deprecated to make it appear crossed out:

/** @deprecated not supported on the server side */
export function classList(
  node: Element,
  value: { [k: string]: boolean },
  prev?: { [k: string]: boolean }
): { [k: string]: boolean };

Then this way at least exports will be isomorphic and import will at least just work, and if anyone tries to use a client-only API on the server they'll get a runtime error. A runtime error in an API is a lot easier to handle than broken import and having to resort to await import() inside components which has a bag of worse problems.

I'll proceed with this, but let me know of there's any objection.

trusktr commented 2 weeks ago

Alright, I updated it so that all client-side functions are exported from server.js; but they throw if called on the server side.

I tested with this version of dom-expressions, by running import('solid-js/html').then(console.log) in the node repl to show that html can be successfully imported.

I also verified that the errors are thrown for client-only APIs with import('solid-js/web').then(m => m.effect(() => {})).

I did this in a separate folder with updated solid-js linked into node_modules. Output sample:

❯ node
Welcome to Node.js v20.6.1.
Type ".help" for more information.
> import('solid-js/html').then(console.log)
Promise {
  <pending>,
  [Symbol(async_id_symbol)]: 374,
  [Symbol(trigger_async_id_symbol)]: 366
}
> [Module: null prototype] { default: [Function: html] }
> import('solid-js/web').then(m => m.effect(() => {}))
Promise {
  <pending>,
  [Symbol(async_id_symbol)]: 711,
  [Symbol(trigger_async_id_symbol)]: 703
}
> Uncaught:
Error: Client-only API called on the server side. Run client-only code in onMount, or run client-only component with <Show>.
    at Module.notSup (file:///Users/trusktr/src/solidjs+solid/packages/solid/web/dist/server.js:654:9)

I think it is nice to be helpful with the error message, but an alternative is to no-op (and let some other error happen instead).

trusktr commented 1 week ago

Converted back to draft, doing some testing to ensure that the build is working dependong on Solid Start ssr mode or not...

trusktr commented 1 week ago

Ok I think this is finally ready again.

ryansolid commented 1 week ago

Yeah just eyeballing this looks pretty good.