This commit is contained in:
skirtle 2025-05-05 20:43:48 +00:00 committed by GitHub
commit 80ce1ccb65
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 167 additions and 6 deletions

View File

@ -0,0 +1,157 @@
import {
type ComponentInternalInstance,
getCurrentInstance,
h,
nodeOps,
render,
} from '@vue/runtime-test'
import { formatComponentName } from '../src/component'
describe('formatComponentName', () => {
test('default name', () => {
let instance: ComponentInternalInstance | null = null
const Comp = {
setup() {
instance = getCurrentInstance()
return () => null
},
}
render(h(Comp), nodeOps.createElement('div'))
expect(formatComponentName(null, Comp)).toBe('Anonymous')
expect(formatComponentName(null, Comp, true)).toBe('App')
expect(formatComponentName(instance, Comp)).toBe('Anonymous')
expect(formatComponentName(instance, Comp, true)).toBe('App')
})
test('name option', () => {
let instance: ComponentInternalInstance | null = null
const Comp = {
name: 'number-input',
setup() {
instance = getCurrentInstance()
return () => null
},
}
render(h(Comp), nodeOps.createElement('div'))
expect(formatComponentName(null, Comp)).toBe('NumberInput')
expect(formatComponentName(instance, Comp, true)).toBe('NumberInput')
})
test('self recursive name', () => {
let instance: ComponentInternalInstance | null = null
const Comp = {
components: {} as any,
setup() {
instance = getCurrentInstance()
return () => null
},
}
Comp.components.ToggleButton = Comp
render(h(Comp), nodeOps.createElement('div'))
expect(formatComponentName(instance, Comp)).toBe('ToggleButton')
})
test('name from parent', () => {
let instance: ComponentInternalInstance | null = null
const Comp = {
setup() {
instance = getCurrentInstance()
return () => null
},
}
const Parent = {
components: {
list_item: Comp,
},
render() {
return h(Comp)
},
}
render(h(Parent), nodeOps.createElement('div'))
expect(formatComponentName(instance, Comp)).toBe('ListItem')
})
test('functional components', () => {
const UserAvatar = () => null
expect(formatComponentName(null, UserAvatar)).toBe('UserAvatar')
UserAvatar.displayName = 'UserPicture'
expect(formatComponentName(null, UserAvatar)).toBe('UserPicture')
expect(formatComponentName(null, () => null)).toBe('Anonymous')
})
test('Name from file', () => {
const Comp = {
__file: './src/locale-dropdown.vue',
}
expect(formatComponentName(null, Comp)).toBe('LocaleDropdown')
})
test('inferred name', () => {
const Comp = {
__name: 'MainSidebar',
}
expect(formatComponentName(null, Comp)).toBe('MainSidebar')
})
test('global component', () => {
let instance: ComponentInternalInstance | null = null
const Comp = {
setup() {
instance = getCurrentInstance()
return () => null
},
}
render(h(Comp), nodeOps.createElement('div'))
instance!.appContext.components.FieldLabel = Comp
expect(formatComponentName(instance, Comp)).toBe('FieldLabel')
})
test('name precedence', () => {
let instance: ComponentInternalInstance | null = null
const Dummy = () => null
const Comp: Record<string, any> = {
components: { Dummy },
setup() {
instance = getCurrentInstance()
return () => null
},
}
const Parent = {
components: { Dummy } as any,
render() {
return h(Comp)
},
}
render(h(Parent), nodeOps.createElement('div'))
expect(formatComponentName(instance, Comp)).toBe('Anonymous')
expect(formatComponentName(instance, Comp, true)).toBe('App')
instance!.appContext.components.CompA = Comp
expect(formatComponentName(instance, Comp)).toBe('CompA')
expect(formatComponentName(instance, Comp, true)).toBe('CompA')
Parent.components.CompB = Comp
expect(formatComponentName(instance, Comp)).toBe('CompB')
Comp.components.CompC = Comp
expect(formatComponentName(instance, Comp)).toBe('CompC')
Comp.__file = './CompD.js'
expect(formatComponentName(instance, Comp)).toBe('CompD')
Comp.__name = 'CompE'
expect(formatComponentName(instance, Comp)).toBe('CompE')
Comp.name = 'CompF'
expect(formatComponentName(instance, Comp)).toBe('CompF')
})
})

View File

@ -894,7 +894,7 @@ function setupStatefulComponent(
// bail here and wait for re-entry.
instance.asyncDep = setupResult
if (__DEV__ && !instance.suspense) {
const name = Component.name ?? 'Anonymous'
const name = formatComponentName(instance, Component)
warn(
`Component <${name}>: setup function returned a promise, but no ` +
`<Suspense> boundary was found in the parent component tree. ` +
@ -1226,9 +1226,11 @@ export function formatComponentName(
}
}
if (!name && instance && instance.parent) {
if (!name && instance) {
// try to infer the name based on reverse resolution
const inferFromRegistry = (registry: Record<string, any> | undefined) => {
const inferFromRegistry = (
registry: Record<string, any> | undefined | null,
) => {
for (const key in registry) {
if (registry[key] === Component) {
return key
@ -1236,10 +1238,12 @@ export function formatComponentName(
}
}
name =
inferFromRegistry(
instance.components ||
inferFromRegistry(instance.components) ||
(instance.parent &&
inferFromRegistry(
(instance.parent.type as ComponentOptions).components,
) || inferFromRegistry(instance.appContext.components)
)) ||
inferFromRegistry(instance.appContext.components)
}
return name ? classify(name) : isRoot ? `App` : `Anonymous`