Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 48 additions & 11 deletions src/utils/find.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { VNode, ComponentPublicInstance } from 'vue'
import {
ComponentPublicInstance,
VNode,
VNodeArrayChildren,
VNodeNormalizedChildren
} from 'vue'
import { FindAllComponentsSelector } from '../types'
import { matchName } from './matchName'

Expand Down Expand Up @@ -28,26 +33,55 @@ function matches(node: VNode, selector: FindAllComponentsSelector): boolean {
return false
}

/**
* Filters out the null, undefined and primitive values,
* to only keep VNode and VNodeArrayChildren values
* @param value
*/
function nodesAsObject<Node>(
value:
| string
| number
| boolean
| VNodeArrayChildren
| VNode
| null
| undefined
| void
): value is VNodeArrayChildren | VNode {
return !!value && typeof value === 'object'
}

/**
* Collect all children
* @param nodes
* @param children
*/
function aggregateChildren(nodes, children) {
function aggregateChildren(nodes: VNode[], children: VNodeNormalizedChildren) {
if (children && Array.isArray(children)) {
;[...children].reverse().forEach((n: VNode) => {
nodes.unshift(n)
const reversedNodes = [...children].reverse().filter(nodesAsObject)
reversedNodes.forEach((node: VNodeArrayChildren | VNode) => {
if (Array.isArray(node)) {
aggregateChildren(nodes, node)
} else {
nodes.unshift(node)
Copy link
Member Author

@cexbrayat cexbrayat May 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need help here, please.
If I follow correctly until that point, children is of type VNodeNormalizedChildren.
That means its elements, after filtering the potential null/undefined ones (which I added to be safe), can be either string | number | boolean | VNodeArrayChildren | VNode (see https://github.com/vuejs/vue-next/blob/24168bbb333727417ddcb310c615afc55ee923ad/packages/runtime-core/src/vnode.ts#L85-L92) . The current code considers it is always a VNode. Can we be sure of that?
If yes, I can force the cast and add a comment explaining why.
If no, we need to handle the other cases, but I'm not sure what needs to be done.
cc @dobromir-hristov

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I was just collecting all children, then in the match I am filtering away the unnecessary.
Children can be other things, but I did not yet find such cases cases.

String means its just a plain DOM element.
VNode is Vue component instance.
Boolean I have no idea when 😆
VNodeArrayChildren is probably for multi root components? idk.. :/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I did not manage to reproduce a case where we have VNodeArrayChildren either. To be safe, I added a branch if we end up with it, and call aggregateChildren on them. We should be safe in all cases.

}
})
}
}

function findAllVNodes(vnode: VNode, selector: any): VNode[] {
const matchingNodes = []
const nodes = [vnode]
function findAllVNodes(
vnode: VNode,
selector: FindAllComponentsSelector
): VNode[] {
const matchingNodes: VNode[] = []
const nodes: VNode[] = [vnode]
while (nodes.length) {
const node = nodes.shift()
const node = nodes.shift()!
aggregateChildren(nodes, node.children)
aggregateChildren(nodes, node.component?.subTree.children)
if (node.component) {
aggregateChildren(nodes, node.component.subTree.children)
}
if (matches(node, selector)) {
matchingNodes.push(node)
}
Expand All @@ -56,8 +90,11 @@ function findAllVNodes(vnode: VNode, selector: any): VNode[] {
return matchingNodes
}

export function find(root: VNode, selector: any): ComponentPublicInstance[] {
export function find(
root: VNode,
selector: FindAllComponentsSelector
): ComponentPublicInstance[] {
return findAllVNodes(root, selector).map(
(vnode: VNode) => vnode.component.proxy
(vnode: VNode) => vnode.component!.proxy!
)
}