diff --git a/.changeset/chatty-apricots-fly.md b/.changeset/chatty-apricots-fly.md new file mode 100644 index 000000000000..fed096789767 --- /dev/null +++ b/.changeset/chatty-apricots-fly.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[fix] fire navigation-end event only at end of navigation diff --git a/packages/kit/src/runtime/client/renderer.js b/packages/kit/src/runtime/client/renderer.js index 92e9f1cf5d4d..79d15f9600d5 100644 --- a/packages/kit/src/runtime/client/renderer.js +++ b/packages/kit/src/runtime/client/renderer.js @@ -204,10 +204,13 @@ export class Renderer { this._init(result); } - /** @param {{ path: string, query: URLSearchParams }} destination */ - notify({ path, query }) { - dispatchEvent(new CustomEvent('sveltekit:navigation-start')); - + /** + * @param {import('./types').NavigationInfo} info + * @param {string[]} chain + * @param {boolean} no_cache + * @param {{hash?: string, scroll: { x: number, y: number } | null, keepfocus: boolean}} [opts] + */ + async handle_navigation(info, chain, no_cache, opts) { if (this.started) { this.stores.navigating.set({ from: { @@ -215,11 +218,13 @@ export class Renderer { query: this.current.page.query }, to: { - path, - query + path: info.path, + query: info.query } }); } + + await this.update(info, chain, no_cache, opts); } /** @@ -291,11 +296,12 @@ export class Renderer { } await 0; - dispatchEvent(new CustomEvent('sveltekit:navigation-end')); + this.loading.promise = null; this.loading.id = null; if (!this.router) return; + const leaf_node = navigation_result.state.branch[navigation_result.state.branch.length - 1]; if (leaf_node && leaf_node.module.router === false) { this.router.disable(); diff --git a/packages/kit/src/runtime/client/router.js b/packages/kit/src/runtime/client/router.js index 9c9a01023982..79c8cb7f710c 100644 --- a/packages/kit/src/runtime/client/router.js +++ b/packages/kit/src/runtime/client/router.js @@ -39,6 +39,8 @@ export class Router { this.base = base; this.routes = routes; this.trailing_slash = trailing_slash; + /** Keeps tracks of multiple navigations caused by redirects during rendering */ + this.navigating = 0; /** @type {import('./renderer').Renderer} */ this.renderer = renderer; @@ -253,6 +255,11 @@ export class Router { throw new Error('Attempted to navigate to a URL that does not belong to this app'); } + if (!this.navigating) { + dispatchEvent(new CustomEvent('sveltekit:navigation-start')); + } + this.navigating++; + // remove trailing slashes if (info.path !== '/') { const has_trailing_slash = info.path.endsWith('/'); @@ -269,11 +276,11 @@ export class Router { } } - this.renderer.notify({ - path: info.path, - query: info.query - }); + await this.renderer.handle_navigation(info, chain, false, { hash, scroll, keepfocus }); - await this.renderer.update(info, chain, false, { hash, scroll, keepfocus }); + this.navigating--; + if (!this.navigating) { + dispatchEvent(new CustomEvent('sveltekit:navigation-end')); + } } } diff --git a/packages/kit/test/apps/basics/src/routes/redirect/_tests.js b/packages/kit/test/apps/basics/src/routes/redirect/_tests.js index 9522daabe147..2c76ba30b1a1 100644 --- a/packages/kit/test/apps/basics/src/routes/redirect/_tests.js +++ b/packages/kit/test/apps/basics/src/routes/redirect/_tests.js @@ -5,7 +5,7 @@ export default function (test) { test('redirect', '/redirect', async ({ base, page, clicknav }) => { await clicknav('[href="/redirect/a"]'); - assert.equal(page.url(), `${base}/redirect/c`); + await page.waitForURL(`${base}/redirect/c`); assert.equal(await page.textContent('h1'), 'c'); }); diff --git a/packages/kit/test/apps/basics/src/routes/redirect/c.svelte b/packages/kit/test/apps/basics/src/routes/redirect/c.svelte index 70f3d5c81534..232c276f86cb 100644 --- a/packages/kit/test/apps/basics/src/routes/redirect/c.svelte +++ b/packages/kit/test/apps/basics/src/routes/redirect/c.svelte @@ -1 +1 @@ -