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 { 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<string, string | 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) {
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 };
}
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);
});
function navigate(target: Target, id: number) {
if (id) {
// popstate or initial navigation
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) {
@@ -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;

View File

@@ -20,4 +20,10 @@ export type Route = {
export type ScrollPosition = {
x: number;
y: number;
};
export type Target = {
url: URL;
route: Route;
data: RouteData;
};

View File

@@ -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__);
};

View File

@@ -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;
})