fix(nuxt): catch chunk errors directly in navigation

This commit is contained in:
Daniel Roe 2024-09-13 12:40:56 +01:00
parent a341e680fb
commit be622645e0
No known key found for this signature in database
GPG Key ID: 3714AB03996F442B
4 changed files with 26 additions and 6 deletions

View File

@ -375,7 +375,9 @@ export function createNuxtApp (options: CreateOptions) {
if (chunkErrorEvent) { if (chunkErrorEvent) {
window.addEventListener(chunkErrorEvent, (event) => { window.addEventListener(chunkErrorEvent, (event) => {
nuxtApp.callHook('app:chunkError', { error: (event as Event & { payload: Error }).payload }) nuxtApp.callHook('app:chunkError', { error: (event as Event & { payload: Error }).payload })
event.preventDefault() if (nuxtApp.isHydrating || event.payload.message.includes('Unable to preload CSS')) {
event.preventDefault()
}
}) })
} }
window.useNuxtApp = window.useNuxtApp || useNuxtApp window.useNuxtApp = window.useNuxtApp || useNuxtApp

View File

@ -10,7 +10,7 @@ export default defineNuxtPlugin({
const router = useRouter() const router = useRouter()
const config = useRuntimeConfig() const config = useRuntimeConfig()
const chunkErrors = new Set() const chunkErrors = new Set<Error>()
router.beforeEach(() => { chunkErrors.clear() }) router.beforeEach(() => { chunkErrors.clear() })
nuxtApp.hook('app:chunkError', ({ error }) => { chunkErrors.add(error) }) nuxtApp.hook('app:chunkError', ({ error }) => { chunkErrors.add(error) })
@ -26,7 +26,7 @@ export default defineNuxtPlugin({
}) })
router.onError((error, to) => { router.onError((error, to) => {
if (chunkErrors.has(error) || error.message.includes('Failed to fetch dynamically imported module')) { if (chunkErrors.has(error)) {
reloadAppAtPath(to) reloadAppAtPath(to)
} }
}) })

View File

@ -1165,16 +1165,29 @@ describe('errors', () => {
// TODO: need to create test for webpack // TODO: need to create test for webpack
it.runIf(!isDev())('should handle chunk loading errors', async () => { it.runIf(!isDev())('should handle chunk loading errors', async () => {
const { page, consoleLogs } = await renderPage('/') const { page, consoleLogs } = await renderPage()
await page.route(/\.css/, route => route.abort('timedout')) // verify CSS link preload failure doesn't break the page
await page.goto(url('/'))
await page.waitForFunction(() => window.useNuxtApp?.()._route.fullPath === '/' && !window.useNuxtApp?.().isHydrating)
const initialLogs = consoleLogs.map(c => c.text).join('')
expect(initialLogs).toContain('caught chunk load error')
consoleLogs.length = 0
await page.getByText('Increment state').click() await page.getByText('Increment state').click()
await page.getByText('Increment state').click() await page.getByText('Increment state').click()
expect(await page.innerText('div')).toContain('Some value: 3') expect(await page.innerText('div')).toContain('Some value: 3')
await page.route(/.*/, route => route.abort('timedout'), { times: 1 }) await page.route(/.*/, route => route.abort('timedout'), { times: 1 })
await page.getByText('Chunk error').click() await page.getByText('Chunk error').click()
await page.waitForURL(url('/chunk-error')) await page.waitForURL(url('/chunk-error'))
expect(consoleLogs.map(c => c.text).join('')).toContain('Failed to load resource')
expect(await page.innerText('div')).toContain('Chunk error page') const logs = consoleLogs.map(c => c.text).join('')
expect(logs).toContain('caught chunk load error')
expect(logs).toContain('Failed to load resource')
await page.waitForFunction(() => window.useNuxtApp?.()._route.fullPath === '/chunk-error') await page.waitForFunction(() => window.useNuxtApp?.()._route.fullPath === '/chunk-error')
expect(await page.innerText('div')).toContain('Chunk error page')
await page.locator('div').getByText('State: 3').waitFor() await page.locator('div').getByText('State: 3').waitFor()
await page.close() await page.close()

View File

@ -0,0 +1,5 @@
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('app:chunkError', () => {
console.log('caught chunk load error')
})
})