async function navigate()

in kit/svelteKitCustomClient/client.js [978:1196]


	async function navigate({
		url,
		scroll,
		keepfocus,
		redirect_chain,
		details,
		type,
		delta,
		nav_token = {},
		accepted,
		blocked,
	}) {
		const originalUrl = new URL(url.href);
		const renamedPathname = getHfDocFullPath(url.pathname);
		if (renamedPathname) {
			url.pathname = renamedPathname;
		}
		const intent = get_navigation_intent(url, false);
		const nav = before_navigate({ url, type, delta, intent });

		if (!nav) {
			blocked();
			return;
		}

		// store this before calling `accepted()`, which may change the index
		const previous_history_index = current_history_index;

		accepted();

		navigating = true;

		if (started) {
			stores.navigating.set(nav.navigation);
		}

		token = nav_token;
		let navigation_result = intent && (await load_route(intent));

		if (!navigation_result) {
			if (is_external_url(url, base)) {
				return await native_navigation(url);
			}
			navigation_result = await server_fallback(
				url,
				{ id: null },
				await handle_error(new Error(`Not found: ${url.pathname}`), {
					url,
					params: {},
					route: { id: null },
				}),
				404
			);
		}

		// if this is an internal navigation intent, use the normalized
		// URL for the rest of the function
		url = intent?.url || url;

		// abort if user navigated during update
		if (token !== nav_token) {
			nav.reject(new Error("navigation was aborted"));
			return false;
		}

		if (navigation_result.type === "redirect") {
			if (redirect_chain.length > 10 || redirect_chain.includes(url.pathname)) {
				navigation_result = await load_root_error_page({
					status: 500,
					error: await handle_error(new Error("Redirect loop"), {
						url,
						params: {},
						route: { id: null },
					}),
					url,
					route: { id: null },
				});
			} else {
				goto(
					new URL(navigation_result.location, url).href,
					{},
					[...redirect_chain, url.pathname],
					nav_token
				);
				return false;
			}
		} else if (/** @type {number} */ (navigation_result.props.page?.status) >= 400) {
			const updated = await stores.updated.check();
			if (updated) {
				await native_navigation(url);
			}
		}

		// reset invalidation only after a finished navigation. If there are redirects or
		// additional invalidations, they should get the same invalidation treatment
		invalidated.length = 0;
		force_invalidation = false;

		updating = true;

		update_scroll_positions(previous_history_index);
		capture_snapshot(previous_history_index);

		// ensure the url pathname matches the page's trailing slash option
		if (
			navigation_result.props.page?.url &&
			navigation_result.props.page.url.pathname !== url.pathname
		) {
			url.pathname = navigation_result.props.page?.url.pathname;
		}

		if (details) {
			const change = details.replaceState ? 0 : 1;
			details.state[INDEX_KEY] = current_history_index += change;
			history[details.replaceState ? "replaceState" : "pushState"](details.state, "", originalUrl);

			if (!details.replaceState) {
				// if we navigated back, then pushed a new state, we can
				// release memory by pruning the scroll/snapshot lookup
				let i = current_history_index + 1;
				while (snapshots[i] || scroll_positions[i]) {
					delete snapshots[i];
					delete scroll_positions[i];
					i += 1;
				}
			}
		}

		// reset preload synchronously after the history state has been set to avoid race conditions
		load_cache = null;

		if (started) {
			current = navigation_result.state;

			// reset url before updating page store
			if (navigation_result.props.page) {
				navigation_result.props.page.url = url;
			}

			const after_navigate = (
				await Promise.all(
					callbacks.on_navigate.map((fn) =>
						fn(/** @type {import('@sveltejs/kit').OnNavigate} */ (nav.navigation))
					)
				)
			).filter((value) => typeof value === "function");

			if (after_navigate.length > 0) {
				function cleanup() {
					callbacks.after_navigate = callbacks.after_navigate.filter(
						// @ts-ignore
						(fn) => !after_navigate.includes(fn)
					);
				}

				after_navigate.push(cleanup);

				// @ts-ignore
				callbacks.after_navigate.push(...after_navigate);
			}

			root.$set(navigation_result.props);
		} else {
			initialize(navigation_result);
		}

		const { activeElement } = document;

		// need to render the DOM before we can scroll to the rendered elements and do focus management
		await tick();

		// we reset scroll before dealing with focus, to avoid a flash of unscrolled content
		if (autoscroll) {
			const deep_linked =
				url.hash && document.getElementById(decodeURIComponent(url.hash.slice(1)));
			if (scroll) {
				scrollTo(scroll.x, scroll.y);
			} else if (deep_linked) {
				// Here we use `scrollIntoView` on the element instead of `scrollTo`
				// because it natively supports the `scroll-margin` and `scroll-behavior`
				// CSS properties.
				deep_linked.scrollIntoView();
			} else {
				scrollTo(0, 0);
			}
		}

		const changed_focus =
			// reset focus only if any manual focus management didn't override it
			document.activeElement !== activeElement &&
			// also refocus when activeElement is body already because the
			// focus event might not have been fired on it yet
			document.activeElement !== document.body;

		if (!keepfocus && !changed_focus) {
			reset_focus();
		}

		autoscroll = true;

		if (navigation_result.props.page) {
			page = navigation_result.props.page;
		}

		navigating = false;

		if (type === "popstate") {
			restore_snapshot(current_history_index);
		}

		nav.fulfil(undefined);

		callbacks.after_navigate.forEach((fn) =>
			fn(/** @type {import('@sveltejs/kit').AfterNavigate} */ (nav.navigation))
		);
		stores.navigating.set(null);

		updating = false;
	}