From b055f4e422702eaa11c52c5fcf2bd177f9c90d56 Mon Sep 17 00:00:00 2001 From: Brecht Savelkoul Date: Fri, 24 Nov 2017 20:30:16 +0100 Subject: [PATCH 1/2] Async routes experiment --- index.js | 35 +++++++++++++++++++++++------------ test.js | 15 +++++++++++++++ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index 767bcc5f..0602f96b 100644 --- a/index.js +++ b/index.js @@ -143,20 +143,19 @@ Choo.prototype.start = function () { this.emitter.prependListener(self._events.RENDER, nanoraf(function () { var renderTiming = nanotiming('choo.render') - self.state.href = self._createLocation() - var newTree = self.router(self.state.href) - assert.ok(newTree, 'choo.render: no valid DOM node returned for location ' + self.state.href) - - assert.equal(self._tree.nodeName, newTree.nodeName, 'choo.render: The target node <' + - self._tree.nodeName.toLowerCase() + '> is not the same type as the new node <' + - newTree.nodeName.toLowerCase() + '>.') - - var morphTiming = nanotiming('choo.morph') - nanomorph(self._tree, newTree) - morphTiming() - renderTiming() + var res = self.router(self.state.href) + if (typeof res === 'string' || (self._hasWindow && res instanceof window.HTMLElement)) { + self.morph(res) + renderTiming() + } else if (res && typeof res.then === 'function') { + res.then(function (tree) { + assert.ok(typeof res === 'string' || (self._hasWindow && res instanceof window.HTMLElement)) + self.morph(tree) + renderTiming() + }) + } })) documentReady(function () { @@ -167,6 +166,18 @@ Choo.prototype.start = function () { return this._tree } +Choo.prototype.morph = function (newTree) { + assert.ok(newTree, 'choo.render: no valid DOM node returned for location ' + this.state.href) + + assert.equal(this._tree.nodeName, newTree.nodeName, 'choo.render: The target node <' + + this._tree.nodeName.toLowerCase() + '> is not the same type as the new node <' + + newTree.nodeName.toLowerCase() + '>.') + + var morphTiming = nanotiming('choo.morph') + nanomorph(this._tree, newTree) + morphTiming() +} + Choo.prototype.mount = function mount (selector) { assert.equal(typeof window, 'object', 'choo.mount: window was not found. .mount() must be called in a browser, use .toString() if running in Node') assert.ok(typeof selector === 'string' || typeof selector === 'object', 'choo.mount: selector should be type String or HTMLElement') diff --git a/test.js b/test.js index e3b85f33..b231dc65 100644 --- a/test.js +++ b/test.js @@ -17,3 +17,18 @@ tape('should render on the server', function (t) { t.equal(res.toString().trim(), exp, 'result was OK') t.end() }) + +tape('should render async route', function (t) { + var app = choo() + app.route('/', function (state, emit) { + return new Promise(function (resolve) { + var strong = 'Hello filthy planet' + resolve(html`

${raw(strong)}

`) + }) + }) + app.toString('/').then(function (res) { + var exp = '

Hello filthy planet

' + t.equal(res.trim(), exp, 'result was OK') + t.end() + }) +}) From 36519799c279fbf3aa80d00903516ed79a2def27 Mon Sep 17 00:00:00 2001 From: Brecht Savelkoul Date: Wed, 29 Nov 2017 19:46:12 +0100 Subject: [PATCH 2/2] Throw error when trying to `toString` async route --- index.js | 11 ++++++----- test.js | 24 +++++++++++++----------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/index.js b/index.js index 0602f96b..fd2a7155 100644 --- a/index.js +++ b/index.js @@ -146,12 +146,12 @@ Choo.prototype.start = function () { self.state.href = self._createLocation() var res = self.router(self.state.href) - if (typeof res === 'string' || (self._hasWindow && res instanceof window.HTMLElement)) { + if (self._hasWindow && res instanceof window.HTMLElement) { self.morph(res) renderTiming() } else if (res && typeof res.then === 'function') { res.then(function (tree) { - assert.ok(typeof res === 'string' || (self._hasWindow && res instanceof window.HTMLElement)) + assert.ok(self._hasWindow && res instanceof window.HTMLElement) self.morph(tree) renderTiming() }) @@ -215,7 +215,8 @@ Choo.prototype.toString = function (location, state) { this.state.href = location.replace(/\?.+$/, '') this.state.query = nanoquery(location) - var html = this.router(location) - assert.ok(html, 'choo.toString: no valid value returned for the route ' + location) - return html.toString() + var res = this.router(location) + assert.ok(res, 'choo.toString: no valid value returned for the route ' + location) + assert.ok(typeof res.then !== 'function', 'choo.toString: not possible to stringify async route' + location) + return res.toString() } diff --git a/test.js b/test.js index b231dc65..27c9b930 100644 --- a/test.js +++ b/test.js @@ -18,17 +18,19 @@ tape('should render on the server', function (t) { t.end() }) -tape('should render async route', function (t) { +tape('async route should throw on server', function (t) { var app = choo() - app.route('/', function (state, emit) { - return new Promise(function (resolve) { - var strong = 'Hello filthy planet' - resolve(html`

${raw(strong)}

`) - }) - }) - app.toString('/').then(function (res) { - var exp = '

Hello filthy planet

' - t.equal(res.trim(), exp, 'result was OK') - t.end() + app.route('/', async function (state, emit) { + var strong = 'Hello filthy planet' + return html` +

${raw(strong)}

+ ` }) + try { + var res = app.toString('/') + t.notOk(res, 'this should not be executed') + } catch (e) { + t.ok(e, 'throws expected error') + } + t.end() })