/
applySpec.js
106 lines (89 loc) · 2.66 KB
/
applySpec.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
import {_isArray} from './_internals/_isArray'
// recursively traverse the given spec object to find the highest arity function
function __findHighestArity(spec, max = 0) {
for (const key in spec) {
if (spec.hasOwnProperty(key) === false || key === 'constructor') continue
if (typeof spec[key] === 'object') {
max = Math.max(max, __findHighestArity(spec[key]))
}
if (typeof spec[key] === 'function') {
max = Math.max(max, spec[key].length)
}
}
return max
}
function __filterUndefined() {
const defined = []
let i = 0
const l = arguments.length
while (i < l) {
if (typeof arguments[i] === 'undefined') break
defined[i] = arguments[i]
i++
}
return defined
}
function __applySpecWithArity(spec, arity, cache) {
const remaining = arity - cache.length
if (remaining === 1)
return x =>
__applySpecWithArity(spec, arity, __filterUndefined(...cache, x))
if (remaining === 2)
return (x, y) =>
__applySpecWithArity(spec, arity, __filterUndefined(...cache, x, y))
if (remaining === 3)
return (x, y, z) =>
__applySpecWithArity(spec, arity, __filterUndefined(...cache, x, y, z))
if (remaining === 4)
return (x, y, z, a) =>
__applySpecWithArity(
spec,
arity,
__filterUndefined(...cache, x, y, z, a)
)
if (remaining > 4)
return (...args) =>
__applySpecWithArity(spec, arity, __filterUndefined(...cache, ...args))
// handle spec as Array
if (_isArray(spec)) {
const ret = []
let i = 0
const l = spec.length
for (; i < l; i++) {
// handle recursive spec inside array
if (typeof spec[i] === 'object' || _isArray(spec[i])) {
ret[i] = __applySpecWithArity(spec[i], arity, cache)
}
// apply spec to the key
if (typeof spec[i] === 'function') {
ret[i] = spec[i](...cache)
}
}
return ret
}
// handle spec as Object
const ret = {}
// apply callbacks to each property in the spec object
for (const key in spec) {
if (spec.hasOwnProperty(key) === false || key === 'constructor') continue
// apply the spec recursively
if (typeof spec[key] === 'object') {
ret[key] = __applySpecWithArity(spec[key], arity, cache)
continue
}
// apply spec to the key
if (typeof spec[key] === 'function') {
ret[key] = spec[key](...cache)
}
}
return ret
}
export function applySpec(spec, ...args) {
// get the highest arity spec function, cache the result and pass to __applySpecWithArity
const arity = __findHighestArity(spec)
if (arity === 0) {
return () => ({})
}
const toReturn = __applySpecWithArity(spec, arity, args)
return toReturn
}