Files
buuh/packages/core/lib/lazy.js
T

54 lines
1.7 KiB
JavaScript
Raw Normal View History

// Lazy (async) routes — the v8 answer to choojs/choo#653.
//
// A lazy route handler loads its view on first match via a native dynamic
// import (or any promise-returning loader) and caches it forever after.
// In the browser, while the view is loading, the handler renders the
// optional loading view — or the PENDING sentinel, which tells choo to
// keep whatever is already on screen. When the module arrives the wrapper
// emits a render and the real view morphs in. On the server, toStream()
// awaits load() before rendering; toString() refuses lazy routes loudly
// (the missing server story is what stalled #653 — here both halves ship
// together).
import { equal } from './assert.js'
// Sentinel: "keep the current tree, a view is on its way"
export const PENDING = { __chooPending: true }
export default function lazy (loader, loading) {
equal(typeof loader, 'function', 'choo.lazy: loader should be type function')
let view = null
let promise = null
function load () {
if (!promise) {
promise = Promise.resolve(loader()).then(function (mod) {
view = (mod && mod.default) || mod
equal(typeof view, 'function', 'choo.lazy: loader should resolve to a view function (module default export)')
return view
})
}
return promise
}
function lazyView (state, emit) {
if (view) return view(state, emit)
load().then(function () {
emit(state.events.RENDER)
}, function (err) {
// surface load failures instead of hanging on the loading view
emit('error', err)
throw err
})
if (loading) return loading(state, emit)
return PENDING
}
lazyView.__lazy = true
lazyView.load = load
return lazyView
}