-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconfutils.js
119 lines (94 loc) · 2 KB
/
confutils.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
const toString = Function.prototype.toString
const objectString = toString.call(Object)
const api = {
join,
append,
prepend,
merge
}
module.exports = {
...api,
resolve,
define
}
function resolve(confs, context) {
const [first, ...rest] = normalizeConfs(confs, {
...context,
...api
})
let result = first
for (const conf of rest) {
result = merge(result, conf)
}
return result
}
function define(conf) {
const self = function confFn(overrides) {
return overrides
? define(resolve([conf, ...[].concat(overrides)]))
: conf
}
Object.assign(self, conf)
return self
}
function join(fn) {
return new JoinDef(fn)
}
function append(tail) {
return join(body =>
body !== undefined ? [].concat(body).concat(tail) : tail
)
}
function prepend(head) {
return join(body =>
body !== undefined ? [].concat(head).concat(body) : head
)
}
function merge(a, b) {
if (b === undefined) {
return a
}
if (b instanceof JoinDef) {
return b.fn(a)
}
if (isPlainObject(a) && isPlainObject(b)) {
return mergeObjects(a, b)
}
return b
}
function normalizeConfs(confs, context) {
const results = []
for (const conf of [].concat(confs)) {
if (Array.isArray(conf)) {
results.push(conf.map(d => normalizeConfs(d, context)))
} else if (typeof conf === 'function') {
results.push(normalizeConfs(conf(context), context))
} else {
results.push(conf)
}
}
return results.reduce(concat)
}
function concat(a, b) {
return [].concat(a).concat(b)
}
function mergeObjects(a, b) {
const keys = [...new Set([...Object.keys(a), ...Object.keys(b)])]
const result = {}
for (const k of keys) {
result[k] = merge(a[k], b[k])
}
return result
}
function isPlainObject(v) {
if (!v || Array.isArray(v) || typeof v !== 'object') {
return false
}
const proto = Object.getPrototypeOf(v)
return !proto || toString.call(proto.constructor) === objectString
}
class JoinDef {
constructor(fn) {
this.fn = fn
}
}