| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- import type { VNode } from './vnode'
- import {
- type ComponentInternalInstance,
- type ConcreteComponent,
- type Data,
- formatComponentName,
- } from './component'
- import { isFunction, isString } from '@vue/shared'
- import { isRef, pauseTracking, resetTracking, toRaw } from '@vue/reactivity'
- import { ErrorCodes, callWithErrorHandling } from './errorHandling'
- type ComponentVNode = VNode & {
- type: ConcreteComponent
- }
- const stack: VNode[] = []
- type TraceEntry = {
- vnode: ComponentVNode
- recurseCount: number
- }
- type ComponentTraceStack = TraceEntry[]
- export function pushWarningContext(vnode: VNode) {
- stack.push(vnode)
- }
- export function popWarningContext() {
- stack.pop()
- }
- let isWarning = false
- export function warn(msg: string, ...args: any[]) {
- if (isWarning) return
- isWarning = true
- // 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()
- if (appWarnHandler) {
- callWithErrorHandling(
- appWarnHandler,
- instance,
- ErrorCodes.APP_WARN_HANDLER,
- [
- // eslint-disable-next-line no-restricted-syntax
- msg + args.map(a => a.toString?.() ?? JSON.stringify(a)).join(''),
- instance && instance.proxy,
- trace
- .map(
- ({ vnode }) => `at <${formatComponentName(instance, vnode.type)}>`,
- )
- .join('\n'),
- trace,
- ],
- )
- } else {
- const warnArgs = [`[Vue warn]: ${msg}`, ...args]
- /* istanbul ignore if */
- if (
- trace.length &&
- // avoid spamming console during tests
- !__TEST__
- ) {
- warnArgs.push(`\n`, ...formatTrace(trace))
- }
- console.warn(...warnArgs)
- }
- resetTracking()
- isWarning = false
- }
- export 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.
- const normalizedStack: ComponentTraceStack = []
- while (currentVNode) {
- const last = normalizedStack[0]
- if (last && last.vnode === currentVNode) {
- last.recurseCount++
- } else {
- normalizedStack.push({
- vnode: currentVNode as ComponentVNode,
- recurseCount: 0,
- })
- }
- const parentInstance: ComponentInternalInstance | null =
- currentVNode.component && currentVNode.component.parent
- currentVNode = parentInstance && parentInstance.vnode
- }
- return normalizedStack
- }
- /* istanbul ignore next */
- function formatTrace(trace: ComponentTraceStack): any[] {
- const logs: any[] = []
- trace.forEach((entry, i) => {
- logs.push(...(i === 0 ? [] : [`\n`]), ...formatTraceEntry(entry))
- })
- return logs
- }
- function formatTraceEntry({ vnode, recurseCount }: TraceEntry): any[] {
- const postfix =
- recurseCount > 0 ? `... (${recurseCount} recursive calls)` : ``
- const isRoot = vnode.component ? vnode.component.parent == null : false
- const open = ` at <${formatComponentName(
- vnode.component,
- vnode.type,
- isRoot,
- )}`
- const close = `>` + postfix
- return vnode.props
- ? [open, ...formatProps(vnode.props), close]
- : [open + close]
- }
- /* istanbul ignore next */
- function formatProps(props: Data): any[] {
- const res: any[] = []
- const keys = Object.keys(props)
- keys.slice(0, 3).forEach(key => {
- res.push(...formatProp(key, props[key]))
- })
- if (keys.length > 3) {
- res.push(` ...`)
- }
- return res
- }
- function formatProp(key: string, value: unknown): any[]
- function formatProp(key: string, value: unknown, raw: true): any
- /* istanbul ignore next */
- 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
- ) {
- 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}>` : ``}`]
- } else {
- value = toRaw(value)
- return raw ? value : [`${key}=`, value]
- }
- }
- /**
- * @internal
- */
- export function assertNumber(val: unknown, type: string) {
- if (!__DEV__) return
- if (val === undefined) {
- return
- } else if (typeof val !== 'number') {
- warn(`${type} is not a valid number - ` + `got ${JSON.stringify(val)}.`)
- } else if (isNaN(val)) {
- warn(`${type} is NaN - ` + 'the duration expression might be incorrect.')
- }
- }
|