import {
+ type VNode,
createApp,
defineComponent,
h,
watch,
watchEffect,
} from '@vue/runtime-test'
+import { ErrorCodes, ErrorTypeStrings } from '../src/errorHandling'
describe('error handling', () => {
test('propagation', () => {
expect(handler).toHaveBeenCalledTimes(1)
})
+ test('errors in scheduler job with owner instance should be caught', async () => {
+ let vnode: VNode
+ const x = ref(0)
+ const app = createApp({
+ render() {
+ return (vnode = vnode || h('div', x.value))
+ },
+ })
+
+ app.config.errorHandler = vi.fn()
+ app.mount(nodeOps.createElement('div'))
+
+ const error = new Error('error')
+ Object.defineProperty(vnode!, 'el', {
+ get() {
+ throw error
+ },
+ })
+
+ x.value++
+ await nextTick()
+ expect(app.config.errorHandler).toHaveBeenCalledWith(
+ error,
+ {},
+ ErrorTypeStrings[ErrorCodes.COMPONENT_UPDATE],
+ )
+ })
+
// native event handler handling should be tested in respective renderers
})
FUNCTION_REF,
ASYNC_COMPONENT_LOADER,
SCHEDULER,
+ COMPONENT_UPDATE,
}
export const ErrorTypeStrings: Record<LifecycleHooks | ErrorCodes, string> = {
[ErrorCodes.APP_WARN_HANDLER]: 'app warnHandler',
[ErrorCodes.FUNCTION_REF]: 'ref function',
[ErrorCodes.ASYNC_COMPONENT_LOADER]: 'async component loader',
- [ErrorCodes.SCHEDULER]:
- 'scheduler flush. This is likely a Vue internals bug. ' +
- 'Please open an issue at https://github.com/vuejs/core .',
+ [ErrorCodes.SCHEDULER]: 'scheduler flush',
+ [ErrorCodes.COMPONENT_UPDATE]: 'component update',
}
export type ErrorTypes = LifecycleHooks | ErrorCodes
export function callWithErrorHandling(
fn: Function,
- instance: ComponentInternalInstance | null,
+ instance: ComponentInternalInstance | null | undefined,
type: ErrorTypes,
args?: unknown[],
) {
export function handleError(
err: unknown,
- instance: ComponentInternalInstance | null,
+ instance: ComponentInternalInstance | null | undefined,
type: ErrorTypes,
throwInDev = true,
) {
effect.run()
}
})
+ update.i = instance
update.id = instance.uid
// allowRecurse
// #1801, #2043 component render effects should allow recursive updates
effect.onTrigger = instance.rtg
? e => invokeArrayFns(instance.rtg!, e)
: void 0
- update.ownerInstance = instance
}
update()
/**
* Attached by renderer.ts when setting up a component's render effect
* Used to obtain component information when reporting max recursive updates.
- * dev only.
*/
- ownerInstance?: ComponentInternalInstance
+ i?: ComponentInternalInstance
}
export type SchedulerJobs = SchedulerJob | SchedulerJob[]
if (__DEV__ && check(job)) {
continue
}
- callWithErrorHandling(job, null, ErrorCodes.SCHEDULER)
+ callWithErrorHandling(
+ job,
+ job.i,
+ job.i ? ErrorCodes.COMPONENT_UPDATE : ErrorCodes.SCHEDULER,
+ )
}
}
} finally {
} else {
const count = seen.get(fn)!
if (count > RECURSION_LIMIT) {
- const instance = fn.ownerInstance
+ const instance = fn.i
const componentName = instance && getComponentName(instance.type)
handleError(
`Maximum recursive updates exceeded${