configorama 0.6.1 → 0.6.3

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 CHANGED
@@ -4,6 +4,7 @@ const fs = require('fs')
4
4
  const minimist = require('minimist')
5
5
  const Configorama = require('./src/main')
6
6
  const deepLog = require('./src/utils/deep-log')
7
+ const { logHeader } = require('./src/utils/logs')
7
8
 
8
9
  // Parse command line arguments
9
10
  const argv = minimist(process.argv.slice(2), {
@@ -79,7 +80,7 @@ const options = {
79
80
  }
80
81
 
81
82
  if (options.dynamicArgs.verbose) {
82
- console.log('───────────── Input Options ──────────────────────')
83
+ logHeader('Config Input Options')
83
84
  const dynamicArgs = options.dynamicArgs || {}
84
85
  const {
85
86
  _,
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "configorama",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "Variable support for configuration files",
5
- "main": "lib/index.js",
5
+ "main": "src/index.js",
6
6
  "files": [
7
7
  "cli.js",
8
8
  "src",
@@ -34,8 +34,10 @@
34
34
  "url": "https://github.com/DavidWells/configorama"
35
35
  },
36
36
  "dependencies": {
37
+ "@davidwells/box-logger": "^1.0.10",
37
38
  "@iarna/toml": "^2.2.5",
38
39
  "dot-prop": "^5.3.0",
40
+ "env-stage-loader": "^1.1.1",
39
41
  "find-up": "^3.0.0",
40
42
  "git-url-parse": "^14.0.0",
41
43
  "js-yaml": "^3.14.1",
@@ -60,6 +62,7 @@
60
62
  "lodash.split": "^4.4.2",
61
63
  "minimist": "^1.2.8",
62
64
  "promise.prototype.finally": "^3.1.8",
65
+ "safe-chalk": "^1.0.0",
63
66
  "sync-rpc": "^1.3.6",
64
67
  "traverse": "^0.6.8"
65
68
  },
package/src/main.js CHANGED
@@ -12,6 +12,8 @@ const promiseFinallyShim = require('promise.prototype.finally').shim()
12
12
  const findUp = require('find-up')
13
13
  const traverse = require('traverse')
14
14
  const dotProp = require('dot-prop')
15
+ const chalk = require('./utils/chalk')
16
+
15
17
  /* Default Value resolvers */
16
18
  const getValueFromString = require('./resolvers/valueFromString')
17
19
  const getValueFromNumber = require('./resolvers/valueFromNumber')
@@ -48,6 +50,8 @@ const { encodeUnknown, decodeUnknown } = require('./utils/unknownValues')
48
50
  const { mergeByKeys } = require('./utils/mergeByKeys')
49
51
  const { arrayToJsonPath } = require('./utils/arrayToJsonPath')
50
52
  const { findNestedVariables } = require('./utils/find-nested-variables')
53
+ const { makeBox } = require('@davidwells/box-logger')
54
+ const { logHeader } = require('./utils/logs')
51
55
  /**
52
56
  * Maintainer's notes:
53
57
  *
@@ -160,6 +164,26 @@ class Configorama {
160
164
  this.originalString = fileContents
161
165
  // Keep a copy
162
166
  this.originalConfig = cloneDeep(configObject)
167
+
168
+ const useDotEnv = this.originalConfig.useDotenv || this.originalConfig.useDotEnv
169
+ if ((useDotEnv && useDotEnv === true) || this.opts.useDotEnvFiles) {
170
+ const loadStageEnv = require('env-stage-loader')
171
+ let providerStage
172
+ if (this.originalConfig && this.originalConfig.provider && this.originalConfig.provider.stage) {
173
+ providerStage = this.originalConfig.provider.stage
174
+ // @TODO check value to see if variable and needs pre-resolving to resolve stage vars
175
+ }
176
+ const stage = this.opts.stage || providerStage || 'dev'
177
+ /* Load env variables into process.env */
178
+ const values = loadStageEnv({
179
+ // silent: true,
180
+ // debug: true,
181
+ env: stage,
182
+ // defaultEnv: 'prod',
183
+ // ignoreFiles: ['.env']
184
+ })
185
+ }
186
+
163
187
  // Set configPath for file references
164
188
  this.configPath = fileDirectory
165
189
  }
@@ -407,7 +431,7 @@ class Configorama {
407
431
  }
408
432
 
409
433
  if (VERBOSE) {
410
- console.log('───────────── Input Config ──────────────────────')
434
+ logHeader('Config Input before processing')
411
435
  console.log()
412
436
  deepLog(this.originalConfig)
413
437
  console.log()
@@ -415,60 +439,229 @@ class Configorama {
415
439
 
416
440
  if (VERBOSE || showFoundVariables) {
417
441
  const foundVariables = []
418
- let loggedHeader = false
442
+ const variableData = {}
443
+ let matchCount = 1
419
444
  traverse(this.originalConfig).forEach(function (rawValue) {
420
445
  if (typeof rawValue === 'string' && rawValue.match(variableSyntax)) {
421
446
  const configValuePath = this.path.join('.')
422
447
  if (configValuePath.endsWith('Fn::Sub')) {
423
448
  return
424
449
  }
425
-
426
-
427
- if (!loggedHeader) {
428
- console.log('───────────── Variables Detected ──────────────────────')
429
- console.log()
430
- loggedHeader = true
431
- }
432
450
 
433
451
  const nested = findNestedVariables(rawValue, variableSyntax, variablesKnownTypes, configValuePath)
434
452
  /*
435
- console.log(nested)
453
+ console.log('traverse nested result', nested)
436
454
  /** */
437
455
 
438
- console.log(`▷ Path: ${configValuePath}`)
439
- console.log('\n Key/value:')
440
- console.log(` ${configValuePath}: ${rawValue}`)
441
- if (nested.length > 0) {
442
- const nestedCount = nested.length - 1
443
- console.log('\n Variable:')
444
- console.log(` ${nested[nested.length - 1].fullMatch}`)
445
-
446
- if (nestedCount) {
447
- console.log(`\n Contains ${nestedCount} nested values.`)
456
+ // console.log(`▷ Path: ${configValuePath}`)
457
+ // console.log('\n Key/value:')
458
+ // console.log(` ${configValuePath}: ${rawValue}`)
459
+ const lastItem = nested[nested.length - 1]
460
+ const lastKeyPath = this.path[this.path.length - 1]
461
+ const itemKey = (lastKeyPath.match(/[\d+]$/)) ? `${this.path[this.path.length - 2]}[${lastKeyPath}]` : lastKeyPath
462
+ const key = lastItem.fullMatch
463
+ const varData = {
464
+ path: configValuePath,
465
+ key: itemKey,
466
+ value: rawValue,
467
+ variable: lastItem.fullMatch,
468
+ isRequired: false,
469
+ defaultValue: undefined,
470
+ matchIndex: matchCount++,
471
+ // hasFallback: false,
472
+ resolveOrder: [],
473
+ resolveDetails: nested,
474
+ }
475
+
476
+ function calculateResolveOrder(item) {
477
+ if (item && item.fallbackValues) {
478
+ let hasResolvedFallback
479
+ const order = ([item.valueBeforeFallback]).concat(item.fallbackValues.map((f, i) => {
480
+ if (f.fallbackValues) {
481
+ const [nestedOrder, nestedResolvedFallback] = calculateResolveOrder(f)
482
+ if (!hasResolvedFallback && nestedResolvedFallback) {
483
+ hasResolvedFallback = nestedResolvedFallback
484
+ }
485
+ return nestedOrder // Return just the order part
486
+ }
487
+ if (!hasResolvedFallback && f.isResolvedFallback) {
488
+ hasResolvedFallback = f.stringValue
489
+ }
490
+ return `${f.stringValue || f.variable}${f.isResolvedFallback ? ' (Resolved default fallback)' : ''}`
491
+ })).flat()
492
+
493
+ return [order, hasResolvedFallback]
448
494
  }
449
-
450
- // non mutate remove last
451
- const removeLast = (nested.length > 1) ? nested.slice(0, -1) : nested
452
- console.log()
453
- removeLast.forEach((v) => {
454
- if (v.hasFallback) {
455
- console.log(' Resolve order:')
456
- console.log(` 1. ${v.valueBeforeFallback}`)
457
- v.fallbackValues.forEach((f, i) => {
458
- console.log(` ${i + 2}. ${f.fullMatch}${f.isFallback ? ' (Fallback string value)' : ''}`)
459
- })
460
- }
461
- })
495
+ return [[item.variable], false] // Return array instead of just the value
462
496
  }
463
- console.log()
497
+
498
+ const [resolveOrder, hasResolvedFallback] = calculateResolveOrder(lastItem)
499
+ varData.resolveOrder = resolveOrder
500
+
501
+ if (hasResolvedFallback) {
502
+ varData.defaultValue = hasResolvedFallback
503
+ }
504
+
505
+
506
+ if (!varData.defaultValue) {
507
+ varData.isRequired = true
508
+ }
509
+
510
+
511
+ if (varData.resolveOrder.length > 1) {
512
+ varData.hasFallback = true
513
+ }
514
+
515
+ variableData[key] = (variableData[key] || []).concat(varData)
516
+
464
517
  foundVariables.push(rawValue)
465
518
  }
466
519
  })
467
520
 
468
- console.log(`───────────── Found ${foundVariables.length} Variables ──────────────────────`)
469
- console.log()
470
- deepLog(foundVariables)
471
- console.log()
521
+
522
+ if (!foundVariables.length) {
523
+ logHeader('No Variables Found in Config')
524
+ if (this.configFilePath) {
525
+ console.log(`File: ${this.configFilePath}`)
526
+ }
527
+
528
+ console.log(`\nVariable syntax: `, variableSyntax)
529
+
530
+ const varTypes = Object.keys(this.variableTypes)
531
+ if (varTypes.length) {
532
+ const exclude = ['fallthrough', 'deep']
533
+ console.log('\nAllowed variable types:')
534
+ varTypes.filter((v) => v.type !== 'fallthrough').forEach((v) => {
535
+ const vData = this.variableTypes[v]
536
+ if (exclude.includes(vData.type)) {
537
+ return
538
+ }
539
+ console.log(` - ${vData.type}: `, vData.match)
540
+ })
541
+ }
542
+ console.log()
543
+ }
544
+
545
+ // make foundVariables array unique
546
+ const finalFoundVariables = [...new Set(foundVariables)]
547
+ if (finalFoundVariables.length > 0) {
548
+ const varKeys = Object.keys(variableData)
549
+ const fileName = this.configFilePath ? ` in ${this.configFilePath}` : ''
550
+
551
+ logHeader(`Found ${varKeys.length} Variables${fileName}`)
552
+
553
+ // deepLog('variableData', variableData)
554
+
555
+ if (varKeys.length) {
556
+ console.log()
557
+ const longestKey = varKeys.reduce((acc, k) => {
558
+ return Math.max(acc, k.length)
559
+ }, 0)
560
+ console.log(varKeys.map((k) => {
561
+ const placesWord = variableData[k].length > 1 ? 'places' : 'place'
562
+ return `- ${k.padEnd(longestKey).padEnd(longestKey + 10)} referenced ${variableData[k].length} ${placesWord}`
563
+ }).join('\n'))
564
+ console.log()
565
+ }
566
+
567
+ logHeader('Variable Details')
568
+
569
+ const indent = ''
570
+ varKeys.forEach((key, i) => {
571
+ const variableInstances = variableData[key]
572
+
573
+ const firstInstance = variableInstances[0]
574
+
575
+ let requiredText = ''
576
+ let defaultValueSrc = ''
577
+ if (!firstInstance.defaultValue) {
578
+ // console.log('no default value', firstInstance)
579
+ /* Check if the fallback variable is a self reference */
580
+ const hasDotPropOrSelf = variableInstances.reduce((acc, v) => {
581
+ const dotProp = v.resolveDetails.find((d) => d.varType === 'dot.prop')
582
+ if (dotProp) {
583
+ acc.push(dotProp)
584
+ }
585
+ if (v.resolveDetails && v.resolveDetails.length === 1 && v.resolveDetails[0].varType === 'self:') {
586
+ // console.log('dot.prop', v.resolveDetails)
587
+ acc.push(v.resolveDetails[0])
588
+ }
589
+ return acc
590
+ }, [])
591
+ // console.log('hasDotPropOrSelf', hasDotPropOrSelf)
592
+ if (!hasDotPropOrSelf.length) {
593
+ requiredText = '[Required] '
594
+ } else {
595
+ const fallBackValues = variableInstances.filter((v) => v.resolveDetails.find((d) => d.hasFallback)).map((v) => v.resolveDetails)
596
+ // console.log('fallBackValues', fallBackValues)
597
+ if (fallBackValues.length) {
598
+ // console.log('fallBackValues.resolveDetails', fallBackValues)
599
+ }
600
+
601
+ const cleanPath = hasDotPropOrSelf[0].variable.replace('self:', '')
602
+ defaultValueSrc = cleanPath
603
+ // Find the dot prop value in the original config
604
+ const dotPropValue = dotProp.get(this.originalConfig, cleanPath)
605
+ // console.log('dotPropValue', dotPropValue)
606
+ if (typeof dotPropValue !== 'undefined') {
607
+ requiredText = ''
608
+ const niceString = typeof dotPropValue === 'object' ? JSON.stringify(dotPropValue) : dotPropValue
609
+ // truncate niceString to 100 characters
610
+ const truncatedString = niceString.length > 100 ? niceString.substring(0, 90) + '...' : niceString
611
+ firstInstance.defaultValue = truncatedString
612
+ }
613
+ }
614
+ //this.originalConfig[key] = undefined
615
+ }
616
+ const spacing = ' '
617
+ const titleText = `Variable:${spacing}`
618
+ const reqText = (requiredText) ? `${chalk.red.bold(requiredText)}\n` : ''
619
+ let varMsg = `${reqText}`
620
+ const VALUE_HEX = '#899499' // '#708090'
621
+ const keyChalk = chalk.whiteBright
622
+ const valueChalk = chalk.hex(VALUE_HEX)
623
+
624
+ if (firstInstance.defaultValue) {
625
+ const defaultValueText = `${indent}${keyChalk(`DefaultValue:`.padEnd(titleText.length, ' '))}`
626
+ // ensure padding is even
627
+ varMsg += `${defaultValueText} ${valueChalk(firstInstance.defaultValue)}`
628
+ }
629
+
630
+ if(defaultValueSrc) {
631
+ varMsg += `\n${indent}${keyChalk('DefaultValue path:'.padEnd(titleText.length, ' '))} `
632
+ varMsg += `${valueChalk(defaultValueSrc)}`
633
+ }
634
+
635
+ if (firstInstance.resolveOrder.length > 1) {
636
+ varMsg += `\n${indent}${keyChalk('Resolve Order:'.padEnd(titleText.length, ' '))}`
637
+ const resolveOrder = firstInstance.resolveOrder.join(', ')
638
+ varMsg += ` ${valueChalk(resolveOrder)}`
639
+ }
640
+
641
+ let locationRender = valueChalk(variableInstances[0].path)
642
+
643
+ let locationLabel = `${indent}${keyChalk('Used at:'.padEnd(titleText.length, ' '))}`
644
+ if (variableInstances.length > 1) {
645
+ locationRender = `\n${variableInstances.map((v) => valueChalk(`${indent}- ${v.path}`)).join('\n')}`
646
+ const locationLabelText = `${indent}${keyChalk('Used at:')}`
647
+ locationLabel = locationLabelText
648
+ }
649
+
650
+ varMsg += `\n${locationLabel} ${locationRender}`
651
+
652
+ // console.log(` ${chalk.bold(key)}`)
653
+ console.log(makeBox(varMsg, {
654
+ title: `${key}`,
655
+ borderColor: 'gray',
656
+ // style: 'bold',
657
+ minWidth: 120,
658
+ }))
659
+ if(i < varKeys.length - 1) {
660
+ //console.log()
661
+ }
662
+ })
663
+ }
664
+
472
665
  /* Exit early if list or info flag is set */
473
666
  if (showFoundVariables) {
474
667
  process.exit(0)
@@ -571,7 +764,7 @@ class Configorama {
571
764
  this.config = mergeByKeys(this.config, '', this.mergeKeys)
572
765
  }
573
766
  if (VERBOSE) {
574
- console.log('───────────── Resolved Config ───────────────────')
767
+ logHeader('Resolved Configuration value')
575
768
  console.log()
576
769
  deepLog(this.config)
577
770
  console.log()
@@ -153,7 +153,9 @@ function preProcess(ymlStr = '') {
153
153
 
154
154
  /* If have yaml object and vars not wrapped in quotes, wrap them */
155
155
  if (ymlStr.match(KEY_OBJECT)) {
156
- const hasObjects = matchOutermostBraces(ymlStr)
156
+ const values = matchOutermostBraces(ymlStr)
157
+ // console.log('values', values)
158
+ const hasObjects = values.filter((x) => !x.match(/{{resolve:/))
157
159
  // console.log('hasObjects', hasObjects)
158
160
  if (hasObjects && hasObjects.length) {
159
161
  hasObjects.forEach((txt) => {
@@ -187,6 +189,15 @@ function preProcess(ymlStr = '') {
187
189
  }
188
190
  })
189
191
  }
192
+ // Automagically wrap CF https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references-ssm.html
193
+ const cfParams = values.filter((x) => !x.match(/\s/) && x.match(/{{resolve:/))
194
+ if (cfParams && cfParams.length) {
195
+ cfParams.forEach((txt) => {
196
+ const pat = new RegExp(`([^'"])${txt}([^'"])`, 'g')
197
+ const fixedText = `$1"${txt}"$2`
198
+ ymlStr = ymlStr.replace(pat, fixedText)
199
+ })
200
+ }
190
201
  }
191
202
  // console.log('ymlStr', ymlStr)
192
203
  return ymlStr
@@ -0,0 +1,9 @@
1
+ const safeChalk = require('safe-chalk')
2
+ const minimist = require('minimist')
3
+ const argv = minimist(process.argv.slice(2))
4
+
5
+ // If --json flag disable chalk colors
6
+ const DISABLE_COLORS = argv.json || process.env.NO_COLORS
7
+
8
+ // Export chalk instance for usage in CLI
9
+ module.exports = safeChalk(DISABLE_COLORS)
@@ -51,6 +51,7 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
51
51
 
52
52
  // Store match details
53
53
  const matchInfo = {
54
+ varType: undefined,
54
55
  location,
55
56
  value: input,
56
57
  fullMatch: match[0],
@@ -91,32 +92,33 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
91
92
  matches[i].varString = matches[i].variable
92
93
  /* Save additional meta data about the variable */
93
94
  // console.log('matches[i].varString', matches[i].varString)
94
- if (variablesKnownTypes && variablesKnownTypes.test(matches[i].varString)) {
95
- matches[i].varType = matches[i].varString.match(variablesKnownTypes)[1]
96
- if (FALLBACK_REGEX.test(matches[i].varString)) {
97
- const split = splitByComma(matches[i].varString, regex)
98
- matches[i].hasFallback = true
99
95
 
100
- matches[i].valueBeforeFallback = split[0]
101
- // remove first element from split
102
- matches[i].fallbackValues = split.slice(1).map((item) => {
103
- // console.log('item', item)
104
- const isVariable = variablesKnownTypes.test(item) || VAR_MATCH_REGEX.test(item)
105
- const fallbackData = {
106
- isVariable,
107
- fullMatch: item,
108
- variable: item
109
- }
96
+ // if (variablesKnownTypes && variablesKnownTypes.test(matches[i].varString)) {
97
+ // matches[i].varType = matches[i].varString.match(variablesKnownTypes)[1]
98
+ // if (FALLBACK_REGEX.test(matches[i].varString)) {
99
+ // const split = splitByComma(matches[i].varString, regex)
100
+ // matches[i].hasFallback = true
110
101
 
111
- if (!isVariable && typeof item === 'string') {
112
- fallbackData.stringValue = trimQuotes(item)
113
- fallbackData.isFallback = true
114
- }
102
+ // matches[i].valueBeforeFallback = split[0]
103
+ // // remove first element from split
104
+ // matches[i].fallbackValues = split.slice(1).map((item) => {
105
+ // // console.log('item', item)
106
+ // const isVariable = variablesKnownTypes.test(item) || VAR_MATCH_REGEX.test(item)
107
+ // const fallbackData = {
108
+ // isVariable,
109
+ // fullMatch: item,
110
+ // variable: item,
111
+ // }
112
+
113
+ // if (!isVariable && typeof item === 'string') {
114
+ // fallbackData.stringValue = trimQuotes(item)
115
+ // fallbackData.isResolvedFallback = true
116
+ // }
115
117
 
116
- return fallbackData
117
- })
118
- }
119
- }
118
+ // return fallbackData
119
+ // })
120
+ // }
121
+ // }
120
122
  }
121
123
 
122
124
  // Second pass: Reconstruct each variable with original nested syntax
@@ -161,9 +163,102 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
161
163
  // Reconstruct with all nested variables
162
164
  currentMatch.fullMatch = replaceAllPlaceholders(currentMatch.fullMatch, matches)
163
165
  currentMatch.variable = replaceAllPlaceholders(currentMatch.variable, matches)
166
+ }
164
167
 
165
-
168
+
169
+ // We need to store varString - the variable string with placeholders
170
+ for (let i = 0; i < matches.length; i++) {
171
+ matches[i].varString = matches[i].variable
172
+ /* Save additional meta data about the variable */
173
+ // console.log('matches[i].varString', matches[i].varString)
174
+
175
+ if (variablesKnownTypes && variablesKnownTypes.test(matches[i].varString)) {
176
+ matches[i].varType = matches[i].varString.match(variablesKnownTypes)[1]
177
+ if (FALLBACK_REGEX.test(matches[i].varString)) {
178
+ const split = splitByComma(matches[i].varString, regex)
179
+ matches[i].hasFallback = true
180
+
181
+ matches[i].valueBeforeFallback = split[0]
182
+ // remove first element from split
183
+ matches[i].fallbackValues = split.slice(1).map((item) => {
184
+ // console.log('item', item)
185
+ const isVariable = variablesKnownTypes.test(item) || VAR_MATCH_REGEX.test(item)
186
+ const fallbackData = {
187
+ isVariable,
188
+ fullMatch: item,
189
+ variable: item,
190
+ }
191
+
192
+ if (!isVariable && typeof item === 'string') {
193
+ fallbackData.stringValue = trimQuotes(item)
194
+ fallbackData.isResolvedFallback = true
195
+ }
196
+
197
+ return fallbackData
198
+ })
199
+ }
200
+ } else if (typeof matches[i].varType === 'undefined') {
201
+ matches[i].varType = 'dot.prop'
202
+ }
166
203
  }
204
+
205
+ const finalMatches = matches.map((m) => {
206
+ delete m.placeholder
207
+ if (typeof m.varType === 'undefined') {
208
+ /*
209
+ {
210
+ varType: 'dot.prop',
211
+ location: 'resolvedDomainName',
212
+ value: '${domainByStage.${opt:stage, ${defaultStage}}}',
213
+ fullMatch: '${domainByStage.${opt:stage, ${defaultStage}}}',
214
+ variable: 'domainByStage.${opt:stage, ${defaultStage}}',
215
+ varString: 'domainByStage.__VAR_1__',
216
+ resolveOrder: 3,
217
+ start: 0,
218
+ end: 26
219
+ }
220
+ {
221
+ varType: 'dot.prop',
222
+ location: 'resolvedDomainName',
223
+ value: '${domainByStage.${opt:stage, ${defaultStage}}}',
224
+ fullMatch: '${defaultStage}',
225
+ variable: 'defaultStage',
226
+ varString: 'defaultStage',
227
+ resolveOrder: 1,
228
+ start: 29,
229
+ end: 44
230
+ }
231
+ */
232
+ // console.log('m', m)
233
+ }
234
+ if (m.hasFallback) {
235
+ const combinedFallbacks = m.fallbackValues.reduce((acc, f) => {
236
+ const child = matches.find((m) => m.variable === f.variable)
237
+ if (child && child.fallbackValues && child.fallbackValues.length) {
238
+ const split = splitByComma(child.variable, regex)
239
+ f.valueBeforeFallback = split[0]
240
+ f.fallbackValues = child.fallbackValues
241
+ }
242
+ return acc
243
+ }, m.fallbackValues)
244
+ m.fallbackValues = combinedFallbacks
245
+ }
246
+ if (m.varType === 'dot.prop') {
247
+ // const reversedMatches = matches.reverse()
248
+ // const test = reversedMatches.reduce((acc, f) => {
249
+ // console.log('f', f)
250
+ // const child = reversedMatches.find((m) => m.variable === f.variable)
251
+ // if (child && child.fallbackValues && child.fallbackValues.length) {
252
+ // const split = splitByComma(child.variable, regex)
253
+ // f.valueBeforeFallback = split[0]
254
+ // f.fallbackValues = child.fallbackValues
255
+ // }
256
+ // return acc
257
+ // }, reversedMatches)
258
+ // console.log('test', test)
259
+ }
260
+ return m
261
+ })
167
262
 
168
263
  if (debug) {
169
264
  console.log("\nReconstructed matches:")
@@ -176,7 +271,7 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
176
271
  })
177
272
  }
178
273
 
179
- return matches
274
+ return finalMatches
180
275
  }
181
276
 
182
277
 
@@ -0,0 +1,15 @@
1
+ const { makeHeader } = require('@davidwells/box-logger')
2
+
3
+ function logHeader(message) {
4
+ console.log(makeHeader({
5
+ text: message,
6
+ rightBorder: true,
7
+ minWidth: 80,
8
+ textStyle: 'normal',
9
+ borderColor: 'cyanBright',
10
+ }))
11
+ }
12
+
13
+ module.exports = {
14
+ logHeader
15
+ }