choojs / choo

:steam_locomotive::train: - sturdy 4kb frontend framework

Home Page:https://choo.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

add SSR hyperscript support

ungoldman opened this issue · comments

Hello!

There's been some discussion on IRC about figuring out ways to support hyperscript as a bel alternative when using choo.toString in a server context.

Problem

In a non-browser context, objects returned by hyperscript have a toString method that differs from pelo's toString method. So currently hyperscript doesn't work in this context.

Proposal

After doing some investigating, it seems like the easiest way to support hyperscript on the server in choo is to check for outerHTML and return that before trying to use the toString method returned by pelo.

Context, Research

Element.outerHTML is already a standard feature of the DOM spec that returns the stringified HTML fragment of an element. There's also already a toString() method on there that I believe it inherits from Object.toString, which is the thing that usually returns a very unhelpful string like [object Object].

While my first impulse was to see how hard it would be to modify hyperscript to present choo with a toString method that does the same thing as pelo().toString(), it turns out the object hyperscript's factory function returns in a non-browser context is an instance of https://github.com/1N50MN14/html-element, which is already attempting to comply to the standard Element spec mentioned before.

Proposed solution

With all that info in mind, I think it's easiest to modify one line in choo to check for outerHTML first.

Here's the context:

choo/index.js

Lines 188 to 199 in 6bee708

Choo.prototype.toString = function (location, state) {
this.state = xtend(this.state, state || {})
assert.notEqual(typeof window, 'object', 'choo.mount: window was found. .toString() must be called in Node, use .start() or .mount() if running in the browser')
assert.equal(typeof location, 'string', 'choo.toString: location should be type string')
assert.equal(typeof this.state, 'object', 'choo.toString: state should be type object')
this._matchRoute(location)
var html = this._prerender(this.state)
assert.ok(html, 'choo.toString: no valid value returned for the route ' + location)
return html.toString()
}

And I think replacing return html.toString() with something like this would do the trick:

return html.outerHTML || html.toString()

I don't think there's a chance outerHTML would be falsey, but we could also do something more defensive like this:

return typeof html.outerHTML === 'string' ? html.outerHTML : html.toString()

If this sounds alright to choo maintainers I'll open a PR.

This is a dog

commented

Sounds 💯