From 3ab6f9497d125bb9a2849c72cf5fd73eb95236a2 Mon Sep 17 00:00:00 2001 From: si3nloong Date: Thu, 25 Nov 2021 23:52:35 +0800 Subject: [PATCH 1/4] fix: router changes the URL prematurely --- packages/kit/src/runtime/client/router.js | 49 +++++++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/packages/kit/src/runtime/client/router.js b/packages/kit/src/runtime/client/router.js index 927bbe59f088..1ec9dbed2c2e 100644 --- a/packages/kit/src/runtime/client/router.js +++ b/packages/kit/src/runtime/client/router.js @@ -43,6 +43,8 @@ export class Router { this.trailing_slash = trailing_slash; /** Keeps tracks of multiple navigations caused by redirects during rendering */ this.navigating = 0; + /** @type {Array<{ run: () => Promise, abort: () => void }>} */ + this.navigation_stack = []; /** @type {import('./renderer').Renderer} */ this.renderer = renderer; @@ -158,12 +160,12 @@ export class Router { const i2 = location.href.indexOf('#'); const u1 = i1 >= 0 ? url_string.substring(0, i1) : url_string; const u2 = i2 >= 0 ? location.href.substring(0, i2) : location.href; - history.pushState({}, '', url.href); + // history.pushState({}, '', url.href); if (u1 === u2) { window.dispatchEvent(new HashChangeEvent('hashchange')); } - this._navigate(url, noscroll ? scroll_state() : null, false, [], url.hash); event.preventDefault(); + this._navigate(url, noscroll ? scroll_state() : null, false, [], url.hash); }); addEventListener('popstate', (event) => { @@ -263,6 +265,47 @@ export class Router { } this.navigating++; + const self = this + const cancelable_navigate = () => { + const ctrl = new AbortController(); + const { signal } = ctrl; + + return { + /** + * @param {URL} url + * @returns {Promise} + */ + run: () => new Promise((resolve, reject) => { + self.renderer.handle_navigation(info, chain, false, { hash, scroll, keepfocus }).then(resolve); + signal.addEventListener('abort', () => { + reject(new DOMException("Promise aborted!")); + }); + }), + abort() { + if (!signal.aborted) { + ctrl.abort(); + } + }, + } + } + + // the reason why we have a navigation_stack is + // to cancel the previous navigate action if the user spam the click event + const lastest_cb = this.navigation_stack.shift(); + if (lastest_cb) lastest_cb.abort(); + + const cb = cancelable_navigate(); + this.navigation_stack.push(cb); + + try { + await cb.run(); + } catch (e) { + // if the action aborted, cancel the navigation + return; + } + + history.pushState({}, '', url.href); + // remove trailing slashes if (info.path !== '/') { const has_trailing_slash = info.path.endsWith('/'); @@ -279,8 +322,6 @@ export class Router { } } - await this.renderer.handle_navigation(info, chain, false, { hash, scroll, keepfocus }); - this.navigating--; if (!this.navigating) { dispatchEvent(new CustomEvent('sveltekit:navigation-end')); From 11f9103bfad6b0f78dd7d38bd948d8922c9422e1 Mon Sep 17 00:00:00 2001 From: si3nloong Date: Fri, 26 Nov 2021 00:00:45 +0800 Subject: [PATCH 2/4] fix: lint --- packages/kit/src/runtime/client/router.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/kit/src/runtime/client/router.js b/packages/kit/src/runtime/client/router.js index 1ec9dbed2c2e..9d0ee62855b6 100644 --- a/packages/kit/src/runtime/client/router.js +++ b/packages/kit/src/runtime/client/router.js @@ -265,7 +265,7 @@ export class Router { } this.navigating++; - const self = this + const self = this; const cancelable_navigate = () => { const ctrl = new AbortController(); const { signal } = ctrl; @@ -278,15 +278,15 @@ export class Router { run: () => new Promise((resolve, reject) => { self.renderer.handle_navigation(info, chain, false, { hash, scroll, keepfocus }).then(resolve); signal.addEventListener('abort', () => { - reject(new DOMException("Promise aborted!")); + reject(new DOMException('Promise aborted!')); }); }), abort() { if (!signal.aborted) { ctrl.abort(); } - }, - } + } + }; } // the reason why we have a navigation_stack is From 11f25bd35ece2d15dbf6c37c619268e117529aaa Mon Sep 17 00:00:00 2001 From: si3nloong Date: Fri, 26 Nov 2021 00:02:32 +0800 Subject: [PATCH 3/4] fix: missing semicolon --- packages/kit/src/runtime/client/router.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/runtime/client/router.js b/packages/kit/src/runtime/client/router.js index 9d0ee62855b6..22b55726be47 100644 --- a/packages/kit/src/runtime/client/router.js +++ b/packages/kit/src/runtime/client/router.js @@ -287,7 +287,7 @@ export class Router { } } }; - } + }; // the reason why we have a navigation_stack is // to cancel the previous navigate action if the user spam the click event From 7685ab0c152db86fe16a7d2036fe384d5461c463 Mon Sep 17 00:00:00 2001 From: si3nloong Date: Fri, 26 Nov 2021 00:12:12 +0800 Subject: [PATCH 4/4] chore: rename stack to queue --- packages/kit/src/runtime/client/router.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/kit/src/runtime/client/router.js b/packages/kit/src/runtime/client/router.js index 22b55726be47..ea1dbd55f855 100644 --- a/packages/kit/src/runtime/client/router.js +++ b/packages/kit/src/runtime/client/router.js @@ -44,7 +44,7 @@ export class Router { /** Keeps tracks of multiple navigations caused by redirects during rendering */ this.navigating = 0; /** @type {Array<{ run: () => Promise, abort: () => void }>} */ - this.navigation_stack = []; + this.navigation_queue = []; /** @type {import('./renderer').Renderer} */ this.renderer = renderer; @@ -289,13 +289,13 @@ export class Router { }; }; - // the reason why we have a navigation_stack is + // the reason why we have a navigation_queue is // to cancel the previous navigate action if the user spam the click event - const lastest_cb = this.navigation_stack.shift(); + const lastest_cb = this.navigation_queue.shift(); if (lastest_cb) lastest_cb.abort(); const cb = cancelable_navigate(); - this.navigation_stack.push(cb); + this.navigation_queue.push(cb); try { await cb.run();