-
Notifications
You must be signed in to change notification settings - Fork 669
/
find.js
108 lines (94 loc) · 2.78 KB
/
find.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// @flow
import findDOMNodes from './find-dom-nodes'
import {
DOM_SELECTOR,
REF_SELECTOR,
COMPONENT_SELECTOR,
VUE_VERSION
} from 'shared/consts'
import { throwError } from 'shared/util'
import { matches } from './matches'
export function findAllInstances(rootVm: any) {
const instances = [rootVm]
let i = 0
while (i < instances.length) {
const vm = instances[i]
;(vm.$children || []).forEach(child => {
instances.push(child)
})
i++
}
return instances
}
function findAllVNodes(vnode: VNode, selector: any): Array<VNode> {
const matchingNodes = []
const nodes = [vnode]
while (nodes.length) {
const node = nodes.shift()
if (node.children) {
const children = [...node.children].reverse()
children.forEach(n => {
nodes.unshift(n)
})
}
if (node.child) {
nodes.unshift(node.child._vnode)
}
if (matches(node, selector)) {
matchingNodes.push(node)
}
}
return matchingNodes
}
function removeDuplicateNodes(vNodes: Array<VNode>): Array<VNode> {
const vNodeElms = vNodes.map(vNode => vNode.elm)
return vNodes.filter((vNode, index) => index === vNodeElms.indexOf(vNode.elm))
}
export default function find(
root: VNode | Element,
vm?: Component,
selector: Selector
): Array<VNode | Component> {
if (root instanceof Element && selector.type !== DOM_SELECTOR) {
throwError(
`cannot find a Vue instance on a DOM node. The node ` +
`you are calling find on does not exist in the ` +
`VDom. Are you adding the node as innerHTML?`
)
}
if (
selector.type === COMPONENT_SELECTOR &&
(selector.value.functional ||
(selector.value.options && selector.value.options.functional)) &&
VUE_VERSION < 2.3
) {
throwError(
`find for functional components is not supported ` + `in Vue < 2.3`
)
}
if (root instanceof Element) {
return findDOMNodes(root, selector.value)
}
if (!root && selector.type !== DOM_SELECTOR) {
throwError(
`cannot find a Vue instance on a DOM node. The node ` +
`you are calling find on does not exist in the ` +
`VDom. Are you adding the node as innerHTML?`
)
}
if (!vm && selector.type === REF_SELECTOR) {
throwError(`$ref selectors can only be used on Vue component ` + `wrappers`)
}
if (vm && vm.$refs && selector.value.ref in vm.$refs) {
const refs = vm.$refs[selector.value.ref]
return Array.isArray(refs) ? refs : [refs]
}
const nodes = findAllVNodes(root, selector)
const dedupedNodes = removeDuplicateNodes(nodes)
if (nodes.length > 0 || selector.type !== DOM_SELECTOR) {
return dedupedNodes
}
// Fallback in case element exists in HTML, but not in vnode tree
// (e.g. if innerHTML is set as a domProp)
return findDOMNodes(root.elm, selector.value)
}