forked from ergogen/ergogen
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprepare.js
132 lines (117 loc) · 4.27 KB
/
prepare.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
const u = require('./utils')
const a = require('./assert')
const _extend = exports._extend = (to, from) => {
const to_type = a.type(to)()
const from_type = a.type(from)()
if (from === undefined || from === null) return to
if (from === '$unset') return undefined
if (to_type != from_type) return from
if (from_type == 'object') {
const res = u.deepcopy(to)
for (const key of Object.keys(from)) {
res[key] = _extend(to[key], from[key])
if (res[key] === undefined) delete res[key]
}
return res
} else if (from_type == 'array') {
const res = u.deepcopy(to)
for (const [i, val] of from.entries()) {
res[i] = _extend(res[i], val)
}
return res
} else return from
}
const extend = exports.extend = (...args) => {
let res = args[0]
for (const arg of args) {
if (res == arg) continue
res = _extend(res, arg)
}
return res
}
const traverse = exports.traverse = (config, root, breadcrumbs, op) => {
if (a.type(config)() == 'object') {
const result = {}
for (const [key, val] of Object.entries(config)) {
breadcrumbs.push(key)
op(result, key, traverse(val, root, breadcrumbs, op), root, breadcrumbs)
breadcrumbs.pop()
}
return result
} else if (a.type(config)() == 'array') {
// needed so that arrays can set output the same way as objects within ops
const dummy = {}
const result = []
let index = 0
for (const val of config) {
breadcrumbs.push(`[${index}]`)
op(dummy, 'dummykey', traverse(val, root, breadcrumbs, op), root, breadcrumbs)
result[index] = dummy.dummykey
breadcrumbs.pop()
index++
}
return result
}
return config
}
exports.unnest = config => traverse(config, config, [], (target, key, val) => {
u.deep(target, key, val)
})
exports.inherit = config => traverse(config, config, [], (target, key, val, root, breadcrumbs) => {
if (val && val.$extends !== undefined) {
let candidates = u.deepcopy(val.$extends)
if (a.type(candidates)() !== 'array') candidates = [candidates]
const list = [val]
while (candidates.length) {
const path = candidates.shift()
const other = u.deepcopy(u.deep(root, path))
a.assert(other, `"${path}" (reached from "${breadcrumbs.join('.')}.$extends") does not name a valid inheritance target!`)
let parents = other.$extends || []
if (a.type(parents)() !== 'array') parents = [parents]
candidates = candidates.concat(parents)
list.unshift(other)
}
val = extend.apply(null, list)
delete val.$extends
}
target[key] = val
})
exports.parameterize = config => traverse(config, config, [], (target, key, val, root, breadcrumbs) => {
// we only care about objects
if (a.type(val)() !== 'object') {
target[key] = val
return
}
let params = val.$params
let args = val.$args
// explicitly skipped (probably intermediate) template, remove (by not setting it)
if (val.$skip) return
// nothing to do here, just pass the original value through
if (!params && !args) {
target[key] = val
return
}
// unused template, remove (by not setting it)
if (params && !args) return
if (!params && args) {
throw new Error(`Trying to parameterize through "${breadcrumbs}.$args", but the corresponding "$params" field is missing!`)
}
params = a.strarr(params, `${breadcrumbs}.$params`)
args = a.sane(args, `${breadcrumbs}.$args`, 'array')()
if (params.length !== args.length) {
throw new Error(`The number of "$params" and "$args" don't match for "${breadcrumbs}"!`)
}
let str = JSON.stringify(val)
const zip = rows => rows[0].map((_, i) => rows.map(row => row[i]))
for (const [par, arg] of zip([params, args])) {
str = str.replace(new RegExp(`${par}`, 'g'), arg)
}
try {
val = JSON.parse(str)
} catch (ex) {
throw new Error(`Replacements didn't lead to a valid JSON object at "${breadcrumbs}"! ` + ex)
}
delete val.$params
delete val.$args
target[key] = val
})