configorama 0.6.9 → 0.6.10
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 +57 -28
- package/package.json +4 -2
- package/src/index.js +39 -2
- package/src/main.js +611 -269
- package/src/resolvers/valueFromCron.js +2 -0
- package/src/resolvers/valueFromEnv.js +2 -0
- package/src/resolvers/valueFromEnv.test.js +78 -0
- package/src/resolvers/valueFromEval.js +1 -0
- package/src/resolvers/valueFromGit.js +24 -9
- package/src/resolvers/valueFromNumber.js +1 -0
- package/src/resolvers/valueFromOptions.js +2 -0
- package/src/resolvers/valueFromString.js +1 -0
- package/src/sync.js +13 -4
- package/src/utils/cleanVariable.js +3 -3
- package/src/utils/configWizard.js +567 -0
- package/src/utils/encoders/index.js +15 -0
- package/src/utils/encoders/js-fixes.js +22 -0
- package/src/utils/{unknownValues.js → encoders/unknown-values.js} +10 -1
- package/src/utils/enrichMetadata.js +439 -82
- package/src/utils/find-nested-variables.js +41 -38
- package/src/utils/find-nested-variables.test.js +119 -35
- package/src/utils/getFullFilePath.js +38 -0
- package/src/utils/getVariableType.js +55 -0
- package/src/utils/logs.js +1 -1
- package/src/utils/parse.js +6 -4
- package/src/utils/resolveAlias.js +3 -2
- package/src/utils/splitByComma.js +2 -1
- package/src/utils/splitCsv.js +6 -6
- package/src/utils/resolveAliasOld.js +0 -65
- package/src/utils/x.js +0 -173
package/src/main.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const os = require('os')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const fs = require('fs')
|
|
4
|
+
const enrichMetadata = require('./utils/enrichMetadata')
|
|
4
5
|
/* // disable logs to find broken tests
|
|
5
6
|
console.log = () => {}
|
|
6
7
|
// process.exit(1)
|
|
@@ -14,6 +15,7 @@ const traverse = require('traverse')
|
|
|
14
15
|
const dotProp = require('dot-prop')
|
|
15
16
|
const chalk = require('./utils/chalk')
|
|
16
17
|
const { resolveAlias } = require('./utils/resolveAlias')
|
|
18
|
+
const { resolveFilePathFromMatch } = require('./utils/getFullFilePath')
|
|
17
19
|
|
|
18
20
|
/* Default Value resolvers */
|
|
19
21
|
const getValueFromString = require('./resolvers/valueFromString')
|
|
@@ -27,6 +29,7 @@ const createGitResolver = require('./resolvers/valueFromGit')
|
|
|
27
29
|
const YAML = require('./parsers/yaml')
|
|
28
30
|
const TOML = require('./parsers/toml')
|
|
29
31
|
const INI = require('./parsers/ini')
|
|
32
|
+
const JSON5 = require('./parsers/json5')
|
|
30
33
|
/* functions */
|
|
31
34
|
const md5Function = require('./functions/md5')
|
|
32
35
|
|
|
@@ -50,13 +53,16 @@ const { splitCsv } = require('./utils/splitCsv')
|
|
|
50
53
|
const { replaceAll } = require('./utils/replaceAll')
|
|
51
54
|
const { getTextAfterOccurrence, findNestedVariable } = require('./utils/textUtils')
|
|
52
55
|
const { getFallbackString, verifyVariable } = require('./utils/variableUtils')
|
|
53
|
-
const { encodeUnknown, decodeUnknown } = require('./utils/
|
|
56
|
+
const { encodeUnknown, decodeUnknown } = require('./utils/encoders/unknown-values')
|
|
57
|
+
const { decodeEncodedValue } = require('./utils/encoders')
|
|
58
|
+
const { encodeJsSyntax, decodeJsSyntax, hasParenthesesPlaceholder } = require('./utils/encoders/js-fixes')
|
|
54
59
|
const { mergeByKeys } = require('./utils/mergeByKeys')
|
|
55
60
|
const { arrayToJsonPath } = require('./utils/arrayToJsonPath')
|
|
56
61
|
const { findNestedVariables } = require('./utils/find-nested-variables')
|
|
57
62
|
const { makeBox, makeStackedBoxes } = require('@davidwells/box-logger')
|
|
58
63
|
const { logHeader } = require('./utils/logs')
|
|
59
64
|
const { createEditorLink } = require('./utils/createEditorLink')
|
|
65
|
+
const { runConfigWizard } = require('./utils/configWizard')
|
|
60
66
|
/**
|
|
61
67
|
* Maintainer's notes:
|
|
62
68
|
*
|
|
@@ -90,6 +96,7 @@ const logLines = '────────────────────
|
|
|
90
96
|
|
|
91
97
|
let DEBUG = process.argv.includes('--debug') ? true : false
|
|
92
98
|
let VERBOSE = process.argv.includes('--verbose') ? true : false
|
|
99
|
+
let SETUP_MODE = process.argv.includes('--setup') ? true : false
|
|
93
100
|
// DEBUG = true
|
|
94
101
|
let DEBUG_TYPE = false
|
|
95
102
|
const ENABLE_FUNCTIONS = true
|
|
@@ -123,6 +130,7 @@ function preProcess(configObject, variableSyntax) {
|
|
|
123
130
|
if (typeof str !== 'string') return str
|
|
124
131
|
|
|
125
132
|
let result = str
|
|
133
|
+
// result = result.replace(/\$\{self:/g, '${')
|
|
126
134
|
let changed = true
|
|
127
135
|
|
|
128
136
|
// Keep iterating until no more changes (to handle nested variables)
|
|
@@ -248,11 +256,18 @@ class Configorama {
|
|
|
248
256
|
allowUnknownVars: false,
|
|
249
257
|
// Allow undefined to be an end result.
|
|
250
258
|
allowUndefinedValues: false,
|
|
259
|
+
// Allow unknown file refs to pass through without throwing errors
|
|
260
|
+
allowUnknownFileRefs: false,
|
|
261
|
+
// Return metadata
|
|
262
|
+
returnMetadata: false,
|
|
263
|
+
// Return preResolvedVariableDetails
|
|
264
|
+
returnPreResolvedVariableDetails: false,
|
|
251
265
|
}, options)
|
|
252
266
|
|
|
253
267
|
this.filterCache = {}
|
|
254
268
|
|
|
255
269
|
this.foundVariables = []
|
|
270
|
+
this.fileRefsFound = []
|
|
256
271
|
|
|
257
272
|
// Track variable resolutions for metadata (keyed by path)
|
|
258
273
|
this.resolutionTracking = {}
|
|
@@ -343,6 +358,8 @@ class Configorama {
|
|
|
343
358
|
{
|
|
344
359
|
type: 'self',
|
|
345
360
|
prefix: 'self',
|
|
361
|
+
syntax: '${self:pathToKeyInConfig}',
|
|
362
|
+
description: `Resolves values from the current config object. Supports sub-properties via :key lookup.`,
|
|
346
363
|
match: selfRefSyntax,
|
|
347
364
|
resolver: (varString, o, x, pathValue) => {
|
|
348
365
|
return this.getValueFromSelf(varString, o, x, pathValue)
|
|
@@ -357,6 +374,8 @@ class Configorama {
|
|
|
357
374
|
{
|
|
358
375
|
type: 'file',
|
|
359
376
|
prefix: 'file',
|
|
377
|
+
syntax: '${file(pathToFile.json)}',
|
|
378
|
+
description: `Resolves values from files. Supports sub-properties via :key lookup.`,
|
|
360
379
|
match: fileRefSyntax,
|
|
361
380
|
resolver: (varString, o, x, pathValue) => {
|
|
362
381
|
return this.getValueFromFile(varString, { context: pathValue })
|
|
@@ -387,6 +406,7 @@ class Configorama {
|
|
|
387
406
|
/* Resolve deep references */
|
|
388
407
|
{
|
|
389
408
|
type: 'deep',
|
|
409
|
+
internal: true,
|
|
390
410
|
match: deepRefSyntax,
|
|
391
411
|
resolver: (varString, o, x, pathValue) => {
|
|
392
412
|
// console.log('>>>>>getValueFromDeep', varString)
|
|
@@ -399,23 +419,23 @@ class Configorama {
|
|
|
399
419
|
|
|
400
420
|
/* Nicer self: references. Match key in object */
|
|
401
421
|
const fallThroughSelfMatcher = {
|
|
402
|
-
type: '
|
|
422
|
+
type: 'dot.prop',
|
|
403
423
|
match: (varString, fullObject, valueObject) => {
|
|
404
424
|
/*
|
|
405
|
-
console.log('
|
|
406
|
-
console.log('
|
|
425
|
+
console.log('fallThroughSelfMatcher varString', varString)
|
|
426
|
+
console.log('fallThroughSelfMatcher valueObject', valueObject)
|
|
407
427
|
console.log('fullObject', fullObject)
|
|
408
428
|
/** */
|
|
409
429
|
/* its file ref so we need to shift lookup for self in nested files */
|
|
410
430
|
if (valueObject.isFileRef) {
|
|
411
431
|
const exists = dotProp.get(fullObject, varString)
|
|
412
|
-
// console.log('
|
|
432
|
+
// console.log('fallThroughSelfMatcher exists', exists)
|
|
413
433
|
if (!exists) {
|
|
414
434
|
// @ Todo make recursive
|
|
415
435
|
const deepProperties = [valueObject.path[0]].concat(varString)
|
|
416
436
|
const dotPropPath = deepProperties.join('.')
|
|
417
437
|
const deeperExists = dotProp.get(fullObject, dotPropPath)
|
|
418
|
-
// console.log('
|
|
438
|
+
// console.log('fallThroughSelfMatcher deeper', deeperExists)
|
|
419
439
|
return deeperExists
|
|
420
440
|
}
|
|
421
441
|
}
|
|
@@ -426,10 +446,10 @@ class Configorama {
|
|
|
426
446
|
},
|
|
427
447
|
resolver: (varString, options, config, pathValue) => {
|
|
428
448
|
/*
|
|
429
|
-
console.log('
|
|
430
|
-
console.log('
|
|
431
|
-
console.log('
|
|
432
|
-
console.log('
|
|
449
|
+
console.log('fallThroughSelfMatcher resolver', varString)
|
|
450
|
+
console.log('fallThroughSelfMatcher options', options)
|
|
451
|
+
console.log('fallThroughSelfMatcher config', config)
|
|
452
|
+
console.log('fallThroughSelfMatcher pathValue', pathValue)
|
|
433
453
|
/** */
|
|
434
454
|
return this.getValueFromSelf(varString, options, config, pathValue)
|
|
435
455
|
},
|
|
@@ -437,6 +457,19 @@ class Configorama {
|
|
|
437
457
|
|
|
438
458
|
/* Apply user defined variable sources */
|
|
439
459
|
if (options.variableSources) {
|
|
460
|
+
|
|
461
|
+
// ensure each variable source has a type
|
|
462
|
+
options.variableSources.forEach((v) => {
|
|
463
|
+
if (!v.type) {
|
|
464
|
+
console.log('Variable', v)
|
|
465
|
+
throw new Error('Variable source must have a type')
|
|
466
|
+
}
|
|
467
|
+
if (!v.match || !v.resolver) {
|
|
468
|
+
console.log('Variable', v)
|
|
469
|
+
throw new Error('Variable source must have a match and resolver functions')
|
|
470
|
+
}
|
|
471
|
+
})
|
|
472
|
+
|
|
440
473
|
this.variableTypes = this.variableTypes.concat(options.variableSources)
|
|
441
474
|
}
|
|
442
475
|
|
|
@@ -482,7 +515,7 @@ class Configorama {
|
|
|
482
515
|
toKebabCase: (val) => {
|
|
483
516
|
return kebabCase(val)
|
|
484
517
|
},
|
|
485
|
-
/* Type filters */
|
|
518
|
+
/* Type filters for coercion */
|
|
486
519
|
toNumber: (val, from) => {
|
|
487
520
|
const newVal = Number(val)
|
|
488
521
|
return newVal
|
|
@@ -497,7 +530,37 @@ class Configorama {
|
|
|
497
530
|
return JSON.stringify(val)
|
|
498
531
|
},
|
|
499
532
|
toObject: (val) => {
|
|
500
|
-
return
|
|
533
|
+
return JSON5.parse(val)
|
|
534
|
+
},
|
|
535
|
+
/* Type validation filters */
|
|
536
|
+
Number: (value) => {
|
|
537
|
+
const n = Number(value)
|
|
538
|
+
if (isNaN(n)) throw new Error(`Configorama Error: Expected Number, got "${value}"`)
|
|
539
|
+
return n
|
|
540
|
+
},
|
|
541
|
+
Boolean: (value) => {
|
|
542
|
+
if (typeof value === 'boolean') return value
|
|
543
|
+
const v = String(value).toLowerCase()
|
|
544
|
+
if (['true', '1', 'yes', 'on'].includes(v)) return true
|
|
545
|
+
if (['false', '0', 'no', 'off'].includes(v)) return false
|
|
546
|
+
throw new Error(`Configorama Error: Expected Boolean, got "${value}"`)
|
|
547
|
+
},
|
|
548
|
+
String: (value) => {
|
|
549
|
+
if (value === undefined || value === null || value === 'null') return ''
|
|
550
|
+
return String(value)
|
|
551
|
+
},
|
|
552
|
+
Json: (value) => {
|
|
553
|
+
try {
|
|
554
|
+
return typeof value === 'string' ? JSON.parse(value) : value
|
|
555
|
+
} catch (e) {
|
|
556
|
+
throw new Error(`Configorama Error: Invalid JSON in variable`)
|
|
557
|
+
}
|
|
558
|
+
},
|
|
559
|
+
/* Help filter - identity function that preserves value but provides metadata for wizard */
|
|
560
|
+
help: (value, helpText) => {
|
|
561
|
+
// Identity function - returns value unchanged
|
|
562
|
+
// The helpText argument is extracted during metadata collection for the wizard
|
|
563
|
+
return value
|
|
501
564
|
},
|
|
502
565
|
}
|
|
503
566
|
|
|
@@ -507,7 +570,9 @@ class Configorama {
|
|
|
507
570
|
}
|
|
508
571
|
|
|
509
572
|
// (\|\s*(toUpperCase|toLowerCase|toCamelCase|toKebabCase|capitalize)\s*)+$
|
|
510
|
-
|
|
573
|
+
// Updated to support function-style filters like help('text') with nested parens
|
|
574
|
+
// Use a more permissive pattern that matches anything between parens including nested parens
|
|
575
|
+
this.filterMatch = new RegExp(`(\\|\\s*(${Object.keys(this.filters).join('|')})(?:\\s*\\([^)]*(?:\\([^)]*\\))?[^)]*\\))?\\s*)+}?$`)
|
|
511
576
|
// console.log('this.filterMatch', this.filterMatch)
|
|
512
577
|
|
|
513
578
|
this.functions = {
|
|
@@ -603,7 +668,7 @@ class Configorama {
|
|
|
603
668
|
this.opts
|
|
604
669
|
)
|
|
605
670
|
this.configFileContents = ''
|
|
606
|
-
if (VERBOSE || showFoundVariables) {
|
|
671
|
+
if (VERBOSE || showFoundVariables || this.opts.returnPreResolvedVariableDetails) {
|
|
607
672
|
this.configFileContents = fs.readFileSync(this.configFilePath, 'utf8')
|
|
608
673
|
}
|
|
609
674
|
/*
|
|
@@ -630,16 +695,37 @@ class Configorama {
|
|
|
630
695
|
const variableSyntax = this.variableSyntax
|
|
631
696
|
const variablesKnownTypes = this.variablesKnownTypes
|
|
632
697
|
|
|
633
|
-
if (VERBOSE || showFoundVariables) {
|
|
698
|
+
if (VERBOSE || showFoundVariables || this.opts.returnPreResolvedVariableDetails) {
|
|
634
699
|
// Use collectVariableMetadata to get variable info (DRY - don't duplicate logic)
|
|
635
700
|
const metadata = this.collectVariableMetadata()
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
701
|
+
|
|
702
|
+
const enrich = enrichMetadata(
|
|
703
|
+
metadata,
|
|
704
|
+
this.resolutionTracking,
|
|
705
|
+
this.variableSyntax,
|
|
706
|
+
this.fileRefsFound,
|
|
707
|
+
this.originalConfig,
|
|
708
|
+
this.configFilePath,
|
|
709
|
+
Object.keys(this.filters)
|
|
710
|
+
)
|
|
711
|
+
|
|
712
|
+
if (showFoundVariables) {
|
|
713
|
+
/*
|
|
714
|
+
deepLog('metadata', metadata)
|
|
715
|
+
fs.writeFileSync(`metadata-${path.basename(this.configFilePath)}.json`, JSON.stringify(metadata, null, 2))
|
|
716
|
+
deepLog('enrich', enrich)
|
|
717
|
+
// process.exit(1)
|
|
718
|
+
/** */
|
|
719
|
+
}
|
|
720
|
+
|
|
641
721
|
const variableData = metadata.variables
|
|
722
|
+
const uniqueVariables = metadata.uniqueVariables
|
|
642
723
|
const varKeys = Object.keys(variableData)
|
|
724
|
+
const uniqueVarKeys = Object.keys(uniqueVariables)
|
|
725
|
+
|
|
726
|
+
// if (this.opts.returnPreResolvedVariableDetails) {
|
|
727
|
+
// return metadata
|
|
728
|
+
// }
|
|
643
729
|
|
|
644
730
|
if (!varKeys.length) {
|
|
645
731
|
logHeader('No Variables Found in Config')
|
|
@@ -651,9 +737,9 @@ class Configorama {
|
|
|
651
737
|
|
|
652
738
|
const varTypes = Object.keys(this.variableTypes)
|
|
653
739
|
if (varTypes.length) {
|
|
654
|
-
const exclude = ['
|
|
740
|
+
const exclude = ['dot.prop', 'deep']
|
|
655
741
|
console.log('\nAllowed variable types:')
|
|
656
|
-
varTypes.filter((v) => v.type !== '
|
|
742
|
+
varTypes.filter((v) => v.type !== 'dot.prop').forEach((v) => {
|
|
657
743
|
const vData = this.variableTypes[v]
|
|
658
744
|
if (exclude.includes(vData.type)) {
|
|
659
745
|
return
|
|
@@ -676,166 +762,231 @@ class Configorama {
|
|
|
676
762
|
const longestKey = varKeys.reduce((acc, k) => {
|
|
677
763
|
return Math.max(acc, k.length)
|
|
678
764
|
}, 0)
|
|
679
|
-
// Count all references including nested ones within other variables
|
|
680
|
-
const countAllReferences = (targetVariable) => {
|
|
681
|
-
// Start with direct references
|
|
682
|
-
let count = variableData[targetVariable].length
|
|
683
|
-
|
|
684
|
-
// Check all other variables for nested references to this variable
|
|
685
|
-
varKeys.forEach((otherKey) => {
|
|
686
|
-
if (otherKey === targetVariable) return
|
|
687
|
-
|
|
688
|
-
variableData[otherKey].forEach((instance) => {
|
|
689
|
-
if (instance.resolveDetails) {
|
|
690
|
-
instance.resolveDetails.forEach((detail) => {
|
|
691
|
-
// Check if this resolveDetail references our target variable
|
|
692
|
-
if (detail.fullMatch === targetVariable) {
|
|
693
|
-
count++
|
|
694
|
-
}
|
|
695
|
-
})
|
|
696
|
-
}
|
|
697
|
-
})
|
|
698
|
-
})
|
|
699
|
-
|
|
700
|
-
return count
|
|
701
|
-
}
|
|
702
765
|
|
|
703
|
-
|
|
704
|
-
|
|
766
|
+
// Use uniqueVariables for simpler reference counting
|
|
767
|
+
const referenceData = varKeys.map((k) => {
|
|
768
|
+
// Map from varMatch (e.g., '${env:API_KEY}') to variable name (e.g., 'env:API_KEY')
|
|
769
|
+
// Extract the variable name from the key by removing ${ and }
|
|
770
|
+
const varName = k.replace(/^\$\{/, '').replace(/\}$/, '').split(',')[0].trim()
|
|
771
|
+
const uniqueVar = uniqueVariables[varName]
|
|
772
|
+
const refCount = uniqueVar ? uniqueVar.occurrences.length : variableData[k].length
|
|
705
773
|
const placesWord = refCount > 1 ? 'places' : 'place'
|
|
706
774
|
return `- ${k.padEnd(longestKey).padEnd(longestKey + 10)} referenced ${refCount} ${placesWord}`
|
|
707
|
-
}).join('\n')
|
|
708
|
-
|
|
775
|
+
}).join('\n')
|
|
776
|
+
|
|
777
|
+
console.log(`${referenceData}\n`)
|
|
709
778
|
}
|
|
710
779
|
|
|
711
780
|
logHeader('Variable Details')
|
|
712
781
|
|
|
713
|
-
const lines = this.configFileContents.split('\n')
|
|
714
|
-
|
|
715
|
-
|
|
782
|
+
const lines = this.configFileContents ? this.configFileContents.split('\n') : []
|
|
783
|
+
|
|
716
784
|
const indent = ''
|
|
717
785
|
const boxes = varKeys.map((key, i) => {
|
|
718
786
|
const variableInstances = variableData[key]
|
|
719
787
|
// console.log('variableInstances', variableInstances)
|
|
720
|
-
|
|
721
788
|
const firstInstance = variableInstances[0]
|
|
722
789
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
// console.log('no default value', firstInstance)
|
|
727
|
-
|
|
728
|
-
let dotPropArr = []
|
|
729
|
-
if (firstInstance.defaultValueIsVar && (
|
|
730
|
-
firstInstance.defaultValueIsVar.varType === 'self:' ||
|
|
731
|
-
firstInstance.defaultValueIsVar.varType === 'dot.prop'
|
|
732
|
-
)) {
|
|
733
|
-
dotPropArr = [firstInstance.defaultValueIsVar]
|
|
734
|
-
}
|
|
735
|
-
/* Check if the fallback variable is a self reference */
|
|
736
|
-
const hasDotPropOrSelf = variableInstances.reduce((acc, v) => {
|
|
737
|
-
const dotProp = v.resolveDetails.find((d) => {
|
|
738
|
-
// console.log('d', d)
|
|
739
|
-
return d.varType === 'dot.prop'
|
|
740
|
-
})
|
|
741
|
-
if (dotProp) {
|
|
742
|
-
acc.push(dotProp)
|
|
743
|
-
}
|
|
744
|
-
if (v.resolveDetails && v.resolveDetails.length === 1 && v.resolveDetails[0].varType === 'self:') {
|
|
745
|
-
// console.log('dot.prop', v.resolveDetails)
|
|
746
|
-
acc.push(v.resolveDetails[0])
|
|
747
|
-
}
|
|
748
|
-
return acc
|
|
749
|
-
}, dotPropArr)
|
|
750
|
-
// console.log('hasDotPropOrSelf', hasDotPropOrSelf)
|
|
751
|
-
|
|
752
|
-
if (!hasDotPropOrSelf.length) {
|
|
753
|
-
const debug = (false) ? JSON.stringify(firstInstance, null, 2) : ''
|
|
754
|
-
requiredText = `[Required Variable] ${debug}`
|
|
755
|
-
} else {
|
|
756
|
-
const fallBackValues = variableInstances.filter((v) => v.resolveDetails.find((d) => d.hasFallback)).map((v) => v.resolveDetails)
|
|
757
|
-
// console.log('fallBackValues', fallBackValues)
|
|
758
|
-
if (fallBackValues.length) {
|
|
759
|
-
// console.log('fallBackValues.resolveDetails', fallBackValues)
|
|
760
|
-
}
|
|
790
|
+
// Get uniqueVariable data for description and other metadata
|
|
791
|
+
const varName = key.replace(/^\$\{/, '').replace(/\}$/, '').split(',')[0].trim()
|
|
792
|
+
const uniqueVar = uniqueVariables[varName]
|
|
761
793
|
|
|
762
|
-
|
|
763
|
-
defaultValueSrc = cleanPath
|
|
764
|
-
// Find the dot prop value in the original config
|
|
765
|
-
const dotPropValue = dotProp.get(this.originalConfig, cleanPath)
|
|
766
|
-
// console.log('dotPropValue', dotPropValue)
|
|
767
|
-
if (typeof dotPropValue !== 'undefined') {
|
|
768
|
-
requiredText = ''
|
|
769
|
-
const niceString = typeof dotPropValue === 'object' ? JSON.stringify(dotPropValue) : dotPropValue
|
|
770
|
-
// truncate niceString to 100 characters
|
|
771
|
-
const truncatedString = niceString.length > 100 ? niceString.substring(0, 90) + '...' : niceString
|
|
772
|
-
firstInstance.defaultValue = truncatedString
|
|
773
|
-
} else {
|
|
774
|
-
deepLog('Missing default var', firstInstance)
|
|
775
|
-
throw new Error(
|
|
776
|
-
`Variable misconfiguration at ${firstInstance.variable}\n\n"${hasDotPropOrSelf[0].variable}" resolves to undefined value.\n`
|
|
777
|
-
)
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
//this.originalConfig[key] = undefined
|
|
781
|
-
}
|
|
794
|
+
// Build display message from enriched metadata
|
|
782
795
|
const spacing = ' '
|
|
783
796
|
const titleText = `Variable:${spacing}`
|
|
784
|
-
const
|
|
785
|
-
let varMsg = `${reqText}`
|
|
786
|
-
const VALUE_HEX = '#899499' // '#708090'
|
|
797
|
+
const VALUE_HEX = '#899499'
|
|
787
798
|
const keyChalk = chalk.whiteBright
|
|
788
799
|
const valueChalk = chalk.hex(VALUE_HEX)
|
|
789
800
|
|
|
801
|
+
let varMsg = ''
|
|
802
|
+
let requiredMessage = ''
|
|
803
|
+
|
|
804
|
+
// Show required status from metadata
|
|
805
|
+
if (firstInstance.isRequired) {
|
|
806
|
+
requiredMessage = `${chalk.red.bold('[Required]')}`
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// Show type filter if present (Boolean, String, Number, etc.)
|
|
810
|
+
if (uniqueVar && uniqueVar.occurrences.length > 0) {
|
|
811
|
+
const typeFilters = ['Boolean', 'String', 'Number', 'Array', 'Object']
|
|
812
|
+
const foundTypes = new Set()
|
|
813
|
+
|
|
814
|
+
uniqueVar.occurrences.forEach(occ => {
|
|
815
|
+
if (occ.filters && Array.isArray(occ.filters)) {
|
|
816
|
+
occ.filters.forEach(filter => {
|
|
817
|
+
if (typeFilters.includes(filter)) {
|
|
818
|
+
foundTypes.add(filter)
|
|
819
|
+
}
|
|
820
|
+
})
|
|
821
|
+
}
|
|
822
|
+
})
|
|
823
|
+
|
|
824
|
+
if (foundTypes.size > 0) {
|
|
825
|
+
const typeText = `${indent}${keyChalk('Type:'.padEnd(titleText.length, ' '))}`
|
|
826
|
+
varMsg += `${typeText} ${valueChalk(Array.from(foundTypes).join(', '))}\n`
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// Show description from uniqueVariables if available
|
|
831
|
+
if (uniqueVar && uniqueVar.occurrences.length > 0) {
|
|
832
|
+
// Collect unique descriptions from all occurrences
|
|
833
|
+
const descriptions = uniqueVar.occurrences
|
|
834
|
+
.map(occ => occ.description)
|
|
835
|
+
.filter((desc, index, self) => desc && self.indexOf(desc) === index)
|
|
836
|
+
|
|
837
|
+
if (descriptions.length > 0) {
|
|
838
|
+
const descText = `${indent}${keyChalk('Description:'.padEnd(titleText.length, ' '))}`
|
|
839
|
+
const combinedDesc = descriptions.join('. ')
|
|
840
|
+
varMsg += `${descText} ${valueChalk(combinedDesc)}\n`
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
|
|
846
|
+
// Show default value from metadata
|
|
790
847
|
if (typeof firstInstance.defaultValue !== 'undefined') {
|
|
791
|
-
// console.log('firstInstance.defaultValue', firstInstance.defaultValue)
|
|
792
848
|
const defaultValueRender = firstInstance.defaultValue === '' ? '""' : firstInstance.defaultValue
|
|
793
|
-
const defaultValueText = `${indent}${keyChalk(
|
|
794
|
-
|
|
795
|
-
varMsg += `${defaultValueText} ${valueChalk(defaultValueRender)}`
|
|
849
|
+
const defaultValueText = `${indent}${keyChalk('Default value:'.padEnd(titleText.length, ' '))}`
|
|
850
|
+
varMsg += `${defaultValueText} ${valueChalk(defaultValueRender)}\n`
|
|
796
851
|
}
|
|
797
852
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
varMsg += `${
|
|
853
|
+
// Show default value source path from metadata
|
|
854
|
+
if (firstInstance.defaultValueSrc) {
|
|
855
|
+
varMsg += `${indent}${keyChalk('Default value path:'.padEnd(titleText.length, ' '))} `
|
|
856
|
+
varMsg += `${valueChalk(firstInstance.defaultValueSrc)}\n`
|
|
801
857
|
}
|
|
802
858
|
|
|
859
|
+
// Show resolve order from metadata
|
|
803
860
|
if (firstInstance.resolveOrder.length > 1) {
|
|
804
|
-
varMsg +=
|
|
861
|
+
varMsg += `${indent}${keyChalk('Resolve Order:'.padEnd(titleText.length, ' '))}`
|
|
805
862
|
const resolveOrder = firstInstance.resolveOrder.join(', ')
|
|
806
|
-
varMsg += ` ${valueChalk(resolveOrder)}`
|
|
863
|
+
varMsg += ` ${valueChalk(resolveOrder)}\n`
|
|
807
864
|
}
|
|
808
865
|
|
|
866
|
+
// Show path(s) from metadata
|
|
809
867
|
let locationRender = valueChalk(variableInstances[0].path)
|
|
810
|
-
|
|
811
|
-
let
|
|
868
|
+
let locationLabel = `${indent}${keyChalk('Config Path:'.padEnd(titleText.length, ' '))}`
|
|
869
|
+
let typeText = ''
|
|
812
870
|
if (variableInstances.length > 1) {
|
|
813
|
-
|
|
814
|
-
const
|
|
815
|
-
|
|
871
|
+
const pathIndent = ' '.repeat(titleText.length + 1)
|
|
872
|
+
const pathItems = variableInstances.map((v, idx) => {
|
|
873
|
+
// Show type filter per path if different
|
|
874
|
+
if (uniqueVar && uniqueVar.occurrences.length > 1) {
|
|
875
|
+
const occurrence = uniqueVar.occurrences.find(occ => occ.path === v.path)
|
|
876
|
+
const typeFilters = ['Boolean', 'String', 'Number', 'Array', 'Object']
|
|
877
|
+
const pathType = occurrence && occurrence.filters
|
|
878
|
+
? occurrence.filters.find(f => typeFilters.includes(f))
|
|
879
|
+
: null
|
|
880
|
+
|
|
881
|
+
typeText = pathType ? ` ${chalk.dim(`Type: ${pathType}`)}` : ''
|
|
882
|
+
const prefix = idx === 0 ? '' : `${indent}${pathIndent}`
|
|
883
|
+
return `${prefix}${valueChalk(`- ${v.path}`)}${typeText}`
|
|
884
|
+
}
|
|
885
|
+
const prefix = idx === 0 ? '' : `${indent}${pathIndent}`
|
|
886
|
+
return `${prefix}${valueChalk(`- ${v.path}`)}${typeText}`
|
|
887
|
+
})
|
|
888
|
+
locationRender = pathItems.join('\n')
|
|
889
|
+
locationLabel = `${indent}${keyChalk('Config Paths:'.padEnd(titleText.length, ' '))}`
|
|
890
|
+
} else {
|
|
891
|
+
// look for type filter in the first instance
|
|
892
|
+
const typeFilters = ['Boolean', 'String', 'Number', 'Array', 'Object']
|
|
893
|
+
const pathType = firstInstance.filters
|
|
894
|
+
? firstInstance.filters.find(f => typeFilters.includes(f))
|
|
895
|
+
: null
|
|
896
|
+
|
|
897
|
+
typeText = pathType ? ` ${chalk.dim(`Type: ${pathType}`)}` : ''
|
|
816
898
|
}
|
|
899
|
+
varMsg += `${locationLabel} ${locationRender}`
|
|
900
|
+
|
|
901
|
+
// Find line number in config file based on format (YAML, TOML, JSON, INI)
|
|
902
|
+
const configKey = firstInstance.key
|
|
903
|
+
const line = lines.findIndex((line) => {
|
|
904
|
+
const fileType = this.configFileType
|
|
905
|
+
const escapedKey = configKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
906
|
+
// YAML: key: or key :
|
|
907
|
+
if (fileType === '.yml' || fileType === '.yaml') {
|
|
908
|
+
return new RegExp(`^\\s*${escapedKey}\\s*:`).test(line)
|
|
909
|
+
}
|
|
910
|
+
// TOML: key = or key=
|
|
911
|
+
if (fileType === '.toml') {
|
|
912
|
+
return new RegExp(`^\\s*${escapedKey}\\s*=`).test(line)
|
|
913
|
+
}
|
|
914
|
+
// JSON: "key": or "key" :
|
|
915
|
+
if (fileType === '.json' || fileType === '.json5') {
|
|
916
|
+
return new RegExp(`"${escapedKey}"\\s*:`).test(line)
|
|
917
|
+
}
|
|
918
|
+
// INI: key = or key=
|
|
919
|
+
if (fileType === '.ini') {
|
|
920
|
+
return new RegExp(`^\\s*${escapedKey}\\s*=`).test(line)
|
|
921
|
+
}
|
|
922
|
+
// JS/TS/ESM: key: or "key": or 'key': or `key`: or [`key`]:
|
|
923
|
+
if (['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts'].includes(fileType)) {
|
|
924
|
+
return new RegExp(`(?:${escapedKey}|"${escapedKey}"|'${escapedKey}'|\`${escapedKey}\`|\\[\`${escapedKey}\`\\])\\s*:`).test(line)
|
|
925
|
+
}
|
|
926
|
+
// Default fallback: try YAML-style
|
|
927
|
+
return line.includes(`${configKey}:`)
|
|
928
|
+
})
|
|
929
|
+
const lineNumber = line !== -1 ? line + 1 : 0
|
|
817
930
|
|
|
818
|
-
varMsg += `\n${locationLabel} ${locationRender}`
|
|
819
931
|
|
|
820
|
-
// find the match in our lines
|
|
821
|
-
const line = lines.findIndex((line) => line.includes(key))
|
|
822
|
-
const lineNumber = line + 1
|
|
823
|
-
|
|
824
|
-
// console.log(` ${chalk.bold(key)}`)
|
|
825
932
|
return {
|
|
826
|
-
|
|
933
|
+
content: {
|
|
934
|
+
left: varMsg,
|
|
935
|
+
backgroundColor: 'red',
|
|
936
|
+
width: '100%',
|
|
937
|
+
},
|
|
827
938
|
title: {
|
|
828
|
-
left:
|
|
829
|
-
right: lineNumber ? createEditorLink(this.configFilePath, lineNumber, 1, `Line: ${lineNumber}`, 'gray') : '',
|
|
939
|
+
left: `▷ ${lineNumber ? createEditorLink(this.configFilePath, lineNumber, 1, key) : key}`,
|
|
940
|
+
right: lineNumber ? createEditorLink(this.configFilePath, lineNumber, 1, `${requiredMessage} ${lineNumber ? `Line: ${lineNumber.toString().padEnd(2, ' ')}` : ''}`, 'gray') : '',
|
|
941
|
+
center: typeText,
|
|
942
|
+
paddingBottom: 1,
|
|
943
|
+
paddingTop: (i === 0) ? 1 : 0,
|
|
944
|
+
truncate: true,
|
|
830
945
|
},
|
|
946
|
+
width: '100%',
|
|
831
947
|
}
|
|
832
948
|
})
|
|
833
949
|
|
|
834
950
|
console.log(makeStackedBoxes(boxes, {
|
|
951
|
+
borderText: 'Variable Details. Click on titles to open in editor.',
|
|
835
952
|
borderColor: 'gray',
|
|
836
|
-
minWidth:
|
|
953
|
+
minWidth: '96%',
|
|
837
954
|
borderStyle: 'bold',
|
|
955
|
+
disableTitleSeparator: true,
|
|
838
956
|
}))
|
|
957
|
+
// process.exit(1)
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
// WALK through CLI prompt if --setup flag is set
|
|
962
|
+
if (SETUP_MODE) {
|
|
963
|
+
logHeader('Setup Mode')
|
|
964
|
+
// deepLog('enrich', enrich)
|
|
965
|
+
const userInputs = await runConfigWizard(enrich, this.originalConfig, this.configFilePath)
|
|
966
|
+
|
|
967
|
+
console.log('\n')
|
|
968
|
+
logHeader('User Inputs Summary')
|
|
969
|
+
console.log(JSON.stringify(userInputs, null, 2))
|
|
970
|
+
|
|
971
|
+
// TODO set values
|
|
972
|
+
|
|
973
|
+
// Apply user inputs to options and environment
|
|
974
|
+
if (userInputs.options) {
|
|
975
|
+
Object.assign(this.opts, userInputs.options)
|
|
976
|
+
}
|
|
977
|
+
if (userInputs.env) {
|
|
978
|
+
Object.assign(process.env, userInputs.env)
|
|
979
|
+
}
|
|
980
|
+
// Note: self references are in the config, so no need to apply them
|
|
981
|
+
|
|
982
|
+
console.log()
|
|
983
|
+
logHeader('Resolving Configuration')
|
|
984
|
+
console.log()
|
|
985
|
+
|
|
986
|
+
// process.exit(1)
|
|
987
|
+
|
|
988
|
+
// Continue with normal resolution flow using the new values
|
|
989
|
+
// Don't exit - let it fall through to resolve the config
|
|
839
990
|
}
|
|
840
991
|
|
|
841
992
|
/* Exit early if list or info flag is set */
|
|
@@ -931,9 +1082,8 @@ class Configorama {
|
|
|
931
1082
|
}
|
|
932
1083
|
|
|
933
1084
|
/* fix for file(JS-ref.js, raw) to keep parens and inline code */
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
rawValue = rawValue.replace(OPEN_PAREN_PLACEHOLDER_PATTERN, '(')
|
|
1085
|
+
if (hasParenthesesPlaceholder(rawValue)) {
|
|
1086
|
+
rawValue = decodeJsSyntax(rawValue)
|
|
937
1087
|
this.update(rawValue)
|
|
938
1088
|
}
|
|
939
1089
|
|
|
@@ -977,6 +1127,8 @@ class Configorama {
|
|
|
977
1127
|
collectVariableMetadata() {
|
|
978
1128
|
const variableSyntax = this.variableSyntax
|
|
979
1129
|
const variablesKnownTypes = this.variablesKnownTypes
|
|
1130
|
+
const variableTypes = this.variableTypes
|
|
1131
|
+
const filterMatch = this.filterMatch
|
|
980
1132
|
const foundVariables = []
|
|
981
1133
|
const variableData = {}
|
|
982
1134
|
const fileRefs = []
|
|
@@ -991,29 +1143,117 @@ class Configorama {
|
|
|
991
1143
|
return
|
|
992
1144
|
}
|
|
993
1145
|
|
|
994
|
-
const nested = findNestedVariables(
|
|
1146
|
+
const nested = findNestedVariables(
|
|
1147
|
+
rawValue,
|
|
1148
|
+
variableSyntax,
|
|
1149
|
+
variablesKnownTypes,
|
|
1150
|
+
configValuePath,
|
|
1151
|
+
variableTypes
|
|
1152
|
+
)
|
|
995
1153
|
|
|
996
1154
|
const lastItem = nested[nested.length - 1]
|
|
997
1155
|
const lastKeyPath = this.path[this.path.length - 1]
|
|
998
1156
|
const itemKey = (lastKeyPath.match(/[\d+]$/)) ? `${this.path[this.path.length - 2]}[${lastKeyPath}]` : lastKeyPath
|
|
999
|
-
|
|
1157
|
+
|
|
1158
|
+
// Extract filters from varMatch
|
|
1159
|
+
const originalSrc = lastItem.varMatch || ''
|
|
1160
|
+
const hasFilters = filterMatch && originalSrc.match(filterMatch)
|
|
1161
|
+
let foundFilters = []
|
|
1162
|
+
let keyWithoutFilters = originalSrc
|
|
1163
|
+
|
|
1164
|
+
if (hasFilters) {
|
|
1165
|
+
// Extract filter names from the match (e.g., "| String}" -> ["String"])
|
|
1166
|
+
const filterPart = hasFilters[0].replace(/}?$/, '') // Remove trailing }
|
|
1167
|
+
foundFilters = filterPart
|
|
1168
|
+
.split('|')
|
|
1169
|
+
.map((filter) => filter.trim())
|
|
1170
|
+
.filter(Boolean)
|
|
1171
|
+
|
|
1172
|
+
// Remove filters from the key (replace "| String}" with "}")
|
|
1173
|
+
// Also clean up any trailing whitespace before the closing brace
|
|
1174
|
+
keyWithoutFilters = originalSrc.replace(filterMatch, '}').replace(/\s+}$/, '}')
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
const key = keyWithoutFilters
|
|
1178
|
+
|
|
1179
|
+
// Strip filters from resolveDetails
|
|
1180
|
+
const cleanedResolveDetails = nested.map(detail => {
|
|
1181
|
+
const cleaned = { ...detail }
|
|
1182
|
+
if (cleaned.varMatch && filterMatch) {
|
|
1183
|
+
const match = cleaned.varMatch.match(filterMatch)
|
|
1184
|
+
if (match) {
|
|
1185
|
+
cleaned.varMatch = cleaned.varMatch.replace(filterMatch, '').replace(/\s+$/, '') + '}'
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
if (cleaned.variable && filterMatch) {
|
|
1189
|
+
const match = cleaned.variable.match(filterMatch)
|
|
1190
|
+
if (match) {
|
|
1191
|
+
cleaned.variable = cleaned.variable.replace(filterMatch, '').replace(/\s+$/, '')
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
if (cleaned.varString && filterMatch) {
|
|
1195
|
+
const match = cleaned.varString.match(filterMatch)
|
|
1196
|
+
if (match) {
|
|
1197
|
+
cleaned.varString = cleaned.varString.replace(filterMatch, '').trim()
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
// Also clean fallbackValues if present
|
|
1201
|
+
if (cleaned.fallbackValues && Array.isArray(cleaned.fallbackValues)) {
|
|
1202
|
+
cleaned.fallbackValues = cleaned.fallbackValues.map(fb => {
|
|
1203
|
+
const cleanedFb = { ...fb }
|
|
1204
|
+
if (cleanedFb.varMatch && filterMatch) {
|
|
1205
|
+
const match = cleanedFb.varMatch.match(filterMatch)
|
|
1206
|
+
if (match) {
|
|
1207
|
+
cleanedFb.varMatch = cleanedFb.varMatch.replace(filterMatch, '').trim()
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
if (cleanedFb.variable && filterMatch) {
|
|
1211
|
+
const match = cleanedFb.variable.match(filterMatch)
|
|
1212
|
+
if (match) {
|
|
1213
|
+
cleanedFb.variable = cleanedFb.variable.replace(filterMatch, '').trim()
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
if (cleanedFb.stringValue && filterMatch) {
|
|
1217
|
+
const match = cleanedFb.stringValue.match(filterMatch)
|
|
1218
|
+
if (match) {
|
|
1219
|
+
cleanedFb.stringValue = cleanedFb.stringValue.replace(filterMatch, '').trim()
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
return cleanedFb
|
|
1223
|
+
})
|
|
1224
|
+
}
|
|
1225
|
+
return cleaned
|
|
1226
|
+
})
|
|
1227
|
+
|
|
1000
1228
|
const varData = {
|
|
1001
1229
|
path: configValuePath,
|
|
1002
1230
|
key: itemKey,
|
|
1003
|
-
|
|
1004
|
-
variable:
|
|
1231
|
+
originalStringValue: rawValue,
|
|
1232
|
+
variable: keyWithoutFilters,
|
|
1233
|
+
variableWithFilters: originalSrc,
|
|
1005
1234
|
isRequired: false,
|
|
1006
1235
|
defaultValue: undefined,
|
|
1007
1236
|
matchIndex: matchCount++,
|
|
1008
1237
|
resolveOrder: [],
|
|
1009
|
-
resolveDetails:
|
|
1238
|
+
resolveDetails: cleanedResolveDetails,
|
|
1239
|
+
...(foundFilters.length > 0 && { filters: foundFilters }),
|
|
1010
1240
|
}
|
|
1011
1241
|
let defaultValueIsVar = false
|
|
1012
1242
|
|
|
1013
1243
|
function calculateResolveOrder(item) {
|
|
1244
|
+
// Helper to strip filters from variable strings
|
|
1245
|
+
const stripFilters = (str) => {
|
|
1246
|
+
if (!str || !filterMatch) return str
|
|
1247
|
+
const match = str.match(filterMatch)
|
|
1248
|
+
if (match) {
|
|
1249
|
+
return str.replace(filterMatch, '').trim()
|
|
1250
|
+
}
|
|
1251
|
+
return str
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1014
1254
|
if (item && item.fallbackValues) {
|
|
1015
1255
|
let hasResolvedFallback
|
|
1016
|
-
const order = ([item.valueBeforeFallback]).concat(item.fallbackValues.map((f, i) => {
|
|
1256
|
+
const order = ([stripFilters(item.valueBeforeFallback)]).concat(item.fallbackValues.map((f, i) => {
|
|
1017
1257
|
if (f.fallbackValues) {
|
|
1018
1258
|
const [nestedOrder, nestedResolvedFallback] = calculateResolveOrder(f)
|
|
1019
1259
|
if (!hasResolvedFallback && nestedResolvedFallback) {
|
|
@@ -1023,21 +1263,22 @@ class Configorama {
|
|
|
1023
1263
|
}
|
|
1024
1264
|
|
|
1025
1265
|
if (!hasResolvedFallback && f.isResolvedFallback) {
|
|
1026
|
-
hasResolvedFallback = f.stringValue
|
|
1266
|
+
hasResolvedFallback = stripFilters(f.stringValue)
|
|
1027
1267
|
}
|
|
1028
1268
|
if (f.isResolvedFallback) {
|
|
1029
|
-
hasResolvedFallback = f.stringValue
|
|
1269
|
+
hasResolvedFallback = stripFilters(f.stringValue)
|
|
1030
1270
|
}
|
|
1031
1271
|
|
|
1032
1272
|
if (!hasResolvedFallback && f.isVariable) {
|
|
1033
1273
|
defaultValueIsVar = f
|
|
1034
1274
|
}
|
|
1035
|
-
|
|
1275
|
+
const valueStr = stripFilters(f.stringValue || f.variable)
|
|
1276
|
+
return `${valueStr}${f.isResolvedFallback ? ' (default)' : ''}`
|
|
1036
1277
|
})).flat()
|
|
1037
1278
|
|
|
1038
1279
|
return [order, hasResolvedFallback]
|
|
1039
1280
|
}
|
|
1040
|
-
return [[item.variable], undefined]
|
|
1281
|
+
return [[stripFilters(item.variable)], undefined]
|
|
1041
1282
|
}
|
|
1042
1283
|
|
|
1043
1284
|
const [resolveOrder, hasResolvedFallback] = calculateResolveOrder(lastItem)
|
|
@@ -1061,10 +1302,9 @@ class Configorama {
|
|
|
1061
1302
|
|
|
1062
1303
|
// Extract file references
|
|
1063
1304
|
nested.forEach((detail) => {
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
const fileMatch = detail.varType.match(/^(?:file|text)\((.*?)\)/)
|
|
1305
|
+
// console.log('detail', detail)
|
|
1306
|
+
if (detail.variableType && (detail.variableType === 'file' || detail.variableType === 'text')) {
|
|
1307
|
+
const fileMatch = detail.variable.match(/^(?:file|text)\((.*?)\)/)
|
|
1068
1308
|
if (fileMatch && fileMatch[1]) {
|
|
1069
1309
|
let fileContent = fileMatch[1].trim()
|
|
1070
1310
|
|
|
@@ -1130,19 +1370,19 @@ class Configorama {
|
|
|
1130
1370
|
// Check for self-references that resolve to config values
|
|
1131
1371
|
let dotPropArr = []
|
|
1132
1372
|
if (firstInstance.defaultValueIsVar && (
|
|
1133
|
-
firstInstance.defaultValueIsVar.
|
|
1134
|
-
firstInstance.defaultValueIsVar.
|
|
1373
|
+
firstInstance.defaultValueIsVar.variableType === 'self:' ||
|
|
1374
|
+
firstInstance.defaultValueIsVar.variableType === 'dot.prop'
|
|
1135
1375
|
)) {
|
|
1136
1376
|
dotPropArr = [firstInstance.defaultValueIsVar]
|
|
1137
1377
|
}
|
|
1138
1378
|
|
|
1139
1379
|
const hasDotPropOrSelf = instances.reduce((acc, v) => {
|
|
1140
|
-
|
|
1141
|
-
if (
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1380
|
+
// Only check the outermost variable (last in resolveDetails)
|
|
1381
|
+
if (v.resolveDetails && v.resolveDetails.length > 0) {
|
|
1382
|
+
const outermostDetail = v.resolveDetails[v.resolveDetails.length - 1]
|
|
1383
|
+
if (outermostDetail.variableType === 'dot.prop' || outermostDetail.variableType === 'self') {
|
|
1384
|
+
acc.push(outermostDetail)
|
|
1385
|
+
}
|
|
1146
1386
|
}
|
|
1147
1387
|
return acc
|
|
1148
1388
|
}, dotPropArr)
|
|
@@ -1155,10 +1395,20 @@ class Configorama {
|
|
|
1155
1395
|
const dotPropValue = dotProp.get(this.originalConfig, cleanPath)
|
|
1156
1396
|
if (typeof dotPropValue === 'undefined') {
|
|
1157
1397
|
isTrulyRequired = true
|
|
1398
|
+
} else {
|
|
1399
|
+
// Enrich with default value from self-reference
|
|
1400
|
+
firstInstance.defaultValueSrc = cleanPath
|
|
1401
|
+
const niceString = typeof dotPropValue === 'object' ? JSON.stringify(dotPropValue) : dotPropValue
|
|
1402
|
+
const truncatedString = niceString.length > 100 ? niceString.substring(0, 90) + '...' : niceString
|
|
1403
|
+
firstInstance.defaultValue = truncatedString
|
|
1404
|
+
firstInstance.isRequired = false
|
|
1158
1405
|
}
|
|
1159
1406
|
}
|
|
1160
1407
|
}
|
|
1161
1408
|
|
|
1409
|
+
// Update isRequired based on computed isTrulyRequired
|
|
1410
|
+
firstInstance.isRequired = isTrulyRequired
|
|
1411
|
+
|
|
1162
1412
|
if (isTrulyRequired) {
|
|
1163
1413
|
requiredCount++
|
|
1164
1414
|
} else {
|
|
@@ -1168,13 +1418,23 @@ class Configorama {
|
|
|
1168
1418
|
|
|
1169
1419
|
return {
|
|
1170
1420
|
variables: variableData,
|
|
1421
|
+
uniqueVariables: {},
|
|
1422
|
+
fileDependencies: {
|
|
1423
|
+
globPatterns: fileGlobPatterns,
|
|
1424
|
+
// all: fileRefs,
|
|
1425
|
+
dynamicPaths: fileRefs.filter(ref => ref.indexOf('*') !== -1 || ref.match(variableSyntax)),
|
|
1426
|
+
// resolve files are those that are paths with no * and no inner variables
|
|
1427
|
+
resolvedPaths: fileRefs.filter(ref => ref.indexOf('*') === -1 && !ref.match(variableSyntax)),
|
|
1428
|
+
// Set in enrichMetadata
|
|
1429
|
+
byConfigPath: undefined,
|
|
1430
|
+
// Set in enrichMetadata
|
|
1431
|
+
references: undefined,
|
|
1432
|
+
},
|
|
1171
1433
|
summary: {
|
|
1172
1434
|
totalVariables: varKeys.length,
|
|
1173
1435
|
requiredVariables: requiredCount,
|
|
1174
1436
|
variablesWithDefaults: withDefaultsCount
|
|
1175
1437
|
},
|
|
1176
|
-
fileRefs: fileRefs,
|
|
1177
|
-
fileGlobPatterns: fileGlobPatterns,
|
|
1178
1438
|
}
|
|
1179
1439
|
}
|
|
1180
1440
|
runFunction(variableString) {
|
|
@@ -1198,6 +1458,7 @@ class Configorama {
|
|
|
1198
1458
|
let argsToPass
|
|
1199
1459
|
if (rawArgs && rawArgs.match(/^{([^}]+)}$/)) {
|
|
1200
1460
|
// console.log('OBJECT', hasFunc[2])
|
|
1461
|
+
// TODO use JSON5
|
|
1201
1462
|
argsToPass = [JSON.parse(rawArgs)]
|
|
1202
1463
|
} else {
|
|
1203
1464
|
// TODO fix how commas + spaces are ned
|
|
@@ -1464,48 +1725,79 @@ class Configorama {
|
|
|
1464
1725
|
if (matches.length === 1) {
|
|
1465
1726
|
valueObject.currentVarDetails = matches[0]
|
|
1466
1727
|
valueObject.currentVarDetails.result = results[0]
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
// Initialize resolution history if needed
|
|
1731
|
+
if (!valueObject.resolutionHistory) {
|
|
1732
|
+
valueObject.resolutionHistory = []
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
let result = valueObject.value
|
|
1736
|
+
for (let i = 0; i < matches.length; i += 1) {
|
|
1737
|
+
this.warnIfNotFound(matches[i].variable, results[i])
|
|
1467
1738
|
|
|
1468
1739
|
// Extract metadata from result if present
|
|
1469
|
-
let actualResult = results[
|
|
1740
|
+
let actualResult = results[i]
|
|
1470
1741
|
let resolverType = undefined
|
|
1471
|
-
if (results[
|
|
1472
|
-
if (results[
|
|
1473
|
-
actualResult = results[
|
|
1474
|
-
resolverType = results[
|
|
1475
|
-
} else if (results[
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
resolverType = results[0].__resolverType
|
|
1742
|
+
if (results[i] && typeof results[i] === 'object') {
|
|
1743
|
+
if (results[i].__internal_metadata) {
|
|
1744
|
+
actualResult = results[i].value
|
|
1745
|
+
resolverType = results[i].__resolverType
|
|
1746
|
+
} else if (results[i].__internal_only_flag) {
|
|
1747
|
+
actualResult = results[i]
|
|
1748
|
+
resolverType = results[i].__resolverType
|
|
1479
1749
|
}
|
|
1480
1750
|
}
|
|
1481
|
-
// valueObject.currentVarDetails.varType = results[0].__resolverType
|
|
1482
1751
|
|
|
1483
|
-
// Track resolution history
|
|
1484
|
-
if (!valueObject.resolutionHistory) {
|
|
1485
|
-
valueObject.resolutionHistory = []
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
1752
|
// Extract clean result to avoid circular references
|
|
1489
|
-
// For __internal_only_flag objects (like deep resolver results), extract the value
|
|
1490
|
-
// For real data objects (like file contents), keep them as-is
|
|
1491
1753
|
let cleanResult = actualResult
|
|
1492
1754
|
if (actualResult && typeof actualResult === 'object' && actualResult.__internal_only_flag) {
|
|
1493
1755
|
cleanResult = actualResult.value
|
|
1494
1756
|
}
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1757
|
+
|
|
1758
|
+
let valueBeforeResolution = result
|
|
1759
|
+
|
|
1760
|
+
if (typeof valueBeforeResolution === 'object' && valueBeforeResolution.__internal_only_flag) {
|
|
1761
|
+
valueBeforeResolution = valueBeforeResolution.value
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
const finalResult = decodeEncodedValue(cleanResult)
|
|
1765
|
+
|
|
1766
|
+
// Track this resolution step in history
|
|
1767
|
+
const historyEntry = {}
|
|
1768
|
+
|
|
1769
|
+
historyEntry.match = matches[i].match
|
|
1770
|
+
historyEntry.variable = matches[i].variable
|
|
1771
|
+
if (historyEntry.resultType === 'string' && historyEntry.result.match(/^>passthrough\[/)) {
|
|
1772
|
+
historyEntry.variableType = 'encodedUnknown'
|
|
1502
1773
|
}
|
|
1503
1774
|
if (resolverType) {
|
|
1504
|
-
historyEntry.
|
|
1775
|
+
historyEntry.variableType = resolverType
|
|
1776
|
+
}
|
|
1777
|
+
historyEntry.result = finalResult
|
|
1778
|
+
|
|
1779
|
+
const isDeepResult = typeof finalResult === 'string' && finalResult.match(/^\$\{deep:\d+\}$/)
|
|
1780
|
+
|
|
1781
|
+
if (isDeepResult) {
|
|
1782
|
+
historyEntry.resultAfterDeep = 'TBD'
|
|
1505
1783
|
}
|
|
1506
1784
|
|
|
1785
|
+
historyEntry.resultType = typeof finalResult
|
|
1786
|
+
historyEntry.valueBeforeResolution = valueBeforeResolution
|
|
1787
|
+
historyEntry.from = 'renderMatches'
|
|
1788
|
+
if (isDeepResult) {
|
|
1789
|
+
historyEntry.resultIsDeep = true
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
if (finalResult !== cleanResult) {
|
|
1793
|
+
historyEntry.resultEncoded = cleanResult
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
|
|
1797
|
+
|
|
1798
|
+
|
|
1507
1799
|
// Check if variable has fallback values (comma-separated)
|
|
1508
|
-
const variableParts = splitByComma(matches[
|
|
1800
|
+
const variableParts = splitByComma(matches[i].variable)
|
|
1509
1801
|
if (variableParts.length > 1) {
|
|
1510
1802
|
historyEntry.hasFallback = true
|
|
1511
1803
|
historyEntry.valueBeforeFallback = variableParts[0]
|
|
@@ -1534,10 +1826,10 @@ class Configorama {
|
|
|
1534
1826
|
fallbackData.isResolvedFallback = true
|
|
1535
1827
|
}
|
|
1536
1828
|
} else {
|
|
1537
|
-
// Extract
|
|
1829
|
+
// Extract variableType from variable references
|
|
1538
1830
|
const varTypeMatch = trimmedFallback.match(this.variablesKnownTypes)
|
|
1539
1831
|
if (varTypeMatch && varTypeMatch[1]) {
|
|
1540
|
-
fallbackData.
|
|
1832
|
+
fallbackData.variableType = varTypeMatch[1]
|
|
1541
1833
|
}
|
|
1542
1834
|
}
|
|
1543
1835
|
|
|
@@ -1546,33 +1838,16 @@ class Configorama {
|
|
|
1546
1838
|
}
|
|
1547
1839
|
|
|
1548
1840
|
// Only add to history if not a duplicate (same match + variable)
|
|
1549
|
-
const isDuplicate = valueObject.resolutionHistory.some(entry =>
|
|
1550
|
-
entry.match === historyEntry.match &&
|
|
1841
|
+
const isDuplicate = valueObject.resolutionHistory.some(entry =>
|
|
1842
|
+
entry.match === historyEntry.match &&
|
|
1551
1843
|
entry.variable === historyEntry.variable
|
|
1552
1844
|
)
|
|
1553
|
-
|
|
1845
|
+
|
|
1554
1846
|
if (!isDuplicate) {
|
|
1555
1847
|
valueObject.resolutionHistory.push(historyEntry)
|
|
1556
1848
|
}
|
|
1557
1849
|
|
|
1558
|
-
//
|
|
1559
|
-
if (valueObject.path && valueObject.path.length) {
|
|
1560
|
-
const pathKey = valueObject.path.join('.')
|
|
1561
|
-
if (!this.resolutionTracking[pathKey]) {
|
|
1562
|
-
this.resolutionTracking[pathKey] = {
|
|
1563
|
-
path: pathKey,
|
|
1564
|
-
originalPropertyString: valueObject.originalSource,
|
|
1565
|
-
calls: []
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
this.resolutionTracking[pathKey].resolutionHistory = valueObject.resolutionHistory
|
|
1569
|
-
}
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1572
|
-
let result = valueObject.value
|
|
1573
|
-
for (let i = 0; i < matches.length; i += 1) {
|
|
1574
|
-
this.warnIfNotFound(matches[i].variable, results[i])
|
|
1575
|
-
// console.log('Render MATCHES', results[i])
|
|
1850
|
+
// Process the match
|
|
1576
1851
|
let valueToPop = results[i]
|
|
1577
1852
|
// TODO refactor this. __internal_only_flag needed to stop clash with sync/async file resolution
|
|
1578
1853
|
if (results[i] && typeof results[i] === 'object' && (results[i].__internal_only_flag || results[i].__internal_metadata)) {
|
|
@@ -1586,6 +1861,21 @@ class Configorama {
|
|
|
1586
1861
|
console.log(this.deep)
|
|
1587
1862
|
/** */
|
|
1588
1863
|
}
|
|
1864
|
+
|
|
1865
|
+
// Save resolution history to tracking map for persistence across iterations
|
|
1866
|
+
if (valueObject.path && valueObject.path.length) {
|
|
1867
|
+
const pathKey = valueObject.path.join('.')
|
|
1868
|
+
if (!this.resolutionTracking[pathKey]) {
|
|
1869
|
+
this.resolutionTracking[pathKey] = {
|
|
1870
|
+
path: pathKey,
|
|
1871
|
+
originalPropertyString: valueObject.originalSource,
|
|
1872
|
+
resolvedPropertyValue: undefined,
|
|
1873
|
+
calls: []
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
this.resolutionTracking[pathKey].resolutionHistory = valueObject.resolutionHistory
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1589
1879
|
return result
|
|
1590
1880
|
}
|
|
1591
1881
|
/**
|
|
@@ -1734,7 +2024,7 @@ class Configorama {
|
|
|
1734
2024
|
if (currentDetails &&
|
|
1735
2025
|
currentDetails.resultType === 'number' &&
|
|
1736
2026
|
parentDetails && parentDetails.resultType === 'string' &&
|
|
1737
|
-
parentDetails.result.match(/^\d+$/) && parentDetails.
|
|
2027
|
+
parentDetails.result.match(/^\d+$/) && parentDetails.variableType === 'env'
|
|
1738
2028
|
) {
|
|
1739
2029
|
if (Number(parentDetails.result) === currentDetails.result) {
|
|
1740
2030
|
property = String(valueToPopulate)
|
|
@@ -1990,7 +2280,23 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
1990
2280
|
})
|
|
1991
2281
|
}
|
|
1992
2282
|
property = foundFilters.reduce((acc, filter) => {
|
|
1993
|
-
|
|
2283
|
+
// Check if filter has function-style arguments
|
|
2284
|
+
const funcMatch = filter.match(/^(\w+)\((.*)\)$/)
|
|
2285
|
+
let filterName = filter
|
|
2286
|
+
let filterArgs = []
|
|
2287
|
+
|
|
2288
|
+
if (funcMatch) {
|
|
2289
|
+
filterName = funcMatch[1]
|
|
2290
|
+
const rawArgs = funcMatch[2]
|
|
2291
|
+
if (rawArgs) {
|
|
2292
|
+
const splitter = splitCsv(rawArgs, ', ')
|
|
2293
|
+
filterArgs = formatFunctionArgs(splitter)
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
const newVal = filterArgs.length > 0
|
|
2298
|
+
? this.filters[filterName](acc, ...filterArgs, 'from populateVariable')
|
|
2299
|
+
: this.filters[filterName](acc, 'from populateVariable')
|
|
1994
2300
|
// console.log('PROPERTY', newVal)
|
|
1995
2301
|
return newVal
|
|
1996
2302
|
}, property)
|
|
@@ -2100,10 +2406,27 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
2100
2406
|
this.resolutionTracking[pathKey] = {
|
|
2101
2407
|
path: pathKey,
|
|
2102
2408
|
originalPropertyString: propertyString,
|
|
2409
|
+
resolvedPropertyValue: undefined,
|
|
2103
2410
|
calls: []
|
|
2104
2411
|
}
|
|
2105
2412
|
}
|
|
2106
2413
|
|
|
2414
|
+
// this.resolutionTracking[pathKey].resolutionHistory = this.resolutionTracking[pathKey].resolutionHistory || []
|
|
2415
|
+
|
|
2416
|
+
// const isDuplicate = this.resolutionTracking[pathKey].resolutionHistory.some(entry =>
|
|
2417
|
+
// entry.variableString === variableString
|
|
2418
|
+
// )
|
|
2419
|
+
|
|
2420
|
+
// if (!isDuplicate) {
|
|
2421
|
+
// this.resolutionTracking[pathKey].resolutionHistory.push({
|
|
2422
|
+
// variableString: variableString,
|
|
2423
|
+
// propertyString: propertyString,
|
|
2424
|
+
// caller: caller,
|
|
2425
|
+
// lol: 'what'
|
|
2426
|
+
// })
|
|
2427
|
+
// }
|
|
2428
|
+
|
|
2429
|
+
|
|
2107
2430
|
this.resolutionTracking[pathKey].calls.push({
|
|
2108
2431
|
variableString: variableString,
|
|
2109
2432
|
propertyString: propertyString,
|
|
@@ -2210,7 +2533,6 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
2210
2533
|
this.options,
|
|
2211
2534
|
this.config,
|
|
2212
2535
|
valueObject,
|
|
2213
|
-
|
|
2214
2536
|
).then((val) => {
|
|
2215
2537
|
// Update the last call with the resolved value
|
|
2216
2538
|
if (pathValue && pathValue.length) {
|
|
@@ -2219,7 +2541,8 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
2219
2541
|
// Find the most recent call for this variableString
|
|
2220
2542
|
for (let i = this.resolutionTracking[pathKey].calls.length - 1; i >= 0; i--) {
|
|
2221
2543
|
if (this.resolutionTracking[pathKey].calls[i].variableString === variableString) {
|
|
2222
|
-
|
|
2544
|
+
const v = (typeof val === 'object' && val.__internal_only_flag) ? val.value : val
|
|
2545
|
+
this.resolutionTracking[pathKey].calls[i].resolvedValue = v
|
|
2223
2546
|
this.resolutionTracking[pathKey].calls[i].resolverType = resolverType
|
|
2224
2547
|
break
|
|
2225
2548
|
}
|
|
@@ -2260,17 +2583,22 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
2260
2583
|
// console.log('valueCount', valueCount)
|
|
2261
2584
|
// TODO throw on empty values?
|
|
2262
2585
|
// No fallback value found AND this is undefined, throw error
|
|
2263
|
-
const nestedVars = findNestedVariables(propertyString, this.variableSyntax, this.variablesKnownTypes)
|
|
2586
|
+
const nestedVars = findNestedVariables(propertyString, this.variableSyntax, this.variablesKnownTypes, undefined, this.variableTypes)
|
|
2264
2587
|
// console.log('nestedVars', nestedVars)
|
|
2265
2588
|
const noNestedVars = nestedVars.length < 2
|
|
2589
|
+
|
|
2590
|
+
if (this.opts.allowUnknownFileRefs && variableString.match(fileRefSyntax)) {
|
|
2591
|
+
// Encode the unknown file variable to pass through resolution
|
|
2592
|
+
return Promise.resolve(encodeUnknown(propertyString))
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2266
2595
|
if (valueCount.length === 1 && noNestedVars) {
|
|
2267
|
-
const
|
|
2268
|
-
|
|
2269
|
-
|
|
2596
|
+
const configFilePathMsg = (this.configFilePath) ? `\nIn file ${this.configFilePath} ` : ''
|
|
2597
|
+
const fromLine = (propertyString !== valueObject.originalSource) ? `\n From "${valueObject.originalSource}"\n` : ''
|
|
2598
|
+
|
|
2270
2599
|
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
At location ${valueObject.path ? `"${arrayToJsonPath(valueObject.path)}"` : 'na'}${configFilePath}
|
|
2600
|
+
|
|
2601
|
+
throw new Error(`Unable to resolve config variable "${propertyString}".\n${configFilePathMsg}at location ${valueObject.path ? `"${arrayToJsonPath(valueObject.path)}"` : 'n/a'}${fromLine}
|
|
2274
2602
|
\nFix this reference, your inputs and/or provide a valid fallback value.
|
|
2275
2603
|
\nExample of setting a fallback value: \${${variableString}, "fallbackValue"\}\n`)
|
|
2276
2604
|
}
|
|
@@ -2305,13 +2633,28 @@ Unable to resolve configuration variable
|
|
|
2305
2633
|
}
|
|
2306
2634
|
|
|
2307
2635
|
const newUse = newHasFilter.reduce((acc, currentFilter, i) => {
|
|
2308
|
-
if (
|
|
2309
|
-
|
|
2636
|
+
// Check if filter has function-style arguments: filterName(arg1, arg2)
|
|
2637
|
+
const funcMatch = currentFilter.match(/^(\w+)\((.*)\)$/)
|
|
2638
|
+
let filterName = currentFilter
|
|
2639
|
+
let filterArgs = null
|
|
2640
|
+
|
|
2641
|
+
if (funcMatch) {
|
|
2642
|
+
filterName = funcMatch[1]
|
|
2643
|
+
const rawArgs = funcMatch[2]
|
|
2644
|
+
// Parse arguments using the same logic as functions
|
|
2645
|
+
if (rawArgs) {
|
|
2646
|
+
const splitter = splitCsv(rawArgs, ', ')
|
|
2647
|
+
filterArgs = formatFunctionArgs(splitter)
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
if (!this.filters[filterName]) {
|
|
2652
|
+
throw new Error(`Filter "${filterName}" not found`)
|
|
2310
2653
|
}
|
|
2311
2654
|
return acc.concat({
|
|
2312
|
-
filter: this.filters[
|
|
2313
|
-
filterName:
|
|
2314
|
-
|
|
2655
|
+
filter: this.filters[filterName],
|
|
2656
|
+
filterName: filterName,
|
|
2657
|
+
args: filterArgs
|
|
2315
2658
|
})
|
|
2316
2659
|
}, [])
|
|
2317
2660
|
// console.log('pathValue', pathValue)
|
|
@@ -2573,7 +2916,7 @@ Unable to resolve configuration variable
|
|
|
2573
2916
|
// console.log('matchedFileString', matchedFileString)
|
|
2574
2917
|
|
|
2575
2918
|
// Get function input params if any supplied https://regex101.com/r/qlNFVm/1
|
|
2576
|
-
|
|
2919
|
+
// var funcParamsRegex = /(\w+)\s*\(((?:[^()]+)*)?\s*\)\s*/g
|
|
2577
2920
|
var funcParamsRegex = /(\w+)\s*\(((?:[^()]+)*)?\s*\)/g
|
|
2578
2921
|
// tighter (?<![.\w-])\b(\w+)\s*\(((?:[^()]+)*)?\s*\)\s*
|
|
2579
2922
|
var hasParams = funcParamsRegex.exec(matchedFileString)
|
|
@@ -2597,45 +2940,37 @@ Unable to resolve configuration variable
|
|
|
2597
2940
|
}
|
|
2598
2941
|
// console.log('argsToPass', argsToPass)
|
|
2599
2942
|
|
|
2600
|
-
const
|
|
2601
|
-
|
|
2602
|
-
)
|
|
2603
|
-
|
|
2604
|
-
// Resolve alias if the path contains alias syntax
|
|
2605
|
-
const resolvedPath = resolveAlias(relativePath, this.configPath)
|
|
2606
|
-
// console.log('resolvedPath', resolvedPath)
|
|
2943
|
+
const fileDetails = resolveFilePathFromMatch(matchedFileString, syntax, this.configPath)
|
|
2944
|
+
// console.log('fileDetails', fileDetails)
|
|
2607
2945
|
|
|
2608
|
-
|
|
2946
|
+
const { fullFilePath, resolvedPath, relativePath } = fileDetails
|
|
2609
2947
|
|
|
2610
|
-
|
|
2948
|
+
const exists = fs.existsSync(fullFilePath)
|
|
2611
2949
|
|
|
2612
|
-
|
|
2613
|
-
//
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
if (findUpResult) {
|
|
2622
|
-
fullFilePath = findUpResult
|
|
2623
|
-
}
|
|
2624
|
-
}
|
|
2950
|
+
this.fileRefsFound.push({
|
|
2951
|
+
// location: options.context.path.join('.'),
|
|
2952
|
+
filePath: fullFilePath,
|
|
2953
|
+
relativePath,
|
|
2954
|
+
resolvedVariableString: options.context.value,
|
|
2955
|
+
originalVariableString: options.context.originalSource,
|
|
2956
|
+
containsVariables: options.context.value !== options.context.originalSource,
|
|
2957
|
+
exists,
|
|
2958
|
+
})
|
|
2625
2959
|
|
|
2626
2960
|
let fileExtension = resolvedPath.split('.')
|
|
2627
2961
|
|
|
2628
2962
|
fileExtension = fileExtension[fileExtension.length - 1]
|
|
2629
2963
|
|
|
2630
2964
|
// Validate file exists
|
|
2631
|
-
if (!
|
|
2965
|
+
if (!exists) {
|
|
2632
2966
|
const originalVar = options.context && options.context.originalSource
|
|
2633
2967
|
|
|
2634
2968
|
const findNestedResult = findNestedVariables(
|
|
2635
|
-
originalVar,
|
|
2636
|
-
this.variableSyntax,
|
|
2637
|
-
this.variablesKnownTypes,
|
|
2638
|
-
options.context.path
|
|
2969
|
+
originalVar,
|
|
2970
|
+
this.variableSyntax,
|
|
2971
|
+
this.variablesKnownTypes,
|
|
2972
|
+
options.context.path,
|
|
2973
|
+
this.variableTypes
|
|
2639
2974
|
)
|
|
2640
2975
|
// console.log('findNestedResult', findNestedResult)
|
|
2641
2976
|
let hasFallback = false
|
|
@@ -2649,9 +2984,10 @@ Unable to resolve configuration variable
|
|
|
2649
2984
|
// console.log('NO FILE FOUND', fullFilePath)
|
|
2650
2985
|
// console.log('variableString', variableString)
|
|
2651
2986
|
|
|
2652
|
-
if (!hasFallback) {
|
|
2987
|
+
if (!hasFallback && !this.opts.allowUnknownFileRefs) {
|
|
2653
2988
|
const errorMsg = makeBox({
|
|
2654
2989
|
title: `File Not Found in ${originalVar}`,
|
|
2990
|
+
minWidth: '100%',
|
|
2655
2991
|
text: `Variable ${variableString} cannot resolve due to missing file.
|
|
2656
2992
|
|
|
2657
2993
|
File not found ${fullFilePath}
|
|
@@ -2667,13 +3003,19 @@ ${JSON.stringify(options.context, null, 2)}`,
|
|
|
2667
3003
|
return Promise.resolve(undefined)
|
|
2668
3004
|
}
|
|
2669
3005
|
|
|
3006
|
+
|
|
3007
|
+
|
|
2670
3008
|
let valueToPopulate
|
|
2671
3009
|
|
|
2672
3010
|
const variableFileContents = fs.readFileSync(fullFilePath, 'utf-8')
|
|
2673
3011
|
|
|
2674
3012
|
/* handle case for referencing raw JS files to inline them */
|
|
2675
|
-
if (argsToPass.length
|
|
2676
|
-
|
|
3013
|
+
if (argsToPass.length
|
|
3014
|
+
&& (argsToPass && argsToPass[0] && argsToPass[0].toLowerCase() === 'raw')
|
|
3015
|
+
|| opts.asRawText
|
|
3016
|
+
) {
|
|
3017
|
+
// Encode foo() to foo__PH_PAREN_OPEN__) to avoid function collisions
|
|
3018
|
+
valueToPopulate = encodeJsSyntax(variableFileContents)
|
|
2677
3019
|
return Promise.resolve(valueToPopulate)
|
|
2678
3020
|
}
|
|
2679
3021
|
|
|
@@ -3020,25 +3362,25 @@ Please use ":" to reference sub properties. ${deepProperties}`
|
|
|
3020
3362
|
}
|
|
3021
3363
|
|
|
3022
3364
|
warnIfNotFound(variableString, valueToPopulate) {
|
|
3023
|
-
let
|
|
3365
|
+
let variableTypeText
|
|
3024
3366
|
if (variableString.match(envRefSyntax)) {
|
|
3025
|
-
|
|
3367
|
+
variableTypeText = 'environment variable'
|
|
3026
3368
|
} else if (variableString.match(optRefSyntax)) {
|
|
3027
|
-
|
|
3369
|
+
variableTypeText = 'option'
|
|
3028
3370
|
} else if (variableString.match(selfRefSyntax)) {
|
|
3029
|
-
|
|
3371
|
+
variableTypeText = 'config attribute'
|
|
3030
3372
|
} else if (variableString.match(fileRefSyntax)) {
|
|
3031
|
-
|
|
3373
|
+
variableTypeText = 'file'
|
|
3032
3374
|
} else if (variableString.match(deepRefSyntax)) {
|
|
3033
|
-
|
|
3375
|
+
variableTypeText = 'deep'
|
|
3034
3376
|
} else if (variableString.match(textRefSyntax)) {
|
|
3035
|
-
|
|
3377
|
+
variableTypeText = 'text'
|
|
3036
3378
|
}
|
|
3037
3379
|
if (!isValidValue(valueToPopulate)) {
|
|
3038
3380
|
// console.log("MISSING", variableString)
|
|
3039
3381
|
// console.log(this.deep)
|
|
3040
3382
|
// console.log(valueToPopulate)
|
|
3041
|
-
const notFoundMsg = `No ${
|
|
3383
|
+
const notFoundMsg = `No ${variableTypeText} found to satisfy the '\${${variableString}}' variable. Attempting fallback value`
|
|
3042
3384
|
if (DEBUG) {
|
|
3043
3385
|
console.log(notFoundMsg)
|
|
3044
3386
|
}
|