// 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 }