vue3-yuanma/packages/runtime-core/src/warning.ts

153 lines
4.0 KiB
TypeScript
Raw Normal View History

2019-08-30 22:36:30 +08:00
import { VNode } from './vnode'
import {
Data,
ComponentInternalInstance,
Component,
formatComponentName
} from './component'
2019-10-08 21:26:09 +08:00
import { isString, isFunction } from '@vue/shared'
import { toRaw, isRef, pauseTracking, resetTracking } from '@vue/reactivity'
import { callWithErrorHandling, ErrorCodes } from './errorHandling'
2019-08-30 22:36:30 +08:00
2019-10-08 21:26:09 +08:00
type ComponentVNode = VNode & {
type: Component
}
const stack: VNode[] = []
2019-08-30 22:36:30 +08:00
type TraceEntry = {
2019-10-08 21:26:09 +08:00
vnode: ComponentVNode
2019-08-30 22:36:30 +08:00
recurseCount: number
}
type ComponentTraceStack = TraceEntry[]
2019-11-02 11:04:28 +08:00
export function pushWarningContext(vnode: VNode) {
2019-08-30 22:36:30 +08:00
stack.push(vnode)
}
export function popWarningContext() {
stack.pop()
}
export function warn(msg: string, ...args: any[]) {
2019-11-03 11:21:02 +08:00
// avoid props formatting or warn handler tracking deps that might be mutated
// during patch, leading to infinite recursion.
pauseTracking()
2019-09-04 06:11:04 +08:00
const instance = stack.length ? stack[stack.length - 1].component : null
const appWarnHandler = instance && instance.appContext.config.warnHandler
const trace = getComponentTrace()
if (appWarnHandler) {
callWithErrorHandling(
appWarnHandler,
instance,
ErrorCodes.APP_WARN_HANDLER,
[
msg + args.join(''),
instance && instance.proxy,
2019-11-03 11:21:02 +08:00
trace
.map(({ vnode }) => `at <${formatComponentName(vnode.type)}>`)
2019-11-03 11:21:02 +08:00
.join('\n'),
trace
]
2019-09-04 06:11:04 +08:00
)
2019-08-30 22:36:30 +08:00
} else {
2019-11-03 11:21:02 +08:00
const warnArgs = [`[Vue warn]: ${msg}`, ...args]
if (
trace.length &&
// avoid spamming console during tests
2019-11-05 00:24:22 +08:00
!__TEST__
2019-11-03 11:21:02 +08:00
) {
warnArgs.push(`\n`, ...formatTrace(trace))
}
console.warn(...warnArgs)
2019-08-30 22:36:30 +08:00
}
2019-11-03 11:21:02 +08:00
resetTracking()
2019-08-30 22:36:30 +08:00
}
function getComponentTrace(): ComponentTraceStack {
let currentVNode: VNode | null = stack[stack.length - 1]
if (!currentVNode) {
return []
}
// we can't just use the stack because it will be incomplete during updates
// that did not start from the root. Re-construct the parent chain using
// instance parent pointers.
2019-10-05 22:48:54 +08:00
const normalizedStack: ComponentTraceStack = []
2019-08-30 22:36:30 +08:00
while (currentVNode) {
2019-10-05 22:48:54 +08:00
const last = normalizedStack[0]
2019-08-30 22:36:30 +08:00
if (last && last.vnode === currentVNode) {
last.recurseCount++
} else {
2019-10-05 22:48:54 +08:00
normalizedStack.push({
2019-11-02 11:04:28 +08:00
vnode: currentVNode as ComponentVNode,
2019-08-30 22:36:30 +08:00
recurseCount: 0
})
}
2019-10-05 22:09:34 +08:00
const parentInstance: ComponentInternalInstance | null = currentVNode.component!
2019-08-30 22:36:30 +08:00
.parent
currentVNode = parentInstance && parentInstance.vnode
}
2019-10-05 22:48:54 +08:00
return normalizedStack
2019-08-30 22:36:30 +08:00
}
2019-11-03 11:21:02 +08:00
function formatTrace(trace: ComponentTraceStack): any[] {
const logs: any[] = []
2019-09-04 06:11:04 +08:00
trace.forEach((entry, i) => {
2019-11-03 11:21:02 +08:00
logs.push(...(i === 0 ? [] : [`\n`]), ...formatTraceEntry(entry))
2019-09-04 06:11:04 +08:00
})
return logs
}
2019-11-03 11:21:02 +08:00
function formatTraceEntry({ vnode, recurseCount }: TraceEntry): any[] {
2019-08-30 22:36:30 +08:00
const postfix =
recurseCount > 0 ? `... (${recurseCount} recursive calls)` : ``
const isRoot = vnode.component!.parent == null
const open = ` at <${formatComponentName(vnode.type, isRoot)}`
2019-08-30 22:36:30 +08:00
const close = `>` + postfix
return vnode.props
? [open, ...formatProps(vnode.props), close]
: [open + close]
2019-08-30 22:36:30 +08:00
}
2019-11-03 11:21:02 +08:00
function formatProps(props: Data): any[] {
const res: any[] = []
const keys = Object.keys(props)
keys.slice(0, 3).forEach(key => {
2019-11-03 11:21:02 +08:00
res.push(...formatProp(key, props[key]))
})
if (keys.length > 3) {
res.push(` ...`)
2019-08-30 22:36:30 +08:00
}
return res
2019-05-28 18:06:00 +08:00
}
2019-11-03 11:21:02 +08:00
function formatProp(key: string, value: unknown): any[]
function formatProp(key: string, value: unknown, raw: true): any
function formatProp(key: string, value: unknown, raw?: boolean): any {
if (isString(value)) {
value = JSON.stringify(value)
return raw ? value : [`${key}=${value}`]
} else if (
typeof value === 'number' ||
typeof value === 'boolean' ||
value == null
) {
2019-11-03 11:21:02 +08:00
return raw ? value : [`${key}=${value}`]
} else if (isRef(value)) {
value = formatProp(key, toRaw(value.value), true)
return raw ? value : [`${key}=Ref<`, value, `>`]
} else if (isFunction(value)) {
return [`${key}=fn${value.name ? `<${value.name}>` : ``}`]
2019-11-03 11:21:02 +08:00
} else {
value = toRaw(value)
return raw ? value : [`${key}=`, value]
}
}