Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: port upstream navigateTo #1197

Merged
merged 1 commit into from
May 8, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 37 additions & 10 deletions packages/bridge/src/runtime/composables/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -76,30 +76,47 @@ export interface NavigateToOptions {
external?: boolean
}

export const navigateTo = (to: RawLocation | undefined | null, options?: NavigateToOptions): Promise<void | Route | NavigationFailure> | RawLocation | Route => {
export const navigateTo = (to: RawLocation | undefined | null, options?: NavigateToOptions): Promise<void | Route | NavigationFailure | false> | 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
Expand All @@ -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)
Expand Down