2019-08-31 00:16:09 +08:00
|
|
|
import { VNode } from './vnode'
|
|
|
|
import { ComponentInstance, LifecycleHooks } from './component'
|
|
|
|
import { warn, pushWarningContext, popWarningContext } from './warning'
|
|
|
|
|
|
|
|
// contexts where user provided function may be executed, in addition to
|
|
|
|
// lifecycle hooks.
|
2019-08-31 03:15:23 +08:00
|
|
|
export const enum ErrorTypes {
|
2019-08-31 03:05:39 +08:00
|
|
|
SETUP_FUNCTION = 1,
|
|
|
|
RENDER_FUNCTION,
|
|
|
|
WATCH_GETTER,
|
2019-08-31 00:16:09 +08:00
|
|
|
WATCH_CALLBACK,
|
2019-08-31 03:05:39 +08:00
|
|
|
WATCH_CLEANUP,
|
2019-08-31 00:16:09 +08:00
|
|
|
NATIVE_EVENT_HANDLER,
|
|
|
|
COMPONENT_EVENT_HANDLER,
|
|
|
|
SCHEDULER
|
|
|
|
}
|
|
|
|
|
|
|
|
export const ErrorTypeStrings: Record<number | string, string> = {
|
|
|
|
[LifecycleHooks.BEFORE_CREATE]: 'beforeCreate hook',
|
|
|
|
[LifecycleHooks.CREATED]: 'created hook',
|
|
|
|
[LifecycleHooks.BEFORE_MOUNT]: 'beforeMount hook',
|
|
|
|
[LifecycleHooks.MOUNTED]: 'mounted hook',
|
|
|
|
[LifecycleHooks.BEFORE_UPDATE]: 'beforeUpdate hook',
|
|
|
|
[LifecycleHooks.UPDATED]: 'updated',
|
|
|
|
[LifecycleHooks.BEFORE_UNMOUNT]: 'beforeUnmount hook',
|
|
|
|
[LifecycleHooks.UNMOUNTED]: 'unmounted hook',
|
|
|
|
[LifecycleHooks.ACTIVATED]: 'activated hook',
|
|
|
|
[LifecycleHooks.DEACTIVATED]: 'deactivated hook',
|
|
|
|
[LifecycleHooks.ERROR_CAPTURED]: 'errorCaptured hook',
|
|
|
|
[LifecycleHooks.RENDER_TRACKED]: 'renderTracked hook',
|
|
|
|
[LifecycleHooks.RENDER_TRIGGERED]: 'renderTriggered hook',
|
2019-08-31 03:15:23 +08:00
|
|
|
[ErrorTypes.SETUP_FUNCTION]: 'setup function',
|
|
|
|
[ErrorTypes.RENDER_FUNCTION]: 'render function',
|
|
|
|
[ErrorTypes.WATCH_GETTER]: 'watcher getter',
|
|
|
|
[ErrorTypes.WATCH_CALLBACK]: 'watcher callback',
|
|
|
|
[ErrorTypes.WATCH_CLEANUP]: 'watcher cleanup function',
|
|
|
|
[ErrorTypes.NATIVE_EVENT_HANDLER]: 'native event handler',
|
|
|
|
[ErrorTypes.COMPONENT_EVENT_HANDLER]: 'component event handler',
|
|
|
|
[ErrorTypes.SCHEDULER]:
|
2019-08-31 00:16:09 +08:00
|
|
|
'scheduler flush. This may be a Vue internals bug. ' +
|
|
|
|
'Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue'
|
|
|
|
}
|
|
|
|
|
2019-08-31 03:15:23 +08:00
|
|
|
type AllErrorTypes = LifecycleHooks | ErrorTypes
|
2019-08-31 00:16:09 +08:00
|
|
|
|
2019-08-31 03:05:39 +08:00
|
|
|
export function callWithErrorHandling(
|
2019-08-31 00:22:41 +08:00
|
|
|
fn: Function,
|
2019-08-31 00:16:09 +08:00
|
|
|
instance: ComponentInstance | null,
|
2019-08-31 03:15:23 +08:00
|
|
|
type: AllErrorTypes,
|
2019-08-31 00:22:41 +08:00
|
|
|
args?: any[]
|
|
|
|
) {
|
|
|
|
let res: any
|
|
|
|
try {
|
|
|
|
res = args ? fn(...args) : fn()
|
|
|
|
} catch (err) {
|
|
|
|
handleError(err, instance, type)
|
|
|
|
}
|
|
|
|
return res
|
2019-08-31 00:16:09 +08:00
|
|
|
}
|
|
|
|
|
2019-08-31 03:05:39 +08:00
|
|
|
export function callWithAsyncErrorHandling(
|
|
|
|
fn: Function,
|
|
|
|
instance: ComponentInstance | null,
|
2019-08-31 03:15:23 +08:00
|
|
|
type: AllErrorTypes,
|
2019-08-31 03:05:39 +08:00
|
|
|
args?: any[]
|
|
|
|
) {
|
|
|
|
const res = callWithErrorHandling(fn, instance, type, args)
|
|
|
|
if (res != null && !res._isVue && typeof res.then === 'function') {
|
|
|
|
;(res as Promise<any>).catch(err => {
|
|
|
|
handleError(err, instance, type)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2019-08-31 00:16:09 +08:00
|
|
|
export function handleError(
|
|
|
|
err: Error,
|
|
|
|
instance: ComponentInstance | null,
|
2019-08-31 03:15:23 +08:00
|
|
|
type: AllErrorTypes
|
2019-08-31 00:16:09 +08:00
|
|
|
) {
|
|
|
|
const contextVNode = instance ? instance.vnode : null
|
|
|
|
let cur: ComponentInstance | null = instance && instance.parent
|
|
|
|
while (cur) {
|
|
|
|
const errorCapturedHooks = cur.ec
|
|
|
|
if (errorCapturedHooks !== null) {
|
|
|
|
for (let i = 0; i < errorCapturedHooks.length; i++) {
|
2019-08-31 03:05:39 +08:00
|
|
|
if (
|
|
|
|
errorCapturedHooks[i](
|
|
|
|
err,
|
|
|
|
instance && instance.renderProxy,
|
2019-09-01 04:43:02 +08:00
|
|
|
// in production the hook receives only the error code
|
|
|
|
__DEV__ ? ErrorTypeStrings[type] : type
|
2019-08-31 03:05:39 +08:00
|
|
|
)
|
|
|
|
) {
|
2019-08-31 00:16:09 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cur = cur.parent
|
|
|
|
}
|
|
|
|
logError(err, type, contextVNode)
|
|
|
|
}
|
|
|
|
|
2019-08-31 03:15:23 +08:00
|
|
|
function logError(err: Error, type: AllErrorTypes, contextVNode: VNode | null) {
|
2019-08-31 00:16:09 +08:00
|
|
|
if (__DEV__) {
|
|
|
|
const info = ErrorTypeStrings[type]
|
|
|
|
if (contextVNode) {
|
|
|
|
pushWarningContext(contextVNode)
|
|
|
|
}
|
|
|
|
warn(`Unhandled error${info ? ` during execution of ${info}` : ``}`)
|
|
|
|
console.error(err)
|
|
|
|
if (contextVNode) {
|
|
|
|
popWarningContext()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
throw err
|
|
|
|
}
|
|
|
|
}
|