-
-
Notifications
You must be signed in to change notification settings - Fork 173
/
Copy pathsearch.ts
142 lines (127 loc) · 4.19 KB
/
search.ts
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import { INFINITY, NAN, NEGATIVE_INFINITY, UNDEFINED, isPlainObject } from '@vue/devtools-kit'
import type { CustomInspectorNode } from '@vue/devtools-kit'
/**
* Searches a key or value in the object, with a maximum deepness
* @param {*} obj Search target
* @param {string} searchTerm Search string
* @returns {boolean} Search match
*/
export function searchDeepInObject(obj, searchTerm) {
const seen = new Map()
const result = internalSearchObject(obj, searchTerm.toLowerCase(), seen, 0)
seen.clear()
return result
}
const SEARCH_MAX_DEPTH = 10
/**
* Executes a search on each field of the provided object
* @param {*} obj Search target
* @param {string} searchTerm Search string
* @param {Map<any,boolean>} seen Map containing the search result to prevent stack overflow by walking on the same object multiple times
* @param {number} depth Deep search depth level, which is capped to prevent performance issues
* @returns {boolean} Search match
*/
function internalSearchObject(obj, searchTerm, seen, depth) {
if (depth > SEARCH_MAX_DEPTH)
return false
let match = false
const keys = Object.keys(obj)
let key, value
for (let i = 0; i < keys.length; i++) {
key = keys[i]
value = obj[key]
match = internalSearchCheck(searchTerm, key, value, seen, depth + 1)
if (match)
break
}
return match
}
/**
* Checks if the provided field matches the search terms
* @param {string} searchTerm Search string
* @param {string} key Field key (null if from array)
* @param {*} value Field value
* @param {Map<any,boolean>} seen Map containing the search result to prevent stack overflow by walking on the same object multiple times
* @param {number} depth Deep search depth level, which is capped to prevent performance issues
* @returns {boolean} Search match
*/
function internalSearchCheck(searchTerm, key, value, seen, depth) {
let match = false
let result
if (key === '_custom') {
key = value.display
value = value.value
}
(result = specialTokenToString(value)) && (value = result)
if (key && compare(key, searchTerm)) {
match = true
seen.set(value, true)
}
else if (seen.has(value)) {
match = seen.get(value)
}
else if (Array.isArray(value)) {
seen.set(value, null)
match = internalSearchArray(value, searchTerm, seen, depth)
seen.set(value, match)
}
else if (isPlainObject(value)) {
seen.set(value, null)
match = internalSearchObject(value, searchTerm, seen, depth)
seen.set(value, match)
}
else if (compare(value, searchTerm)) {
match = true
seen.set(value, true)
}
return match
}
/**
* Compares two values
* @param {*} value Mixed type value that will be cast to string
* @param {string} searchTerm Search string
* @returns {boolean} Search match
*/
function compare(value, searchTerm) {
return (`${value}`).toLowerCase().includes(searchTerm)
}
export function specialTokenToString(value) {
if (value === null)
return 'null'
else if (value === UNDEFINED)
return 'undefined'
else if (value === NAN)
return 'NaN'
else if (value === INFINITY)
return 'Infinity'
else if (value === NEGATIVE_INFINITY)
return '-Infinity'
return false
}
/**
* Executes a search on each value of the provided array
* @param {*} array Search target
* @param {string} searchTerm Search string
* @param {Map<any,boolean>} seen Map containing the search result to prevent stack overflow by walking on the same object multiple times
* @param {number} depth Deep search depth level, which is capped to prevent performance issues
* @returns {boolean} Search match
*/
function internalSearchArray(array, searchTerm, seen, depth) {
if (depth > SEARCH_MAX_DEPTH)
return false
let match = false
let value
for (let i = 0; i < array.length; i++) {
value = array[i]
match = internalSearchCheck(searchTerm, null, value, seen, depth + 1)
if (match)
break
}
return match
}
/**
* Recursively search for target tree Id in nodes and there children
*/
export function getValidNodeId(treeNode: CustomInspectorNode[], targetId?: string) {
return treeNode.some(({ id: treeNodeId, children }) => (children && getValidNodeId(children, targetId)) || treeNodeId === targetId) && (targetId as string)
}