import { VNode } from './vnode'
import { Data, ComponentInternalInstance, Component } from './component'
import { isString, isFunction } from '@vue/shared'
-import { toRaw } from '@vue/reactivity'
+import { toRaw, isRef, pauseTracking, resumeTracking } from '@vue/reactivity'
import { callWithErrorHandling, ErrorCodes } from './errorHandling'
type ComponentVNode = VNode & {
}
export function warn(msg: string, ...args: any[]) {
+ // avoid props formatting or warn handler tracking deps that might be mutated
+ // during patch, leading to infinite recursion.
+ pauseTracking()
+
const instance = stack.length ? stack[stack.length - 1].component : null
const appWarnHandler = instance && instance.appContext.config.warnHandler
const trace = getComponentTrace()
[
msg + args.join(''),
instance && instance.renderProxy,
- formatTrace(trace).join('')
+ trace
+ .map(({ vnode }) => `at <${formatComponentName(vnode)}>`)
+ .join('\n'),
+ trace
]
)
- return
- }
-
- console.warn(`[Vue warn]: ${msg}`, ...args)
- // avoid spamming console during tests
- if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') {
- return
- }
- if (!trace.length) {
- return
- }
- if (trace.length > 1 && console.groupCollapsed) {
- console.groupCollapsed('at', ...formatTraceEntry(trace[0]))
- const logs: string[] = []
- trace.slice(1).forEach((entry, i) => {
- if (i !== 0) logs.push('\n')
- logs.push(...formatTraceEntry(entry, i + 1))
- })
- console.log(...logs)
- console.groupEnd()
} else {
- console.log(...formatTrace(trace))
+ const warnArgs = [`[Vue warn]: ${msg}`, ...args]
+ if (
+ trace.length &&
+ // avoid spamming console during tests
+ (typeof process === 'undefined' || process.env.NODE_ENV !== 'test')
+ ) {
+ warnArgs.push(`\n`, ...formatTrace(trace))
+ }
+ console.warn(...warnArgs)
}
+
+ resumeTracking()
}
function getComponentTrace(): ComponentTraceStack {
return normalizedStack
}
-function formatTrace(trace: ComponentTraceStack): string[] {
- const logs: string[] = []
+function formatTrace(trace: ComponentTraceStack): any[] {
+ const logs: any[] = []
trace.forEach((entry, i) => {
- const formatted = formatTraceEntry(entry, i)
- if (i === 0) {
- logs.push('at', ...formatted)
- } else {
- logs.push('\n', ...formatted)
- }
+ logs.push(...(i === 0 ? [] : [`\n`]), ...formatTraceEntry(entry))
})
return logs
}
-function formatTraceEntry(
- { vnode, recurseCount }: TraceEntry,
- depth: number = 0
-): string[] {
- const padding = depth === 0 ? '' : ' '.repeat(depth * 2 + 1)
+function formatTraceEntry({ vnode, recurseCount }: TraceEntry): any[] {
const postfix =
recurseCount > 0 ? `... (${recurseCount} recursive calls)` : ``
- const open = padding + `<${formatComponentName(vnode)}`
+ const open = ` at <${formatComponentName(vnode)}`
const close = `>` + postfix
const rootLabel = vnode.component!.parent == null ? `(Root)` : ``
return vnode.props
function formatComponentName(vnode: ComponentVNode, file?: string): string {
const Component = vnode.type
- let name = isFunction(Component) ? Component.displayName : Component.name
+ let name = isFunction(Component)
+ ? Component.displayName || Component.name
+ : Component.name
if (!name && file) {
const match = file.match(/([^/\\]+)\.vue$/)
if (match) {
name = match[1]
}
}
- return name ? classify(name) : 'AnonymousComponent'
+ return name ? classify(name) : 'Anonymous'
}
-function formatProps(props: Data): string[] {
- const res: string[] = []
+function formatProps(props: Data): any[] {
+ const res: any[] = []
for (const key in props) {
- const value = props[key]
- if (isString(value)) {
- res.push(`${key}=${JSON.stringify(value)}`)
- } else {
- res.push(`${key}=`, String(toRaw(value)))
- }
+ res.push(...formatProp(key, props[key]))
}
return res
}
+
+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' || value == null) {
+ return raw ? value : [`${key}=${value}`]
+ } else if (isRef(value)) {
+ value = formatProp(key, toRaw(value.value), true)
+ return raw ? value : [`${key}=Ref<`, value, `>`]
+ } else {
+ value = toRaw(value)
+ return raw ? value : [`${key}=`, value]
+ }
+}