2022-11-10 13:52:04 +00:00
import { getCurrentInstance , inject , onUnmounted } from 'vue'
2022-08-24 16:04:56 +00:00
import type { Router , RouteLocationNormalizedLoaded , NavigationGuard , RouteLocationNormalized , RouteLocationRaw , NavigationFailure , RouteLocationPathRaw } from 'vue-router'
2022-03-16 21:39:47 +00:00
import { sendRedirect } from 'h3'
2022-08-24 16:04:56 +00:00
import { hasProtocol , joinURL , parseURL } from 'ufo'
2022-09-14 09:22:03 +00:00
import { useNuxtApp , useRuntimeConfig } from '../nuxt'
2022-12-11 21:44:52 +00:00
import type { NuxtError } from './error'
import { createError } from './error'
2022-09-14 09:22:03 +00:00
import { useState } from './state'
2023-01-23 11:18:33 +00:00
import { setResponseStatus } from './ssr'
2022-02-21 13:03:42 +00:00
export const useRouter = ( ) = > {
return useNuxtApp ( ) ? . $router as Router
}
2022-08-07 04:57:11 +00:00
export const useRoute = ( ) : RouteLocationNormalizedLoaded = > {
2022-08-02 09:58:03 +00:00
if ( getCurrentInstance ( ) ) {
2022-08-07 04:57:11 +00:00
return inject ( '_route' , useNuxtApp ( ) . _route )
2022-08-02 09:58:03 +00:00
}
return useNuxtApp ( ) . _route
2022-02-21 13:03:42 +00:00
}
2022-11-10 13:52:04 +00:00
export const onBeforeRouteLeave = ( guard : NavigationGuard ) = > {
const unsubscribe = useRouter ( ) . beforeEach ( ( to , from , next ) = > {
if ( to === from ) { return }
return guard ( to , from , next )
} )
onUnmounted ( unsubscribe )
}
export const onBeforeRouteUpdate = ( guard : NavigationGuard ) = > {
const unsubscribe = useRouter ( ) . beforeEach ( guard )
onUnmounted ( unsubscribe )
}
2022-02-21 13:03:42 +00:00
export interface RouteMiddleware {
( to : RouteLocationNormalized , from : RouteLocationNormalized ) : ReturnType < NavigationGuard >
}
export const defineNuxtRouteMiddleware = ( middleware : RouteMiddleware ) = > middleware
export interface AddRouteMiddlewareOptions {
global ? : boolean
}
interface AddRouteMiddleware {
( name : string , middleware : RouteMiddleware , options? : AddRouteMiddlewareOptions ) : void
( middleware : RouteMiddleware ) : void
}
export const addRouteMiddleware : AddRouteMiddleware = ( name : string | RouteMiddleware , middleware? : RouteMiddleware , options : AddRouteMiddlewareOptions = { } ) = > {
const nuxtApp = useNuxtApp ( )
if ( options . global || typeof name === 'function' ) {
nuxtApp . _middleware . global . push ( typeof name === 'function' ? name : middleware )
} else {
nuxtApp . _middleware . named [ name ] = middleware
}
}
const isProcessingMiddleware = ( ) = > {
try {
if ( useNuxtApp ( ) . _processingMiddleware ) {
return true
}
} catch {
// Within an async middleware
return true
}
return false
}
2022-03-16 21:39:47 +00:00
export interface NavigateToOptions {
replace? : boolean
2022-08-24 16:04:56 +00:00
redirectCode? : number ,
external? : boolean
2022-03-16 21:39:47 +00:00
}
2022-09-03 12:30:03 +00:00
export const navigateTo = ( to : RouteLocationRaw | undefined | null , options? : NavigateToOptions ) : Promise < void | NavigationFailure > | RouteLocationRaw = > {
2022-05-05 20:46:54 +00:00
if ( ! to ) {
to = '/'
}
2022-08-24 16:04:56 +00:00
const toPath = typeof to === 'string' ? to : ( ( to as RouteLocationPathRaw ) . path || '/' )
const isExternal = hasProtocol ( toPath , true )
2022-09-03 12:30:03 +00:00
if ( isExternal && ! options ? . external ) {
2022-08-24 16:04:56 +00:00
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.' )
}
// Early redirect on client-side
2022-09-19 08:54:35 +00:00
if ( process . client && ! isExternal && isProcessingMiddleware ( ) ) {
2022-02-21 13:03:42 +00:00
return to
}
2022-08-24 16:04:56 +00:00
2022-03-16 21:39:47 +00:00
const router = useRouter ( )
2022-08-24 16:04:56 +00:00
2022-04-19 19:13:11 +00:00
if ( process . server ) {
const nuxtApp = useNuxtApp ( )
if ( nuxtApp . ssrContext && nuxtApp . ssrContext . event ) {
2023-01-23 11:18:33 +00:00
// Let vue-router handle internal redirects within middleware
// to prevent the navigation happening after response is sent
if ( isProcessingMiddleware ( ) && ! isExternal ) {
setResponseStatus ( options ? . redirectCode || 302 )
return to
}
2022-08-24 16:04:56 +00:00
const redirectLocation = isExternal ? toPath : joinURL ( useRuntimeConfig ( ) . app . baseURL , router . resolve ( to ) . fullPath || '/' )
2023-01-23 11:18:33 +00:00
return nuxtApp . callHook ( 'app:redirected' )
. then ( ( ) = > sendRedirect ( nuxtApp . ssrContext ! . event , redirectLocation , options ? . redirectCode || 302 ) )
2022-04-07 11:28:04 +00:00
}
2022-03-16 21:39:47 +00:00
}
2022-08-24 16:04:56 +00:00
2022-03-16 21:39:47 +00:00
// Client-side redirection using vue-router
2022-08-24 16:04:56 +00:00
if ( isExternal ) {
2022-09-03 12:30:03 +00:00
if ( options ? . replace ) {
2022-08-24 16:04:56 +00:00
location . replace ( toPath )
} else {
location . href = toPath
}
return Promise . resolve ( )
}
2022-09-03 12:30:03 +00:00
return options ? . replace ? router . replace ( to ) : router . push ( to )
2022-02-21 13:03:42 +00:00
}
/** This will abort navigation within a Nuxt route middleware handler. */
2022-09-08 08:52:00 +00:00
export const abortNavigation = ( err? : string | Partial < NuxtError > ) = > {
2022-02-21 13:03:42 +00:00
if ( process . dev && ! isProcessingMiddleware ( ) ) {
throw new Error ( 'abortNavigation() is only usable inside a route middleware handler.' )
}
if ( err ) {
2022-09-08 08:52:00 +00:00
throw createError ( err )
2022-02-21 13:03:42 +00:00
}
return false
}
2022-08-31 08:02:48 +00:00
export const setPageLayout = ( layout : string ) = > {
if ( process . server ) {
2022-10-25 12:25:49 +00:00
if ( process . dev && getCurrentInstance ( ) && useState ( '_layout' ) . value !== layout ) {
console . warn ( '[warn] [nuxt] `setPageLayout` should not be called to change the layout on the server within a component as this will cause hydration errors.' )
}
2022-08-31 08:02:48 +00:00
useState ( '_layout' ) . value = layout
}
const nuxtApp = useNuxtApp ( )
2022-10-25 12:25:49 +00:00
if ( process . dev && nuxtApp . isHydrating && useState ( '_layout' ) . value !== layout ) {
console . warn ( '[warn] [nuxt] `setPageLayout` should not be called to change the layout during hydration as this will cause hydration errors.' )
}
2022-08-31 08:02:48 +00:00
const inMiddleware = isProcessingMiddleware ( )
if ( inMiddleware || process . server || nuxtApp . isHydrating ) {
const unsubscribe = useRouter ( ) . beforeResolve ( ( to ) = > {
to . meta . layout = layout
unsubscribe ( )
} )
}
if ( ! inMiddleware ) {
useRoute ( ) . meta . layout = layout
}
}