mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-11 08:33:53 +00:00
feat(bridge): support addRouteMiddleware
, navigateTo
and abortNavigation
(#3193)
This commit is contained in:
parent
d046c9620b
commit
3c563fa48f
@ -30,6 +30,10 @@ export async function setupAutoImports () {
|
||||
// Add auto-imports that are added by ad-hoc modules in nuxt 3
|
||||
autoImports.push({ name: 'useRouter', as: 'useRouter', from: '#app' })
|
||||
autoImports.push({ name: 'useRoute', as: 'useRoute', from: '#app' })
|
||||
autoImports.push({ name: 'addRouteMiddleware', as: 'addRouteMiddleware', from: '#app' })
|
||||
autoImports.push({ name: 'navigateTo', as: 'navigateTo', from: '#app' })
|
||||
autoImports.push({ name: 'abortNavigation', as: 'abortNavigation', from: '#app' })
|
||||
autoImports.push({ name: 'defineNuxtRouteMiddleware', as: 'defineNuxtRouteMiddleware', from: '#app' })
|
||||
|
||||
// Add bridge-only auto-imports
|
||||
autoImports.push({ name: 'useNuxt2Meta', as: 'useNuxt2Meta', from: '#app' })
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Vue from 'vue'
|
||||
import { createHooks } from 'hookable'
|
||||
import { setNuxtAppInstance } from '#app'
|
||||
import { callWithNuxt, setNuxtAppInstance } from '#app'
|
||||
|
||||
// Reshape payload to match key `useLazyAsyncData` expects
|
||||
function proxiedState (state) {
|
||||
@ -19,7 +19,7 @@ function proxiedState (state) {
|
||||
})
|
||||
}
|
||||
|
||||
export default (ctx, inject) => {
|
||||
export default async (ctx, inject) => {
|
||||
const nuxtApp = {
|
||||
vueApp: {
|
||||
component: Vue.component.bind(Vue),
|
||||
@ -48,6 +48,27 @@ export default (ctx, inject) => {
|
||||
nuxtApp.hook = nuxtApp.hooks.hook
|
||||
nuxtApp.callHook = nuxtApp.hooks.callHook
|
||||
|
||||
const middleware = await import('#build/middleware').then(r => r.default)
|
||||
nuxtApp._middleware = nuxtApp._middleware || {
|
||||
global: [],
|
||||
named: middleware
|
||||
}
|
||||
|
||||
ctx.app.router.beforeEach(async (to, from, next) => {
|
||||
nuxtApp._processingMiddleware = true
|
||||
|
||||
for (const middleware of nuxtApp._middleware.global) {
|
||||
const result = await callWithNuxt(nuxtApp, middleware, [to, from])
|
||||
if (result || result === false) { return next(result) }
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
ctx.app.router.afterEach(() => {
|
||||
delete nuxtApp._processingMiddleware
|
||||
})
|
||||
|
||||
if (!Array.isArray(ctx.app.created)) {
|
||||
ctx.app.created = [ctx.app.created].filter(Boolean)
|
||||
}
|
||||
|
@ -58,12 +58,24 @@ export const setNuxtAppInstance = (nuxt: NuxtAppCompat | null) => {
|
||||
currentNuxtAppInstance = nuxt
|
||||
}
|
||||
|
||||
export function defineNuxtPlugin (plugin: (nuxtApp: NuxtAppCompat) => void): (ctx: Context) => void {
|
||||
return (ctx) => {
|
||||
setNuxtAppInstance(ctx.$_nuxtApp)
|
||||
plugin(ctx.$_nuxtApp)
|
||||
/**
|
||||
* Ensures that the setup function passed in has access to the Nuxt instance via `useNuxt`.
|
||||
*
|
||||
* @param nuxt A Nuxt instance
|
||||
* @param setup The function to call
|
||||
*/
|
||||
export function callWithNuxt<T extends (...args: any[]) => any> (nuxt: NuxtAppCompat, setup: T, args?: Parameters<T>) {
|
||||
setNuxtAppInstance(nuxt)
|
||||
const p: ReturnType<T> = args ? setup(...args as Parameters<T>) : setup()
|
||||
if (process.server) {
|
||||
// Unset nuxt instance to prevent context-sharing in server-side
|
||||
setNuxtAppInstance(null)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
export function defineNuxtPlugin (plugin: (nuxtApp: NuxtAppCompat) => void): (ctx: Context) => void {
|
||||
return ctx => callWithNuxt(ctx.$_nuxtApp, plugin, [ctx.$_nuxtApp])
|
||||
}
|
||||
|
||||
export const useNuxtApp = (): NuxtAppCompat => {
|
||||
|
@ -2,7 +2,7 @@ import { getCurrentInstance, onBeforeUnmount, isRef, watch, reactive, toRef, isR
|
||||
import type { CombinedVueInstance } from 'vue/types/vue'
|
||||
import type { MetaInfo } from 'vue-meta'
|
||||
import type VueRouter from 'vue-router'
|
||||
import type { Route } from 'vue-router'
|
||||
import type { Location, Route } from 'vue-router'
|
||||
import type { RuntimeConfig } from '@nuxt/schema'
|
||||
import defu from 'defu'
|
||||
import { useNuxtApp } from './app'
|
||||
@ -141,3 +141,69 @@ export const useNuxt2Meta = (metaOptions: Reffed<MetaInfo> | (() => Reffed<MetaI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface AddRouteMiddlewareOptions {
|
||||
global?: boolean
|
||||
}
|
||||
|
||||
/** internal */
|
||||
function convertToLegacyMiddleware (middleware) {
|
||||
return async (ctx: any) => {
|
||||
const result = await middleware(ctx.route, ctx.from)
|
||||
if (result instanceof Error) {
|
||||
return ctx.error(result)
|
||||
}
|
||||
if (result) {
|
||||
return ctx.redirect(result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
export const addRouteMiddleware = (name: string, middleware: any, options: AddRouteMiddlewareOptions = {}) => {
|
||||
const nuxtApp = useNuxtApp()
|
||||
if (options.global) {
|
||||
nuxtApp._middleware.global.push(middleware)
|
||||
} else {
|
||||
nuxtApp._middleware.named[name] = convertToLegacyMiddleware(middleware)
|
||||
}
|
||||
}
|
||||
|
||||
const isProcessingMiddleware = () => {
|
||||
try {
|
||||
if (useNuxtApp()._processingMiddleware) {
|
||||
return true
|
||||
}
|
||||
} catch {
|
||||
// Within an async middleware
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export const navigateTo = (to: Route) => {
|
||||
if (isProcessingMiddleware()) {
|
||||
return to
|
||||
}
|
||||
const router: VueRouter = process.server ? useRouter() : (window as any).$nuxt.router
|
||||
return router.push(to)
|
||||
}
|
||||
|
||||
/** This will abort navigation within a Nuxt route middleware handler. */
|
||||
export const abortNavigation = (err?: Error | string) => {
|
||||
if (process.dev && !isProcessingMiddleware()) {
|
||||
throw new Error('abortNavigation() is only usable inside a route middleware handler.')
|
||||
}
|
||||
if (err) {
|
||||
throw err instanceof Error ? err : new Error(err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type RouteMiddlewareReturn = void | Error | string | Location | boolean
|
||||
|
||||
export interface RouteMiddleware {
|
||||
(to: Route, from: Route): RouteMiddlewareReturn | Promise<RouteMiddlewareReturn>
|
||||
}
|
||||
|
||||
export const defineNuxtRouteMiddleware = (middleware: RouteMiddleware) => middleware
|
||||
|
@ -60,7 +60,10 @@ const isProcessingMiddleware = () => {
|
||||
if (useNuxtApp()._processingMiddleware) {
|
||||
return true
|
||||
}
|
||||
} catch {}
|
||||
} catch {
|
||||
// Within an async middleware
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user