test: test for app-level APIs
This commit is contained in:
parent
1e4535dc78
commit
98d1406214
@ -86,6 +86,7 @@ describe('reactivity/readonly', () => {
|
||||
observed.a = 2
|
||||
expect(observed.a).toBe(1)
|
||||
expect(dummy).toBe(1)
|
||||
expect(`target is readonly`).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
it('should trigger effects when unlocked', () => {
|
||||
@ -178,9 +179,11 @@ describe('reactivity/readonly', () => {
|
||||
observed[0].a = 2
|
||||
expect(observed[0].a).toBe(1)
|
||||
expect(dummy).toBe(1)
|
||||
expect(`target is readonly`).toHaveBeenWarnedTimes(1)
|
||||
observed[0] = { a: 2 }
|
||||
expect(observed[0].a).toBe(1)
|
||||
expect(dummy).toBe(1)
|
||||
expect(`target is readonly`).toHaveBeenWarnedTimes(2)
|
||||
})
|
||||
|
||||
it('should trigger effects when unlocked', () => {
|
||||
|
206
packages/runtime-core/__tests__/apiApp.spec.ts
Normal file
206
packages/runtime-core/__tests__/apiApp.spec.ts
Normal file
@ -0,0 +1,206 @@
|
||||
import {
|
||||
createApp,
|
||||
h,
|
||||
nodeOps,
|
||||
serializeInner,
|
||||
mockWarn,
|
||||
provide,
|
||||
inject,
|
||||
resolveComponent,
|
||||
resolveDirective,
|
||||
applyDirectives,
|
||||
Plugin,
|
||||
ref
|
||||
} from '@vue/runtime-test'
|
||||
|
||||
describe('api: createApp', () => {
|
||||
mockWarn()
|
||||
|
||||
test('mount', () => {
|
||||
const Comp = {
|
||||
props: {
|
||||
count: {
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return this.count
|
||||
}
|
||||
}
|
||||
|
||||
const root1 = nodeOps.createElement('div')
|
||||
createApp().mount(Comp, root1)
|
||||
expect(serializeInner(root1)).toBe(`0`)
|
||||
|
||||
// mount with props
|
||||
const root2 = nodeOps.createElement('div')
|
||||
const app2 = createApp()
|
||||
app2.mount(Comp, root2, { count: 1 })
|
||||
expect(serializeInner(root2)).toBe(`1`)
|
||||
|
||||
// remount warning
|
||||
const root3 = nodeOps.createElement('div')
|
||||
app2.mount(Comp, root3)
|
||||
expect(serializeInner(root3)).toBe(``)
|
||||
expect(`already been mounted`).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('provide', () => {
|
||||
const app = createApp()
|
||||
app.provide('foo', 1)
|
||||
app.provide('bar', 2)
|
||||
|
||||
const Root = {
|
||||
setup() {
|
||||
// test override
|
||||
provide('foo', 3)
|
||||
return () => h(Child)
|
||||
}
|
||||
}
|
||||
|
||||
const Child = {
|
||||
setup() {
|
||||
const foo = inject('foo')
|
||||
const bar = inject('bar')
|
||||
return () => `${foo},${bar}`
|
||||
}
|
||||
}
|
||||
|
||||
const root = nodeOps.createElement('div')
|
||||
app.mount(Root, root)
|
||||
expect(serializeInner(root)).toBe(`3,2`)
|
||||
})
|
||||
|
||||
test('component', () => {
|
||||
const app = createApp()
|
||||
app.component('FooBar', () => 'foobar!')
|
||||
app.component('BarBaz', () => 'barbaz!')
|
||||
|
||||
const Root = {
|
||||
// local override
|
||||
components: {
|
||||
BarBaz: () => 'barbaz-local!'
|
||||
},
|
||||
setup() {
|
||||
// resolve in setup
|
||||
const FooBar = resolveComponent('foo-bar') as any
|
||||
return () => {
|
||||
// resolve in render
|
||||
const BarBaz = resolveComponent('bar-baz') as any
|
||||
return h('div', [h(FooBar), h(BarBaz)])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const root = nodeOps.createElement('div')
|
||||
app.mount(Root, root)
|
||||
expect(serializeInner(root)).toBe(`<div>foobar!barbaz-local!</div>`)
|
||||
})
|
||||
|
||||
test('directive', () => {
|
||||
const app = createApp()
|
||||
|
||||
const spy1 = jest.fn()
|
||||
const spy2 = jest.fn()
|
||||
const spy3 = jest.fn()
|
||||
app.directive('FooBar', {
|
||||
mounted: spy1
|
||||
})
|
||||
app.directive('BarBaz', {
|
||||
mounted: spy2
|
||||
})
|
||||
|
||||
const Root = {
|
||||
// local override
|
||||
directives: {
|
||||
BarBaz: { mounted: spy3 }
|
||||
},
|
||||
setup() {
|
||||
// resolve in setup
|
||||
const FooBar = resolveDirective('foo-bar') as any
|
||||
return () => {
|
||||
// resolve in render
|
||||
const BarBaz = resolveDirective('bar-baz') as any
|
||||
return applyDirectives(h('div'), [[FooBar], [BarBaz]])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const root = nodeOps.createElement('div')
|
||||
app.mount(Root, root)
|
||||
expect(spy1).toHaveBeenCalled()
|
||||
expect(spy2).not.toHaveBeenCalled()
|
||||
expect(spy3).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('use', () => {
|
||||
const PluginA: Plugin = app => app.provide('foo', 1)
|
||||
const PluginB: Plugin = {
|
||||
install: app => app.provide('bar', 2)
|
||||
}
|
||||
|
||||
const app = createApp()
|
||||
app.use(PluginA)
|
||||
app.use(PluginB)
|
||||
|
||||
const Root = {
|
||||
setup() {
|
||||
const foo = inject('foo')
|
||||
const bar = inject('bar')
|
||||
return () => `${foo},${bar}`
|
||||
}
|
||||
}
|
||||
const root = nodeOps.createElement('div')
|
||||
app.mount(Root, root)
|
||||
expect(serializeInner(root)).toBe(`1,2`)
|
||||
})
|
||||
|
||||
test('config.errorHandler', () => {
|
||||
const app = createApp()
|
||||
|
||||
const error = new Error()
|
||||
const count = ref(0)
|
||||
|
||||
const handler = (app.config.errorHandler = jest.fn(
|
||||
(err, instance, info) => {
|
||||
expect(err).toBe(error)
|
||||
expect((instance as any).count).toBe(count.value)
|
||||
expect(info).toBe(`render function`)
|
||||
}
|
||||
))
|
||||
|
||||
const Root = {
|
||||
setup() {
|
||||
const count = ref(0)
|
||||
return {
|
||||
count
|
||||
}
|
||||
},
|
||||
render() {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
app.mount(Root, nodeOps.createElement('div'))
|
||||
expect(handler).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('config.warnHandler', () => {
|
||||
const app = createApp()
|
||||
|
||||
const handler = (app.config.warnHandler = jest.fn(
|
||||
(msg, instance, trace) => {}
|
||||
))
|
||||
|
||||
const Root = {
|
||||
setup() {}
|
||||
}
|
||||
|
||||
app.mount(Root, nodeOps.createElement('div'))
|
||||
expect(handler).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test.todo('mixin')
|
||||
|
||||
test.todo('config.optionsMergeStrategies')
|
||||
})
|
@ -1,19 +0,0 @@
|
||||
describe('api: createApp', () => {
|
||||
test('mount', () => {})
|
||||
|
||||
test('provide', () => {})
|
||||
|
||||
test('component', () => {})
|
||||
|
||||
test('directive', () => {})
|
||||
|
||||
test('use', () => {})
|
||||
|
||||
test.todo('mixin')
|
||||
|
||||
test('config.errorHandler', () => {})
|
||||
|
||||
test('config.warnHandler', () => {})
|
||||
|
||||
test.todo('config.optionsMergeStrategies')
|
||||
})
|
@ -109,6 +109,7 @@ describe('directives', () => {
|
||||
render() {
|
||||
_prevVnode = _vnode
|
||||
_vnode = applyDirectives(h('div', count.value), [
|
||||
[
|
||||
{
|
||||
beforeMount,
|
||||
mounted,
|
||||
@ -123,6 +124,7 @@ describe('directives', () => {
|
||||
'foo',
|
||||
// modifiers
|
||||
{ ok: true }
|
||||
]
|
||||
])
|
||||
return _vnode
|
||||
}
|
||||
|
@ -36,12 +36,12 @@ export interface AppConfig {
|
||||
performance: boolean
|
||||
errorHandler?: (
|
||||
err: Error,
|
||||
instance: ComponentRenderProxy,
|
||||
instance: ComponentRenderProxy | null,
|
||||
info: string
|
||||
) => void
|
||||
warnHandler?: (
|
||||
msg: string,
|
||||
instance: ComponentRenderProxy,
|
||||
instance: ComponentRenderProxy | null,
|
||||
trace: string
|
||||
) => void
|
||||
}
|
||||
@ -56,7 +56,7 @@ export interface AppContext {
|
||||
|
||||
type PluginInstallFunction = (app: App) => any
|
||||
|
||||
type Plugin =
|
||||
export type Plugin =
|
||||
| PluginInstallFunction
|
||||
| {
|
||||
install: PluginInstallFunction
|
||||
@ -82,6 +82,8 @@ export function createAppAPI(render: RootRenderFunction): () => App {
|
||||
return function createApp(): App {
|
||||
const context = createAppContext()
|
||||
|
||||
let isMounted = false
|
||||
|
||||
const app: App = {
|
||||
get config() {
|
||||
return context.config
|
||||
@ -134,14 +136,20 @@ export function createAppAPI(render: RootRenderFunction): () => App {
|
||||
}
|
||||
},
|
||||
|
||||
mount(rootComponent, rootContainer, rootProps?: Data) {
|
||||
mount(rootComponent, rootContainer, rootProps?: Data): any {
|
||||
if (!isMounted) {
|
||||
const vnode = createVNode(rootComponent, rootProps)
|
||||
// store app context on the root VNode.
|
||||
// this will be set on the root instance on initial mount.
|
||||
vnode.appContext = context
|
||||
render(vnode, rootContainer)
|
||||
return (vnode.component as ComponentInstance)
|
||||
.renderProxy as ComponentRenderProxy
|
||||
isMounted = true
|
||||
return (vnode.component as ComponentInstance).renderProxy
|
||||
} else if (__DEV__) {
|
||||
warn(
|
||||
`App has already been mounted. Create a new app instance instead.`
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
provide(key, value) {
|
||||
@ -164,15 +172,21 @@ export function resolveAsset(type: 'components' | 'directives', name: string) {
|
||||
if (instance) {
|
||||
let camelized
|
||||
let capitalized
|
||||
let res
|
||||
const local = (instance.type as any)[type]
|
||||
const global = instance.appContext[type]
|
||||
const res =
|
||||
if (local) {
|
||||
res =
|
||||
local[name] ||
|
||||
local[(camelized = camelize(name))] ||
|
||||
local[(capitalized = capitalize(name))] ||
|
||||
local[(capitalized = capitalize(camelized))]
|
||||
}
|
||||
if (!res) {
|
||||
const global = instance.appContext[type]
|
||||
res =
|
||||
global[name] ||
|
||||
global[camelized] ||
|
||||
global[capitalized]
|
||||
global[camelized || (camelized = camelize(name))] ||
|
||||
global[capitalized || capitalize(camelized)]
|
||||
}
|
||||
if (__DEV__ && !res) {
|
||||
warn(`Failed to resolve ${type.slice(0, -1)}: ${name}`)
|
||||
}
|
@ -6,7 +6,7 @@ export interface InjectionKey<T> extends Symbol {}
|
||||
export function provide<T>(key: InjectionKey<T> | string, value: T) {
|
||||
if (!currentInstance) {
|
||||
if (__DEV__) {
|
||||
warn(`provide() is used without an active component instance.`)
|
||||
warn(`provide() can only be used inside setup().`)
|
||||
}
|
||||
} else {
|
||||
let provides = currentInstance.provides
|
||||
@ -27,17 +27,16 @@ export function provide<T>(key: InjectionKey<T> | string, value: T) {
|
||||
export function inject<T>(key: InjectionKey<T> | string): T | undefined
|
||||
export function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T
|
||||
export function inject(key: InjectionKey<any> | string, defaultValue?: any) {
|
||||
if (!currentInstance) {
|
||||
// TODO warn
|
||||
} else {
|
||||
// TODO should also check for app-level provides
|
||||
const provides = currentInstance.parent && currentInstance.provides
|
||||
if (provides && key in provides) {
|
||||
if (currentInstance) {
|
||||
const provides = currentInstance.provides
|
||||
if (key in provides) {
|
||||
return provides[key as any] as any
|
||||
} else if (defaultValue !== undefined) {
|
||||
return defaultValue
|
||||
} else if (__DEV__) {
|
||||
warn(`injection "${key}" not found.`)
|
||||
}
|
||||
} else if (__DEV__) {
|
||||
warn(`inject() can only be used inside setup().`)
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ import {
|
||||
callWithErrorHandling,
|
||||
callWithAsyncErrorHandling
|
||||
} from './errorHandling'
|
||||
import { AppContext, createAppContext, resolveAsset } from './apiCreateApp'
|
||||
import { AppContext, createAppContext, resolveAsset } from './apiApp'
|
||||
import { Directive } from './directives'
|
||||
|
||||
export type Data = { [key: string]: unknown }
|
||||
|
||||
@ -31,41 +32,42 @@ export type ComponentRenderProxy<P = {}, S = {}, PublicProps = P> = {
|
||||
} & P &
|
||||
S
|
||||
|
||||
type SetupFunction<Props, RawBindings> = (
|
||||
props: Props,
|
||||
ctx: SetupContext
|
||||
) => RawBindings | (() => VNodeChild)
|
||||
|
||||
type RenderFunction<Props = {}, RawBindings = {}> = <
|
||||
Bindings extends UnwrapRef<RawBindings>
|
||||
>(
|
||||
this: ComponentRenderProxy<Props, Bindings>
|
||||
) => VNodeChild
|
||||
|
||||
interface ComponentOptionsWithoutProps<Props = Data, RawBindings = Data> {
|
||||
props?: undefined
|
||||
setup?: SetupFunction<Props, RawBindings>
|
||||
interface ComponentOptionsBase<Props, RawBindings> {
|
||||
setup?: (
|
||||
props: Props,
|
||||
ctx: SetupContext
|
||||
) => RawBindings | (() => VNodeChild) | void
|
||||
render?: RenderFunction<Props, RawBindings>
|
||||
components?: Record<string, Component>
|
||||
directives?: Record<string, Directive>
|
||||
// TODO full 2.x options compat
|
||||
}
|
||||
|
||||
interface ComponentOptionsWithoutProps<Props = {}, RawBindings = {}>
|
||||
extends ComponentOptionsBase<Props, RawBindings> {
|
||||
props?: undefined
|
||||
}
|
||||
|
||||
interface ComponentOptionsWithArrayProps<
|
||||
PropNames extends string = string,
|
||||
RawBindings = Data,
|
||||
RawBindings = {},
|
||||
Props = { [key in PropNames]?: unknown }
|
||||
> {
|
||||
> extends ComponentOptionsBase<Props, RawBindings> {
|
||||
props: PropNames[]
|
||||
setup?: SetupFunction<Props, RawBindings>
|
||||
render?: RenderFunction<Props, RawBindings>
|
||||
}
|
||||
|
||||
interface ComponentOptionsWithProps<
|
||||
PropsOptions = ComponentPropsOptions,
|
||||
RawBindings = Data,
|
||||
RawBindings = {},
|
||||
Props = ExtractPropTypes<PropsOptions>
|
||||
> {
|
||||
> extends ComponentOptionsBase<Props, RawBindings> {
|
||||
props: PropsOptions
|
||||
setup?: SetupFunction<Props, RawBindings>
|
||||
render?: RenderFunction<Props, RawBindings>
|
||||
}
|
||||
|
||||
export type ComponentOptions =
|
||||
@ -105,7 +107,7 @@ interface SetupContext {
|
||||
emit: ((event: string, ...args: unknown[]) => void)
|
||||
}
|
||||
|
||||
export type ComponentInstance<P = Data, S = Data> = {
|
||||
export type ComponentInstance<P = {}, S = {}> = {
|
||||
type: FunctionalComponent | ComponentOptions
|
||||
parent: ComponentInstance | null
|
||||
appContext: AppContext
|
||||
@ -193,12 +195,13 @@ export function createComponentInstance(
|
||||
vnode: VNode,
|
||||
parent: ComponentInstance | null
|
||||
): ComponentInstance {
|
||||
// inherit parent app context - or - if root, adopt from root vnode
|
||||
const appContext =
|
||||
(parent ? parent.appContext : vnode.appContext) || emptyAppContext
|
||||
const instance = {
|
||||
vnode,
|
||||
parent,
|
||||
// inherit parent app context - or - if root, adopt from root vnode
|
||||
appContext:
|
||||
(parent ? parent.appContext : vnode.appContext) || emptyAppContext,
|
||||
appContext,
|
||||
type: vnode.type as any,
|
||||
root: null as any, // set later so it can point to itself
|
||||
next: null,
|
||||
@ -209,7 +212,7 @@ export function createComponentInstance(
|
||||
propsProxy: null,
|
||||
setupContext: null,
|
||||
effects: null,
|
||||
provides: parent ? parent.provides : {},
|
||||
provides: parent ? parent.provides : Object.create(appContext.provides),
|
||||
|
||||
// setup context properties
|
||||
data: EMPTY_OBJ,
|
||||
|
@ -5,11 +5,10 @@ const comp = resolveComponent('comp')
|
||||
const foo = resolveDirective('foo')
|
||||
const bar = resolveDirective('bar')
|
||||
|
||||
return applyDirectives(
|
||||
h(comp),
|
||||
return applyDirectives(h(comp), [
|
||||
[foo, this.x],
|
||||
[bar, this.y]
|
||||
)
|
||||
])
|
||||
*/
|
||||
|
||||
import { VNode, cloneVNode } from './vnode'
|
||||
@ -22,7 +21,7 @@ import {
|
||||
} from './component'
|
||||
import { callWithAsyncErrorHandling, ErrorTypes } from './errorHandling'
|
||||
import { HostNode } from './createRenderer'
|
||||
import { resolveAsset } from './apiCreateApp'
|
||||
import { resolveAsset } from './apiApp'
|
||||
|
||||
export interface DirectiveBinding {
|
||||
instance: ComponentRenderProxy | null
|
||||
@ -103,10 +102,7 @@ type DirectiveArguments = Array<
|
||||
| [Directive, any, string, DirectiveModifiers]
|
||||
>
|
||||
|
||||
export function applyDirectives(
|
||||
vnode: VNode,
|
||||
...directives: DirectiveArguments
|
||||
) {
|
||||
export function applyDirectives(vnode: VNode, directives: DirectiveArguments) {
|
||||
const instance = currentRenderingInstance
|
||||
if (instance !== null) {
|
||||
vnode = cloneVNode(vnode)
|
||||
|
@ -13,6 +13,8 @@ export const enum ErrorTypes {
|
||||
NATIVE_EVENT_HANDLER,
|
||||
COMPONENT_EVENT_HANDLER,
|
||||
DIRECTIVE_HOOK,
|
||||
APP_ERROR_HANDLER,
|
||||
APP_WARN_HANDLER,
|
||||
SCHEDULER
|
||||
}
|
||||
|
||||
@ -38,6 +40,8 @@ export const ErrorTypeStrings: Record<number | string, string> = {
|
||||
[ErrorTypes.NATIVE_EVENT_HANDLER]: 'native event handler',
|
||||
[ErrorTypes.COMPONENT_EVENT_HANDLER]: 'component event handler',
|
||||
[ErrorTypes.DIRECTIVE_HOOK]: 'directive hook',
|
||||
[ErrorTypes.APP_ERROR_HANDLER]: 'app errorHandler',
|
||||
[ErrorTypes.APP_WARN_HANDLER]: 'app warnHandler',
|
||||
[ErrorTypes.SCHEDULER]:
|
||||
'scheduler flush. This may be a Vue internals bug. ' +
|
||||
'Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue'
|
||||
@ -81,25 +85,35 @@ export function handleError(
|
||||
type: AllErrorTypes
|
||||
) {
|
||||
const contextVNode = instance ? instance.vnode : null
|
||||
let cur: ComponentInstance | null = instance && instance.parent
|
||||
if (instance) {
|
||||
let cur: ComponentInstance | null = instance.parent
|
||||
// the exposed instance is the render proxy to keep it consistent with 2.x
|
||||
const exposedInstance = instance.renderProxy
|
||||
// in production the hook receives only the error code
|
||||
const errorInfo = __DEV__ ? ErrorTypeStrings[type] : type
|
||||
while (cur) {
|
||||
const errorCapturedHooks = cur.ec
|
||||
if (errorCapturedHooks !== null) {
|
||||
for (let i = 0; i < errorCapturedHooks.length; i++) {
|
||||
if (
|
||||
errorCapturedHooks[i](
|
||||
err,
|
||||
instance && instance.renderProxy,
|
||||
// in production the hook receives only the error code
|
||||
__DEV__ ? ErrorTypeStrings[type] : type
|
||||
)
|
||||
) {
|
||||
if (errorCapturedHooks[i](err, exposedInstance, errorInfo)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
cur = cur.parent
|
||||
}
|
||||
// app-level handling
|
||||
const appErrorHandler = instance.appContext.config.errorHandler
|
||||
if (appErrorHandler) {
|
||||
callWithErrorHandling(
|
||||
appErrorHandler,
|
||||
null,
|
||||
ErrorTypes.APP_ERROR_HANDLER,
|
||||
[err, exposedInstance, errorInfo]
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
logError(err, type, contextVNode)
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ export { PublicShapeFlags as ShapeFlags } from './shapeFlags'
|
||||
export { getCurrentInstance } from './component'
|
||||
|
||||
// For custom renderers
|
||||
export { createAppAPI } from './apiCreateApp'
|
||||
export { createAppAPI } from './apiApp'
|
||||
export { createRenderer } from './createRenderer'
|
||||
export {
|
||||
handleError,
|
||||
@ -42,8 +42,8 @@ export { applyDirectives, resolveDirective } from './directives'
|
||||
|
||||
// Types -----------------------------------------------------------------------
|
||||
|
||||
export { App } from './apiCreateApp'
|
||||
export { VNode } from './vnode'
|
||||
export { App, AppConfig, AppContext, Plugin } from './apiApp'
|
||||
export { VNode, VNodeTypes } from './vnode'
|
||||
export { FunctionalComponent, ComponentInstance } from './component'
|
||||
export { RendererOptions } from './createRenderer'
|
||||
export { Slot, Slots } from './componentSlots'
|
||||
|
@ -12,7 +12,7 @@ import { RawSlots } from './componentSlots'
|
||||
import { PatchFlags } from './patchFlags'
|
||||
import { ShapeFlags } from './shapeFlags'
|
||||
import { isReactive } from '@vue/reactivity'
|
||||
import { AppContext } from './apiCreateApp'
|
||||
import { AppContext } from './apiApp'
|
||||
|
||||
export const Fragment = Symbol('Fragment')
|
||||
export const Text = Symbol('Text')
|
||||
|
@ -21,13 +21,24 @@ export function popWarningContext() {
|
||||
}
|
||||
|
||||
export function warn(msg: string, ...args: any[]) {
|
||||
// TODO app level warn handler
|
||||
const instance = stack.length ? stack[stack.length - 1].component : null
|
||||
const appWarnHandler = instance && instance.appContext.config.warnHandler
|
||||
const trace = getComponentTrace()
|
||||
|
||||
if (appWarnHandler) {
|
||||
appWarnHandler(
|
||||
msg + args.join(''),
|
||||
instance && instance.renderProxy,
|
||||
formatTrace(trace).join('')
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
console.warn(`[Vue warn]: ${msg}`, ...args)
|
||||
// avoid spamming console during tests
|
||||
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') {
|
||||
return
|
||||
}
|
||||
const trace = getComponentTrace()
|
||||
if (!trace.length) {
|
||||
return
|
||||
}
|
||||
@ -41,16 +52,7 @@ export function warn(msg: string, ...args: any[]) {
|
||||
console.log(...logs)
|
||||
console.groupEnd()
|
||||
} else {
|
||||
const logs: string[] = []
|
||||
trace.forEach((entry, i) => {
|
||||
const formatted = formatTraceEntry(entry, i)
|
||||
if (i === 0) {
|
||||
logs.push('at', ...formatted)
|
||||
} else {
|
||||
logs.push('\n', ...formatted)
|
||||
}
|
||||
})
|
||||
console.log(...logs)
|
||||
console.log(...formatTrace(trace))
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,6 +85,19 @@ function getComponentTrace(): ComponentTraceStack {
|
||||
return normlaizedStack
|
||||
}
|
||||
|
||||
function formatTrace(trace: ComponentTraceStack): string[] {
|
||||
const logs: string[] = []
|
||||
trace.forEach((entry, i) => {
|
||||
const formatted = formatTraceEntry(entry, i)
|
||||
if (i === 0) {
|
||||
logs.push('at', ...formatted)
|
||||
} else {
|
||||
logs.push('\n', ...formatted)
|
||||
}
|
||||
})
|
||||
return logs
|
||||
}
|
||||
|
||||
function formatTraceEntry(
|
||||
{ vnode, recurseCount }: TraceEntry,
|
||||
depth: number = 0
|
||||
|
Loading…
x
Reference in New Issue
Block a user