From c1ce4443b4ccaaf5d4e013dac1cc50b5c6e27d53 Mon Sep 17 00:00:00 2001 From: Ryota Watanabe <43837308+wattanx@users.noreply.github.com> Date: Wed, 8 May 2024 22:21:29 +0900 Subject: [PATCH] fix: port additional fixes/changes in upstream `navigateTo` (#1197) --- .../bridge/src/runtime/composables/router.ts | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/packages/bridge/src/runtime/composables/router.ts b/packages/bridge/src/runtime/composables/router.ts index 22fe890f..01144d11 100644 --- a/packages/bridge/src/runtime/composables/router.ts +++ b/packages/bridge/src/runtime/composables/router.ts @@ -3,7 +3,7 @@ import type VueRouter from 'vue-router' import type { Location, RawLocation, Route, NavigationFailure } from 'vue-router' import { sendRedirect } from 'h3' import { useRouter as useVueRouter, useRoute as useVueRoute } from 'vue-router/composables' -import { hasProtocol, joinURL, parseURL } from 'ufo' +import { hasProtocol, joinURL, parseURL, withQuery } from 'ufo' import { useNuxtApp, callWithNuxt, useRuntimeConfig } from '../nuxt' import { createError, showError } from './error' import type { NuxtError } from './error' @@ -76,30 +76,47 @@ export interface NavigateToOptions { external?: boolean } -export const navigateTo = (to: RawLocation | undefined | null, options?: NavigateToOptions): Promise | RawLocation | Route => { +export const navigateTo = (to: RawLocation | undefined | null, options?: NavigateToOptions): Promise | false | void | RawLocation | Route => { if (!to) { to = '/' } - const toPath = typeof to === 'string' ? to : (to.path || '/') - const isExternal = hasProtocol(toPath, true) + const toPath = typeof to === 'string' ? to : (withQuery((to as Route).path || '/', to.query || {}) + (to.hash || '')) + + const isExternal = options?.external || hasProtocol(toPath, { acceptRelative: true }) if (isExternal && !options?.external) { throw new Error('Navigating to external URL is not allowed by default. Use `nagivateTo (url, { external: true })`.') } if (isExternal && parseURL(toPath).protocol === 'script:') { throw new Error('Cannot navigate to an URL with script protocol.') } + + const inMiddleware = isProcessingMiddleware() + // Early redirect on client-side - if (process.client && !isExternal && isProcessingMiddleware()) { + if (process.client && !isExternal && inMiddleware) { return to } + const router = useRouter() + + const nuxtApp = useNuxtApp() + if (process.server) { - const nuxtApp = useNuxtApp() - if (nuxtApp.ssrContext && nuxtApp.ssrContext.event) { - const redirectLocation = isExternal ? toPath : joinURL(useRuntimeConfig().app.baseURL, router.resolve(to).resolved.fullPath || '/') + if (nuxtApp.ssrContext) { + const fullPath = typeof to === 'string' || isExternal ? toPath : router.resolve(to).resolved.fullPath || '/' + const location = isExternal ? toPath : joinURL(useRuntimeConfig().app.baseURL, fullPath) + + const redirect = async function (response: any) { + // @ts-expect-error + await nuxtApp.callHook('app:redirected') - // @ts-expect-error - return nuxtApp.callHook('app:redirected').then(() => sendRedirect(nuxtApp.ssrContext!.event, redirectLocation, options?.redirectCode || 302)) + await sendRedirect(nuxtApp.ssrContext!.event, location, options?.redirectCode || 302) + return response + } + + // TODO: We wait to perform the redirect last in case any other middleware will intercept the redirect + + return redirect(!inMiddleware ? undefined : /* abort route navigation */ false) } } // Client-side redirection using vue-router @@ -109,6 +126,16 @@ export const navigateTo = (to: RawLocation | undefined | null, options?: Navigat } else { location.href = toPath } + // Within in a Nuxt route middleware handler + if (inMiddleware) { + // Abort navigation when app is hydrated + if (!nuxtApp.isHydrating) { + return false + } + // When app is hydrating (i.e. on page load), we don't want to abort navigation as + // it would lead to a 404 error / page that's blinking before location changes. + return new Promise(() => {}) + } return Promise.resolve() } return options?.replace ? router.replace(to) : router.push(to)