-
Notifications
You must be signed in to change notification settings - Fork 160
/
getter.js
151 lines (130 loc) · 3.25 KB
/
getter.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
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
143
144
145
146
147
148
149
150
151
import Immutable, { List } from 'immutable'
import { isFunction, isArray } from './utils'
import { isKeyPath } from './key-path'
/**
* Getter helper functions
* A getter is an array with the form:
* [<KeyPath>, ...<KeyPath>, <function>]
*/
const identity = (x) => x
/**
* Checks if something is a getter literal, ex: ['dep1', 'dep2', function(dep1, dep2) {...}]
* @param {*} toTest
* @return {boolean}
*/
function isGetter(toTest) {
return (isArray(toTest) && isFunction(toTest[toTest.length - 1]))
}
/**
* Returns the compute function from a getter
* @param {Getter} getter
* @return {function}
*/
function getComputeFn(getter) {
return getter[getter.length - 1]
}
/**
* Returns an array of deps from a getter
* @param {Getter} getter
* @return {function}
*/
function getDeps(getter) {
return getter.slice(0, getter.length - 1)
}
/**
* Returns an array of deps from a getter and all its deps
* @param {Getter} getter
* @param {Immutable.Set} existing
* @return {Immutable.Set}
*/
function getFlattenedDeps(getter, existing) {
if (!existing) {
existing = Immutable.Set()
}
const toAdd = Immutable.Set().withMutations(set => {
if (!isGetter(getter)) {
throw new Error('getFlattenedDeps must be passed a Getter')
}
getDeps(getter).forEach(dep => {
if (isKeyPath(dep)) {
set.add(Immutable.List(dep))
} else if (isGetter(dep)) {
set.union(getFlattenedDeps(dep))
} else {
throw new Error('Invalid getter, each dependency must be a KeyPath or Getter')
}
})
})
return existing.union(toAdd)
}
/**
* Returns a set of deps that have been flattened and expanded
* expanded ex: ['store1', 'key1'] => [['store1'], ['store1', 'key1']]
*
* Note: returns a keypath as an Immutable.List(['store1', 'key1')
* @param {Getter} getter
* @param {Number} maxDepth
* @return {Immutable.Set}
*/
function getCanonicalKeypathDeps(getter, maxDepth) {
if (maxDepth === undefined) {
throw new Error('Must supply maxDepth argument')
}
const cacheKey = `__storeDeps_${maxDepth}`
if (getter.hasOwnProperty(cacheKey)) {
return getter[cacheKey]
}
const deps = Immutable.Set().withMutations(set => {
getFlattenedDeps(getter).forEach(keypath => {
if (keypath.size <= maxDepth) {
set.add(keypath)
} else {
set.add(keypath.slice(0, maxDepth))
}
})
})
Object.defineProperty(getter, cacheKey, {
enumerable: false,
configurable: false,
writable: false,
value: deps,
})
return deps
}
/**
* @param {KeyPath}
* @return {Getter}
*/
function fromKeyPath(keyPath) {
if (!isKeyPath(keyPath)) {
throw new Error('Cannot create Getter from KeyPath: ' + keyPath)
}
return [keyPath, identity]
}
/**
* Adds non enumerated __storeDeps property
* @param {Getter}
*/
function getStoreDeps(getter) {
if (getter.hasOwnProperty('__storeDeps')) {
return getter.__storeDeps
}
const storeDeps = getFlattenedDeps(getter)
.filter(x => !!x)
Object.defineProperty(getter, '__storeDeps', {
enumerable: false,
configurable: false,
writable: false,
value: storeDeps,
})
return storeDeps
}
export default {
isGetter,
getComputeFn,
getFlattenedDeps,
getCanonicalKeypathDeps,
getStoreDeps,
getDeps,
fromKeyPath,
}