Skip to content

Commit 0d0874e

Browse files
committed
refactor: properly find/stub anonymous component
Instead of always patching the missing name with the registration name, we now look into the parent registration only if the name is missing. Some cases were trickier to handle (for example when we try to find a component that has been stubbed), but it looks like we are on par with what we had previously and we do not have the risk of multiple registration names erasing each other anymore.
1 parent 61eb1cc commit 0d0874e

File tree

3 files changed

+48
-18
lines changed

3 files changed

+48
-18
lines changed

src/stubs.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { hyphenate } from './utils/vueShared'
1212
import { MOUNT_COMPONENT_REF, MOUNT_PARENT_NAME } from './constants'
1313
import { config } from './config'
1414
import { matchName } from './utils/matchName'
15+
import { ComponentInternalInstance } from '@vue/runtime-core'
1516

1617
interface StubOptions {
1718
name?: string
@@ -74,18 +75,7 @@ export function stubComponents(
7475
stubs: Record<any, any> = {},
7576
shallow: boolean = false
7677
) {
77-
transformVNodeArgs((args) => {
78-
const locallyRegisteredComponents = (args[0] as any).components as
79-
| Record<string, VNodeTypes>
80-
| undefined
81-
if (locallyRegisteredComponents) {
82-
for (const registrationName in locallyRegisteredComponents) {
83-
const component = locallyRegisteredComponents[registrationName]
84-
if (!component['name'] && !component['displayName']) {
85-
component['name'] = registrationName
86-
}
87-
}
88-
}
78+
transformVNodeArgs((args, instance: ComponentInternalInstance | null) => {
8979
const [nodeType, props, children, patchFlag, dynamicProps] = args
9080
const type = nodeType as VNodeTypes
9181
// args[0] can either be:
@@ -102,7 +92,19 @@ export function stubComponents(
10292
}
10393

10494
if (isComponent(type) || isFunctionalComponent(type)) {
105-
const name = type['name'] || type['displayName']
95+
let name = type['name'] || type['displayName']
96+
97+
// if no name, then check the locally registered components in the parent
98+
if (!name && instance && instance.parent) {
99+
// try to infer the name based on local resolution
100+
const registry = (instance.type as any).components
101+
for (const key in registry) {
102+
if (registry[key] === type) {
103+
name = key
104+
break
105+
}
106+
}
107+
}
106108
if (!name && !shallow) {
107109
return args
108110
}

src/utils/find.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,40 @@ function matches(node: VNode, selector: FindAllComponentsSelector): boolean {
2121
return node.el?.matches?.(selector)
2222
}
2323

24-
if (typeof selector === 'object' && typeof node.type === 'object') {
25-
if (selector === node.type) return true
24+
const nodeType = node.type
25+
if (typeof selector === 'object' && typeof nodeType === 'object') {
26+
// we are looking for this exact component
27+
if (selector === nodeType) {
28+
return true
29+
}
2630

27-
if (selector.name && ('name' in node.type || 'displayName' in node.type)) {
31+
let componentName
32+
if ('name' in nodeType || 'displayName' in nodeType) {
2833
// match normal component definitions or functional components
29-
return matchName(selector.name, node.type.name || node.type.displayName)
34+
componentName = nodeType.name || nodeType.displayName
35+
}
36+
let selectorName = selector.name
37+
38+
// the component and selector both have a name
39+
if (componentName && selectorName) {
40+
return matchName(selectorName, componentName)
41+
}
42+
43+
// if a name is missing, then check the locally registered components in the parent
44+
if (node.component.parent) {
45+
const registry = (node.component.parent as any).components
46+
for (const key in registry) {
47+
// is it the selector
48+
if (!selectorName && registry[key] === selector) {
49+
selectorName = key
50+
}
51+
// is it the component
52+
if (!componentName && registry[key] === nodeType) {
53+
componentName = key
54+
}
55+
}
56+
// we may have one or both missing names
57+
return matchName(selectorName, componentName)
3058
}
3159
}
3260

src/utils/matchName.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { camelize, capitalize } from './vueShared'
22

3-
export function matchName(target, sourceName) {
3+
export function matchName(target: string, sourceName: string) {
44
const camelized = camelize(target)
55
const capitalized = capitalize(camelized)
66

0 commit comments

Comments
 (0)