From ebc370c6f63e5048c3f69afbe632d191a3957cd1 Mon Sep 17 00:00:00 2001 From: Ryota Watanabe <43837308+wattanx@users.noreply.github.com> Date: Wed, 9 Oct 2024 07:09:02 +0900 Subject: [PATCH] fix(nuxt): avoid throwing 404 error before middleware finishes (#29054) --- .../nuxt/src/pages/runtime/plugins/router.ts | 25 +++++++++++-------- test/basic.test.ts | 16 +++++++----- test/fixtures/basic/nuxt.config.ts | 1 + 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/packages/nuxt/src/pages/runtime/plugins/router.ts b/packages/nuxt/src/pages/runtime/plugins/router.ts index 7f2e5faca6..bb41746480 100644 --- a/packages/nuxt/src/pages/runtime/plugins/router.ts +++ b/packages/nuxt/src/pages/runtime/plugins/router.ts @@ -148,16 +148,8 @@ const plugin: Plugin<{ router: Router }> = defineNuxtPlugin({ if (import.meta.server && failure?.type === 4 /* ErrorTypes.NAVIGATION_ABORTED */) { return } - if (to.matched.length === 0) { - await nuxtApp.runWithContext(() => showError(createError({ - statusCode: 404, - fatal: false, - statusMessage: `Page not found: ${to.fullPath}`, - data: { - path: to.fullPath, - }, - }))) - } else if (import.meta.server && to.redirectedFrom && to.fullPath !== initialURL) { + + if (import.meta.server && to.redirectedFrom && to.fullPath !== initialURL) { await nuxtApp.runWithContext(() => navigateTo(to.fullPath || '/')) } }) @@ -252,6 +244,19 @@ const plugin: Plugin<{ router: Router }> = defineNuxtPlugin({ await nuxtApp.callHook('page:loading:end') }) + router.afterEach(async (to, _from) => { + if (to.matched.length === 0) { + await nuxtApp.runWithContext(() => showError(createError({ + statusCode: 404, + fatal: false, + statusMessage: `Page not found: ${to.fullPath}`, + data: { + path: to.fullPath, + }, + }))) + } + }) + nuxtApp.hooks.hookOnce('app:created', async () => { try { // #4920, #4982 diff --git a/test/basic.test.ts b/test/basic.test.ts index 35eceb27da..8c70bdfda2 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -167,8 +167,7 @@ describe('pages', () => { expect(headers.get('location')).toEqual('/') }) - // TODO: https://github.com/nuxt/nuxt/pull/29054 - it.todo('allows routes to be added dynamically', async () => { + it('allows routes to be added dynamically', async () => { const html = await $fetch('/add-route-test') expect(html).toContain('Hello Nuxt 3!') }) @@ -250,8 +249,7 @@ describe('pages', () => { await serverPage.close() }) - it.todo('returns 500 when there is an infinite redirect', async () => { - // TODO: Fix infinite redirect without catchall + it.runIf(isDev())('returns 500 when there is an infinite redirect', async () => { const { status } = await fetch('/catchall/redirect-infinite', { redirect: 'manual' }) expect(status).toEqual(500) }) @@ -274,8 +272,7 @@ describe('pages', () => { await expectNoClientErrors('/catchall/not-found') }) - // TODO: https://github.com/nuxt/nuxt/pull/29054 - it.todo('should render correctly when loaded on a different path', async () => { + it('should render correctly when loaded on a different path', async () => { const { page, pageErrors } = await renderPage() await page.goto(url('/proxy')) await page.waitForFunction(() => window.useNuxtApp?.() && !window.useNuxtApp?.().isHydrating) @@ -1278,6 +1275,13 @@ describe('middlewares', () => { expect(html).toContain('Hello Nuxt 3!') }) + it('should allow redirection from a non-existent route with `ssr: false`', async () => { + const page = await createPage('/redirect/catchall') + + expect(await page.getByRole('heading').textContent()).toMatchInlineSnapshot('"[...slug].vue"') + await page.close() + }) + it('should allow aborting navigation on server-side', async () => { const res = await fetch('/?abort', { headers: { diff --git a/test/fixtures/basic/nuxt.config.ts b/test/fixtures/basic/nuxt.config.ts index 3af6b3e9c7..75063b9cd7 100644 --- a/test/fixtures/basic/nuxt.config.ts +++ b/test/fixtures/basic/nuxt.config.ts @@ -62,6 +62,7 @@ export default defineNuxtConfig({ }, routeRules: { '/route-rules/spa': { ssr: false }, + '/redirect/catchall': { ssr: false }, '/head-spa': { ssr: false }, '/route-rules/middleware': { appMiddleware: 'route-rules-middleware' }, '/hydration/spa-redirection/**': { ssr: false },