vue3-yuanma/packages/runtime-core/src/errorHandling.ts
2018-11-08 14:09:52 -05:00

126 lines
3.5 KiB
TypeScript

import { ComponentInstance } from './component'
import { warn, pushWarningContext, popWarningContext } from './warning'
import { VNode } from './vdom'
import { VNodeFlags } from './flags'
export const enum ErrorTypes {
BEFORE_CREATE = 1,
CREATED,
BEFORE_MOUNT,
MOUNTED,
BEFORE_UPDATE,
UPDATED,
BEFORE_UNMOUNT,
UNMOUNTED,
ACTIVATED,
DEACTIVATED,
ERROR_CAPTURED,
RENDER,
RENDER_TRACKED,
RENDER_TRIGGERED,
WATCH_CALLBACK,
NATIVE_EVENT_HANDLER,
COMPONENT_EVENT_HANDLER,
SCHEDULER
}
const ErrorTypeStrings: Record<number, string> = {
[ErrorTypes.BEFORE_CREATE]: 'in beforeCreate lifecycle hook',
[ErrorTypes.CREATED]: 'in created lifecycle hook',
[ErrorTypes.BEFORE_MOUNT]: 'in beforeMount lifecycle hook',
[ErrorTypes.MOUNTED]: 'in mounted lifecycle hook',
[ErrorTypes.BEFORE_UPDATE]: 'in beforeUpdate lifecycle hook',
[ErrorTypes.UPDATED]: 'in updated lifecycle hook',
[ErrorTypes.BEFORE_UNMOUNT]: 'in beforeUnmount lifecycle hook',
[ErrorTypes.UNMOUNTED]: 'in unmounted lifecycle hook',
[ErrorTypes.ACTIVATED]: 'in activated lifecycle hook',
[ErrorTypes.DEACTIVATED]: 'in deactivated lifecycle hook',
[ErrorTypes.ERROR_CAPTURED]: 'in errorCaptured lifecycle hook',
[ErrorTypes.RENDER]: 'in render function',
[ErrorTypes.RENDER_TRACKED]: 'in renderTracked debug hook',
[ErrorTypes.RENDER_TRIGGERED]: 'in renderTriggered debug hook',
[ErrorTypes.WATCH_CALLBACK]: 'in watcher callback',
[ErrorTypes.NATIVE_EVENT_HANDLER]: 'in native event handler',
[ErrorTypes.COMPONENT_EVENT_HANDLER]: 'in component event handler',
[ErrorTypes.SCHEDULER]:
'when flushing updates. This may be a Vue internals bug.'
}
export function callLifecycleHookWithHandler(
hook: Function,
instanceProxy: ComponentInstance,
type: ErrorTypes,
arg?: any
) {
try {
const res = hook.call(instanceProxy, arg)
if (res && typeof res.then === 'function') {
;(res as Promise<any>).catch(err => {
handleError(err, instanceProxy._self, type)
})
}
} catch (err) {
handleError(err, instanceProxy._self, type)
}
}
export function handleError(
err: Error,
instance: ComponentInstance | VNode | null,
type: ErrorTypes
) {
const isFunctional = instance && (instance as VNode)._isVNode
const contextVNode =
instance &&
((isFunctional
? instance
: (instance as ComponentInstance).$parentVNode) as VNode | null)
let cur: ComponentInstance | null = null
if (isFunctional) {
let vnode = instance as VNode | null
while (vnode && !(vnode.flags & VNodeFlags.COMPONENT_STATEFUL)) {
vnode = vnode.contextVNode
}
if (vnode) {
cur = vnode.children as ComponentInstance
}
} else if (instance) {
cur = (instance as ComponentInstance).$parent
}
while (cur) {
cur = cur._self
const handler = cur.errorCaptured
if (handler) {
try {
const captured = handler.call(
cur,
err,
type,
isFunctional ? null : instance
)
if (captured) return
} catch (err2) {
logError(err2, ErrorTypes.ERROR_CAPTURED, contextVNode)
}
}
cur = cur.$parent
}
logError(err, type, contextVNode)
}
function logError(err: Error, type: ErrorTypes, contextVNode: VNode | null) {
if (__DEV__) {
const info = ErrorTypeStrings[type]
if (contextVNode) {
pushWarningContext(contextVNode)
}
warn(`Unhandled error${info ? ` ${info}` : ``}`)
if (contextVNode) {
popWarningContext()
}
console.error(err)
} else {
throw err
}
}