return a promise from init - fixes #99

This commit is contained in:
Rich Harris
2018-01-20 19:49:41 -05:00
parent 4590aa313c
commit a09c33d6a5
4 changed files with 49 additions and 44 deletions

View File

@@ -1,5 +1,5 @@
import { detach, findAnchor, scroll_state, which } from './utils'; 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; export let component: Component;
let target: Node; let target: Node;
@@ -19,7 +19,7 @@ if ('scrollRestoration' in history) {
history.scrollRestoration = 'manual'; 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; if (url.origin !== window.location.origin) return null;
for (const route of routes) { for (const route of routes) {
@@ -30,7 +30,7 @@ function select_route(url: URL): { route: Route, data: RouteData } {
const query: Record<string, string | true> = {}; const query: Record<string, string | true> = {};
for (const [key, value] of url.searchParams) query[key] = value || true; 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) { function navigate(target: Target, id: number) {
const selected = select_route(url); if (id) {
if (selected) { // popstate or initial navigation
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));
prefetching = null;
const token = current_token = {};
loaded.then(({ Component, data }) => {
render(Component, data, scroll_history[id], token);
});
cid = id; cid = id;
return true; } else {
// clicked on a link. preserve scroll state
scroll_history[cid] = scroll_state();
id = cid = ++uid;
scroll_history[cid] = { x: 0, y: 0 };
} }
cid = id;
const loaded = prefetching && prefetching.href === target.url.href ?
prefetching.promise :
target.route.load().then(mod => prepare_route(mod.default, target.data));
prefetching = null;
const token = current_token = {};
return loaded.then(({ Component, data }) => {
render(Component, data, scroll_history[id], token);
});
} }
function handle_click(event: MouseEvent) { function handle_click(event: MouseEvent) {
@@ -147,7 +143,9 @@ function handle_click(event: MouseEvent) {
// Don't handle hash changes // Don't handle hash changes
if (url.pathname === window.location.pathname && url.search === window.location.search) return; 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(); event.preventDefault();
history.pushState({ id: cid }, '', url.href); history.pushState({ id: cid }, '', url.href);
} }
@@ -157,7 +155,9 @@ function handle_popstate(event: PopStateEvent) {
scroll_history[cid] = scroll_state(); scroll_history[cid] = scroll_state();
if (event.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 { } else {
// hashchange // hashchange
cid = ++uid; cid = ++uid;
@@ -205,7 +205,7 @@ export function init(_target: Node, _routes: Route[]) {
inited = true; inited = true;
} }
setTimeout(() => { return Promise.resolve().then(() => {
const { hash, href } = window.location; const { hash, href } = window.location;
const deep_linked = hash && document.querySelector(hash); const deep_linked = hash && document.querySelector(hash);
@@ -214,12 +214,16 @@ export function init(_target: Node, _routes: Route[]) {
scroll_state(); scroll_state();
history.replaceState({ id: uid }, '', href); 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 }) { 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); if (history) history[opts.replaceState ? 'replaceState' : 'pushState']({ id: cid }, '', href);
} else { } else {
window.location.href = href; window.location.href = href;

View File

@@ -21,3 +21,9 @@ export type ScrollPosition = {
x: number; x: number;
y: number; y: number;
}; };
export type Target = {
url: URL;
route: Route;
data: RouteData;
};

View File

@@ -1,6 +1,5 @@
import { init } from '../../../runtime.js'; import { init } from '../../../runtime.js';
window.init = () => { window.init = () => {
init(document.querySelector('#sapper'), __routes__); return init(document.querySelector('#sapper'), __routes__);
window.READY = true;
}; };

View File

@@ -183,7 +183,7 @@ function run(env) {
}); });
it('navigates to a new page without reloading', () => { it('navigates to a new page without reloading', () => {
return nightmare.goto(base).init().wait(200) return nightmare.goto(base).init().wait(100)
.then(() => { .then(() => {
return capture(() => nightmare.click('a[href="/about"]')); return capture(() => nightmare.click('a[href="/about"]'));
}) })
@@ -204,7 +204,6 @@ function run(env) {
return nightmare return nightmare
.goto(`${base}/about`) .goto(`${base}/about`)
.init() .init()
.wait(100)
.click('.goto') .click('.goto')
.wait(() => window.location.pathname === '/blog/what-is-sapper') .wait(() => window.location.pathname === '/blog/what-is-sapper')
.wait(100) .wait(100)
@@ -218,7 +217,6 @@ function run(env) {
return nightmare return nightmare
.goto(`${base}/about`) .goto(`${base}/about`)
.init() .init()
.wait(100)
.then(() => { .then(() => {
return capture(() => { return capture(() => {
return nightmare return nightmare
@@ -235,7 +233,6 @@ function run(env) {
return nightmare return nightmare
.goto(`${base}/blog/a-very-long-post#four`) .goto(`${base}/blog/a-very-long-post#four`)
.init() .init()
.wait(100)
.evaluate(() => window.scrollY) .evaluate(() => window.scrollY)
.then(scrollY => { .then(scrollY => {
assert.ok(scrollY > 0, scrollY); assert.ok(scrollY > 0, scrollY);
@@ -246,7 +243,6 @@ function run(env) {
return nightmare return nightmare
.goto(`${base}/blog`) .goto(`${base}/blog`)
.init() .init()
.wait(200)
.then(() => { .then(() => {
return capture(() => { return capture(() => {
return nightmare return nightmare
@@ -310,7 +306,7 @@ function run(env) {
it('calls a delete handler', () => { it('calls a delete handler', () => {
return nightmare return nightmare
.goto(`${base}/delete-test`) .goto(`${base}/delete-test`)
.init().wait(100) .init()
.click('.del') .click('.del')
.wait(() => window.deleted) .wait(() => window.deleted)
.evaluate(() => window.deleted.id) .evaluate(() => window.deleted.id)
@@ -325,7 +321,7 @@ function run(env) {
.evaluate(() => { .evaluate(() => {
window.el = document.querySelector('.hydrate-test'); window.el = document.querySelector('.hydrate-test');
}) })
.init().wait(100) .init()
.evaluate(() => { .evaluate(() => {
return document.querySelector('.hydrate-test') === window.el; return document.querySelector('.hydrate-test') === window.el;
}) })