From 0d0874e3ce20061b08ac99c7d4f4c07a2a96bdd7 Mon Sep 17 00:00:00 2001 From: cexbrayat Date: Wed, 1 Jul 2020 19:56:45 +0200 Subject: [PATCH] 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. --- src/stubs.ts | 28 +++++++++++++++------------- src/utils/find.ts | 36 ++++++++++++++++++++++++++++++++---- src/utils/matchName.ts | 2 +- 3 files changed, 48 insertions(+), 18 deletions(-) diff --git a/src/stubs.ts b/src/stubs.ts index 6eb05c2b6..d4a68e2e6 100644 --- a/src/stubs.ts +++ b/src/stubs.ts @@ -12,6 +12,7 @@ import { hyphenate } from './utils/vueShared' import { MOUNT_COMPONENT_REF, MOUNT_PARENT_NAME } from './constants' import { config } from './config' import { matchName } from './utils/matchName' +import { ComponentInternalInstance } from '@vue/runtime-core' interface StubOptions { name?: string @@ -74,18 +75,7 @@ export function stubComponents( stubs: Record = {}, shallow: boolean = false ) { - transformVNodeArgs((args) => { - const locallyRegisteredComponents = (args[0] as any).components as - | Record - | undefined - if (locallyRegisteredComponents) { - for (const registrationName in locallyRegisteredComponents) { - const component = locallyRegisteredComponents[registrationName] - if (!component['name'] && !component['displayName']) { - component['name'] = registrationName - } - } - } + transformVNodeArgs((args, instance: ComponentInternalInstance | null) => { const [nodeType, props, children, patchFlag, dynamicProps] = args const type = nodeType as VNodeTypes // args[0] can either be: @@ -102,7 +92,19 @@ export function stubComponents( } if (isComponent(type) || isFunctionalComponent(type)) { - const name = type['name'] || type['displayName'] + let name = type['name'] || type['displayName'] + + // if no name, then check the locally registered components in the parent + if (!name && instance && instance.parent) { + // try to infer the name based on local resolution + const registry = (instance.type as any).components + for (const key in registry) { + if (registry[key] === type) { + name = key + break + } + } + } if (!name && !shallow) { return args } diff --git a/src/utils/find.ts b/src/utils/find.ts index d25055bd0..de8dcba58 100644 --- a/src/utils/find.ts +++ b/src/utils/find.ts @@ -21,12 +21,40 @@ function matches(node: VNode, selector: FindAllComponentsSelector): boolean { return node.el?.matches?.(selector) } - if (typeof selector === 'object' && typeof node.type === 'object') { - if (selector === node.type) return true + const nodeType = node.type + if (typeof selector === 'object' && typeof nodeType === 'object') { + // we are looking for this exact component + if (selector === nodeType) { + return true + } - if (selector.name && ('name' in node.type || 'displayName' in node.type)) { + let componentName + if ('name' in nodeType || 'displayName' in nodeType) { // match normal component definitions or functional components - return matchName(selector.name, node.type.name || node.type.displayName) + componentName = nodeType.name || nodeType.displayName + } + let selectorName = selector.name + + // the component and selector both have a name + if (componentName && selectorName) { + return matchName(selectorName, componentName) + } + + // if a name is missing, then check the locally registered components in the parent + if (node.component.parent) { + const registry = (node.component.parent as any).components + for (const key in registry) { + // is it the selector + if (!selectorName && registry[key] === selector) { + selectorName = key + } + // is it the component + if (!componentName && registry[key] === nodeType) { + componentName = key + } + } + // we may have one or both missing names + return matchName(selectorName, componentName) } } diff --git a/src/utils/matchName.ts b/src/utils/matchName.ts index f1b54ac39..824a73f34 100644 --- a/src/utils/matchName.ts +++ b/src/utils/matchName.ts @@ -1,6 +1,6 @@ import { camelize, capitalize } from './vueShared' -export function matchName(target, sourceName) { +export function matchName(target: string, sourceName: string) { const camelized = camelize(target) const capitalized = capitalize(camelized)