fix(nuxt): process middleware after plugins (#4645)

This commit is contained in:
Daniel Roe 2022-05-02 11:00:08 +01:00 committed by GitHub
parent 82f959d91f
commit 4826918ed0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 58 deletions

View File

@ -86,6 +86,7 @@ interface Router {
}
export default defineNuxtPlugin<{ route: Route, router: Router }>((nuxtApp) => {
const initialURL = process.client ? window.location.href : nuxtApp.ssrContext.url
const routes = []
const hooks: { [key in keyof RouterHooks]: RouterHooks[key][] } = {
@ -100,7 +101,7 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>((nuxtApp) => {
return () => hooks[hook].splice(hooks[hook].indexOf(guard), 1)
}
const route: Route = reactive(getRouteFromPath(process.client ? window.location.href : nuxtApp.ssrContext.url))
const route: Route = reactive(getRouteFromPath(initialURL))
async function handleNavigation (url: string, replace?: boolean): Promise<void> {
try {
// Resolve route
@ -193,6 +194,7 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>((nuxtApp) => {
named: {}
}
nuxtApp.hooks.hookOnce('app:created', async () => {
router.beforeEach(async (to, from) => {
to.meta = reactive(to.meta || {})
nuxtApp._processingMiddleware = true
@ -204,7 +206,7 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>((nuxtApp) => {
if (process.server) {
if (result === false || result instanceof Error) {
const error = result || createError({
statusMessage: `Route navigation aborted: ${nuxtApp.ssrContext.url}`
statusMessage: `Route navigation aborted: ${initialURL}`
})
return callWithNuxt(nuxtApp, throwError, [error])
}
@ -217,14 +219,11 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>((nuxtApp) => {
delete nuxtApp._processingMiddleware
})
if (process.server) {
nuxtApp.hooks.hookOnce('app:created', async () => {
await router.push(nuxtApp.ssrContext.url)
if (route.fullPath !== nuxtApp.ssrContext.url) {
await navigateTo(route.fullPath)
await router.replace(initialURL)
if (route.fullPath !== initialURL) {
await callWithNuxt(nuxtApp, navigateTo, [route.fullPath])
}
})
}
return {
provide: {

View File

@ -48,7 +48,7 @@ function createCurrentLocation (
return path + search + hash
}
export default defineNuxtPlugin((nuxtApp) => {
export default defineNuxtPlugin(async (nuxtApp) => {
nuxtApp.vueApp.component('NuxtPage', NuxtPage)
// TODO: remove before release - present for backwards compatibility & intentionally undocumented
nuxtApp.vueApp.component('NuxtNestedPage', NuxtPage)
@ -59,6 +59,7 @@ export default defineNuxtPlugin((nuxtApp) => {
? createWebHistory(baseURL)
: createMemoryHistory(baseURL)
const initialURL = process.server ? nuxtApp.ssrContext.url : createCurrentLocation(baseURL, window.location)
const router = createRouter({
...routerOptions,
history: routerHistory,
@ -82,8 +83,7 @@ export default defineNuxtPlugin((nuxtApp) => {
}
// Allows suspending the route object until page navigation completes
const path = process.server ? nuxtApp.ssrContext.url : createCurrentLocation(baseURL, window.location)
const _activeRoute = shallowRef(router.resolve(path) as RouteLocation)
const _activeRoute = shallowRef(router.resolve(initialURL) as RouteLocation)
const syncCurrentRoute = () => { _activeRoute.value = router.currentRoute.value }
nuxtApp.hook('page:finish', syncCurrentRoute)
router.afterEach((to, from) => {
@ -107,6 +107,28 @@ export default defineNuxtPlugin((nuxtApp) => {
named: {}
}
router.afterEach((to) => {
if (to.matched.length === 0) {
callWithNuxt(nuxtApp, throwError, [createError({
statusCode: 404,
statusMessage: `Page not found: ${to.fullPath}`
})])
} else if (process.server && to.matched[0].name === '404' && nuxtApp.ssrContext) {
nuxtApp.ssrContext.res.statusCode = 404
}
})
try {
if (process.server) {
await router.push(initialURL)
}
await router.isReady()
} catch (error) {
// We'll catch 404s here
callWithNuxt(nuxtApp, throwError, [error])
}
router.beforeEach(async (to, from) => {
to.meta = reactive(to.meta)
nuxtApp._processingMiddleware = true
@ -141,7 +163,7 @@ export default defineNuxtPlugin((nuxtApp) => {
if (process.server) {
if (result === false || result instanceof Error) {
const error = result || createError({
statusMessage: `Route navigation aborted: ${nuxtApp.ssrContext.url}`
statusMessage: `Route navigation aborted: ${initialURL}`
})
return callWithNuxt(nuxtApp, throwError, [error])
}
@ -150,37 +172,24 @@ export default defineNuxtPlugin((nuxtApp) => {
}
})
router.afterEach(() => {
delete nuxtApp._processingMiddleware
})
nuxtApp.hook('app:created', async () => {
router.afterEach((to) => {
if (to.matched.length === 0) {
callWithNuxt(nuxtApp, throwError, [createError({
statusCode: 404,
statusMessage: `Page not found: ${to.fullPath}`
})])
} else if (process.server && to.matched[0].name === '404' && nuxtApp.ssrContext) {
nuxtApp.ssrContext.res.statusCode = 404
}
})
if (process.server) {
router.afterEach(async (to) => {
if (to.fullPath !== nuxtApp.ssrContext.url) {
await navigateTo(to.fullPath)
delete nuxtApp._processingMiddleware
if (process.server) {
if (to.fullPath !== initialURL) {
await callWithNuxt(nuxtApp, navigateTo, [to.fullPath])
}
}
})
}
nuxtApp.hooks.hookOnce('app:created', async () => {
try {
if (process.server) {
await router.push(nuxtApp.ssrContext.url)
}
await router.isReady()
await router.replace({
path: initialURL,
force: true
})
} catch (error) {
// We'll catch middleware errors or deliberate exceptions here
callWithNuxt(nuxtApp, throwError, [error])
}
})

View File

@ -1,6 +1,11 @@
export default defineNuxtRouteMiddleware(async (to) => {
const nuxtApp = useNuxtApp()
if (to.path.startsWith('/redirect/')) {
await new Promise(resolve => setTimeout(resolve, 100))
return navigateTo(to.path.slice('/redirect/'.length - 1))
}
const pluginPath = nuxtApp.$path()
if (process.server && !/redirect|navigate/.test(pluginPath) && to.path !== pluginPath) {
throw new Error('plugin did not run before middleware')
}
})

View File

@ -2,9 +2,11 @@ export default defineNuxtPlugin(() => {
useHead({
titleTemplate: '%s - Fixture'
})
const path = useRoute().path
return {
provide: {
myPlugin: () => 'Injected by my-plugin'
myPlugin: () => 'Injected by my-plugin',
path: () => path
}
}
})