-
Notifications
You must be signed in to change notification settings - Fork 142
/
Copy pathgetter.js
151 lines (130 loc) · 3.31 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'
const CACHE_OPTIONS = ['default', 'always', 'never']
/**
* Getter helper functions
* A getter is an array with the form:
* [<KeyPath>, ...<KeyPath>, <function>]
*/
const identity = (x) => x
/**
* Add override options to a getter
* @param {getter} getter
* @param {object} options
* @param {boolean} options.cache
* @param {*} options.cacheKey
* @returns {getter}
*/
function Getter(getter, options={}) {
if (!isKeyPath(getter) && !isGetter(getter)) {
throw new Error('createGetter must be passed a keyPath or Getter')
}
if (getter.hasOwnProperty('__options')) {
throw new Error('Cannot reassign options to getter')
}
getter.__options = {}
getter.__options.cache = CACHE_OPTIONS.indexOf(options.cache) > -1 ? options.cache : 'default'
getter.__options.cacheKey = options.cacheKey !== undefined ? options.cacheKey : null
return getter
}
/**
* Retrieve an option from getter options
* @param {getter} getter
* @param {string} Name of option to retrieve
* @returns {*}
*/
function getGetterOption(getter, option) {
if (getter.__options) {
return getter.__options[option]
}
}
/**
* 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(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)
}
/**
* @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)
.map(keyPath => keyPath.first())
.filter(x => !!x)
Object.defineProperty(getter, '__storeDeps', {
enumerable: false,
configurable: false,
writable: false,
value: storeDeps,
})
return storeDeps
}
export default {
isGetter,
getComputeFn,
getFlattenedDeps,
getGetterOption,
getStoreDeps,
getDeps,
fromKeyPath,
Getter,
}