fix(nuxt): show error page on island page error (#31081)

This commit is contained in:
Julien Huang 2025-03-01 08:40:12 +01:00 committed by Daniel Roe
parent 6f8cfd679d
commit d153ea9d23
No known key found for this signature in database
GPG Key ID: CBC814C393D93268
3 changed files with 54 additions and 10 deletions

View File

@ -184,16 +184,23 @@ export default defineComponent({
...props.context,
props: props.props ? JSON.stringify(props.props) : undefined,
}))
const result = import.meta.server || !import.meta.dev ? await r.json() : (r as FetchResponse<NuxtIslandResponse>)._data
// TODO: support passing on more headers
if (import.meta.server && import.meta.prerender) {
const hints = r.headers.get('x-nitro-prerender')
if (hints) {
appendResponseHeader(event!, 'x-nitro-prerender', hints)
try {
const result = import.meta.server || !import.meta.dev ? await r.json() : (r as FetchResponse<NuxtIslandResponse>)._data
// TODO: support passing on more headers
if (import.meta.server && import.meta.prerender) {
const hints = r.headers.get('x-nitro-prerender')
if (hints) {
appendResponseHeader(event!, 'x-nitro-prerender', hints)
}
}
setPayload(key, result)
return result
} catch (e: any) {
if (r.status !== 200) {
throw new Error(e.toString(), { cause: r })
}
throw e
}
setPayload(key, result)
return result
}
async function fetchComponent (force = false) {

View File

@ -2,6 +2,8 @@ import { defineComponent, getCurrentInstance, h, ref } from 'vue'
import NuxtIsland from '#app/components/nuxt-island'
import { useRoute } from '#app/composables/router'
import { isPrerendered } from '#app/composables/payload'
import { createError, showError } from '#app/composables/error'
import { useNuxtApp } from '#app/nuxt'
/* @__NO_SIDE_EFFECTS__ */
export const createServerComponent = (name: string) => {
@ -46,10 +48,9 @@ export const createIslandPage = (name: string) => {
expose({
refresh: () => islandRef.value?.refresh(),
})
const nuxtApp = useNuxtApp()
const route = useRoute()
const path = import.meta.client && await isPrerendered(route.path) ? route.path : route.fullPath.replace(/#.*$/, '')
return () => {
return h('div', [
h(NuxtIsland, {
@ -57,6 +58,16 @@ export const createIslandPage = (name: string) => {
lazy: props.lazy,
ref: islandRef,
context: { url: path },
onError: (e) => {
if (e.cause && e.cause instanceof Response) {
throw createError({
statusCode: e.cause.status,
statusText: e.cause.statusText,
status: e.cause.status,
})
}
nuxtApp.runWithContext(() => showError(e))
},
}, slots),
])
}

View File

@ -0,0 +1,26 @@
import { mountSuspended } from '@nuxt/test-utils/runtime'
import { flushPromises } from '@vue/test-utils'
import { describe, expect, it, vi } from 'vitest'
import { Suspense } from 'vue'
import { createIslandPage } from '~/packages/nuxt/src/components/runtime/server-component'
vi.mock('#app/composables/error', async (og) => {
return {
...(await og()),
showError: vi.fn(),
}
})
describe('Island pages', () => {
it('expect to show error', async () => {
await mountSuspended({
setup () {
return () => h(Suspense, {}, {
default: () => h(createIslandPage('pagedontexist')),
})
},
})
await flushPromises()
expect(showError).toHaveBeenCalledOnce()
})
})