configorama 0.4.9 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +116 -0
- package/lib/main.js +419 -481
- package/lib/parsers/yaml.js +30 -18
- package/lib/parsers/yaml.test.js +169 -0
- package/lib/resolvers/valueFromEnv.js +10 -0
- package/lib/resolvers/valueFromGit.js +95 -1
- package/lib/resolvers/valueFromNumber.js +2 -2
- package/lib/utils/PromiseTracker.js +10 -8
- package/lib/utils/arrayToJsonPath.js +11 -0
- package/lib/utils/cloudformationSchema.js +10 -6
- package/lib/utils/find-project-root.js +25 -0
- package/lib/utils/formatFunctionArgs.js +2 -2
- package/lib/utils/handleSignalEvents.js +17 -14
- package/lib/utils/isValidValue.js +2 -2
- package/lib/utils/lodash.js +91 -0
- package/lib/utils/mergeByKeys.js +29 -0
- package/lib/utils/parse.js +62 -0
- package/lib/utils/replaceAll.js +16 -0
- package/lib/utils/splitByComma.js +7 -2
- package/lib/utils/splitCsv.js +29 -0
- package/lib/utils/textUtils.js +31 -0
- package/lib/utils/unknownValues.js +46 -0
- package/lib/utils/variableUtils.js +52 -0
- package/package.json +30 -19
package/lib/main.js
CHANGED
|
@@ -3,9 +3,8 @@ const path = require('path')
|
|
|
3
3
|
const fs = require('fs')
|
|
4
4
|
const promiseFinallyShim = require('promise.prototype.finally').shim()
|
|
5
5
|
// @TODO only import lodash we need
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
const findUp = require('find-up')
|
|
8
|
-
const replaceall = require('replaceall')
|
|
9
8
|
const traverse = require('traverse')
|
|
10
9
|
const dotProp = require('dot-prop')
|
|
11
10
|
/* Default Value resolvers */
|
|
@@ -21,16 +20,29 @@ const TOML = require('./parsers/toml')
|
|
|
21
20
|
const md5Function = require('./functions/md5')
|
|
22
21
|
|
|
23
22
|
/* Utility/helpers */
|
|
24
|
-
const splitByComma = require('./utils/splitByComma')
|
|
25
23
|
const cleanVariable = require('./utils/cleanVariable')
|
|
26
24
|
const appendDeepVariable = require('./utils/appendDeepVariable')
|
|
27
25
|
const isValidValue = require('./utils/isValidValue')
|
|
28
26
|
const PromiseTracker = require('./utils/PromiseTracker')
|
|
29
27
|
const handleSignalEvents = require('./utils/handleSignalEvents')
|
|
30
28
|
const formatFunctionArgs = require('./utils/formatFunctionArgs')
|
|
31
|
-
const cloudFormationSchema = require('./utils/cloudformationSchema')
|
|
32
29
|
const trimSurroundingQuotes = require('./utils/trimSurroundingQuotes')
|
|
33
30
|
const deepLog = require('./utils/deep-log')
|
|
31
|
+
const { splitByComma } = require('./utils/splitByComma')
|
|
32
|
+
const {
|
|
33
|
+
isArray, isString, isNumber, isObject, isDate, isRegExp, isFunction,
|
|
34
|
+
isEmpty, trim, camelCase, kebabCase, capitalize, split, map, mapValues,
|
|
35
|
+
assign, set, cloneDeep
|
|
36
|
+
} = require('./utils/lodash')
|
|
37
|
+
const { parseFileContents } = require('./utils/parse')
|
|
38
|
+
const { splitCsv } = require('./utils/splitCsv')
|
|
39
|
+
const { replaceAll } = require('./utils/replaceAll')
|
|
40
|
+
const { getTextAfterOccurance, findNestedVariable } = require('./utils/textUtils')
|
|
41
|
+
const { getFallbackString, verifyVariable } = require('./utils/variableUtils')
|
|
42
|
+
const { encodeUnknown, decodeUnknown } = require('./utils/unknownValues')
|
|
43
|
+
const { mergeByKeys } = require('./utils/mergeByKeys')
|
|
44
|
+
const { arrayToJsonPath } = require('./utils/arrayToJsonPath')
|
|
45
|
+
|
|
34
46
|
/**
|
|
35
47
|
* Maintainer's notes:
|
|
36
48
|
*
|
|
@@ -45,8 +57,10 @@ const deepLog = require('./utils/deep-log')
|
|
|
45
57
|
* pause population, noting the continued depth to traverse. This motivated "deep" variables.
|
|
46
58
|
* Original issue #4687
|
|
47
59
|
*/
|
|
48
|
-
|
|
49
60
|
const deepRefSyntax = RegExp(/(\${)?deep:\d+(\.[^}]+)*()}?/)
|
|
61
|
+
const deepIndexReplacePattern = new RegExp(/^deep:|(\.[^}]+)*$/g)
|
|
62
|
+
const deepIndexPattern = /deep\:(\d*)/
|
|
63
|
+
const deepPrefixReplacePattern = /(?:^deep:)\d+\.?/g
|
|
50
64
|
const fileRefSyntax = RegExp(/^file\((~?[a-zA-Z0-9._\-\/,'" ]+?)\)/g)
|
|
51
65
|
// TODO update file regex ^file\((~?[a-zA-Z0-9._\-\/, ]+?)\)
|
|
52
66
|
// To match file(asyncValue.js, lol) input params
|
|
@@ -58,7 +72,9 @@ const funcStartOfLineRegex = /^(\w+)\s*\(((?:[^()]+)*)?\s*\)\s*/
|
|
|
58
72
|
const subFunctionRegex = /(\w+):(\w+)\s*\(((?:[^()]+)*)?\s*\)\s*/
|
|
59
73
|
const base64WrapperRegex = /\[_\[([A-Za-z0-9+/=\s]*)\]_\]/g
|
|
60
74
|
const logLines = '─────────────────────────────────────────────────'
|
|
61
|
-
|
|
75
|
+
|
|
76
|
+
let DEBUG = process.argv.includes('--debug') ? true : false
|
|
77
|
+
// DEBUG = true
|
|
62
78
|
|
|
63
79
|
const ENABLE_FUNCTIONS = true
|
|
64
80
|
|
|
@@ -77,12 +93,14 @@ class Configorama {
|
|
|
77
93
|
allowUndefinedValues: false,
|
|
78
94
|
}, options)
|
|
79
95
|
|
|
80
|
-
|
|
81
|
-
|
|
96
|
+
this.filterCache = {}
|
|
97
|
+
|
|
98
|
+
const defaultSyntax = '\\${((?!AWS|stageVariables)[ ~:a-zA-Z0-9=+!@#%*<>?._\'",|\\-\\/\\(\\)\\\\]+?)}'
|
|
99
|
+
|
|
82
100
|
const variableSyntax = options.syntax || defaultSyntax
|
|
83
101
|
let varRegex
|
|
84
102
|
if (typeof variableSyntax === 'string') {
|
|
85
|
-
varRegex = RegExp(variableSyntax, 'g')
|
|
103
|
+
varRegex = new RegExp(variableSyntax, 'g')
|
|
86
104
|
// this.variableSyntax = /\${((?!AWS)([ ~:a-zA-Z0-9=+!@#%*<>?._'",|\-\/\(\)\\]+?|(\w+)\s*\(((?:[^()]+)*)?\s*\)\s*))}/
|
|
87
105
|
} else if (variableSyntax instanceof RegExp) {
|
|
88
106
|
varRegex = variableSyntax
|
|
@@ -94,7 +112,7 @@ class Configorama {
|
|
|
94
112
|
// set config objects
|
|
95
113
|
this.config = fileOrObject
|
|
96
114
|
// Keep a copy
|
|
97
|
-
this.originalConfig =
|
|
115
|
+
this.originalConfig = cloneDeep(fileOrObject)
|
|
98
116
|
// Set configPath for file references
|
|
99
117
|
this.configPath = options.configDir || process.cwd()
|
|
100
118
|
} else if (typeof fileOrObject === 'string') {
|
|
@@ -103,51 +121,19 @@ class Configorama {
|
|
|
103
121
|
const fileDirectory = path.dirname(path.resolve(fileOrObject))
|
|
104
122
|
const fileType = path.extname(fileOrObject)
|
|
105
123
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const ymlText = YAML.preProcess(fileContents, varRegex)
|
|
116
|
-
const result = YAML.load(ymlText, {
|
|
117
|
-
filename: fileOrObject,
|
|
118
|
-
schema: cloudFormationSchema.schema
|
|
119
|
-
})
|
|
120
|
-
if (result.error) {
|
|
121
|
-
throw result.error
|
|
122
|
-
}
|
|
123
|
-
configObject = result.data
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
} else if (fileType.match(/\.(toml)/)) {
|
|
127
|
-
configObject = TOML.parse(fileContents)
|
|
128
|
-
} else if (fileType.match(/\.(json)/)) {
|
|
129
|
-
configObject = JSON.parse(fileContents)
|
|
130
|
-
} else if (fileType.match(/\.(js)/)) {
|
|
131
|
-
let jsFile
|
|
132
|
-
try {
|
|
133
|
-
jsFile = require(fileOrObject)
|
|
134
|
-
if (typeof jsFile !== 'function') {
|
|
135
|
-
configObject = jsFile
|
|
136
|
-
} else {
|
|
137
|
-
let jsArgs = opts.dynamicArgs || {}
|
|
138
|
-
if (jsArgs && typeof jsArgs === 'function') {
|
|
139
|
-
jsArgs = jsArgs()
|
|
140
|
-
}
|
|
141
|
-
configObject = jsFile(jsArgs)
|
|
142
|
-
}
|
|
143
|
-
} catch (err) {
|
|
144
|
-
throw new Error(err)
|
|
145
|
-
}
|
|
146
|
-
}
|
|
124
|
+
// Parse file contents using extracted function
|
|
125
|
+
const configObject = parseFileContents(
|
|
126
|
+
fileContents,
|
|
127
|
+
fileType,
|
|
128
|
+
fileOrObject,
|
|
129
|
+
varRegex,
|
|
130
|
+
this.opts
|
|
131
|
+
)
|
|
132
|
+
|
|
147
133
|
// set config objects
|
|
148
134
|
this.config = configObject
|
|
149
135
|
// Keep a copy
|
|
150
|
-
this.originalConfig =
|
|
136
|
+
this.originalConfig = cloneDeep(configObject)
|
|
151
137
|
// Set configPath for file references
|
|
152
138
|
this.configPath = fileDirectory
|
|
153
139
|
}
|
|
@@ -182,7 +168,7 @@ class Configorama {
|
|
|
182
168
|
match: selfRefSyntax,
|
|
183
169
|
resolver: (varString, o, x, pathValue) => {
|
|
184
170
|
return this.getValueFromSelf(varString, o, x, pathValue)
|
|
185
|
-
}
|
|
171
|
+
},
|
|
186
172
|
},
|
|
187
173
|
/**
|
|
188
174
|
* File references
|
|
@@ -195,7 +181,7 @@ class Configorama {
|
|
|
195
181
|
resolver: (varString, o, x, pathValue) => {
|
|
196
182
|
// console.log('pathValue getValueFromFile', pathValue)
|
|
197
183
|
return this.getValueFromFile(varString)
|
|
198
|
-
}
|
|
184
|
+
},
|
|
199
185
|
},
|
|
200
186
|
|
|
201
187
|
// Git refs
|
|
@@ -207,20 +193,14 @@ class Configorama {
|
|
|
207
193
|
// return this.getValueFromFunction(varString)
|
|
208
194
|
// }
|
|
209
195
|
// },
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
resolver: (varString, o, x, pathValue) => {
|
|
214
|
-
// console.log('pathValue getValueFromString', pathValue)
|
|
215
|
-
return this.getValueFromString(varString)
|
|
216
|
-
}
|
|
217
|
-
},
|
|
218
|
-
// Deep references
|
|
196
|
+
/* Resolve string references */
|
|
197
|
+
getValueFromString,
|
|
198
|
+
/* Resolve deep references */
|
|
219
199
|
{
|
|
220
200
|
match: deepRefSyntax,
|
|
221
201
|
resolver: (varString) => {
|
|
222
202
|
return this.getValueFromDeep(varString)
|
|
223
|
-
}
|
|
203
|
+
},
|
|
224
204
|
},
|
|
225
205
|
// Numbers
|
|
226
206
|
getValueFromNumber,
|
|
@@ -251,7 +231,7 @@ class Configorama {
|
|
|
251
231
|
},
|
|
252
232
|
resolver: (varString, o, x, pathValue) => {
|
|
253
233
|
return this.getValueFromSelf(varString, o, x, pathValue)
|
|
254
|
-
}
|
|
234
|
+
},
|
|
255
235
|
}
|
|
256
236
|
|
|
257
237
|
/* Apply user defined variable sources */
|
|
@@ -264,6 +244,9 @@ class Configorama {
|
|
|
264
244
|
|
|
265
245
|
// Additional filters on values. ${thing | filterFunction}
|
|
266
246
|
this.filters = {
|
|
247
|
+
capitalize: (val) => {
|
|
248
|
+
return capitalize(val)
|
|
249
|
+
},
|
|
267
250
|
toUpperCase: (val) => {
|
|
268
251
|
if (typeof val === 'string') {
|
|
269
252
|
return val.toUpperCase()
|
|
@@ -283,14 +266,28 @@ class Configorama {
|
|
|
283
266
|
}
|
|
284
267
|
},
|
|
285
268
|
toCamelCase: (val) => {
|
|
286
|
-
return
|
|
269
|
+
return camelCase(val)
|
|
287
270
|
},
|
|
288
271
|
toKebabCase: (val) => {
|
|
289
|
-
return
|
|
272
|
+
return kebabCase(val)
|
|
273
|
+
},
|
|
274
|
+
/* Type filters */
|
|
275
|
+
toNumber: (val, from) => {
|
|
276
|
+
const newVal = Number(val)
|
|
277
|
+
return newVal
|
|
278
|
+
},
|
|
279
|
+
toString: (val) => {
|
|
280
|
+
return String(val)
|
|
281
|
+
},
|
|
282
|
+
toBoolean: (val) => {
|
|
283
|
+
return Boolean(val)
|
|
284
|
+
},
|
|
285
|
+
toJson: (val) => {
|
|
286
|
+
return JSON.stringify(val)
|
|
287
|
+
},
|
|
288
|
+
toObject: (val) => {
|
|
289
|
+
return JSON.parse(val)
|
|
290
290
|
},
|
|
291
|
-
capitalize: (val) => {
|
|
292
|
-
return _.capitalize(val)
|
|
293
|
-
}
|
|
294
291
|
}
|
|
295
292
|
|
|
296
293
|
// Apply user defined filters
|
|
@@ -298,17 +295,21 @@ class Configorama {
|
|
|
298
295
|
this.filters = Object.assign({}, this.filters, options.filters)
|
|
299
296
|
}
|
|
300
297
|
|
|
298
|
+
// (\|\s*(toUpperCase|toLowerCase|toCamelCase|toKebabCase|capitalize)\s*)+$
|
|
299
|
+
this.filterMatch = new RegExp(`(\\|\\s*(${Object.keys(this.filters).join('|')})\\s*)+}?$`)
|
|
300
|
+
// console.log('this.filterMatch', this.filterMatch)
|
|
301
|
+
|
|
301
302
|
this.functions = {
|
|
302
303
|
split: (value, delimiter, limit) => {
|
|
303
304
|
const delimit = delimiter || ','
|
|
304
|
-
const splitVal =
|
|
305
|
+
const splitVal = split(value, delimit)
|
|
305
306
|
return splitVal
|
|
306
307
|
},
|
|
307
308
|
join: (value, delimiter) => {
|
|
308
|
-
if (
|
|
309
|
+
if (isString(value)) {
|
|
309
310
|
value = [value]
|
|
310
311
|
}
|
|
311
|
-
if (!
|
|
312
|
+
if (!isArray(value)) {
|
|
312
313
|
throw new Error('value must be array for join() function')
|
|
313
314
|
}
|
|
314
315
|
const delimit = delimiter || ','
|
|
@@ -334,16 +335,16 @@ class Configorama {
|
|
|
334
335
|
if (typeof value === 'string' && typeof otherValue === 'string') {
|
|
335
336
|
return value + otherValue
|
|
336
337
|
}
|
|
337
|
-
if (
|
|
338
|
+
if (isArray(value) && isArray(otherValue)) {
|
|
338
339
|
return otherValue.concat(value)
|
|
339
340
|
}
|
|
340
|
-
return
|
|
341
|
+
return assign({}, value, otherValue)
|
|
341
342
|
},
|
|
342
343
|
math: () => {},
|
|
343
344
|
upperKeys: (o) => {
|
|
344
|
-
return Object.keys(o).reduce((c, k) => (c[k.toUpperCase()] = o[k], c), {}) // eslint-disable-line
|
|
345
|
+
return Object.keys(o).reduce((c, k) => ((c[k.toUpperCase()] = o[k]), c), {}) // eslint-disable-line
|
|
345
346
|
},
|
|
346
|
-
md5: md5Function
|
|
347
|
+
md5: md5Function,
|
|
347
348
|
}
|
|
348
349
|
|
|
349
350
|
// Apply user defined functions
|
|
@@ -365,7 +366,7 @@ class Configorama {
|
|
|
365
366
|
}
|
|
366
367
|
|
|
367
368
|
/**
|
|
368
|
-
* Populate all variables in the service,
|
|
369
|
+
* Populate all variables in the service, conveniently remove and restore the service attributes
|
|
369
370
|
* that confuse the population methods.
|
|
370
371
|
* @param cliOpts An options hive to use for ${opt:...} variables.
|
|
371
372
|
* @returns {Promise.<TResult>|*} A promise resolving to the populated service.
|
|
@@ -375,66 +376,72 @@ class Configorama {
|
|
|
375
376
|
const configoramaOpts = this.opts
|
|
376
377
|
const originalConfig = this.originalConfig
|
|
377
378
|
return this.initialCall(() => {
|
|
378
|
-
return Promise.resolve()
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
379
|
+
return Promise.resolve()
|
|
380
|
+
.then(() => {
|
|
381
|
+
return this.populateObjectImpl(this.config).finally(() => {
|
|
382
|
+
// TODO populate function values here?
|
|
383
|
+
// console.log('Final Config', this.config)
|
|
384
|
+
const transform = this.runFunction.bind(this)
|
|
385
|
+
const varSyntax = this.variableSyntax
|
|
386
|
+
// Traverse resolved object and run functions
|
|
387
|
+
// console.log('this.config', this.config)
|
|
388
|
+
traverse(this.config).forEach(function (rawValue) {
|
|
389
|
+
/* Pass through unknown variables */
|
|
390
|
+
if (!configoramaOpts.allowUndefinedValues && typeof rawValue === 'undefined') {
|
|
391
|
+
const configValuePath = this.path.join('.')
|
|
392
|
+
const ogValue = dotProp.get(originalConfig, configValuePath)
|
|
393
|
+
const varDisplay = ogValue ? `"${ogValue}" variable` : 'variable'
|
|
394
|
+
const errorMessage = `
|
|
393
395
|
Config error:
|
|
394
396
|
"${configValuePath}" resolved to "undefined"
|
|
395
397
|
Verify the ${varDisplay} in config at "${configValuePath}"`
|
|
396
|
-
|
|
397
|
-
}
|
|
398
|
-
if (typeof rawValue === 'string') {
|
|
399
|
-
/* Process inline functions like merge() */
|
|
400
|
-
if (ENABLE_FUNCTIONS && rawValue.match(/> function /)) {
|
|
401
|
-
// console.log('RAW FUNCTION', rawFunction)
|
|
402
|
-
const funcString = rawValue.replace(/> function /g, '')
|
|
403
|
-
// console.log('funcString', funcString)
|
|
404
|
-
const func = cleanVariable(funcString, varSyntax, true)
|
|
405
|
-
const funcVal = transform(func)
|
|
406
|
-
const hasObjectRef = rawValue.match(/\.\S*$/)
|
|
407
|
-
if (hasObjectRef && typeof funcVal === 'object') {
|
|
408
|
-
const objectPath = hasObjectRef[0].replace(/^\./, '')
|
|
409
|
-
// console.log('objectPath', objectPath)
|
|
410
|
-
/* get value from object and update */
|
|
411
|
-
const valueFromObject = dotProp.get(funcVal, objectPath)
|
|
412
|
-
// console.log('valueFromObject', valueFromObject)
|
|
413
|
-
this.update(valueFromObject)
|
|
414
|
-
} else {
|
|
415
|
-
this.update(funcVal)
|
|
416
|
-
}
|
|
398
|
+
throw new Error(errorMessage)
|
|
417
399
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
400
|
+
if (typeof rawValue === 'string') {
|
|
401
|
+
/* Process inline functions like merge() */
|
|
402
|
+
if (ENABLE_FUNCTIONS && rawValue.match(/> function /)) {
|
|
403
|
+
// console.log('RAW FUNCTION', rawFunction)
|
|
404
|
+
const funcString = rawValue.replace(/> function /g, '')
|
|
405
|
+
// console.log('funcString', funcString)
|
|
406
|
+
const func = cleanVariable(funcString, varSyntax, true)
|
|
407
|
+
const funcVal = transform(func)
|
|
408
|
+
const hasObjectRef = rawValue.match(/\.\S*$/)
|
|
409
|
+
if (hasObjectRef && typeof funcVal === 'object') {
|
|
410
|
+
const objectPath = hasObjectRef[0].replace(/^\./, '')
|
|
411
|
+
// console.log('objectPath', objectPath)
|
|
412
|
+
/* get value from object and update */
|
|
413
|
+
const valueFromObject = dotProp.get(funcVal, objectPath)
|
|
414
|
+
// console.log('valueFromObject', valueFromObject)
|
|
415
|
+
this.update(valueFromObject)
|
|
416
|
+
} else {
|
|
417
|
+
this.update(funcVal)
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/* Allow for unknown variables to pass through */
|
|
422
|
+
if (rawValue.match(/>passthrough/)) {
|
|
423
|
+
const newValues = decodeUnknown(rawValue)
|
|
424
|
+
// console.log('>>>> newValues', newValues)
|
|
425
|
+
this.update(newValues)
|
|
426
|
+
}
|
|
425
427
|
}
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
if (DEBUG) {
|
|
431
|
+
console.log(`Variable process ran ${this.callCount} times`)
|
|
432
|
+
// console.log('FINAL Value', this.config)
|
|
433
|
+
// console.log(this.deep)
|
|
426
434
|
}
|
|
427
435
|
})
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
436
|
+
})
|
|
437
|
+
.then(() => {
|
|
438
|
+
// console.log('this.config', this.config)
|
|
439
|
+
/* Final post-processing here */
|
|
440
|
+
if (this.mergeKeys && this.config) {
|
|
441
|
+
this.config = mergeByKeys(this.config, '', this.mergeKeys)
|
|
433
442
|
}
|
|
443
|
+
return this.config
|
|
434
444
|
})
|
|
435
|
-
}).then(() => {
|
|
436
|
-
return this.config
|
|
437
|
-
})
|
|
438
445
|
})
|
|
439
446
|
}
|
|
440
447
|
runFunction(variableString) {
|
|
@@ -452,7 +459,6 @@ class Configorama {
|
|
|
452
459
|
// test for object
|
|
453
460
|
const functionName = hasFunc[1]
|
|
454
461
|
const rawArgs = hasFunc[2]
|
|
455
|
-
|
|
456
462
|
// TODO @DWELLS. Loop through all raw args and parse to correct datatype
|
|
457
463
|
// argument is object
|
|
458
464
|
let argsToPass
|
|
@@ -462,7 +468,7 @@ class Configorama {
|
|
|
462
468
|
} else {
|
|
463
469
|
// TODO fix how commas + spaces are ned
|
|
464
470
|
const splitter = splitCsv(rawArgs, ', ')
|
|
465
|
-
// console.log('
|
|
471
|
+
// console.log('splitter', splitter)
|
|
466
472
|
argsToPass = formatFunctionArgs(splitter)
|
|
467
473
|
}
|
|
468
474
|
// console.log('argsToPass runFunction', argsToPass)
|
|
@@ -470,12 +476,10 @@ class Configorama {
|
|
|
470
476
|
// TODO check for camelCase version. | toUpperCase messes with function name
|
|
471
477
|
const theFunction = this.functions[functionName] || this.functions[functionName.toLowerCase()]
|
|
472
478
|
|
|
473
|
-
if (!theFunction) {
|
|
474
|
-
throw new Error(`Function "${functionName}" not found`)
|
|
475
|
-
}
|
|
479
|
+
if (!theFunction) throw new Error(`Function "${functionName}" not found`)
|
|
476
480
|
|
|
477
481
|
const funcValue = theFunction(...argsToPass)
|
|
478
|
-
// console.log('
|
|
482
|
+
// console.log('funcValue', funcValue)
|
|
479
483
|
// console.log('typeof funcValue', typeof funcValue)
|
|
480
484
|
let replaceVal = funcValue
|
|
481
485
|
if (typeof funcValue === 'string') {
|
|
@@ -525,23 +529,20 @@ class Configorama {
|
|
|
525
529
|
* @returns {TerminalProperty[]} The terminal properties of the given root object, with the path
|
|
526
530
|
* and value of each
|
|
527
531
|
*/
|
|
528
|
-
getProperties(root, atRoot, current,
|
|
529
|
-
let context =
|
|
530
|
-
if (!context)
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
if (!results) {
|
|
535
|
-
results = []
|
|
536
|
-
}
|
|
532
|
+
getProperties(root, atRoot, current, _context, _results) {
|
|
533
|
+
let context = _context
|
|
534
|
+
if (!context) context = []
|
|
535
|
+
let results = _results
|
|
536
|
+
if (!results) results = []
|
|
537
|
+
|
|
537
538
|
const addContext = (value, key) => {
|
|
538
539
|
return this.getProperties(root, false, value, context.concat(key), results)
|
|
539
540
|
}
|
|
540
|
-
if (
|
|
541
|
-
|
|
542
|
-
} else if (
|
|
541
|
+
if (isArray(current)) {
|
|
542
|
+
map(current, addContext)
|
|
543
|
+
} else if (isObject(current) && !isDate(current) && !isRegExp(current) && !isFunction(current)) {
|
|
543
544
|
if (atRoot || current !== root) {
|
|
544
|
-
|
|
545
|
+
mapValues(current, addContext)
|
|
545
546
|
}
|
|
546
547
|
} else {
|
|
547
548
|
// TODO Add values to leaves here
|
|
@@ -549,23 +550,22 @@ class Configorama {
|
|
|
549
550
|
path: context,
|
|
550
551
|
value: current,
|
|
551
552
|
}
|
|
552
|
-
|
|
553
|
-
const thePath = (leaf.path.length > 1) ? leaf.path.join('.') : leaf.path[0]
|
|
553
|
+
const thePath = leaf.path.length > 1 ? leaf.path.join('.') : leaf.path[0]
|
|
554
554
|
let originalValue = dotProp.get(this.originalConfig, thePath)
|
|
555
555
|
// TODO @DWELLS make recursive
|
|
556
556
|
if (!originalValue) {
|
|
557
557
|
const parentArray = leaf.path.slice(0, -1)
|
|
558
|
-
const parentPath =
|
|
558
|
+
const parentPath = parentArray > 1 ? parentArray.join('.') : parentArray[0]
|
|
559
559
|
originalValue = dotProp.get(this.originalConfig, parentPath)
|
|
560
560
|
}
|
|
561
561
|
leaf.originalSource = originalValue
|
|
562
|
-
if (originalValue &&
|
|
562
|
+
if (originalValue && isString(originalValue)) {
|
|
563
563
|
const varString = cleanVariable(originalValue, this.variableSyntax)
|
|
564
564
|
if (varString.match(fileRefSyntax)) {
|
|
565
565
|
leaf.isFileRef = true
|
|
566
566
|
}
|
|
567
567
|
}
|
|
568
|
-
//
|
|
568
|
+
// dotProp.get(this.originalConfig, thePath)
|
|
569
569
|
results.push(leaf)
|
|
570
570
|
}
|
|
571
571
|
return results
|
|
@@ -585,14 +585,14 @@ class Configorama {
|
|
|
585
585
|
// console.log('properties', properties)
|
|
586
586
|
const variables = properties.filter((property) => {
|
|
587
587
|
// Initial check if value has variable string in it
|
|
588
|
-
return
|
|
588
|
+
return isString(property.value) && property.value.match(this.variableSyntax)
|
|
589
589
|
})
|
|
590
|
-
|
|
590
|
+
// console.log('variables', variables)
|
|
591
|
+
return map(variables, (valueObject) => {
|
|
591
592
|
// console.log('valueObject', valueObject)
|
|
592
|
-
return this.populateValue(valueObject, false)
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
})
|
|
593
|
+
return this.populateValue(valueObject, false, '_populateVariables').then((populated) => {
|
|
594
|
+
return assign({}, valueObject, { populated: populated.value })
|
|
595
|
+
})
|
|
596
596
|
})
|
|
597
597
|
}
|
|
598
598
|
/**
|
|
@@ -602,13 +602,15 @@ class Configorama {
|
|
|
602
602
|
* @returns {Promise<number>} resolving with the number of changes that were applied to the given
|
|
603
603
|
* target
|
|
604
604
|
*/
|
|
605
|
-
assignProperties(target, populations) {
|
|
606
|
-
|
|
607
|
-
|
|
605
|
+
assignProperties(target, populations) {
|
|
606
|
+
// eslint-disable-line class-methods-use-this
|
|
607
|
+
return Promise.all(populations).then((results) => {
|
|
608
|
+
return results.forEach((result) => {
|
|
608
609
|
if (result.value !== result.populated) {
|
|
609
|
-
|
|
610
|
+
set(target, result.path, result.populated)
|
|
610
611
|
}
|
|
611
|
-
})
|
|
612
|
+
})
|
|
613
|
+
})
|
|
612
614
|
}
|
|
613
615
|
/**
|
|
614
616
|
* Populate the variables in the given object.
|
|
@@ -620,20 +622,19 @@ class Configorama {
|
|
|
620
622
|
}
|
|
621
623
|
populateObjectImpl(objectToPopulate) {
|
|
622
624
|
this.callCount = this.callCount + 1
|
|
623
|
-
|
|
625
|
+
|
|
624
626
|
if (DEBUG) {
|
|
625
|
-
deepLog(`objectToPopulate ${this.callCount}`, objectToPopulate)
|
|
627
|
+
deepLog(`objectToPopulate called ${this.callCount} times`, objectToPopulate)
|
|
628
|
+
// process.exit(0)
|
|
626
629
|
}
|
|
627
630
|
|
|
628
631
|
const leaves = this.getProperties(objectToPopulate, true, objectToPopulate)
|
|
632
|
+
// console.log('leaves', leaves)
|
|
629
633
|
const populations = this.populateVariables(leaves)
|
|
630
|
-
|
|
631
634
|
// console.log("FILL LEAVES", populations)
|
|
632
635
|
|
|
633
636
|
if (populations.length === 0) {
|
|
634
|
-
if (DEBUG)
|
|
635
|
-
console.log('Config Population Finished')
|
|
636
|
-
}
|
|
637
|
+
if (DEBUG) console.log('Config Population Finished')
|
|
637
638
|
return Promise.resolve(objectToPopulate)
|
|
638
639
|
}
|
|
639
640
|
|
|
@@ -656,20 +657,15 @@ class Configorama {
|
|
|
656
657
|
* @returns {Object|String|MatchResult[]} The given property or the identified matches
|
|
657
658
|
*/
|
|
658
659
|
getMatches(property) {
|
|
659
|
-
if (typeof property !== 'string')
|
|
660
|
-
return property
|
|
661
|
-
}
|
|
660
|
+
if (typeof property !== 'string') return property
|
|
662
661
|
const matches = property.match(this.variableSyntax)
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
return property
|
|
666
|
-
}
|
|
667
|
-
return _.map(matches, match => {
|
|
662
|
+
if (!matches || !matches.length) return property
|
|
663
|
+
return map(matches, (match) => {
|
|
668
664
|
// console.log('match', match)
|
|
669
|
-
return
|
|
665
|
+
return {
|
|
670
666
|
match: match,
|
|
671
667
|
variable: cleanVariable(match, this.variableSyntax),
|
|
672
|
-
}
|
|
668
|
+
}
|
|
673
669
|
})
|
|
674
670
|
}
|
|
675
671
|
/**
|
|
@@ -680,7 +676,7 @@ class Configorama {
|
|
|
680
676
|
*/
|
|
681
677
|
populateMatches(matches, valueObject, root) {
|
|
682
678
|
// console.log('matches', matches)
|
|
683
|
-
return
|
|
679
|
+
return map(matches, (match) => {
|
|
684
680
|
return this.splitAndGet(match.variable, valueObject, root)
|
|
685
681
|
})
|
|
686
682
|
}
|
|
@@ -699,13 +695,12 @@ class Configorama {
|
|
|
699
695
|
let result = valueObject.value
|
|
700
696
|
for (let i = 0; i < matches.length; i += 1) {
|
|
701
697
|
this.warnIfNotFound(matches[i].variable, results[i])
|
|
702
|
-
// console.log('
|
|
698
|
+
// console.log('Render MATCHES', results[i])
|
|
703
699
|
let valueToPop = results[i]
|
|
704
700
|
// TODO refactor this. __internal_only_flag needed to stop clash with sync/async file resolution
|
|
705
701
|
if (results[i] && typeof results[i] === 'object' && results[i].__internal_only_flag) {
|
|
706
702
|
valueToPop = results[i].value
|
|
707
703
|
}
|
|
708
|
-
|
|
709
704
|
result = this.populateVariable(valueObject, matches[i].match, valueToPop)
|
|
710
705
|
/*
|
|
711
706
|
console.log('> valueToPop', valueToPop)
|
|
@@ -719,35 +714,32 @@ class Configorama {
|
|
|
719
714
|
/**
|
|
720
715
|
* Populate the given value, recursively if root is true
|
|
721
716
|
* @param valueObject The value to populate variables within
|
|
722
|
-
* @param root Whether the caller is the root
|
|
717
|
+
* @param root Whether the caller is the root populater and thereby whether to recursively
|
|
723
718
|
* populate
|
|
724
719
|
* @returns {PromiseLike<T>} A promise that resolves to the populated value, recursively if root
|
|
725
720
|
* is true
|
|
726
721
|
*/
|
|
727
|
-
populateValue(valueObject, root) {
|
|
722
|
+
populateValue(valueObject, root, caller) {
|
|
728
723
|
if (DEBUG) {
|
|
729
724
|
console.log('─────────────────────────────────────────────▶')
|
|
730
|
-
console.log('>>>>>>>> populateValue')
|
|
725
|
+
console.log('>>>>>>>> populateValue', caller)
|
|
731
726
|
console.log(valueObject)
|
|
732
727
|
}
|
|
733
728
|
const property = valueObject.value
|
|
734
729
|
const matches = this.getMatches(property)
|
|
735
730
|
/*
|
|
736
|
-
console.log('
|
|
731
|
+
console.log('populateValue matches', matches)
|
|
737
732
|
/** */
|
|
738
|
-
if (!
|
|
733
|
+
if (!isArray(matches)) {
|
|
739
734
|
return Promise.resolve(property)
|
|
740
735
|
}
|
|
741
736
|
const populations = this.populateMatches(matches, valueObject, root)
|
|
742
737
|
return Promise.all(populations)
|
|
743
|
-
.then(results => this.renderMatches(valueObject, matches, results))
|
|
738
|
+
.then((results) => this.renderMatches(valueObject, matches, results))
|
|
744
739
|
.then((result) => {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
if (root && matches.length) {
|
|
750
|
-
return this.populateValue({ value: result.value }, root)
|
|
740
|
+
// console.log('renderMatches result', result)
|
|
741
|
+
if (root && isArray(matches)) {
|
|
742
|
+
return this.populateValue({ value: result.value }, root, 'self populateValue')
|
|
751
743
|
}
|
|
752
744
|
return result
|
|
753
745
|
})
|
|
@@ -766,7 +758,7 @@ class Configorama {
|
|
|
766
758
|
/**
|
|
767
759
|
* Split the cleaned variable string containing one or more comma delimited variables and get a
|
|
768
760
|
* final value for the entirety of the string
|
|
769
|
-
* @param
|
|
761
|
+
* @param variable The variable string to split and get a final value for
|
|
770
762
|
* @param property The original property string the given variable was extracted from
|
|
771
763
|
* @returns {Promise} A promise resolving to the final value of the given variable
|
|
772
764
|
*/
|
|
@@ -776,29 +768,26 @@ class Configorama {
|
|
|
776
768
|
console.log('valueObject', valueObject)
|
|
777
769
|
console.log('root', root)
|
|
778
770
|
}
|
|
779
|
-
|
|
780
771
|
/* requires node 8.11+
|
|
781
772
|
if (valueObject.value.match(/(?<!^)> function /)) {
|
|
782
773
|
// valueObject.value = valueObject.value.replace(/(?<!^)> function /, '')
|
|
783
774
|
// valueObject.value = valueObject.value.replace(/^> function /, '')
|
|
784
775
|
// valueObject.value = `> function ${valueObject.value}`
|
|
785
|
-
}
|
|
786
|
-
*/
|
|
776
|
+
}*/
|
|
787
777
|
|
|
788
778
|
const parts = splitByComma(variable)
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
console.log('All parts:', parts)
|
|
796
|
-
console.log('-----')
|
|
797
|
-
}
|
|
798
|
-
return this.overwrite(parts, valueObject)
|
|
779
|
+
if (DEBUG) {
|
|
780
|
+
console.log('parts', parts)
|
|
781
|
+
console.log('parts variable:', variable)
|
|
782
|
+
console.log('parts property:', valueObject.value)
|
|
783
|
+
console.log('All parts:', parts)
|
|
784
|
+
console.log('-----')
|
|
799
785
|
}
|
|
800
|
-
|
|
801
|
-
|
|
786
|
+
if (parts.length <= 1) {
|
|
787
|
+
return this.getValueFromSource(parts[0], valueObject, 'splitAndGet')
|
|
788
|
+
}
|
|
789
|
+
// More than 2 parts, so we need to overwrite
|
|
790
|
+
return this.overwrite(parts, valueObject)
|
|
802
791
|
}
|
|
803
792
|
/**
|
|
804
793
|
* Populate a given property, given the matched string to replace and the value to replace the
|
|
@@ -811,35 +800,54 @@ class Configorama {
|
|
|
811
800
|
*/
|
|
812
801
|
populateVariable(valueObject, matchedString, valueToPopulate) {
|
|
813
802
|
let property = valueObject.value
|
|
803
|
+
// console.log('init property', property)
|
|
804
|
+
let DEBUG_TYPE = false
|
|
814
805
|
if (DEBUG) {
|
|
815
|
-
console.log('────────START
|
|
816
|
-
console.log('
|
|
817
|
-
console.log('
|
|
818
|
-
console.log(
|
|
819
|
-
console.log(
|
|
806
|
+
console.log('────────START populateVar──────────────')
|
|
807
|
+
console.log('populateVar: valueToPopulate', valueToPopulate)
|
|
808
|
+
console.log('populateVar: typeof valueToPopulate', typeof valueToPopulate)
|
|
809
|
+
console.log(`populateVar: path "${valueObject.path}"`)
|
|
810
|
+
console.log(`populateVar: value \`${valueObject.value}\``)
|
|
811
|
+
console.log(`populateVar: originalSource \`${valueObject.originalSource}\``)
|
|
812
|
+
console.log('populateVar: property', property)
|
|
813
|
+
console.log('populateVar: matchedString', matchedString)
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
const originalSrc = valueObject.originalSource || ''
|
|
817
|
+
const hasFilters = originalSrc.match(this.filterMatch)
|
|
818
|
+
let foundFilters = []
|
|
819
|
+
if (hasFilters) {
|
|
820
|
+
foundFilters = hasFilters[1]
|
|
821
|
+
.split('|')
|
|
822
|
+
.map((filter) => filter.trim())
|
|
823
|
+
.filter(Boolean)
|
|
820
824
|
}
|
|
825
|
+
// console.log('foundFilters', foundFilters)
|
|
821
826
|
|
|
822
827
|
// total replacement
|
|
823
828
|
if (property === matchedString) {
|
|
829
|
+
if (DEBUG_TYPE) console.log('DEBUG_TYPE total replacement')
|
|
824
830
|
const v = valueObject.value || ''
|
|
825
|
-
const originalSrc = valueObject.originalSource || ''
|
|
826
831
|
property = valueToPopulate
|
|
832
|
+
|
|
827
833
|
/* Handle ${self:custom.ref, ''} with deep values */
|
|
828
834
|
if (v.match(deepRefSyntax) && originalSrc.match(this.variableSyntax) && !v.match(/deep\:(\d*)\..*}$/)) {
|
|
829
|
-
// console.log('
|
|
835
|
+
// console.log('deep var', this.deep)
|
|
830
836
|
// console.log('originalSrc', originalSrc)
|
|
831
837
|
// console.log('value', v)
|
|
832
|
-
let deepIndex = Number(v.match(
|
|
838
|
+
let deepIndex = Number(v.match(deepIndexPattern)[1])
|
|
833
839
|
let item = this.deep[deepIndex]
|
|
834
|
-
|
|
840
|
+
|
|
835
841
|
if (item.match(deepRefSyntax)) {
|
|
836
|
-
deepIndex = Number(item.match(
|
|
842
|
+
deepIndex = Number(item.match(deepIndexPattern)[1])
|
|
837
843
|
item = this.deep[deepIndex]
|
|
838
844
|
}
|
|
839
845
|
property = this.deep[deepIndex]
|
|
846
|
+
// console.log('NEW PROPERTY after deep ref', property)
|
|
840
847
|
}
|
|
841
848
|
// partial replacement, string
|
|
842
|
-
} else if (
|
|
849
|
+
} else if (isString(valueToPopulate)) {
|
|
850
|
+
if (DEBUG_TYPE) console.log('DEBUG_TYPE isString')
|
|
843
851
|
// if (property.match(/^> function /g)) {
|
|
844
852
|
//
|
|
845
853
|
// const innerFunc = /> function (\w+)\s*\(((?:[^()]+)*)?\s*\)\s*/
|
|
@@ -849,7 +857,6 @@ class Configorama {
|
|
|
849
857
|
// console.log('xxxx', rep)
|
|
850
858
|
// console.log('valueToPopulate', valueToPopulate)
|
|
851
859
|
// }
|
|
852
|
-
|
|
853
860
|
|
|
854
861
|
let currentMatchedString = matchedString
|
|
855
862
|
/* Address fall through values if found */
|
|
@@ -859,7 +866,6 @@ class Configorama {
|
|
|
859
866
|
currentMatchedString = valueObject.value
|
|
860
867
|
}
|
|
861
868
|
}
|
|
862
|
-
|
|
863
869
|
/*
|
|
864
870
|
console.log('>------')
|
|
865
871
|
console.log('isString og matchedString', matchedString)
|
|
@@ -870,28 +876,56 @@ class Configorama {
|
|
|
870
876
|
console.log('isString currentMatchedString', currentMatchedString)
|
|
871
877
|
console.log('>------')
|
|
872
878
|
/** */
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
879
|
+
// Handle comma ${opt:stage, dev} and remove extra }
|
|
880
|
+
if (
|
|
881
|
+
currentMatchedString.match(this.variableSyntax) &&
|
|
882
|
+
!valueToPopulate.match(this.variableSyntax) &&
|
|
883
|
+
valueToPopulate.match(/}$/)
|
|
884
|
+
) {
|
|
885
|
+
valueToPopulate = valueToPopulate.replace(/}$/, '')
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
property = replaceAll(currentMatchedString, valueToPopulate, property)
|
|
889
|
+
// console.log('property replaceAll', property)
|
|
877
890
|
|
|
878
891
|
// if (property.match(/^> function /g)) {
|
|
879
892
|
// console.log('REPLACE after', property)
|
|
880
893
|
// }
|
|
881
|
-
|
|
894
|
+
|
|
882
895
|
// partial replacement, number
|
|
883
|
-
} else if (
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
896
|
+
} else if (isNumber(valueToPopulate)) {
|
|
897
|
+
if (DEBUG_TYPE) console.log('DEBUG_TYPE isNumber')
|
|
898
|
+
property = replaceAll(matchedString, String(valueToPopulate), property)
|
|
899
|
+
// TODO This was temp fix for array value mismatch from filters. This fixes filterInner: ${commas | split(${self:inner}, 2) }
|
|
900
|
+
// } else if (isArray(valueToPopulate) && valueToPopulate.length === 1) {
|
|
901
|
+
// property = replaceAll(matchedString, String(valueToPopulate[0]), property)
|
|
902
|
+
} else if (isObject(valueToPopulate)) {
|
|
903
|
+
if (DEBUG_TYPE) console.log('DEBUG_TYPEisObject')
|
|
904
|
+
|
|
905
|
+
const objStr = JSON.stringify(valueToPopulate)
|
|
906
|
+
/* Check if variable inside another variable. E.g. ${env:${self:someObject}} that resolves to ${env:{...}} */
|
|
907
|
+
if (
|
|
908
|
+
property.trim() !== matchedString.trim() &&
|
|
909
|
+
property.indexOf(matchedString) !== -1 &&
|
|
910
|
+
matchedString.match(this.variableSyntax) &&
|
|
911
|
+
property.match(this.variableSyntax)
|
|
912
|
+
) {
|
|
913
|
+
const isVar = /^\${[a-zA-Z0-9_]+:/.test(property)
|
|
914
|
+
if (isVar) {
|
|
915
|
+
// console.log('INSIDE', property, matchedString)
|
|
916
|
+
// console.log('isVar', isVar)
|
|
917
|
+
throw new Error(
|
|
918
|
+
`Invalid variable syntax "${property}" resolves to "${replaceAll(matchedString, objStr, property)}"`,
|
|
919
|
+
)
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
// console.log('OBJECT MATCH', `"${objStr}"`)
|
|
923
|
+
property = replaceAll(matchedString, objStr, property) // .replace(/}$/, '').replace(/^\$\{/, '')
|
|
891
924
|
// console.log('property', property)
|
|
892
925
|
// TODO run functions here
|
|
893
|
-
// console.log('
|
|
926
|
+
// console.log('other new prop', property)
|
|
894
927
|
} else {
|
|
928
|
+
if (DEBUG_TYPE) console.log('DEBUG_TYPE else')
|
|
895
929
|
let missingValue = matchedString
|
|
896
930
|
|
|
897
931
|
if (matchedString.match(deepRefSyntax)) {
|
|
@@ -917,18 +951,15 @@ class Configorama {
|
|
|
917
951
|
originalSource: valueObject.originalSource,
|
|
918
952
|
// set __internal_only_flag to note this is object we make not a resolved value
|
|
919
953
|
__internal_only_flag: true,
|
|
920
|
-
caller: 'nestedVar'
|
|
954
|
+
caller: 'nestedVar',
|
|
921
955
|
}
|
|
922
956
|
}
|
|
923
957
|
|
|
924
958
|
const errorMessage = `
|
|
925
959
|
Missing Value ${missingValue} - ${matchedString}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
For variable:
|
|
930
|
-
|
|
931
|
-
${valueObject.path}: ${valueObject.originalSource}
|
|
960
|
+
\nMake sure the property is being passed in correctly
|
|
961
|
+
\nFor variable:
|
|
962
|
+
\n${valueObject.path}: ${valueObject.originalSource}
|
|
932
963
|
`
|
|
933
964
|
throw new Error(errorMessage)
|
|
934
965
|
}
|
|
@@ -938,7 +969,7 @@ ${valueObject.path}: ${valueObject.originalSource}
|
|
|
938
969
|
const prop = cleanVariable(property, this.variableSyntax)
|
|
939
970
|
// console.log('prop', prop)
|
|
940
971
|
if (property.match(/^> function /g) && prop) {
|
|
941
|
-
// console.log('
|
|
972
|
+
// console.log('func prop', property)
|
|
942
973
|
// console.log('Prop', prop)
|
|
943
974
|
}
|
|
944
975
|
const func = funcRegex.exec(property)
|
|
@@ -946,10 +977,10 @@ ${valueObject.path}: ${valueObject.originalSource}
|
|
|
946
977
|
if (func && property.match(/^> function /g)) {
|
|
947
978
|
/* IMPORTANT fix `finalProp` for nested function reference
|
|
948
979
|
nestedOne: 'hi'
|
|
949
|
-
nestedTwo: ${merge('
|
|
980
|
+
nestedTwo: ${merge('nice', 'wow')}
|
|
950
981
|
mergeNested: ${merge('lol', ${nestedTwo})}
|
|
951
982
|
*/
|
|
952
|
-
const finalProp =
|
|
983
|
+
const finalProp = property.match(/(?<!^)> function /) ? prop : property
|
|
953
984
|
|
|
954
985
|
return {
|
|
955
986
|
value: finalProp, // prop to fix nested ¯\_(ツ)_/¯
|
|
@@ -989,17 +1020,44 @@ ${valueObject.path}: ${valueObject.originalSource}
|
|
|
989
1020
|
// property = newer
|
|
990
1021
|
// }
|
|
991
1022
|
}
|
|
992
|
-
|
|
1023
|
+
|
|
1024
|
+
// console.log('foundFilters', foundFilters)
|
|
1025
|
+
|
|
1026
|
+
/* Apply filters if found */
|
|
1027
|
+
//console.log('> property', property)
|
|
1028
|
+
if (
|
|
1029
|
+
foundFilters.length > 0 &&
|
|
1030
|
+
typeof valueToPopulate === 'string' &&
|
|
1031
|
+
!valueToPopulate.match(deepRefSyntax) &&
|
|
1032
|
+
!property.match(this.variableSyntax)
|
|
1033
|
+
) {
|
|
1034
|
+
// If filter cache exists we need to remove filter that have already been run
|
|
1035
|
+
if (this.filterCache[valueObject.path]) {
|
|
1036
|
+
foundFilters = foundFilters.filter((filter) => {
|
|
1037
|
+
return !this.filterCache[valueObject.path].includes(filter)
|
|
1038
|
+
})
|
|
1039
|
+
}
|
|
1040
|
+
property = foundFilters.reduce((acc, filter) => {
|
|
1041
|
+
const newVal = this.filters[filter](acc, 'from populateVariable')
|
|
1042
|
+
// console.log('PROPERTY', newVal)
|
|
1043
|
+
return newVal
|
|
1044
|
+
}, property)
|
|
1045
|
+
this.filterCache[valueObject.path] = (this.filterCache[valueObject.path] || []).concat(foundFilters)
|
|
1046
|
+
// console.log('NEW PROPERTY', property)
|
|
1047
|
+
// console.log('typeof property', typeof property)
|
|
1048
|
+
}
|
|
1049
|
+
// console.log('filterCache', this.filterCache)
|
|
1050
|
+
// console.log('XXXX property', typeof property)
|
|
993
1051
|
// console.log('XXXX path', valueObject.path)
|
|
994
1052
|
// console.log('XXXX originalSource', valueObject.originalSource)
|
|
1053
|
+
// console.log('end property', property)
|
|
995
1054
|
return {
|
|
996
1055
|
value: property,
|
|
997
1056
|
path: valueObject.path,
|
|
998
1057
|
originalSource: valueObject.originalSource,
|
|
999
|
-
// set __internal_only_flag to note this is object we make not a resolved value
|
|
1000
|
-
__internal_only_flag: true,
|
|
1058
|
+
__internal_only_flag: true, // set __internal_only_flag to note this is object we make not a resolved value
|
|
1001
1059
|
caller: 'end',
|
|
1002
|
-
count: this.callCount
|
|
1060
|
+
count: this.callCount,
|
|
1003
1061
|
}
|
|
1004
1062
|
}
|
|
1005
1063
|
// ###############
|
|
@@ -1015,21 +1073,18 @@ ${valueObject.path}: ${valueObject.originalSource}
|
|
|
1015
1073
|
overwrite(variableStrings, valueObject) {
|
|
1016
1074
|
const propertyString = valueObject.value
|
|
1017
1075
|
// console.log('variableStrings', variableStrings)
|
|
1018
|
-
|
|
1019
1076
|
// console.log('propertyString', typeof propertyString)
|
|
1020
1077
|
const variableValues = variableStrings.map((variableString) => {
|
|
1021
1078
|
// This runs on nested variable resolution
|
|
1022
1079
|
return this.getValueFromSource(variableString, valueObject, 'overwrite')
|
|
1023
1080
|
})
|
|
1024
|
-
|
|
1025
1081
|
// console.log('variableValues', variableValues)
|
|
1026
|
-
|
|
1027
|
-
return Promise.all(variableValues).then(values => {
|
|
1082
|
+
return Promise.all(variableValues).then((values) => {
|
|
1028
1083
|
let deepPropertyStr = propertyString
|
|
1029
1084
|
let deepProperties = 0
|
|
1030
1085
|
values.forEach((value, index) => {
|
|
1031
|
-
// console.log('
|
|
1032
|
-
if (
|
|
1086
|
+
// console.log('───────────────────────────────> value', value)
|
|
1087
|
+
if (isString(value) && value.match(this.variableSyntax)) {
|
|
1033
1088
|
deepProperties += 1
|
|
1034
1089
|
// console.log('makeDeepVariable overwrite', value)
|
|
1035
1090
|
const deepVariable = this.makeDeepVariable(value)
|
|
@@ -1041,19 +1096,20 @@ ${valueObject.path}: ${valueObject.originalSource}
|
|
|
1041
1096
|
})
|
|
1042
1097
|
return deepProperties > 0
|
|
1043
1098
|
? Promise.resolve(deepPropertyStr) // return deep variable replacement of original
|
|
1044
|
-
: Promise.resolve(values.find(isValidValue))// resolve first valid value, else undefined
|
|
1099
|
+
: Promise.resolve(values.find(isValidValue)) // resolve first valid value, else undefined
|
|
1045
1100
|
})
|
|
1046
1101
|
}
|
|
1047
|
-
|
|
1048
1102
|
/**
|
|
1049
1103
|
* Given any variable string, return the value it should be populated with.
|
|
1050
1104
|
* @param variableString The variable string to retrieve a value for.
|
|
1051
1105
|
* @returns {Promise.<TResult>|*} A promise resolving to the given variables value.
|
|
1052
1106
|
*/
|
|
1053
1107
|
getValueFromSource(variableString, valueObject, caller) {
|
|
1108
|
+
// console.log('getValueFromSource caller', caller)
|
|
1054
1109
|
const propertyString = valueObject.value
|
|
1055
1110
|
const pathValue = valueObject.path
|
|
1056
|
-
//
|
|
1111
|
+
// console.log('getValueFromSource propertyString', propertyString)
|
|
1112
|
+
// console.log(`tracker contains ${variableString}`, this.tracker.contains(variableString))
|
|
1057
1113
|
if (this.tracker.contains(variableString)) {
|
|
1058
1114
|
// console.log('try to get', variableString)
|
|
1059
1115
|
return this.tracker.get(variableString, propertyString)
|
|
@@ -1071,29 +1127,39 @@ ${valueObject.path}: ${valueObject.originalSource}
|
|
|
1071
1127
|
}
|
|
1072
1128
|
|
|
1073
1129
|
const filters = propertyString.match(/\s\|/)
|
|
1130
|
+
let promiseKey
|
|
1074
1131
|
// TODO match () or pipes |
|
|
1075
1132
|
if (filters) {
|
|
1133
|
+
const string = cleanVariable(propertyString, this.variableSyntax, true)
|
|
1134
|
+
// console.log('string', string)
|
|
1135
|
+
const deeperValue = getTextAfterOccurance(string, variableString)
|
|
1136
|
+
// console.log('deeperValue', deeperValue)
|
|
1137
|
+
// console.log('filters', filters)
|
|
1076
1138
|
// console.log('variableString', variableString)
|
|
1139
|
+
promiseKey = deeperValue.match(/\s\|/) ? deeperValue : undefined
|
|
1077
1140
|
|
|
1078
1141
|
// TODO clean this up
|
|
1079
1142
|
const t = variableString.split('|')
|
|
1143
|
+
// console.log('variableString', variableString)
|
|
1144
|
+
// console.log('valueObject', valueObject)
|
|
1145
|
+
// console.log('t', t)
|
|
1146
|
+
const _filter = string
|
|
1147
|
+
.split('|')
|
|
1148
|
+
.filter((value, index, arr) => {
|
|
1149
|
+
return index > 0
|
|
1150
|
+
})
|
|
1151
|
+
.map((f) => {
|
|
1152
|
+
return trim(f)
|
|
1153
|
+
})
|
|
1154
|
+
// console.log('filters to run', _filter)
|
|
1080
1155
|
|
|
1081
|
-
|
|
1082
|
-
const filterz = string.split('|').filter((value, index, arr) => {
|
|
1083
|
-
return index > 0
|
|
1084
|
-
}).map((f) => {
|
|
1085
|
-
return _.trim(f)
|
|
1086
|
-
})
|
|
1087
|
-
// console.log('filters to run', filterz)
|
|
1088
|
-
|
|
1089
|
-
newHasFilter = filterz
|
|
1156
|
+
newHasFilter = _filter
|
|
1090
1157
|
// If current variable string has no pipes, it has no filters
|
|
1091
1158
|
if (!variableString.match(/\|/)) {
|
|
1092
1159
|
newHasFilter = null
|
|
1093
1160
|
}
|
|
1094
|
-
// console.log('
|
|
1095
|
-
|
|
1096
|
-
variableString = _.trim(t[0])
|
|
1161
|
+
// console.log('newHasFilter', newHasFilter)
|
|
1162
|
+
variableString = trim(t[0])
|
|
1097
1163
|
}
|
|
1098
1164
|
|
|
1099
1165
|
let resolverFunction
|
|
@@ -1113,21 +1179,17 @@ ${valueObject.path}: ${valueObject.originalSource}
|
|
|
1113
1179
|
}
|
|
1114
1180
|
return false
|
|
1115
1181
|
})
|
|
1116
|
-
|
|
1117
1182
|
// console.log('found', found)
|
|
1118
1183
|
|
|
1119
1184
|
if (found && resolverFunction) {
|
|
1120
1185
|
// TODO finalize resolverFunction API
|
|
1121
|
-
const valuePromise = resolverFunction(
|
|
1122
|
-
variableString,
|
|
1123
|
-
this.options,
|
|
1124
|
-
this.config,
|
|
1125
|
-
valueObject
|
|
1126
|
-
).then((val) => {
|
|
1186
|
+
const valuePromise = resolverFunction(variableString, this.options, this.config, valueObject).then((val) => {
|
|
1127
1187
|
// console.log('VALUE', val)
|
|
1128
|
-
if (
|
|
1188
|
+
if (
|
|
1189
|
+
val === null ||
|
|
1190
|
+
typeof val === 'undefined' ||
|
|
1129
1191
|
/* match deep refs as empty {}, they need resolving via functions */
|
|
1130
|
-
(typeof val === 'object' &&
|
|
1192
|
+
(typeof val === 'object' && isEmpty(val) && variableString.match(/deep\:/))
|
|
1131
1193
|
) {
|
|
1132
1194
|
// console.log('variableString', variableString)
|
|
1133
1195
|
const cleanV = cleanVariable(propertyString, this.variableSyntax)
|
|
@@ -1135,9 +1197,8 @@ ${valueObject.path}: ${valueObject.originalSource}
|
|
|
1135
1197
|
|
|
1136
1198
|
if (variableString.match(/deep\:/)) {
|
|
1137
1199
|
// return Promise.resolve(this.getValueFromDeep(variableString))
|
|
1138
|
-
const deepIndex = variableString.match(
|
|
1139
|
-
const
|
|
1140
|
-
const deepRef = variableString.replace(deepPrefixReplace, '')
|
|
1200
|
+
const deepIndex = variableString.match(deepIndexPattern)
|
|
1201
|
+
const deepRef = variableString.replace(deepPrefixReplacePattern, '')
|
|
1141
1202
|
// console.log('deepRef', deepRef)
|
|
1142
1203
|
// console.log('deepIndexMatch', deepIndex)
|
|
1143
1204
|
if (deepIndex[1] && this.deep.length) {
|
|
@@ -1152,22 +1213,21 @@ ${valueObject.path}: ${valueObject.originalSource}
|
|
|
1152
1213
|
// TODO throw on empty values?
|
|
1153
1214
|
// No fallback value found AND this is undefined, throw error
|
|
1154
1215
|
if (valueCount.length === 1) {
|
|
1155
|
-
throw new Error(`
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
Please fix this reference or provide a valid fallback value.
|
|
1160
|
-
Like so: \${${variableString}, "fallbackValue"\}.`)
|
|
1216
|
+
throw new Error(`
|
|
1217
|
+
Unable to resolve variable ${propertyString} from "${valueObject.originalSource}" at path ${valueObject.path ? `"${arrayToJsonPath(valueObject.path)}"` : 'na'}
|
|
1218
|
+
\nFix this reference, your inputs and/or provide a valid fallback value.
|
|
1219
|
+
\nExample of setting a fallback value: \${${variableString}, "fallbackValue"\}\n`)
|
|
1161
1220
|
}
|
|
1162
|
-
|
|
1163
1221
|
// no value resolved but fallback value exists, keep moving on
|
|
1164
1222
|
return Promise.resolve(val)
|
|
1165
1223
|
}
|
|
1224
|
+
/*
|
|
1166
1225
|
// console.log('------')
|
|
1167
1226
|
// console.log('propertyString', propertyString)
|
|
1168
1227
|
// console.log('resolved val', val)
|
|
1169
1228
|
// console.log('------')
|
|
1170
1229
|
// console.log('newHasFilter', newHasFilter)
|
|
1230
|
+
/** */
|
|
1171
1231
|
// No filters found. return value
|
|
1172
1232
|
if (!newHasFilter) {
|
|
1173
1233
|
return Promise.resolve(val)
|
|
@@ -1179,47 +1239,50 @@ Like so: \${${variableString}, "fallbackValue"\}.`)
|
|
|
1179
1239
|
}
|
|
1180
1240
|
return acc.concat({
|
|
1181
1241
|
filter: this.filters[currentFilter],
|
|
1242
|
+
filterName: currentFilter,
|
|
1182
1243
|
// args: argsToPass
|
|
1183
1244
|
})
|
|
1184
1245
|
}, [])
|
|
1246
|
+
// console.log('newUse', newUse)
|
|
1185
1247
|
|
|
1186
1248
|
if (typeof val === 'string' && val.match(/deep:/)) {
|
|
1187
1249
|
// TODO refactor the deep filter logic here. match | filter | filter..
|
|
1188
|
-
const allFilters = propertyString
|
|
1189
|
-
|
|
1250
|
+
const allFilters = propertyString
|
|
1251
|
+
.replace(/}$/, '')
|
|
1252
|
+
.split('|')
|
|
1253
|
+
.reduce((acc, currentFilter, i) => {
|
|
1254
|
+
if (i === 0) {
|
|
1255
|
+
return acc
|
|
1256
|
+
}
|
|
1257
|
+
acc += `| ${trim(currentFilter)}`
|
|
1190
1258
|
return acc
|
|
1191
|
-
}
|
|
1192
|
-
acc += `| ${_.trim(currentFilter)}`
|
|
1193
|
-
return acc
|
|
1194
|
-
}, '')
|
|
1195
|
-
|
|
1259
|
+
}, '')
|
|
1196
1260
|
// add filters to deep references if filter is used
|
|
1197
|
-
const deepValueWithFilters =
|
|
1261
|
+
const deepValueWithFilters = newHasFilter[1] ? val.replace(/}$/, ` ${allFilters}}`) : val
|
|
1198
1262
|
// console.log('deepValueWithFilters', deepValueWithFilters)
|
|
1199
1263
|
return Promise.resolve(deepValueWithFilters)
|
|
1200
1264
|
}
|
|
1201
|
-
|
|
1202
|
-
// console.log('newUse', newUse)
|
|
1203
1265
|
/* Loop over filters used and produce new value */
|
|
1204
1266
|
const newValue = newUse.reduce((a, c) => {
|
|
1205
1267
|
// Fix for async value resolution. That code file refs returns object with .value
|
|
1206
|
-
const theValue =
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
return c.filter(theValue)
|
|
1268
|
+
const theValue = typeof a === 'object' && a.__internal_only_flag ? a.value : a
|
|
1269
|
+
if (typeof c.filter !== 'function') {
|
|
1270
|
+
return theValue
|
|
1271
|
+
}
|
|
1272
|
+
if (c.args) {
|
|
1273
|
+
this.filterCache[pathValue] = (this.filterCache[pathValue] || []).concat(c.filterName)
|
|
1274
|
+
return c.filter(theValue, ...c.args, 'from getValueFromSource with args')
|
|
1213
1275
|
}
|
|
1214
|
-
|
|
1276
|
+
this.filterCache[pathValue] = (this.filterCache[pathValue] || []).concat(c.filterName)
|
|
1277
|
+
return c.filter(theValue, 'from getValueFromSource')
|
|
1215
1278
|
}, val)
|
|
1279
|
+
// console.log('newValue', newValue)
|
|
1216
1280
|
|
|
1217
1281
|
return Promise.resolve(newValue)
|
|
1218
1282
|
})
|
|
1219
|
-
|
|
1283
|
+
// console.log('newHasFilter', newHasFilter)
|
|
1220
1284
|
// TODO do something with func here?
|
|
1221
|
-
|
|
1222
|
-
return this.tracker.add(variableString, valuePromise, propertyString, newHasFilter)
|
|
1285
|
+
return this.tracker.add(variableString, valuePromise, propertyString, newHasFilter, promiseKey)
|
|
1223
1286
|
}
|
|
1224
1287
|
|
|
1225
1288
|
/* fall through case with self refs */
|
|
@@ -1253,7 +1316,7 @@ Like so: \${${variableString}, "fallbackValue"\}.`)
|
|
|
1253
1316
|
}
|
|
1254
1317
|
const fallbackStr = getFallbackString(split, nestedVar)
|
|
1255
1318
|
return this.getValueFromSource(variableString, {
|
|
1256
|
-
value: fallbackStr
|
|
1319
|
+
value: fallbackStr,
|
|
1257
1320
|
}, 'nestedVar')
|
|
1258
1321
|
}
|
|
1259
1322
|
|
|
@@ -1265,11 +1328,11 @@ Like so: \${${variableString}, "fallbackValue"\}.`)
|
|
|
1265
1328
|
if (fallbackValue) {
|
|
1266
1329
|
// recurse on fallback and check again
|
|
1267
1330
|
return this.getValueFromSource(`${variableString})`, {
|
|
1268
|
-
value: propertyString
|
|
1331
|
+
value: propertyString,
|
|
1269
1332
|
}, 'cleanClean.match(fileRefSyntax)')
|
|
1270
1333
|
}
|
|
1271
1334
|
}
|
|
1272
|
-
// const fallbackValue = split[1]
|
|
1335
|
+
// const fallbackValue = split[1]
|
|
1273
1336
|
// console.log('variableString', variableString)
|
|
1274
1337
|
// console.log('propertyString', propertyString)
|
|
1275
1338
|
// console.log('fallbackValue', fallbackValue)
|
|
@@ -1281,20 +1344,23 @@ Like so: \${${variableString}, "fallbackValue"\}.`)
|
|
|
1281
1344
|
|
|
1282
1345
|
// has fallback but needs deeper lookup. Call getValueFromSource again
|
|
1283
1346
|
if (fallbackValue) {
|
|
1284
|
-
if (DEBUG)
|
|
1285
|
-
|
|
1286
|
-
}
|
|
1347
|
+
if (DEBUG) console.log('fallbackValue', fallbackValue)
|
|
1348
|
+
// console.log('fallbackValue', fallbackValue)
|
|
1287
1349
|
// recurse on fallback and check again
|
|
1288
|
-
return this.getValueFromSource(
|
|
1289
|
-
|
|
1290
|
-
|
|
1350
|
+
return this.getValueFromSource(
|
|
1351
|
+
fallbackValue,
|
|
1352
|
+
{
|
|
1353
|
+
value: propertyString,
|
|
1354
|
+
},
|
|
1355
|
+
'fallbackValue',
|
|
1356
|
+
)
|
|
1291
1357
|
}
|
|
1292
1358
|
}
|
|
1293
1359
|
|
|
1294
1360
|
// Variable NOT FOUND. Warn user
|
|
1295
1361
|
const errorMessage = [
|
|
1296
1362
|
`Invalid variable reference syntax`,
|
|
1297
|
-
`Key: "${
|
|
1363
|
+
`Key: "${valueObject.path ? valueObject.path.join('.') : 'na'}"`,
|
|
1298
1364
|
`Variable: "${variableString}" from ${propertyString} not found`,
|
|
1299
1365
|
]
|
|
1300
1366
|
|
|
@@ -1325,28 +1391,21 @@ Like so: \${${variableString}, "fallbackValue"\}.`)
|
|
|
1325
1391
|
|
|
1326
1392
|
return this.tracker.add(variableString, notFoundPromise, propertyString, newHasFilter)
|
|
1327
1393
|
}
|
|
1328
|
-
getValueFromString(variableString) {
|
|
1329
|
-
const valueToPopulate = variableString.replace(/^['"]|['"]$/g, '')
|
|
1330
|
-
return Promise.resolve(valueToPopulate)
|
|
1331
|
-
}
|
|
1332
|
-
|
|
1333
1394
|
getValueFromSelf(variableString, o, x, data) {
|
|
1334
1395
|
/*
|
|
1335
1396
|
console.log('getValueFromSelf variableString', variableString)
|
|
1336
1397
|
/** */
|
|
1337
|
-
|
|
1338
1398
|
// console.log('self data', data)
|
|
1339
|
-
|
|
1340
1399
|
const split = variableString.split(':')
|
|
1341
|
-
const variable =
|
|
1400
|
+
const variable = split.length && split[1] ? split[1] : variableString
|
|
1342
1401
|
const valueToPopulate = this.config
|
|
1343
|
-
let deepProperties = variable.split('.').filter(property => property)
|
|
1402
|
+
let deepProperties = variable.split('.').filter((property) => property)
|
|
1344
1403
|
// console.log('self deep', deepProperties)
|
|
1345
1404
|
// console.log('self valueToPopulate', valueToPopulate)
|
|
1346
1405
|
|
|
1347
1406
|
/* its file ref so we need to shift lookup for self in nested files */
|
|
1348
1407
|
if (data.isFileRef) {
|
|
1349
|
-
const dotPropPath =
|
|
1408
|
+
const dotPropPath = deepProperties.length > 1 ? deepProperties.join('.') : deepProperties[0]
|
|
1350
1409
|
const exists = dotProp.get(valueToPopulate, dotPropPath)
|
|
1351
1410
|
// console.log('self exists', exists)
|
|
1352
1411
|
if (!exists) {
|
|
@@ -1355,7 +1414,6 @@ Like so: \${${variableString}, "fallbackValue"\}.`)
|
|
|
1355
1414
|
// console.log('self fixed deepProperties', deepProperties)
|
|
1356
1415
|
}
|
|
1357
1416
|
}
|
|
1358
|
-
|
|
1359
1417
|
return this.getDeeperValue(deepProperties, valueToPopulate).then((res) => {
|
|
1360
1418
|
/*
|
|
1361
1419
|
console.log('self getDeeperValue variableString', variableString)
|
|
@@ -1364,7 +1422,6 @@ Like so: \${${variableString}, "fallbackValue"\}.`)
|
|
|
1364
1422
|
return res
|
|
1365
1423
|
})
|
|
1366
1424
|
}
|
|
1367
|
-
|
|
1368
1425
|
getValueFromFile(variableString) {
|
|
1369
1426
|
// console.log('From file', `"${variableString}"`)
|
|
1370
1427
|
let matchedFileString = variableString.match(fileRefSyntax)[0]
|
|
@@ -1378,7 +1435,7 @@ Like so: \${${variableString}, "fallbackValue"\}.`)
|
|
|
1378
1435
|
if (hasParams) {
|
|
1379
1436
|
const splitter = splitCsv(hasParams[2])
|
|
1380
1437
|
const argsFound = splitter.map((arg) => {
|
|
1381
|
-
const cleanArg =
|
|
1438
|
+
const cleanArg = trim(arg).replace(/^'|"/, '').replace(/'|"$/, '')
|
|
1382
1439
|
return cleanArg
|
|
1383
1440
|
})
|
|
1384
1441
|
|
|
@@ -1392,12 +1449,11 @@ Like so: \${${variableString}, "fallbackValue"\}.`)
|
|
|
1392
1449
|
}
|
|
1393
1450
|
// console.log('argsToPass', argsToPass)
|
|
1394
1451
|
|
|
1395
|
-
const relativePath = trimSurroundingQuotes(
|
|
1396
|
-
.replace(fileRefSyntax, (match, varName) => varName.trim())
|
|
1397
|
-
|
|
1398
|
-
|
|
1452
|
+
const relativePath = trimSurroundingQuotes(
|
|
1453
|
+
matchedFileString.replace(fileRefSyntax, (match, varName) => varName.trim()).replace('~', os.homedir()),
|
|
1454
|
+
)
|
|
1399
1455
|
|
|
1400
|
-
let fullFilePath =
|
|
1456
|
+
let fullFilePath = path.isAbsolute(relativePath) ? relativePath : path.join(this.configPath, relativePath)
|
|
1401
1457
|
|
|
1402
1458
|
// console.log('fullFilePath', fullFilePath)
|
|
1403
1459
|
|
|
@@ -1405,7 +1461,7 @@ Like so: \${${variableString}, "fallbackValue"\}.`)
|
|
|
1405
1461
|
// Get real path to handle potential symlinks (but don't fatal error)
|
|
1406
1462
|
fullFilePath = fs.realpathSync(fullFilePath)
|
|
1407
1463
|
|
|
1408
|
-
|
|
1464
|
+
// Only match files that are relative
|
|
1409
1465
|
} else if (relativePath.match(/\.\//)) {
|
|
1410
1466
|
// TODO test higher parent refs
|
|
1411
1467
|
const cleanName = path.basename(relativePath)
|
|
@@ -1471,17 +1527,16 @@ Check if your javascript is exporting a function that returns a value.`
|
|
|
1471
1527
|
deepProperties.splice(0, 1)
|
|
1472
1528
|
// Trim prop keys for starting/trailing spaces
|
|
1473
1529
|
deepProperties = deepProperties.map((prop) => {
|
|
1474
|
-
return
|
|
1530
|
+
return trim(prop)
|
|
1475
1531
|
})
|
|
1476
|
-
return this.getDeeperValue(deepProperties, valueToPopulateResolved)
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
const errorMessage = `Invalid variable syntax when referencing file "${relativePath}".
|
|
1532
|
+
return this.getDeeperValue(deepProperties, valueToPopulateResolved).then((deepValueToPopulateResolved) => {
|
|
1533
|
+
if (typeof deepValueToPopulateResolved === 'undefined') {
|
|
1534
|
+
const errorMessage = `Invalid variable syntax when referencing file "${relativePath}".
|
|
1480
1535
|
Check if your javascript is returning the correct data.`
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1536
|
+
return Promise.reject(new Error(errorMessage))
|
|
1537
|
+
}
|
|
1538
|
+
return Promise.resolve(deepValueToPopulateResolved)
|
|
1539
|
+
})
|
|
1485
1540
|
})
|
|
1486
1541
|
}
|
|
1487
1542
|
|
|
@@ -1527,13 +1582,9 @@ Please use ":" to reference sub properties`
|
|
|
1527
1582
|
}
|
|
1528
1583
|
return Promise.resolve(valueToPopulate)
|
|
1529
1584
|
}
|
|
1530
|
-
|
|
1531
|
-
getDeepIndex(variableString) {
|
|
1532
|
-
const deepIndexReplace = RegExp(/^deep:|(\.[^}]+)*$/g)
|
|
1533
|
-
return variableString.replace(deepIndexReplace, '')
|
|
1534
|
-
}
|
|
1535
1585
|
getVariableFromDeep(variableString) {
|
|
1536
|
-
const index =
|
|
1586
|
+
const index = variableString.replace(deepIndexReplacePattern, '')
|
|
1587
|
+
// const index = this.getDeepIndex(variableString)
|
|
1537
1588
|
/*
|
|
1538
1589
|
console.log('FIND INDEX', index)
|
|
1539
1590
|
console.log(this.deep)
|
|
@@ -1541,20 +1592,20 @@ Please use ":" to reference sub properties`
|
|
|
1541
1592
|
return this.deep[index]
|
|
1542
1593
|
}
|
|
1543
1594
|
getValueFromDeep(variableString) {
|
|
1544
|
-
const deepPrefixReplace = RegExp(/(?:^deep:)\d+\.?/g)
|
|
1545
1595
|
const variable = this.getVariableFromDeep(variableString)
|
|
1546
|
-
const deepRef = variableString.replace(
|
|
1596
|
+
const deepRef = variableString.replace(deepPrefixReplacePattern, '')
|
|
1547
1597
|
/*
|
|
1548
1598
|
console.log("GET getValueFromDeep", variableString)
|
|
1549
1599
|
console.log('deepRef', deepRef)
|
|
1550
|
-
console.log('
|
|
1600
|
+
console.log('getValueFromDeep variable', variable)
|
|
1551
1601
|
/** */
|
|
1552
|
-
let ret = this.populateValue({ value: variable })
|
|
1602
|
+
let ret = this.populateValue({ value: variable }, undefined, 'getValueFromDeep')
|
|
1553
1603
|
// console.log('variable ret', ret)
|
|
1554
|
-
if (deepRef.length) {
|
|
1604
|
+
if (deepRef.length) {
|
|
1605
|
+
// if there is a deep reference remaining
|
|
1555
1606
|
ret = ret.then((result) => {
|
|
1556
1607
|
// console.log('DEEP RESULT', result)
|
|
1557
|
-
if (
|
|
1608
|
+
if (isString(result.value) && result.value.match(this.variableSyntax)) {
|
|
1558
1609
|
// console.log('makeDeepVariable getValueFromDeep', result.value)
|
|
1559
1610
|
const deepVariable = this.makeDeepVariable(result.value)
|
|
1560
1611
|
return Promise.resolve(appendDeepVariable(deepVariable, deepRef))
|
|
@@ -1564,7 +1615,6 @@ Please use ":" to reference sub properties`
|
|
|
1564
1615
|
}
|
|
1565
1616
|
return ret
|
|
1566
1617
|
}
|
|
1567
|
-
|
|
1568
1618
|
makeDeepVariable(variable) {
|
|
1569
1619
|
// console.log('MAKE DEEP', variable)
|
|
1570
1620
|
let index = this.deep.findIndex((item) => variable === item)
|
|
@@ -1581,7 +1631,6 @@ Please use ":" to reference sub properties`
|
|
|
1581
1631
|
|
|
1582
1632
|
return deepVar
|
|
1583
1633
|
}
|
|
1584
|
-
|
|
1585
1634
|
/**
|
|
1586
1635
|
* Get a value that is within the given valueToPopulate. The deepProperties specify what value
|
|
1587
1636
|
* to retrieve from the given valueToPopulate. The trouble is that anywhere along this chain a
|
|
@@ -1601,16 +1650,17 @@ Please use ":" to reference sub properties`
|
|
|
1601
1650
|
console.log('deepProperties', deepProperties)
|
|
1602
1651
|
console.log('valueToPopulate', valueToPopulate)
|
|
1603
1652
|
/** */
|
|
1604
|
-
|
|
1605
1653
|
const veryDeep = deepProperties.reduce(async (reducedValueParam, subProperty) => {
|
|
1606
1654
|
let reducedValue = await reducedValueParam
|
|
1607
1655
|
// console.log('reducedValue', reducedValue)
|
|
1608
1656
|
// console.log(typeof reducedValue)
|
|
1609
1657
|
// console.log('subProperty', `"${subProperty}"`)
|
|
1610
1658
|
|
|
1611
|
-
if (
|
|
1659
|
+
if (isString(reducedValue) && reducedValue.match(deepRefSyntax)) {
|
|
1660
|
+
// build mode
|
|
1612
1661
|
reducedValue = appendDeepVariable(reducedValue, subProperty)
|
|
1613
|
-
} else {
|
|
1662
|
+
} else {
|
|
1663
|
+
// get mode
|
|
1614
1664
|
if (typeof reducedValue === 'undefined') {
|
|
1615
1665
|
// was reducedValue = {}
|
|
1616
1666
|
// Adding internal flag signals this value is unknown
|
|
@@ -1629,7 +1679,7 @@ Please use ":" to reference sub properties`
|
|
|
1629
1679
|
} catch (e) {}
|
|
1630
1680
|
|
|
1631
1681
|
reducedValue = reducedValue[subProperty]
|
|
1632
|
-
} else if (
|
|
1682
|
+
} else if (isString(reducedValue)) {
|
|
1633
1683
|
try {
|
|
1634
1684
|
// if JSON parse it
|
|
1635
1685
|
reducedValue = JSON.parse(reducedValue)
|
|
@@ -1666,126 +1716,14 @@ Please use ":" to reference sub properties`
|
|
|
1666
1716
|
// console.log("MISSING", variableString)
|
|
1667
1717
|
// console.log(this.deep)
|
|
1668
1718
|
// console.log(valueToPopulate)
|
|
1669
|
-
|
|
1670
1719
|
const notFoundMsg = `No ${varType} found to satisfy the '\${${variableString}}' variable. Attempting fallback value`
|
|
1671
1720
|
if (DEBUG) {
|
|
1672
1721
|
console.log(notFoundMsg)
|
|
1673
1722
|
}
|
|
1674
|
-
|
|
1675
1723
|
// errors make fallbacks not function. throw new Error(errorMsg)
|
|
1676
1724
|
}
|
|
1677
1725
|
return valueToPopulate
|
|
1678
1726
|
}
|
|
1679
1727
|
}
|
|
1680
1728
|
|
|
1681
|
-
function findNestedVariable(split, originalSource) {
|
|
1682
|
-
return split.find((thing) => {
|
|
1683
|
-
if (originalSource && typeof originalSource === 'string') {
|
|
1684
|
-
return originalSource.indexOf(`\${${thing}}`) > -1
|
|
1685
|
-
}
|
|
1686
|
-
return false
|
|
1687
|
-
})
|
|
1688
|
-
}
|
|
1689
|
-
|
|
1690
|
-
/**
|
|
1691
|
-
* Get fallback variable string
|
|
1692
|
-
* @param {array} split - array from split at comma
|
|
1693
|
-
* @param {string} nestedVar - fallback variable to reconstruct variable string from
|
|
1694
|
-
* @return {string} - returns new ${variable, string}
|
|
1695
|
-
*/
|
|
1696
|
-
function getFallbackString(split, nestedVar) {
|
|
1697
|
-
let isSet = false
|
|
1698
|
-
const newVar = split.reduce((acc, curr) => {
|
|
1699
|
-
if (curr === nestedVar || isSet) {
|
|
1700
|
-
acc = acc.concat(curr)
|
|
1701
|
-
isSet = true
|
|
1702
|
-
}
|
|
1703
|
-
return acc
|
|
1704
|
-
}, []).join(', ')
|
|
1705
|
-
const cleanC = `\${${newVar.replace(/^\${/, '').replace(/}$/, '')}}`
|
|
1706
|
-
return cleanC
|
|
1707
|
-
}
|
|
1708
|
-
|
|
1709
|
-
function verifyVariable(variableString, valueObject, variableTypes, config) {
|
|
1710
|
-
const isRealVariable = variableTypes.some((r, i) => {
|
|
1711
|
-
if (r.match instanceof RegExp && variableString.match(r.match)) {
|
|
1712
|
-
return true
|
|
1713
|
-
} else if (typeof r.match === 'function') {
|
|
1714
|
-
if (r.match(variableString, config, valueObject)) {
|
|
1715
|
-
return true
|
|
1716
|
-
}
|
|
1717
|
-
}
|
|
1718
|
-
return false
|
|
1719
|
-
})
|
|
1720
|
-
// If not found in variable resolvers and is missing a colon throw
|
|
1721
|
-
if (!isRealVariable && variableString.match(/:/)) {
|
|
1722
|
-
// console.log('variableString', variableString)
|
|
1723
|
-
throw new Error(`
|
|
1724
|
-
Variable \${${variableString}} is invalid variable syntax.
|
|
1725
|
-
Value Path: ${(valueObject.path) ? valueObject.path.join('.') : 'na'}
|
|
1726
|
-
Original Value: ${valueObject.originalSource}
|
|
1727
|
-
|
|
1728
|
-
Remove or update the \${${variableString}} to fix
|
|
1729
|
-
`)
|
|
1730
|
-
}
|
|
1731
|
-
return isRealVariable
|
|
1732
|
-
}
|
|
1733
|
-
|
|
1734
|
-
function encodeUnknown(v) {
|
|
1735
|
-
return `>passthrough[_[${Buffer.from(v).toString('base64')}]_]`
|
|
1736
|
-
}
|
|
1737
|
-
|
|
1738
|
-
function decodeUnknown(rawValue) {
|
|
1739
|
-
const x = findUnknownValues(rawValue)
|
|
1740
|
-
let val = rawValue.replace(/>passthrough/g, '')
|
|
1741
|
-
if (x.length) {
|
|
1742
|
-
x.forEach(({ match, value }) => {
|
|
1743
|
-
// console.log('match', match)
|
|
1744
|
-
const decodedValue = Buffer.from(value, 'base64').toString('ascii')
|
|
1745
|
-
// console.log('decodedValue', decodedValue)
|
|
1746
|
-
val = val.replace(match, decodedValue)
|
|
1747
|
-
})
|
|
1748
|
-
}
|
|
1749
|
-
return val
|
|
1750
|
-
}
|
|
1751
|
-
|
|
1752
|
-
function findUnknownValues(text) {
|
|
1753
|
-
let matches
|
|
1754
|
-
let links = []
|
|
1755
|
-
while ((matches = base64WrapperRegex.exec(text)) !== null) {
|
|
1756
|
-
if (matches.index === base64WrapperRegex.lastIndex) {
|
|
1757
|
-
base64WrapperRegex.lastIndex++ // avoid infinite loops with zero-width matches
|
|
1758
|
-
}
|
|
1759
|
-
links.push({
|
|
1760
|
-
match: matches[0],
|
|
1761
|
-
value: matches[1]
|
|
1762
|
-
})
|
|
1763
|
-
}
|
|
1764
|
-
return links
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
function isPromise(obj) { // eslint-disable-line
|
|
1768
|
-
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'
|
|
1769
|
-
}
|
|
1770
|
-
|
|
1771
|
-
// TODO fix argument parsing to handle commas
|
|
1772
|
-
function splitCsv(str, splitter) {
|
|
1773
|
-
const splitSyntax = splitter || ','
|
|
1774
|
-
// Split at comma SPACE ", "
|
|
1775
|
-
return str.split(splitSyntax).reduce((accum, curr) => {
|
|
1776
|
-
if (accum.isConcatting) {
|
|
1777
|
-
accum.soFar[accum.soFar.length - 1] += ',' + curr
|
|
1778
|
-
} else {
|
|
1779
|
-
accum.soFar.push(curr)
|
|
1780
|
-
}
|
|
1781
|
-
if (curr.split('"').length % 2 == 0) { // eslint-disable-line
|
|
1782
|
-
accum.isConcatting = !accum.isConcatting
|
|
1783
|
-
}
|
|
1784
|
-
return accum
|
|
1785
|
-
}, {
|
|
1786
|
-
soFar: [],
|
|
1787
|
-
isConcatting: false
|
|
1788
|
-
}).soFar
|
|
1789
|
-
}
|
|
1790
|
-
|
|
1791
1729
|
module.exports = Configorama
|