From a09c33d6a5cbab383973ec0abb2c9cab5af93d67 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 20 Jan 2018 19:49:41 -0500 Subject: [PATCH 1/2] return a promise from init - fixes #99 --- src/runtime/index.ts | 68 ++++++++++++++++++++------------------ src/runtime/interfaces.ts | 6 ++++ test/app/templates/main.js | 3 +- test/common/test.js | 10 ++---- 4 files changed, 46 insertions(+), 41 deletions(-) diff --git a/src/runtime/index.ts b/src/runtime/index.ts index c30a89d8c..ab0ba4a7f 100644 --- a/src/runtime/index.ts +++ b/src/runtime/index.ts @@ -1,5 +1,5 @@ import { detach, findAnchor, scroll_state, which } from './utils'; -import { Component, ComponentConstructor, Params, Query, Route, RouteData, ScrollPosition } from './interfaces'; +import { Component, ComponentConstructor, Params, Query, Route, RouteData, ScrollPosition, Target } from './interfaces'; export let component: Component; let target: Node; @@ -19,7 +19,7 @@ if ('scrollRestoration' in history) { history.scrollRestoration = 'manual'; } -function select_route(url: URL): { route: Route, data: RouteData } { +function select_route(url: URL): Target { if (url.origin !== window.location.origin) return null; for (const route of routes) { @@ -30,7 +30,7 @@ function select_route(url: URL): { route: Route, data: RouteData } { const query: Record = {}; for (const [key, value] of url.searchParams) query[key] = value || true; - return { route, data: { params, query } }; + return { url, route, data: { params, query } }; } } } @@ -83,35 +83,31 @@ function prepare_route(Component: ComponentConstructor, data: RouteData) { }); } -function navigate(url: URL, id: number) { - const selected = select_route(url); - if (selected) { - if (id) { - // popstate or initial navigation - cid = id; - } else { - // clicked on a link. preserve scroll state - scroll_history[cid] = scroll_state(); - - id = cid = ++uid; - scroll_history[cid] = { x: 0, y: 0 }; - } +function navigate(target: Target, id: number) { + if (id) { + // popstate or initial navigation + cid = id; + } else { + // clicked on a link. preserve scroll state + scroll_history[cid] = scroll_state(); + + id = cid = ++uid; + scroll_history[cid] = { x: 0, y: 0 }; + } - const loaded = prefetching && prefetching.href === url.href ? - prefetching.promise : - selected.route.load().then(mod => prepare_route(mod.default, selected.data)); + cid = id; - prefetching = null; + const loaded = prefetching && prefetching.href === target.url.href ? + prefetching.promise : + target.route.load().then(mod => prepare_route(mod.default, target.data)); - const token = current_token = {}; + prefetching = null; - loaded.then(({ Component, data }) => { - render(Component, data, scroll_history[id], token); - }); + const token = current_token = {}; - cid = id; - return true; - } + return loaded.then(({ Component, data }) => { + render(Component, data, scroll_history[id], token); + }); } function handle_click(event: MouseEvent) { @@ -147,7 +143,9 @@ function handle_click(event: MouseEvent) { // Don't handle hash changes if (url.pathname === window.location.pathname && url.search === window.location.search) return; - if (navigate(url, null)) { + const target = select_route(url); + if (target) { + navigate(target, null); event.preventDefault(); history.pushState({ id: cid }, '', url.href); } @@ -157,7 +155,9 @@ function handle_popstate(event: PopStateEvent) { scroll_history[cid] = scroll_state(); if (event.state) { - navigate(new URL(window.location.href), event.state.id); + const url = new URL(window.location.href); + const target = select_route(url); + navigate(target, event.state.id); } else { // hashchange cid = ++uid; @@ -205,7 +205,7 @@ export function init(_target: Node, _routes: Route[]) { inited = true; } - setTimeout(() => { + return Promise.resolve().then(() => { const { hash, href } = window.location; const deep_linked = hash && document.querySelector(hash); @@ -214,12 +214,16 @@ export function init(_target: Node, _routes: Route[]) { scroll_state(); history.replaceState({ id: uid }, '', href); - navigate(new URL(window.location.href), uid); + + const target = select_route(new URL(window.location.href)); + return navigate(target, uid); }); } export function goto(href: string, opts = { replaceState: false }) { - if (navigate(new URL(href, window.location.href), null)) { + const target = select_route(new URL(href, window.location.href)); + if (target) { + navigate(target, null); if (history) history[opts.replaceState ? 'replaceState' : 'pushState']({ id: cid }, '', href); } else { window.location.href = href; diff --git a/src/runtime/interfaces.ts b/src/runtime/interfaces.ts index 02d6b707b..30d90252c 100644 --- a/src/runtime/interfaces.ts +++ b/src/runtime/interfaces.ts @@ -20,4 +20,10 @@ export type Route = { export type ScrollPosition = { x: number; y: number; +}; + +export type Target = { + url: URL; + route: Route; + data: RouteData; }; \ No newline at end of file diff --git a/test/app/templates/main.js b/test/app/templates/main.js index 6bfcae99c..f6cd6ed30 100644 --- a/test/app/templates/main.js +++ b/test/app/templates/main.js @@ -1,6 +1,5 @@ import { init } from '../../../runtime.js'; window.init = () => { - init(document.querySelector('#sapper'), __routes__); - window.READY = true; + return init(document.querySelector('#sapper'), __routes__); }; \ No newline at end of file diff --git a/test/common/test.js b/test/common/test.js index cd56f9a1f..ede60e62f 100644 --- a/test/common/test.js +++ b/test/common/test.js @@ -183,7 +183,7 @@ function run(env) { }); it('navigates to a new page without reloading', () => { - return nightmare.goto(base).init().wait(200) + return nightmare.goto(base).init().wait(100) .then(() => { return capture(() => nightmare.click('a[href="/about"]')); }) @@ -204,7 +204,6 @@ function run(env) { return nightmare .goto(`${base}/about`) .init() - .wait(100) .click('.goto') .wait(() => window.location.pathname === '/blog/what-is-sapper') .wait(100) @@ -218,7 +217,6 @@ function run(env) { return nightmare .goto(`${base}/about`) .init() - .wait(100) .then(() => { return capture(() => { return nightmare @@ -235,7 +233,6 @@ function run(env) { return nightmare .goto(`${base}/blog/a-very-long-post#four`) .init() - .wait(100) .evaluate(() => window.scrollY) .then(scrollY => { assert.ok(scrollY > 0, scrollY); @@ -246,7 +243,6 @@ function run(env) { return nightmare .goto(`${base}/blog`) .init() - .wait(200) .then(() => { return capture(() => { return nightmare @@ -310,7 +306,7 @@ function run(env) { it('calls a delete handler', () => { return nightmare .goto(`${base}/delete-test`) - .init().wait(100) + .init() .click('.del') .wait(() => window.deleted) .evaluate(() => window.deleted.id) @@ -325,7 +321,7 @@ function run(env) { .evaluate(() => { window.el = document.querySelector('.hydrate-test'); }) - .init().wait(100) + .init() .evaluate(() => { return document.querySelector('.hydrate-test') === window.el; }) From 72ae4a1c64b27b2fc1dacf6115e501604e4c5712 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 20 Jan 2018 19:55:21 -0500 Subject: [PATCH 2/2] ugh still need to wait for requests to complete --- test/common/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/common/test.js b/test/common/test.js index ede60e62f..cd5652f1e 100644 --- a/test/common/test.js +++ b/test/common/test.js @@ -242,7 +242,7 @@ function run(env) { it('reuses prefetch promise', () => { return nightmare .goto(`${base}/blog`) - .init() + .init().wait(100) .then(() => { return capture(() => { return nightmare