Repository: nuxt/nuxt
File: packages/nuxt/src/app/composables/router.ts (TypeScript)

Code before:
import { getCurrentInstance, hasInjectionContext, inject, onScopeDispose } from 'vue'
import type { Ref } from 'vue'
import type { NavigationFailure, NavigationGuard, RouteLocationNormalized, RouteLocationRaw, Router, useRoute as _useRoute, useRouter as _useRouter } from 'vue-router'
import { sanitizeStatusCode } from 'h3'
import { hasProtocol, isScriptProtocol, joinURL, parseQuery, parseURL, withQuery } from 'ufo'

import type { PageMeta } from '../../pages/runtime/composables'

import { useNuxtApp, useRuntimeConfig } from '../nuxt'
import { PageRouteSymbol } from '../components/injections'
import type { NuxtError } from './error'
import { createError, showError } from './error'
import { captureStackTrace } from 'errx'

/** @since 3.0.0 */
export const useRouter: typeof _useRouter = () => {
  return useNuxtApp()?.$router as Router
}

/** @since 3.0.0 */
export const useRoute: typeof _useRoute = () => {
  if (import.meta.dev && !getCurrentInstance() && isProcessingMiddleware()) {
    const distURL = import.meta.url.replace(/\/app\/.*$/, '/app')
    const stack = captureStackTrace().filter(entry => !entry.source.startsWith(distURL))
    const trace = stack.map(({ source, line, column }) => `at ${source.replace(/^file:\/\//, '')}:${line}:${column}`).join('\n')
    console.warn('[nuxt] Calling `useRoute` within middleware may lead to misleading results. Instead, use the (to, from) arguments passed to the middleware to access the new and old routes. Learn more: https://nuxt.com/docs/4.x/guide/directory-structure/app/middleware#accessing-route-in-middleware' + ('\n' + trace))
  }
  if (hasInjectionContext()) {
    return inject(PageRouteSymbol, useNuxtApp()._route)
  }
  return useNuxtApp()._route
}

/** @since 3.0.0 */
export const onBeforeRouteLeave = (guard: NavigationGuard) => {
  const unsubscribe = useRouter().beforeEach((to, from, next) => {
    if (to === from) { return }
    return guard(to, from, next)
  })
  onScopeDispose(unsubscribe)
}

/** @since 3.0.0 */
export const onBeforeRouteUpdate = (guard: NavigationGuard) => {
  const unsubscribe = useRouter().beforeEach(guard)
  onScopeDispose(unsubscribe)
}


Review comment (refactor):
would it be worth moving this to a utility?

there may be other cases where we could provide a stack trace

Code after:
import { getCurrentInstance, hasInjectionContext, inject, onScopeDispose } from 'vue'
import type { Ref } from 'vue'
import type { NavigationFailure, NavigationGuard, RouteLocationNormalized, RouteLocationRaw, Router, useRoute as _useRoute, useRouter as _useRouter } from 'vue-router'
import { sanitizeStatusCode } from 'h3'
import { hasProtocol, isScriptProtocol, joinURL, parseQuery, parseURL, withQuery } from 'ufo'

import type { PageMeta } from '../../pages/runtime/composables'

import { useNuxtApp, useRuntimeConfig } from '../nuxt'
import { PageRouteSymbol } from '../components/injections'
import type { NuxtError } from './error'
import { createError, showError } from './error'
import { getUserTrace } from '../utils'

/** @since 3.0.0 */
export const useRouter: typeof _useRouter = () => {
  return useNuxtApp()?.$router as Router
}

/** @since 3.0.0 */
export const useRoute: typeof _useRoute = () => {
  if (import.meta.dev && !getCurrentInstance() && isProcessingMiddleware()) {
    const middleware = useNuxtApp()._processingMiddleware
    const trace = getUserTrace().map(({ source, line, column }) => `at ${source}:${line}:${column}`).join('\n')
    console.warn(`[nuxt] \`useRoute\` was called within middleware${typeof middleware === 'string' ? ` (\`${middleware}\`)` : ''}. This may lead to misleading results. Instead, use the (to, from) arguments passed to the middleware to access the new and old routes. Learn more: https://nuxt.com/docs/4.x/guide/directory-structure/app/middleware#accessing-route-in-middleware` + ('\n' + trace))
  }
  if (hasInjectionContext()) {
    return inject(PageRouteSymbol, useNuxtApp()._route)
  }
  return useNuxtApp()._route
}

/** @since 3.0.0 */
export const onBeforeRouteLeave = (guard: NavigationGuard) => {
  const unsubscribe = useRouter().beforeEach((to, from, next) => {
    if (to === from) { return }
    return guard(to, from, next)
  })
  onScopeDispose(unsubscribe)
}

/** @since 3.0.0 */
export const onBeforeRouteUpdate = (guard: NavigationGuard) => {
  const unsubscribe = useRouter().beforeEach(guard)
  onScopeDispose(unsubscribe)
}

export interface RouteMiddleware {