mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 06:20:11 +00:00
fix: only show preload error toast for stale chunk errors
Add isStaleChunkError() filter that checks for hashed JS/CSS/MJS assets under /assets/ before showing the toast. Non-asset URLs (e.g. /api/i18n) and general network errors no longer trigger the toast. Logging and Sentry reporting remain unconditional.
This commit is contained in:
16
src/App.vue
16
src/App.vue
@@ -18,7 +18,7 @@ import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||
import { app } from '@/scripts/app'
|
||||
import { useWorkspaceStore } from '@/stores/workspaceStore'
|
||||
import { electronAPI } from '@/utils/envUtil'
|
||||
import { parsePreloadError } from '@/utils/preloadErrorUtil'
|
||||
import { isStaleChunkError, parsePreloadError } from '@/utils/preloadErrorUtil'
|
||||
import { useConflictDetection } from '@/workbench/extensions/manager/composables/useConflictDetection'
|
||||
|
||||
const { t } = useI18n()
|
||||
@@ -96,12 +96,14 @@ onMounted(() => {
|
||||
}
|
||||
})
|
||||
}
|
||||
useToastStore().add({
|
||||
severity: 'error',
|
||||
summary: t('g.preloadErrorTitle'),
|
||||
detail: t('g.preloadError'),
|
||||
life: 10000
|
||||
})
|
||||
if (isStaleChunkError(info)) {
|
||||
useToastStore().add({
|
||||
severity: 'error',
|
||||
summary: t('g.preloadErrorTitle'),
|
||||
detail: t('g.preloadError'),
|
||||
life: 10000
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Capture resource load failures (CSS, scripts) in non-localhost distributions
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { parsePreloadError } from './preloadErrorUtil'
|
||||
import { isStaleChunkError, parsePreloadError } from './preloadErrorUtil'
|
||||
|
||||
describe('parsePreloadError', () => {
|
||||
it('parses CSS preload error', () => {
|
||||
@@ -90,3 +90,74 @@ describe('parsePreloadError', () => {
|
||||
expect(result.chunkName).toBe('index')
|
||||
})
|
||||
})
|
||||
|
||||
describe('isStaleChunkError', () => {
|
||||
it('returns true for hashed JS chunk under /assets/', () => {
|
||||
const info = parsePreloadError(
|
||||
new Error(
|
||||
'Failed to fetch dynamically imported module: /assets/vendor-vue-core-abc123.js'
|
||||
)
|
||||
)
|
||||
expect(isStaleChunkError(info)).toBe(true)
|
||||
})
|
||||
|
||||
it('returns true for hashed CSS chunk under /assets/', () => {
|
||||
const info = parsePreloadError(
|
||||
new Error('Unable to preload CSS for /assets/style-9f8e7d.css')
|
||||
)
|
||||
expect(isStaleChunkError(info)).toBe(true)
|
||||
})
|
||||
|
||||
it('returns true for hashed mjs chunk under /assets/', () => {
|
||||
const info = parsePreloadError(
|
||||
new Error(
|
||||
'Failed to fetch dynamically imported module: /assets/chunk-abc123.mjs'
|
||||
)
|
||||
)
|
||||
expect(isStaleChunkError(info)).toBe(true)
|
||||
})
|
||||
|
||||
it('returns false for non-asset URLs like /api/i18n', () => {
|
||||
const info = parsePreloadError(
|
||||
new Error(
|
||||
'Failed to fetch dynamically imported module: https://cloud.comfy.org/api/i18n'
|
||||
)
|
||||
)
|
||||
expect(isStaleChunkError(info)).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false for unhashed asset files', () => {
|
||||
const info = parsePreloadError(
|
||||
new Error('Failed to fetch dynamically imported module: /assets/index.js')
|
||||
)
|
||||
expect(isStaleChunkError(info)).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false when no URL can be extracted', () => {
|
||||
const info = parsePreloadError(new Error('Something failed'))
|
||||
expect(isStaleChunkError(info)).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false for font files', () => {
|
||||
const info = parsePreloadError(
|
||||
new Error('Unable to preload CSS for /assets/inter-abc123.woff2')
|
||||
)
|
||||
expect(isStaleChunkError(info)).toBe(false)
|
||||
})
|
||||
|
||||
it('returns false for image files', () => {
|
||||
const info = parsePreloadError(
|
||||
new Error('Unable to preload CSS for /assets/logo-abc123.png')
|
||||
)
|
||||
expect(isStaleChunkError(info)).toBe(false)
|
||||
})
|
||||
|
||||
it('returns true for full URL with hashed asset path', () => {
|
||||
const info = parsePreloadError(
|
||||
new Error(
|
||||
'Failed to fetch dynamically imported module: https://cloud.comfy.org/assets/vendor-three-def456.js'
|
||||
)
|
||||
)
|
||||
expect(isStaleChunkError(info)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -64,6 +64,22 @@ function extractChunkName(url: string): string | null {
|
||||
return withoutHash || null
|
||||
}
|
||||
|
||||
const HASHED_ASSET_RE = /\/assets\/.+-[a-f0-9]{6,}\.(js|mjs|css)$/
|
||||
|
||||
/**
|
||||
* Determines if a preload error is a genuine stale chunk error — i.e. a hashed
|
||||
* JS/CSS asset under /assets/ that 404'd, typically after a new deployment
|
||||
* changed chunk hashes. Returns false for non-asset URLs (e.g. /api/i18n),
|
||||
* unknown file types, and errors with no extractable URL.
|
||||
*/
|
||||
export function isStaleChunkError(info: PreloadErrorInfo): boolean {
|
||||
if (!info.url) return false
|
||||
if (info.fileType !== 'js' && info.fileType !== 'css') return false
|
||||
|
||||
const pathname = new URL(info.url, 'https://cloud.comfy.org').pathname
|
||||
return HASHED_ASSET_RE.test(pathname)
|
||||
}
|
||||
|
||||
export function parsePreloadError(error: Error): PreloadErrorInfo {
|
||||
const message = error.message || String(error)
|
||||
const url = extractUrl(message)
|
||||
|
||||
Reference in New Issue
Block a user