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

View File

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

View File

@ -1,6 +1,11 @@
export default defineNuxtRouteMiddleware(async (to) => { export default defineNuxtRouteMiddleware(async (to) => {
const nuxtApp = useNuxtApp()
if (to.path.startsWith('/redirect/')) { if (to.path.startsWith('/redirect/')) {
await new Promise(resolve => setTimeout(resolve, 100)) await new Promise(resolve => setTimeout(resolve, 100))
return navigateTo(to.path.slice('/redirect/'.length - 1)) 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({ useHead({
titleTemplate: '%s - Fixture' titleTemplate: '%s - Fixture'
}) })
const path = useRoute().path
return { return {
provide: { provide: {
myPlugin: () => 'Injected by my-plugin' myPlugin: () => 'Injected by my-plugin',
path: () => path
} }
} }
}) })