From 7710ed30fa1382afd72798544931c5d9e9646c34 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Wed, 7 Jun 2023 13:18:50 +0100 Subject: [PATCH] fix(nuxt): skip vue render when redirecting (#21412) --- packages/nuxt/src/app/composables/router.ts | 8 ++++---- packages/nuxt/src/app/entry.ts | 1 + packages/nuxt/src/core/runtime/nitro/renderer.ts | 3 +++ test/basic.test.ts | 7 +++++++ test/bundle.test.ts | 2 +- test/fixtures/basic/pages/navigate-to-error.vue | 14 ++++++++++++++ 6 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 test/fixtures/basic/pages/navigate-to-error.vue diff --git a/packages/nuxt/src/app/composables/router.ts b/packages/nuxt/src/app/composables/router.ts index cab9987dd0..383433cce9 100644 --- a/packages/nuxt/src/app/composables/router.ts +++ b/packages/nuxt/src/app/composables/router.ts @@ -118,7 +118,7 @@ export const navigateTo = (to: RouteLocationRaw | undefined | null, options?: Na const fullPath = typeof to === 'string' || isExternal ? toPath : router.resolve(to).fullPath || '/' const location = isExternal ? toPath : joinURL(useRuntimeConfig().app.baseURL, fullPath) - async function redirect () { + async function redirect (response: any) { // TODO: consider deprecating in favour of `app:rendered` and removing await nuxtApp.callHook('app:redirected') const encodedLoc = location.replace(/"/g, '%22') @@ -127,16 +127,16 @@ export const navigateTo = (to: RouteLocationRaw | undefined | null, options?: Na body: ``, headers: { location } } - return inMiddleware ? /* abort route navigation */ false : undefined + return response } // We wait to perform the redirect last in case any other middleware will intercept the redirect // and redirect somewhere else instead. if (!isExternal && inMiddleware) { - router.afterEach(final => (final.fullPath === fullPath) ? redirect() : undefined) + router.afterEach(final => final.fullPath === fullPath ? redirect(false) : undefined) return to } - return redirect() + return redirect(!inMiddleware ? undefined : /* abort route navigation */ false) } } diff --git a/packages/nuxt/src/app/entry.ts b/packages/nuxt/src/app/entry.ts index db77ed4823..4aca681905 100644 --- a/packages/nuxt/src/app/entry.ts +++ b/packages/nuxt/src/app/entry.ts @@ -41,6 +41,7 @@ if (process.server) { await nuxt.hooks.callHook('app:error', err) nuxt.payload.error = (nuxt.payload.error || err) as any } + if (ssrContext?._renderResponse) { throw new Error('skipping render') } return vueApp } diff --git a/packages/nuxt/src/core/runtime/nitro/renderer.ts b/packages/nuxt/src/core/runtime/nitro/renderer.ts index 14c914ecbb..02ed61eae6 100644 --- a/packages/nuxt/src/core/runtime/nitro/renderer.ts +++ b/packages/nuxt/src/core/runtime/nitro/renderer.ts @@ -247,6 +247,9 @@ export default defineRenderHandler(async (event): Promise { + // We use error to bypass full render if we have an early response we can make + if (ssrContext._renderResponse && error.message === 'skipping render') { return {} as ReturnType } + // Use explicitly thrown error in preference to subsequent rendering errors const _err = (!ssrError && ssrContext.payload?.error) || error await ssrContext.nuxt?.hooks.callHook('app:error', _err) diff --git a/test/basic.test.ts b/test/basic.test.ts index 113ef10c20..8f274c2a64 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -633,6 +633,13 @@ describe('navigate', () => { expect(status).toEqual(302) }) + it('should not run setup function in path redirected to', async () => { + const { headers, status } = await fetch('/navigate-to-error', { redirect: 'manual' }) + + expect(headers.get('location')).toEqual('/setup-should-not-run') + expect(status).toEqual(302) + }) + it('supports directly aborting navigation on SSR', async () => { const { status } = await fetch('/navigate-to-false', { redirect: 'manual' }) diff --git a/test/bundle.test.ts b/test/bundle.test.ts index 833368f566..3ea70402a0 100644 --- a/test/bundle.test.ts +++ b/test/bundle.test.ts @@ -45,7 +45,7 @@ describe.skipIf(isWindows || process.env.TEST_BUILDER === 'webpack' || process.e it('default server bundle size', async () => { stats.server = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir) - expect(roundToKilobytes(stats.server.totalBytes)).toMatchInlineSnapshot('"62.6k"') + expect(roundToKilobytes(stats.server.totalBytes)).toMatchInlineSnapshot('"62.8k"') const modules = await analyzeSizes('node_modules/**/*', serverDir) expect(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot('"2285k"') diff --git a/test/fixtures/basic/pages/navigate-to-error.vue b/test/fixtures/basic/pages/navigate-to-error.vue new file mode 100644 index 0000000000..ef0cc0cd55 --- /dev/null +++ b/test/fixtures/basic/pages/navigate-to-error.vue @@ -0,0 +1,14 @@ + + +