configorama 0.6.19 → 0.7.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/README.md +78 -2
- package/cli.js +1 -0
- package/index.d.ts +90 -37
- package/package.json +8 -2
- package/src/index.js +42 -14
- package/src/main.js +72 -51
- package/src/parsers/index.js +10 -0
- package/src/parsers/typescript.js +20 -43
- package/src/parsers/yaml.js +35 -2
- package/src/resolvers/valueFromFile.js +87 -24
- package/src/resolvers/valueFromGit.js +11 -3
- package/src/utils/encoders/js-fixes.js +44 -0
- package/src/utils/parsing/mergeByKeys.js +4 -3
- package/src/utils/parsing/parse.js +4 -2
- package/src/utils/paths/getFullFilePath.js +1 -1
- package/src/utils/resolution/preResolveVariable.js +1 -0
- package/src/utils/strings/quoteUtils.js +2 -2
- package/src/utils/strings/splitCsv.js +1 -1
- package/src/utils/ui/logs.js +4 -4
- package/src/utils/validation/warnIfNotFound.js +3 -3
- package/src/utils/variables/findNestedVariables.js +2 -2
- package/types/cli.d.ts +3 -0
- package/types/cli.d.ts.map +1 -0
- package/types/src/functions/md5.d.ts +3 -0
- package/types/src/functions/md5.d.ts.map +1 -0
- package/types/src/index.d.ts +100 -0
- package/types/src/index.d.ts.map +1 -0
- package/types/src/main.d.ts +275 -0
- package/types/src/main.d.ts.map +1 -0
- package/types/src/parsers/esm.d.ts +15 -0
- package/types/src/parsers/esm.d.ts.map +1 -0
- package/types/src/parsers/hcl.d.ts +1 -0
- package/types/src/parsers/hcl.d.ts.map +1 -0
- package/types/src/parsers/index.d.ts +18 -0
- package/types/src/parsers/index.d.ts.map +1 -0
- package/types/src/parsers/ini.d.ts +6 -0
- package/types/src/parsers/ini.d.ts.map +1 -0
- package/types/src/parsers/json5.d.ts +10 -0
- package/types/src/parsers/json5.d.ts.map +1 -0
- package/types/src/parsers/toml.d.ts +7 -0
- package/types/src/parsers/toml.d.ts.map +1 -0
- package/types/src/parsers/typescript.d.ts +15 -0
- package/types/src/parsers/typescript.d.ts.map +1 -0
- package/types/src/parsers/yaml.d.ts +45 -0
- package/types/src/parsers/yaml.d.ts.map +1 -0
- package/types/src/resolvers/valueFromCron.d.ts +14 -0
- package/types/src/resolvers/valueFromCron.d.ts.map +1 -0
- package/types/src/resolvers/valueFromEnv.d.ts +8 -0
- package/types/src/resolvers/valueFromEnv.d.ts.map +1 -0
- package/types/src/resolvers/valueFromEval.d.ts +7 -0
- package/types/src/resolvers/valueFromEval.d.ts.map +1 -0
- package/types/src/resolvers/valueFromFile.d.ts +58 -0
- package/types/src/resolvers/valueFromFile.d.ts.map +1 -0
- package/types/src/resolvers/valueFromGit.d.ts +11 -0
- package/types/src/resolvers/valueFromGit.d.ts.map +1 -0
- package/types/src/resolvers/valueFromNumber.d.ts +6 -0
- package/types/src/resolvers/valueFromNumber.d.ts.map +1 -0
- package/types/src/resolvers/valueFromOptions.d.ts +9 -0
- package/types/src/resolvers/valueFromOptions.d.ts.map +1 -0
- package/types/src/resolvers/valueFromSelf.d.ts +1 -0
- package/types/src/resolvers/valueFromSelf.d.ts.map +1 -0
- package/types/src/resolvers/valueFromString.d.ts +6 -0
- package/types/src/resolvers/valueFromString.d.ts.map +1 -0
- package/types/src/sync.d.ts +3 -0
- package/types/src/sync.d.ts.map +1 -0
- package/types/src/utils/PromiseTracker.d.ts +19 -0
- package/types/src/utils/PromiseTracker.d.ts.map +1 -0
- package/types/src/utils/encoders/index.d.ts +2 -0
- package/types/src/utils/encoders/index.d.ts.map +1 -0
- package/types/src/utils/encoders/js-fixes.d.ts +23 -0
- package/types/src/utils/encoders/js-fixes.d.ts.map +1 -0
- package/types/src/utils/encoders/unknown-values.d.ts +18 -0
- package/types/src/utils/encoders/unknown-values.d.ts.map +1 -0
- package/types/src/utils/handleSignalEvents.d.ts +3 -0
- package/types/src/utils/handleSignalEvents.d.ts.map +1 -0
- package/types/src/utils/lodash.d.ts +4 -0
- package/types/src/utils/lodash.d.ts.map +1 -0
- package/types/src/utils/parsing/arrayToJsonPath.d.ts +5 -0
- package/types/src/utils/parsing/arrayToJsonPath.d.ts.map +1 -0
- package/types/src/utils/parsing/cloudformationSchema.d.ts +2 -0
- package/types/src/utils/parsing/cloudformationSchema.d.ts.map +1 -0
- package/types/src/utils/parsing/enrichMetadata.d.ts +17 -0
- package/types/src/utils/parsing/enrichMetadata.d.ts.map +1 -0
- package/types/src/utils/parsing/mergeByKeys.d.ts +5 -0
- package/types/src/utils/parsing/mergeByKeys.d.ts.map +1 -0
- package/types/src/utils/parsing/parse.d.ts +54 -0
- package/types/src/utils/parsing/parse.d.ts.map +1 -0
- package/types/src/utils/parsing/preProcess.d.ts +10 -0
- package/types/src/utils/parsing/preProcess.d.ts.map +1 -0
- package/types/src/utils/paths/filePathUtils.d.ts +32 -0
- package/types/src/utils/paths/filePathUtils.d.ts.map +1 -0
- package/types/src/utils/paths/findLineForKey.d.ts +12 -0
- package/types/src/utils/paths/findLineForKey.d.ts.map +1 -0
- package/types/src/utils/paths/findProjectRoot.d.ts +2 -0
- package/types/src/utils/paths/findProjectRoot.d.ts.map +1 -0
- package/types/src/utils/paths/getFullFilePath.d.ts +25 -0
- package/types/src/utils/paths/getFullFilePath.d.ts.map +1 -0
- package/types/src/utils/paths/resolveAlias.d.ts +14 -0
- package/types/src/utils/paths/resolveAlias.d.ts.map +1 -0
- package/types/src/utils/regex/index.d.ts +14 -0
- package/types/src/utils/regex/index.d.ts.map +1 -0
- package/types/src/utils/resolution/preResolveVariable.d.ts +51 -0
- package/types/src/utils/resolution/preResolveVariable.d.ts.map +1 -0
- package/types/src/utils/strings/bracketMatcher.d.ts +25 -0
- package/types/src/utils/strings/bracketMatcher.d.ts.map +1 -0
- package/types/src/utils/strings/formatFunctionArgs.d.ts +3 -0
- package/types/src/utils/strings/formatFunctionArgs.d.ts.map +1 -0
- package/types/src/utils/strings/quoteUtils.d.ts +31 -0
- package/types/src/utils/strings/quoteUtils.d.ts.map +1 -0
- package/types/src/utils/strings/replaceAll.d.ts +9 -0
- package/types/src/utils/strings/replaceAll.d.ts.map +1 -0
- package/types/src/utils/strings/splitByComma.d.ts +2 -0
- package/types/src/utils/strings/splitByComma.d.ts.map +1 -0
- package/types/src/utils/strings/splitCsv.d.ts +10 -0
- package/types/src/utils/strings/splitCsv.d.ts.map +1 -0
- package/types/src/utils/strings/textUtils.d.ts +15 -0
- package/types/src/utils/strings/textUtils.d.ts.map +1 -0
- package/types/src/utils/ui/chalk.d.ts +70 -0
- package/types/src/utils/ui/chalk.d.ts.map +1 -0
- package/types/src/utils/ui/configWizard.d.ts +67 -0
- package/types/src/utils/ui/configWizard.d.ts.map +1 -0
- package/types/src/utils/ui/createEditorLink.d.ts +11 -0
- package/types/src/utils/ui/createEditorLink.d.ts.map +1 -0
- package/types/src/utils/ui/deep-log.d.ts +3 -0
- package/types/src/utils/ui/deep-log.d.ts.map +1 -0
- package/types/src/utils/ui/logs.d.ts +2 -0
- package/types/src/utils/ui/logs.d.ts.map +1 -0
- package/types/src/utils/validation/warnIfNotFound.d.ts +15 -0
- package/types/src/utils/validation/warnIfNotFound.d.ts.map +1 -0
- package/types/src/utils/variables/appendDeepVariable.d.ts +3 -0
- package/types/src/utils/variables/appendDeepVariable.d.ts.map +1 -0
- package/types/src/utils/variables/cleanVariable.d.ts +3 -0
- package/types/src/utils/variables/cleanVariable.d.ts.map +1 -0
- package/types/src/utils/variables/findNestedVariables.d.ts +23 -0
- package/types/src/utils/variables/findNestedVariables.d.ts.map +1 -0
- package/types/src/utils/variables/getVariableType.d.ts +8 -0
- package/types/src/utils/variables/getVariableType.d.ts.map +1 -0
- package/types/src/utils/variables/variableUtils.d.ts +21 -0
- package/types/src/utils/variables/variableUtils.d.ts.map +1 -0
package/src/main.js
CHANGED
|
@@ -27,7 +27,7 @@ const handleSignalEvents = require('./utils/handleSignalEvents')
|
|
|
27
27
|
/* Utils - encoders */
|
|
28
28
|
const { encodeUnknown, decodeUnknown } = require('./utils/encoders/unknown-values')
|
|
29
29
|
const { decodeEncodedValue } = require('./utils/encoders')
|
|
30
|
-
const { encodeJsSyntax, decodeJsSyntax, hasParenthesesPlaceholder } = require('./utils/encoders/js-fixes')
|
|
30
|
+
const { encodeJsSyntax, decodeJsSyntax, hasParenthesesPlaceholder, encodeJsonForVariable } = require('./utils/encoders/js-fixes')
|
|
31
31
|
|
|
32
32
|
/* Utils - parsing */
|
|
33
33
|
const enrichMetadata = require('./utils/parsing/enrichMetadata')
|
|
@@ -106,8 +106,8 @@ const deepRefSyntax = RegExp(/(\${)?deep:\d+(\.[^}]+)*()}?/)
|
|
|
106
106
|
const deepIndexReplacePattern = new RegExp(/^deep:|(\.[^}]+)*$/g)
|
|
107
107
|
const deepIndexPattern = /deep\:(\d*)/
|
|
108
108
|
const deepPrefixReplacePattern = /(?:^deep:)\d+\.?/g
|
|
109
|
-
const fileRefSyntax = RegExp(/^file\((~?[@\{\}\:\$a-zA-Z0-9._\-\/,'" ]+?)\)/g)
|
|
110
|
-
const textRefSyntax = RegExp(/^text\((~?[@\{\}\:\$a-zA-Z0-9._\-\/,'" ]+?)\)/g)
|
|
109
|
+
const fileRefSyntax = RegExp(/^file\((~?[@\{\}\:\$a-zA-Z0-9._\-\/,'" =+]+?)\)/g)
|
|
110
|
+
const textRefSyntax = RegExp(/^text\((~?[@\{\}\:\$a-zA-Z0-9._\-\/,'" =+]+?)\)/g)
|
|
111
111
|
// TODO update file regex ^file\((~?[a-zA-Z0-9._\-\/, ]+?)\)
|
|
112
112
|
// To match file(asyncValue.js, lol) input params
|
|
113
113
|
const envRefSyntax = RegExp(/^env:/g)
|
|
@@ -132,7 +132,7 @@ class Configorama {
|
|
|
132
132
|
|
|
133
133
|
const options = opts || {}
|
|
134
134
|
// Set opts to pass into JS file calls
|
|
135
|
-
this.
|
|
135
|
+
this.settings = Object.assign({}, {
|
|
136
136
|
// Allow for unknown variable syntax to pass through without throwing errors
|
|
137
137
|
allowUnknownVariables: false,
|
|
138
138
|
// Allow undefined to be an end result.
|
|
@@ -149,7 +149,7 @@ class Configorama {
|
|
|
149
149
|
|
|
150
150
|
// Backward compat: allowUnknownVars -> allowUnknownVariables
|
|
151
151
|
if (options.allowUnknownVars !== undefined && options.allowUnknownVariables === undefined) {
|
|
152
|
-
this.
|
|
152
|
+
this.settings.allowUnknownVariables = options.allowUnknownVars
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
this.filterCache = {}
|
|
@@ -375,11 +375,13 @@ class Configorama {
|
|
|
375
375
|
}
|
|
376
376
|
|
|
377
377
|
/* attach self matcher last */
|
|
378
|
-
this.variableTypes = this.variableTypes.concat(fallThroughSelfMatcher)
|
|
378
|
+
this.variableTypes = this.variableTypes.concat(/** @type {any} */ (fallThroughSelfMatcher))
|
|
379
379
|
|
|
380
380
|
// const variablesKnownTypes = new RegExp(`^(${this.variableTypes.map((v) => v.prefix || v.type).join('|')}):`)
|
|
381
381
|
const variablesKnownTypes = combineRegexes(
|
|
382
|
-
|
|
382
|
+
/** @type {RegExp[]} */ (this.variableTypes
|
|
383
|
+
.filter((v) => v.type !== 'string' && v.match instanceof RegExp)
|
|
384
|
+
.map((v) => v.match))
|
|
383
385
|
)
|
|
384
386
|
this.variablesKnownTypes = variablesKnownTypes
|
|
385
387
|
|
|
@@ -547,11 +549,11 @@ class Configorama {
|
|
|
547
549
|
* Populate all variables in the service, conveniently remove and restore the service attributes
|
|
548
550
|
* that confuse the population methods.
|
|
549
551
|
* @param cliOpts An options hive to use for ${opt:...} variables.
|
|
550
|
-
* @returns {Promise
|
|
552
|
+
* @returns {Promise<any>} A promise resolving to the populated service.
|
|
551
553
|
*/
|
|
552
554
|
async init(cliOpts) {
|
|
553
555
|
this.options = cliOpts || {}
|
|
554
|
-
const configoramaOpts = this.
|
|
556
|
+
const configoramaOpts = this.settings
|
|
555
557
|
|
|
556
558
|
const showFoundVariables = configoramaOpts && configoramaOpts.dynamicArgs && (configoramaOpts.dynamicArgs.list || configoramaOpts.dynamicArgs.info)
|
|
557
559
|
|
|
@@ -562,10 +564,10 @@ class Configorama {
|
|
|
562
564
|
contents: this.originalString,
|
|
563
565
|
filePath: this.configFilePath,
|
|
564
566
|
varRegex: this.variableSyntax,
|
|
565
|
-
dynamicArgs: this.
|
|
567
|
+
dynamicArgs: this.settings.dynamicArgs
|
|
566
568
|
})
|
|
567
569
|
this.configFileContents = ''
|
|
568
|
-
if (VERBOSE || showFoundVariables || this.
|
|
570
|
+
if (VERBOSE || showFoundVariables || this.settings.returnPreResolvedVariableDetails || SETUP_MODE) {
|
|
569
571
|
this.configFileContents = fs.readFileSync(this.configFilePath, 'utf8')
|
|
570
572
|
}
|
|
571
573
|
/*
|
|
@@ -595,7 +597,7 @@ class Configorama {
|
|
|
595
597
|
const variableSyntax = this.variableSyntax
|
|
596
598
|
const variablesKnownTypes = this.variablesKnownTypes
|
|
597
599
|
|
|
598
|
-
if (VERBOSE || showFoundVariables || this.
|
|
600
|
+
if (VERBOSE || showFoundVariables || this.settings.returnPreResolvedVariableDetails || SETUP_MODE) {
|
|
599
601
|
const metadata = this.collectVariableMetadata()
|
|
600
602
|
|
|
601
603
|
const enrich = await enrichMetadata(
|
|
@@ -607,7 +609,7 @@ class Configorama {
|
|
|
607
609
|
this.configFilePath,
|
|
608
610
|
Object.keys(this.filters),
|
|
609
611
|
undefined, // resolvedConfig not available yet
|
|
610
|
-
this.
|
|
612
|
+
this.settings.options,
|
|
611
613
|
this.variableTypes
|
|
612
614
|
)
|
|
613
615
|
|
|
@@ -625,7 +627,7 @@ class Configorama {
|
|
|
625
627
|
const varKeys = Object.keys(variableData)
|
|
626
628
|
const uniqueVarKeys = Object.keys(uniqueVariables)
|
|
627
629
|
|
|
628
|
-
if (this.
|
|
630
|
+
if (this.settings.returnPreResolvedVariableDetails) {
|
|
629
631
|
return Object.assign({}, {
|
|
630
632
|
resolved: false,
|
|
631
633
|
originalConfig: this.originalConfig
|
|
@@ -644,7 +646,7 @@ class Configorama {
|
|
|
644
646
|
if (varTypes.length) {
|
|
645
647
|
const exclude = ['dot.prop', 'deep']
|
|
646
648
|
console.log('\nAllowed variable types:')
|
|
647
|
-
varTypes.
|
|
649
|
+
varTypes.forEach((v) => {
|
|
648
650
|
const vData = this.variableTypes[v]
|
|
649
651
|
if (exclude.includes(vData.type)) {
|
|
650
652
|
return
|
|
@@ -1141,7 +1143,7 @@ class Configorama {
|
|
|
1141
1143
|
}
|
|
1142
1144
|
|
|
1143
1145
|
const useDotEnv = this.originalConfig.useDotenv || this.originalConfig.useDotEnv
|
|
1144
|
-
if ((useDotEnv && useDotEnv === true) || this.
|
|
1146
|
+
if ((useDotEnv && useDotEnv === true) || this.settings.useDotEnvFiles) {
|
|
1145
1147
|
let providerStage
|
|
1146
1148
|
/* has hardcoded stage */
|
|
1147
1149
|
if (
|
|
@@ -1244,8 +1246,8 @@ class Configorama {
|
|
|
1244
1246
|
.then(() => {
|
|
1245
1247
|
// console.log('this.config', this.config)
|
|
1246
1248
|
/* Final post-processing here */
|
|
1247
|
-
if (this.mergeKeys && this.config) {
|
|
1248
|
-
this.config = mergeByKeys(this.config, '', this.mergeKeys)
|
|
1249
|
+
if (this.settings.mergeKeys && this.config) {
|
|
1250
|
+
this.config = mergeByKeys(this.config, '', this.settings.mergeKeys)
|
|
1249
1251
|
}
|
|
1250
1252
|
if (VERBOSE) {
|
|
1251
1253
|
logHeader('Resolved Configuration value')
|
|
@@ -1409,6 +1411,7 @@ class Configorama {
|
|
|
1409
1411
|
})
|
|
1410
1412
|
|
|
1411
1413
|
const varData = {
|
|
1414
|
+
filters: foundFilters.length > 0 ? foundFilters : undefined,
|
|
1412
1415
|
path: configValuePath,
|
|
1413
1416
|
key: itemKey,
|
|
1414
1417
|
originalStringValue: rawValue,
|
|
@@ -1416,10 +1419,12 @@ class Configorama {
|
|
|
1416
1419
|
variableWithFilters: originalSrc,
|
|
1417
1420
|
isRequired: false,
|
|
1418
1421
|
defaultValue: undefined,
|
|
1422
|
+
defaultValueIsVar: undefined,
|
|
1423
|
+
defaultValueSrc: undefined,
|
|
1424
|
+
hasFallback: false,
|
|
1419
1425
|
matchIndex: matchCount++,
|
|
1420
1426
|
resolveOrder: [],
|
|
1421
1427
|
resolveDetails: cleanedResolveDetails,
|
|
1422
|
-
...(foundFilters.length > 0 && { filters: foundFilters }),
|
|
1423
1428
|
}
|
|
1424
1429
|
let defaultValueIsVar = false
|
|
1425
1430
|
|
|
@@ -1691,7 +1696,7 @@ class Configorama {
|
|
|
1691
1696
|
/**
|
|
1692
1697
|
* Populate the variables in the given object.
|
|
1693
1698
|
* @param objectToPopulate The object to populate variables within.
|
|
1694
|
-
* @returns {Promise
|
|
1699
|
+
* @returns {Promise<any>} A promise resolving to the in-place populated object.
|
|
1695
1700
|
*/
|
|
1696
1701
|
populateObject(objectToPopulate) {
|
|
1697
1702
|
return this.initialCall(() => this.populateObjectImpl(objectToPopulate))
|
|
@@ -1741,7 +1746,7 @@ class Configorama {
|
|
|
1741
1746
|
* ]
|
|
1742
1747
|
* @typedef {Object} TerminalProperty
|
|
1743
1748
|
* @property {String[]} path The path to the terminal property
|
|
1744
|
-
* @property {Date|
|
|
1749
|
+
* @property {Date|RegExp|String} value The value of the terminal property
|
|
1745
1750
|
*/
|
|
1746
1751
|
/**
|
|
1747
1752
|
* Generate an array of objects noting the terminal properties of the given root object and their
|
|
@@ -1857,8 +1862,7 @@ class Configorama {
|
|
|
1857
1862
|
* Assign the populated values back to the target object
|
|
1858
1863
|
* @param target The object to which the given populated terminal properties should be applied
|
|
1859
1864
|
* @param populations The fully populated terminal properties
|
|
1860
|
-
* @returns {Promise<
|
|
1861
|
-
* target
|
|
1865
|
+
* @returns {Promise<void>} resolving when changes have been applied to the given target
|
|
1862
1866
|
*/
|
|
1863
1867
|
assignProperties(target, populations) {
|
|
1864
1868
|
// eslint-disable-line class-methods-use-this
|
|
@@ -2098,7 +2102,7 @@ class Configorama {
|
|
|
2098
2102
|
* @param valueObject The value to populate variables within
|
|
2099
2103
|
* @param root Whether the caller is the root populater and thereby whether to recursively
|
|
2100
2104
|
* populate
|
|
2101
|
-
* @returns {
|
|
2105
|
+
* @returns {Promise<any>} A promise that resolves to the populated value, recursively if root
|
|
2102
2106
|
* is true
|
|
2103
2107
|
*/
|
|
2104
2108
|
populateValue(valueObject, root, caller) {
|
|
@@ -2181,11 +2185,14 @@ class Configorama {
|
|
|
2181
2185
|
/**
|
|
2182
2186
|
* Populate a given property, given the matched string to replace and the value to replace the
|
|
2183
2187
|
* matched string with.
|
|
2184
|
-
* @param valueObject
|
|
2188
|
+
* @param {object} valueObject The value object containing the property to populate
|
|
2189
|
+
* @param {any} valueObject.value The property to replace the matched string with the value.
|
|
2190
|
+
* @param {string[]} [valueObject.path] The path to the value in the config.
|
|
2191
|
+
* @param {string} [valueObject.originalSource] The original source string.
|
|
2192
|
+
* @param {Array} [valueObject.resolutionHistory] History of resolution steps.
|
|
2185
2193
|
* @param matchedString The string in the given property that was matched and is to be replaced.
|
|
2186
2194
|
* @param valueToPopulate The value to replace the given matched string in the property with.
|
|
2187
|
-
* @returns {
|
|
2188
|
-
* value for all instances of the given matched string.
|
|
2195
|
+
* @returns {{value: any, path?: string[], originalSource?: string, resolutionHistory?: Array, __internal_only_flag?: boolean, caller?: string, count?: number}} The populated property object
|
|
2189
2196
|
*/
|
|
2190
2197
|
populateVariable(valueObject, matchedString, valueToPopulate) {
|
|
2191
2198
|
let property = valueObject.value
|
|
@@ -2324,23 +2331,30 @@ class Configorama {
|
|
|
2324
2331
|
|
|
2325
2332
|
const objStr = JSON.stringify(valueToPopulate)
|
|
2326
2333
|
/* Check if variable inside another variable. E.g. ${env:${self:someObject}} that resolves to ${env:{...}} */
|
|
2327
|
-
|
|
2334
|
+
const isNestedInVariable = (
|
|
2328
2335
|
property.trim() !== matchedString.trim() &&
|
|
2329
2336
|
property.indexOf(matchedString) !== -1 &&
|
|
2330
2337
|
matchedString.match(this.variableSyntax) &&
|
|
2331
2338
|
property.match(this.variableSyntax)
|
|
2332
|
-
)
|
|
2339
|
+
)
|
|
2340
|
+
// Only encode for file() or text() references where JSON braces break regex matching
|
|
2341
|
+
const isFileOrTextRef = /\bfile\s*\(|\btext\s*\(/.test(property)
|
|
2342
|
+
if (isNestedInVariable && isFileOrTextRef) {
|
|
2343
|
+
// Encode object as base64 to avoid breaking variable syntax with nested braces
|
|
2344
|
+
const encodedObj = encodeJsonForVariable(valueToPopulate)
|
|
2345
|
+
property = replaceAll(matchedString, encodedObj, property)
|
|
2346
|
+
} else if (isNestedInVariable) {
|
|
2333
2347
|
const isVar = /^\${[a-zA-Z0-9_]+:/.test(property)
|
|
2334
2348
|
if (isVar) {
|
|
2335
|
-
// console.log('INSIDE', property, matchedString)
|
|
2336
|
-
// console.log('isVar', isVar)
|
|
2337
2349
|
throw new Error(
|
|
2338
2350
|
`Invalid variable syntax "${property}" resolves to "${replaceAll(matchedString, objStr, property)}"`,
|
|
2339
2351
|
)
|
|
2340
2352
|
}
|
|
2353
|
+
property = replaceAll(matchedString, objStr, property)
|
|
2354
|
+
} else {
|
|
2355
|
+
// console.log('OBJECT MATCH', `"${objStr}"`)
|
|
2356
|
+
property = replaceAll(matchedString, objStr, property)
|
|
2341
2357
|
}
|
|
2342
|
-
// console.log('OBJECT MATCH', `"${objStr}"`)
|
|
2343
|
-
property = replaceAll(matchedString, objStr, property) // .replace(/}$/, '').replace(/^\$\{/, '')
|
|
2344
2358
|
// console.log('property', property)
|
|
2345
2359
|
// TODO run functions here
|
|
2346
2360
|
// console.log('other new prop', property)
|
|
@@ -2366,7 +2380,7 @@ class Configorama {
|
|
|
2366
2380
|
|
|
2367
2381
|
if (nestedVar) {
|
|
2368
2382
|
const fallbackStr = getFallbackString(splitVars, nestedVar)
|
|
2369
|
-
if (!this.
|
|
2383
|
+
if (!this.settings.allowUnknownVariables) {
|
|
2370
2384
|
verifyVariable(nestedVar, valueObject, this.variableTypes, this.config)
|
|
2371
2385
|
}
|
|
2372
2386
|
|
|
@@ -2382,18 +2396,17 @@ class Configorama {
|
|
|
2382
2396
|
}
|
|
2383
2397
|
|
|
2384
2398
|
// If allowUnresolvedVariables and there are fallbacks, use the fallback
|
|
2385
|
-
if (this.
|
|
2399
|
+
if (this.settings.allowUnresolvedVariables && splitVars.length > 1) {
|
|
2386
2400
|
const nextFallback = splitVars[1].trim()
|
|
2387
2401
|
// Strip trailing variable suffix (handles }, }}, >, ]], etc.)
|
|
2388
2402
|
const nextFallbackClean = nextFallback.replace(this.varSuffixPattern, '')
|
|
2389
2403
|
const isQuotedString = /^['"].*['"]$/.test(nextFallbackClean)
|
|
2390
2404
|
const isNumeric = /^-?\d+(\.\d+)?$/.test(nextFallbackClean)
|
|
2391
2405
|
if (isQuotedString || isNumeric) {
|
|
2392
|
-
|
|
2406
|
+
const strValue = nextFallbackClean.replace(/^['"]|['"]$/g, '')
|
|
2393
2407
|
// Convert to number if it's a numeric fallback
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
}
|
|
2408
|
+
/** @type {string|number} */
|
|
2409
|
+
const staticValue = isNumeric ? Number(strValue) : strValue
|
|
2397
2410
|
return {
|
|
2398
2411
|
value: staticValue,
|
|
2399
2412
|
path: valueObject.path,
|
|
@@ -2484,7 +2497,7 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
2484
2497
|
!prop.match(this.variableSyntax)
|
|
2485
2498
|
&&
|
|
2486
2499
|
/* Not file or text refs */
|
|
2487
|
-
!prop.match(fileRefSyntax)
|
|
2500
|
+
!prop.match(fileRefSyntax)
|
|
2488
2501
|
&& !prop.match(textRefSyntax)
|
|
2489
2502
|
/* Not eval refs */
|
|
2490
2503
|
&& !prop.match(getValueFromEval.match)
|
|
@@ -2573,8 +2586,10 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
2573
2586
|
/**
|
|
2574
2587
|
* Resolve the given variable string that expresses a series of fallback values in case the
|
|
2575
2588
|
* initial values are not valid, resolving each variable and resolving to the first valid value.
|
|
2576
|
-
* @param
|
|
2577
|
-
* @
|
|
2589
|
+
* @param variableStrings The overwrite string of variables to populate and choose from.
|
|
2590
|
+
* @param valueObject The value object
|
|
2591
|
+
* @param originalVar The original variable string
|
|
2592
|
+
* @returns {Promise<any>} A promise resolving to the first validly populating variable
|
|
2578
2593
|
* in the given variable strings string.
|
|
2579
2594
|
*/
|
|
2580
2595
|
overwrite(variableStrings, valueObject, originalVar) {
|
|
@@ -2650,7 +2665,10 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
2650
2665
|
/**
|
|
2651
2666
|
* Given any variable string, return the value it should be populated with.
|
|
2652
2667
|
* @param variableString The variable string to retrieve a value for.
|
|
2653
|
-
* @
|
|
2668
|
+
* @param valueObject The value object
|
|
2669
|
+
* @param caller The caller name
|
|
2670
|
+
* @param originalVar The original variable string
|
|
2671
|
+
* @returns {Promise<any>} A promise resolving to the given variables value.
|
|
2654
2672
|
*/
|
|
2655
2673
|
getValueFromSource(variableString, valueObject, caller, originalVar) {
|
|
2656
2674
|
// console.log('getValueFromSrc caller', caller)
|
|
@@ -2749,6 +2767,7 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
2749
2767
|
variableString = trim(t[0])
|
|
2750
2768
|
}
|
|
2751
2769
|
|
|
2770
|
+
/** @type {Function|undefined} */
|
|
2752
2771
|
let resolverFunction
|
|
2753
2772
|
let resolverType
|
|
2754
2773
|
/* Loop over variables and set getterFunction when match found. */
|
|
@@ -2845,12 +2864,12 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
2845
2864
|
// console.log('nestedVars', nestedVars)
|
|
2846
2865
|
const noNestedVars = nestedVars.length < 2
|
|
2847
2866
|
|
|
2848
|
-
if (this.
|
|
2867
|
+
if (this.settings.allowUnknownFileRefs && variableString.match(fileRefSyntax)) {
|
|
2849
2868
|
// Encode the unknown file variable to pass through resolution
|
|
2850
2869
|
return Promise.resolve(encodeUnknown(propertyString))
|
|
2851
2870
|
}
|
|
2852
2871
|
|
|
2853
|
-
if (this.
|
|
2872
|
+
if (this.settings.allowUnresolvedVariables) {
|
|
2854
2873
|
// Check if outer expression has fallbacks we can use
|
|
2855
2874
|
// valueCount[0] is the primary var, valueCount[1+] are fallbacks
|
|
2856
2875
|
if (valueCount.length > 1) {
|
|
@@ -3027,7 +3046,7 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
3027
3046
|
// console.log('nestedVar', nestedVar)
|
|
3028
3047
|
|
|
3029
3048
|
if (nestedVar) {
|
|
3030
|
-
if (!this.
|
|
3049
|
+
if (!this.settings.allowUnknownVariables) {
|
|
3031
3050
|
verifyVariable(nestedVar, valueObject, this.variableTypes, this.config)
|
|
3032
3051
|
}
|
|
3033
3052
|
const fallbackStr = getFallbackString(split, nestedVar)
|
|
@@ -3105,7 +3124,7 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
3105
3124
|
let allowSpecialCase = false
|
|
3106
3125
|
/* handle special cases for cloudformation ${Sub} values */
|
|
3107
3126
|
if (this.originalConfig && key.endsWith('Fn::Sub')) {
|
|
3108
|
-
if (this.
|
|
3127
|
+
if (this.settings.verifySubReferences) {
|
|
3109
3128
|
const params = this.originalConfig.Parameters || (this.originalConfig.resources || {}).Parameters
|
|
3110
3129
|
const resources = this.originalConfig.Resources || (this.originalConfig.resources || {}).Resources
|
|
3111
3130
|
/* Cloudformation Resource References */
|
|
@@ -3130,7 +3149,7 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
3130
3149
|
|
|
3131
3150
|
|
|
3132
3151
|
/* Pass through unknown variables */
|
|
3133
|
-
if (this.
|
|
3152
|
+
if (this.settings.allowUnknownVariables || allowSpecialCase) {
|
|
3134
3153
|
// console.log('allowUnknownVars propertyString', propertyString)
|
|
3135
3154
|
const varMatches = propertyString.match(this.variableSyntax)
|
|
3136
3155
|
let allowUnknownVars = propertyString
|
|
@@ -3188,7 +3207,7 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
3188
3207
|
variableSyntax: this.variableSyntax,
|
|
3189
3208
|
variablesKnownTypes: this.variablesKnownTypes,
|
|
3190
3209
|
variableTypes: this.variableTypes,
|
|
3191
|
-
opts: this.
|
|
3210
|
+
opts: this.settings,
|
|
3192
3211
|
originalConfig: this.originalConfig,
|
|
3193
3212
|
config: this.config,
|
|
3194
3213
|
getDeeperValue: this.getDeeperValue.bind(this),
|
|
@@ -3346,7 +3365,7 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
3346
3365
|
})
|
|
3347
3366
|
}
|
|
3348
3367
|
runFunction(variableString) {
|
|
3349
|
-
|
|
3368
|
+
console.log('runFunction', variableString)
|
|
3350
3369
|
/* If json object value return it */
|
|
3351
3370
|
if (variableString.match(/^\s*{/) && variableString.match(/}\s*$/)) {
|
|
3352
3371
|
return variableString
|
|
@@ -3378,7 +3397,9 @@ Missing Value ${missingValue} - ${matchedString}
|
|
|
3378
3397
|
// TODO check for camelCase version. | toUpperCase messes with function name
|
|
3379
3398
|
const theFunction = this.functions[functionName] || this.functions[functionName.toLowerCase()]
|
|
3380
3399
|
|
|
3381
|
-
if (!theFunction)
|
|
3400
|
+
if (!theFunction) {
|
|
3401
|
+
throw new Error(`Function "${functionName}" not found`)
|
|
3402
|
+
}
|
|
3382
3403
|
|
|
3383
3404
|
const funcValue = theFunction(...argsToPass)
|
|
3384
3405
|
// console.log('funcValue', funcValue)
|
package/src/parsers/index.js
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} ParserFunction
|
|
3
|
+
* @property {Function} parse - Parse string content into object
|
|
4
|
+
* @property {Function} stringify - Convert object to string format
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
const json = require('./json5')
|
|
2
8
|
const toml = require('./toml')
|
|
3
9
|
const yaml = require('./yaml')
|
|
4
10
|
const ini = require('./ini')
|
|
5
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Collection of format parsers for different config file types
|
|
14
|
+
* @type {Object.<string, ParserFunction>}
|
|
15
|
+
*/
|
|
6
16
|
module.exports = {
|
|
7
17
|
json: json,
|
|
8
18
|
toml: toml,
|
|
@@ -2,10 +2,10 @@ const path = require('path')
|
|
|
2
2
|
const fs = require('fs')
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Load TypeScript file and return its export (without executing)
|
|
6
6
|
* @param {string} filePath - Full path to the TypeScript file
|
|
7
|
-
* @param {Object} opts - Additional options
|
|
8
|
-
* @returns {Promise<*>} The
|
|
7
|
+
* @param {Object} opts - Additional options (unused, kept for API compat)
|
|
8
|
+
* @returns {Promise<*>} The exported module from the TypeScript file
|
|
9
9
|
*/
|
|
10
10
|
async function executeTypeScriptFile(filePath, opts = {}) {
|
|
11
11
|
// Check if tsx is available first (preferred)
|
|
@@ -34,6 +34,7 @@ async function executeTypeScriptFile(filePath, opts = {}) {
|
|
|
34
34
|
let tsFile
|
|
35
35
|
if (useTsx) {
|
|
36
36
|
// Use tsx for modern, fast TypeScript execution
|
|
37
|
+
// @ts-ignore - tsx doesn't have type declarations
|
|
37
38
|
const { register } = require('tsx/cjs/api')
|
|
38
39
|
const restore = register()
|
|
39
40
|
try {
|
|
@@ -46,6 +47,7 @@ async function executeTypeScriptFile(filePath, opts = {}) {
|
|
|
46
47
|
} else {
|
|
47
48
|
// Fallback to ts-node
|
|
48
49
|
try {
|
|
50
|
+
// @ts-ignore - ts-node is optional peer dependency
|
|
49
51
|
require('ts-node/register')
|
|
50
52
|
tsFile = require(filePath)
|
|
51
53
|
} catch (err) {
|
|
@@ -53,34 +55,19 @@ async function executeTypeScriptFile(filePath, opts = {}) {
|
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
let tsArgs = opts.dynamicArgs || {}
|
|
60
|
-
if (tsArgs && typeof tsArgs === 'function') {
|
|
61
|
-
tsArgs = tsArgs()
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
const result = tsFile(tsArgs)
|
|
66
|
-
|
|
67
|
-
// Handle promises
|
|
68
|
-
if (result && typeof result.then === 'function') {
|
|
69
|
-
return await result
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return result
|
|
73
|
-
} catch (err) {
|
|
74
|
-
throw new Error(`Error executing TypeScript function: ${err.message}`)
|
|
75
|
-
}
|
|
58
|
+
// Handle ES module default exports
|
|
59
|
+
if (tsFile && typeof tsFile === 'object' && 'default' in tsFile) {
|
|
60
|
+
tsFile = tsFile.default
|
|
76
61
|
}
|
|
62
|
+
|
|
63
|
+
return tsFile
|
|
77
64
|
}
|
|
78
65
|
|
|
79
66
|
/**
|
|
80
|
-
*
|
|
67
|
+
* Load TypeScript file synchronously and return its export
|
|
81
68
|
* @param {string} filePath - Full path to the TypeScript file
|
|
82
|
-
* @param {Object} opts - Additional options
|
|
83
|
-
* @returns {*} The
|
|
69
|
+
* @param {Object} opts - Additional options (unused, kept for API compat)
|
|
70
|
+
* @returns {*} The exported module from the TypeScript file
|
|
84
71
|
*/
|
|
85
72
|
function executeTypeScriptFileSync(filePath, opts = {}) {
|
|
86
73
|
// Check if tsx is available first (preferred)
|
|
@@ -109,6 +96,7 @@ function executeTypeScriptFileSync(filePath, opts = {}) {
|
|
|
109
96
|
let tsFile
|
|
110
97
|
if (useTsx) {
|
|
111
98
|
// Use tsx for modern, fast TypeScript execution
|
|
99
|
+
// @ts-ignore - tsx doesn't have type declarations
|
|
112
100
|
const { register } = require('tsx/cjs/api')
|
|
113
101
|
const restore = register()
|
|
114
102
|
try {
|
|
@@ -121,6 +109,7 @@ function executeTypeScriptFileSync(filePath, opts = {}) {
|
|
|
121
109
|
} else {
|
|
122
110
|
// Fallback to ts-node
|
|
123
111
|
try {
|
|
112
|
+
// @ts-ignore - ts-node is optional peer dependency
|
|
124
113
|
require('ts-node/register')
|
|
125
114
|
tsFile = require(filePath)
|
|
126
115
|
} catch (err) {
|
|
@@ -128,24 +117,12 @@ function executeTypeScriptFileSync(filePath, opts = {}) {
|
|
|
128
117
|
}
|
|
129
118
|
}
|
|
130
119
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
let tsArgs = opts.dynamicArgs || {}
|
|
135
|
-
if (tsArgs && typeof tsArgs === 'function') {
|
|
136
|
-
tsArgs = tsArgs()
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
const result = tsFile(tsArgs)
|
|
141
|
-
|
|
142
|
-
// Note: For sync execution, we don't await promises
|
|
143
|
-
// If the function returns a promise, it will be resolved by the calling code
|
|
144
|
-
return result
|
|
145
|
-
} catch (err) {
|
|
146
|
-
throw new Error(`Error executing TypeScript function: ${err.message}`)
|
|
147
|
-
}
|
|
120
|
+
// Handle ES module default exports
|
|
121
|
+
if (tsFile && typeof tsFile === 'object' && 'default' in tsFile) {
|
|
122
|
+
tsFile = tsFile.default
|
|
148
123
|
}
|
|
124
|
+
|
|
125
|
+
return tsFile
|
|
149
126
|
}
|
|
150
127
|
|
|
151
128
|
module.exports = {
|
package/src/parsers/yaml.js
CHANGED
|
@@ -3,7 +3,12 @@ const TOML = require('./toml')
|
|
|
3
3
|
const JSON = require('./json5')
|
|
4
4
|
const { findOutermostVariables, findOutermostBracesDepthFirst } = require('../utils/strings/bracketMatcher')
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Loader for custom CF syntax
|
|
8
|
+
* @param {string|Buffer} contents - YAML content to load
|
|
9
|
+
* @param {Object} [options] - YAML load options
|
|
10
|
+
* @returns {{data: Object|null, error: Error|null}} Parsed data and error if any
|
|
11
|
+
*/
|
|
7
12
|
function load(contents, options) {
|
|
8
13
|
let data
|
|
9
14
|
let error
|
|
@@ -15,6 +20,12 @@ function load(contents, options) {
|
|
|
15
20
|
return { data, error }
|
|
16
21
|
}
|
|
17
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Parse YAML content into JavaScript object
|
|
25
|
+
* @param {string} ymlContents - YAML string to parse
|
|
26
|
+
* @returns {Object} Parsed YAML object
|
|
27
|
+
* @throws {Error} If YAML parsing fails
|
|
28
|
+
*/
|
|
18
29
|
function parse(ymlContents) {
|
|
19
30
|
// Get document, or throw exception on error
|
|
20
31
|
let ymlObject = {}
|
|
@@ -26,6 +37,12 @@ function parse(ymlContents) {
|
|
|
26
37
|
return ymlObject
|
|
27
38
|
}
|
|
28
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Convert JavaScript object to YAML string
|
|
42
|
+
* @param {Object} object - Object to convert to YAML
|
|
43
|
+
* @returns {string} YAML string representation
|
|
44
|
+
* @throws {Error} If conversion fails
|
|
45
|
+
*/
|
|
29
46
|
function dump(object) {
|
|
30
47
|
let yml
|
|
31
48
|
try {
|
|
@@ -38,6 +55,12 @@ function dump(object) {
|
|
|
38
55
|
return yml
|
|
39
56
|
}
|
|
40
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Convert YAML content to TOML format
|
|
60
|
+
* @param {string} ymlContents - YAML string to convert
|
|
61
|
+
* @returns {string} TOML string representation
|
|
62
|
+
* @throws {Error} If conversion fails
|
|
63
|
+
*/
|
|
41
64
|
function toToml(ymlContents) {
|
|
42
65
|
let toml
|
|
43
66
|
try {
|
|
@@ -48,6 +71,12 @@ function toToml(ymlContents) {
|
|
|
48
71
|
return toml
|
|
49
72
|
}
|
|
50
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Convert YAML content to JSON format
|
|
76
|
+
* @param {string} ymlContents - YAML string to convert
|
|
77
|
+
* @returns {string} JSON string representation
|
|
78
|
+
* @throws {Error} If conversion fails
|
|
79
|
+
*/
|
|
51
80
|
function toJson(ymlContents) {
|
|
52
81
|
let json
|
|
53
82
|
try {
|
|
@@ -61,12 +90,16 @@ function toJson(ymlContents) {
|
|
|
61
90
|
// Alias for backward compatibility
|
|
62
91
|
const matchOutermostBraces = findOutermostBracesDepthFirst
|
|
63
92
|
|
|
64
|
-
|
|
65
93
|
// https://regex101.com/r/XIltbc/1
|
|
66
94
|
const KEY_OBJECT = /^[ \t]*[^":\s]*:\s+\{/gm
|
|
67
95
|
|
|
68
96
|
const INNER_ARRAY = /\[(?:[^\[\]])*\]/g
|
|
69
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Pre-process YAML string to handle nested variables and CloudFormation syntax
|
|
100
|
+
* @param {string} [ymlStr=''] - YAML string to pre-process
|
|
101
|
+
* @returns {string} Pre-processed YAML string
|
|
102
|
+
*/
|
|
70
103
|
function preProcess(ymlStr = '') {
|
|
71
104
|
/*
|
|
72
105
|
return ymlStr
|