configorama 0.6.9 → 0.6.11
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
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
const { splitByComma } = require('./splitByComma')
|
|
2
2
|
const trimQuotes = require('./trimSurroundingQuotes')
|
|
3
|
+
const { getVariableType } = require('./getVariableType')
|
|
3
4
|
const FALLBACK_REGEX = /,\s*/
|
|
4
5
|
const VAR_MATCH_REGEX = /__VAR_\d+__/
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Finds all nested variable interpolations in a string while preserving original syntax
|
|
8
|
-
*
|
|
9
|
+
*
|
|
9
10
|
* This function handles complex nested variables like:
|
|
10
11
|
* ${file(./config.${opt:stage, ${defaultStage}}.json):CREDS}
|
|
11
|
-
*
|
|
12
|
+
*
|
|
12
13
|
* The returned matches will include:
|
|
13
14
|
* 1. innermost variables first (e.g., ${defaultStage})
|
|
14
15
|
* 2. middle variables next (e.g., ${opt:stage, ${defaultStage}})
|
|
15
16
|
* 3. outermost variables last (e.g., the entire expression)
|
|
16
|
-
*
|
|
17
|
+
*
|
|
17
18
|
* Each variable retains its original syntax even in nested form.
|
|
18
|
-
*
|
|
19
|
+
*
|
|
19
20
|
* @param {string} input - The input string containing variable interpolations
|
|
20
21
|
* @param {RegExp} regex - The regex pattern to match variables
|
|
22
|
+
* @param {RegExp} variablesKnownTypes - Combined regex of all known variable types
|
|
23
|
+
* @param {string} location - The location in config where this variable appears
|
|
24
|
+
* @param {Array} variableTypes - Array of variable type definitions from resolvers
|
|
21
25
|
* @param {boolean} debug - Whether to print debug information
|
|
22
26
|
* @returns {Array} Array of match objects with fullMatch, variable, varString and other properties
|
|
23
27
|
*/
|
|
24
|
-
function findNestedVariables(input, regex, variablesKnownTypes, location, debug = false) {
|
|
28
|
+
function findNestedVariables(input, regex, variablesKnownTypes, location, variableTypes, debug = false) {
|
|
25
29
|
// console.log('variablesKnownTypes', variablesKnownTypes)
|
|
26
30
|
// Create a copy of the input for replacement tracking
|
|
27
31
|
let current = input
|
|
@@ -52,10 +56,10 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
|
|
|
52
56
|
|
|
53
57
|
// Store match details
|
|
54
58
|
const matchInfo = {
|
|
55
|
-
|
|
59
|
+
variableType: undefined,
|
|
56
60
|
location,
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
originalStringValue: input,
|
|
62
|
+
varMatch: match[0],
|
|
59
63
|
variable: match[1].trim(),
|
|
60
64
|
varString: match[1],
|
|
61
65
|
resolveOrder: iteration,
|
|
@@ -95,7 +99,7 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
|
|
|
95
99
|
// console.log('matches[i].varString', matches[i].varString)
|
|
96
100
|
|
|
97
101
|
// if (variablesKnownTypes && variablesKnownTypes.test(matches[i].varString)) {
|
|
98
|
-
// matches[i].
|
|
102
|
+
// matches[i].variableType = matches[i].varString.match(variablesKnownTypes)[1]
|
|
99
103
|
// if (FALLBACK_REGEX.test(matches[i].varString)) {
|
|
100
104
|
// const split = splitByComma(matches[i].varString, regex)
|
|
101
105
|
// matches[i].hasFallback = true
|
|
@@ -124,10 +128,10 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
|
|
|
124
128
|
|
|
125
129
|
// Second pass: Reconstruct each variable with original nested syntax
|
|
126
130
|
// We need to do this recursively to ensure all placeholders are replaced properly
|
|
127
|
-
function replaceAllPlaceholders(text = '', matchesArray, key = '
|
|
131
|
+
function replaceAllPlaceholders(text = '', matchesArray, key = 'varMatch') {
|
|
128
132
|
let result = text
|
|
129
133
|
let needsAnotherPass = false
|
|
130
|
-
|
|
134
|
+
|
|
131
135
|
// Replace all placeholders with their original matches
|
|
132
136
|
for (let i = 0; i < matchesArray.length; i++) {
|
|
133
137
|
const m = matchesArray[i]
|
|
@@ -136,33 +140,33 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
|
|
|
136
140
|
needsAnotherPass = true
|
|
137
141
|
}
|
|
138
142
|
}
|
|
139
|
-
|
|
143
|
+
|
|
140
144
|
// If we made replacements, we might need another pass to handle nested placeholders
|
|
141
145
|
if (needsAnotherPass) {
|
|
142
146
|
return replaceAllPlaceholders(result, matchesArray, key)
|
|
143
147
|
}
|
|
144
|
-
|
|
148
|
+
|
|
145
149
|
return result
|
|
146
150
|
}
|
|
147
151
|
|
|
148
152
|
// For each match, reconstruct the original nested syntax
|
|
149
153
|
for (let i = 0; i < matches.length; i++) {
|
|
150
154
|
const currentMatch = matches[i]
|
|
151
|
-
|
|
155
|
+
|
|
152
156
|
// Skip if this match doesn't contain any placeholders
|
|
153
|
-
if (!currentMatch.
|
|
157
|
+
if (!currentMatch.varMatch.includes('__VAR_') && !currentMatch.variable.includes('__VAR_')) {
|
|
154
158
|
continue
|
|
155
159
|
}
|
|
156
160
|
|
|
157
161
|
if (currentMatch.hasFallback) {
|
|
158
162
|
currentMatch.fallbackValues.forEach((item) => {
|
|
159
|
-
item.
|
|
163
|
+
item.varMatch = replaceAllPlaceholders(item.varMatch, matches, 'varMatch')
|
|
160
164
|
item.variable = replaceAllPlaceholders(item.variable, matches, 'variable')
|
|
161
165
|
})
|
|
162
166
|
}
|
|
163
|
-
|
|
167
|
+
|
|
164
168
|
// Reconstruct with all nested variables
|
|
165
|
-
currentMatch.
|
|
169
|
+
currentMatch.varMatch = replaceAllPlaceholders(currentMatch.varMatch, matches)
|
|
166
170
|
currentMatch.variable = replaceAllPlaceholders(currentMatch.variable, matches)
|
|
167
171
|
}
|
|
168
172
|
|
|
@@ -174,7 +178,7 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
|
|
|
174
178
|
// console.log('matches[i].varString', matches[i].varString)
|
|
175
179
|
|
|
176
180
|
if (variablesKnownTypes && variablesKnownTypes.test(matches[i].varString)) {
|
|
177
|
-
matches[i].
|
|
181
|
+
matches[i].variableType = getVariableType(matches[i].varString, variableTypes)
|
|
178
182
|
if (FALLBACK_REGEX.test(matches[i].varString)) {
|
|
179
183
|
const split = splitByComma(matches[i].varString, regex)
|
|
180
184
|
matches[i].hasFallback = true
|
|
@@ -188,7 +192,7 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
|
|
|
188
192
|
const isVariable = variablesKnownTypes.test(innerContent) || VAR_MATCH_REGEX.test(item)
|
|
189
193
|
const fallbackData = {
|
|
190
194
|
isVariable,
|
|
191
|
-
|
|
195
|
+
varMatch: item,
|
|
192
196
|
variable: item,
|
|
193
197
|
}
|
|
194
198
|
|
|
@@ -198,31 +202,30 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
|
|
|
198
202
|
}
|
|
199
203
|
|
|
200
204
|
if (isVariable) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
//
|
|
204
|
-
// fallbackData.fullMatch = item.replace('self:', '')
|
|
205
|
+
fallbackData.variableType = getVariableType(innerContent, variableTypes)
|
|
206
|
+
// if (variableType === 'self:') {
|
|
207
|
+
// fallbackData.varMatch = item.replace('self:', '')
|
|
205
208
|
// fallbackData.variable = item.replace('self:', '')
|
|
206
|
-
// fallbackData.
|
|
209
|
+
// fallbackData.variableType = 'dot.prop'
|
|
207
210
|
// }
|
|
208
211
|
}
|
|
209
212
|
return fallbackData
|
|
210
213
|
})
|
|
211
214
|
}
|
|
212
|
-
} else if (typeof matches[i].
|
|
213
|
-
matches[i].
|
|
215
|
+
} else if (typeof matches[i].variableType === 'undefined') {
|
|
216
|
+
matches[i].variableType = 'dot.prop'
|
|
214
217
|
}
|
|
215
218
|
}
|
|
216
219
|
|
|
217
220
|
const finalMatches = matches.map((m) => {
|
|
218
221
|
delete m.placeholder
|
|
219
|
-
if (typeof m.
|
|
222
|
+
if (typeof m.variableType === 'undefined') {
|
|
220
223
|
/*
|
|
221
224
|
{
|
|
222
|
-
|
|
225
|
+
variableType: 'dot.prop',
|
|
223
226
|
location: 'resolvedDomainName',
|
|
224
|
-
|
|
225
|
-
|
|
227
|
+
originalStringValue: '${domainByStage.${opt:stage, ${defaultStage}}}',
|
|
228
|
+
varMatch: '${domainByStage.${opt:stage, ${defaultStage}}}',
|
|
226
229
|
variable: 'domainByStage.${opt:stage, ${defaultStage}}',
|
|
227
230
|
varString: 'domainByStage.__VAR_1__',
|
|
228
231
|
resolveOrder: 3,
|
|
@@ -230,10 +233,10 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
|
|
|
230
233
|
end: 26
|
|
231
234
|
}
|
|
232
235
|
{
|
|
233
|
-
|
|
236
|
+
variableType: 'dot.prop',
|
|
234
237
|
location: 'resolvedDomainName',
|
|
235
|
-
|
|
236
|
-
|
|
238
|
+
originalStringValue: '${domainByStage.${opt:stage, ${defaultStage}}}',
|
|
239
|
+
varMatch: '${defaultStage}',
|
|
237
240
|
variable: 'defaultStage',
|
|
238
241
|
varString: 'defaultStage',
|
|
239
242
|
resolveOrder: 1,
|
|
@@ -255,7 +258,7 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
|
|
|
255
258
|
}, m.fallbackValues)
|
|
256
259
|
m.fallbackValues = combinedFallbacks
|
|
257
260
|
}
|
|
258
|
-
if (m.
|
|
261
|
+
if (m.variableType === 'dot.prop') {
|
|
259
262
|
// const reversedMatches = matches.reverse()
|
|
260
263
|
// const test = reversedMatches.reduce((acc, f) => {
|
|
261
264
|
// console.log('f', f)
|
|
@@ -276,7 +279,7 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
|
|
|
276
279
|
console.log("\nReconstructed matches:")
|
|
277
280
|
matches.forEach((m, i) => {
|
|
278
281
|
console.log(`Match #${i+1} (order ${m.order}):`)
|
|
279
|
-
console.log(`
|
|
282
|
+
console.log(`VarMatch: ${m.varMatch}`)
|
|
280
283
|
console.log(`Variable: ${m.variable}`)
|
|
281
284
|
console.log(`VarString: ${m.varString}`)
|
|
282
285
|
console.log(`Placeholder: ${m.placeholder}`)
|
|
@@ -335,10 +338,10 @@ function findNestedVariablesOld(input, regex, variablesKnownTypes, debug = false
|
|
|
335
338
|
|
|
336
339
|
// Replace the `__REPLACED_${iteration - 1}__` with the original match
|
|
337
340
|
matches = matches.map((match, index) => {
|
|
338
|
-
const indexOfReplaced = match.
|
|
341
|
+
const indexOfReplaced = match.varMatch.match(/__REPLACED_(\d+)__/)
|
|
339
342
|
if (indexOfReplaced) {
|
|
340
343
|
const replacedIndex = parseInt(indexOfReplaced[1])
|
|
341
|
-
match.
|
|
344
|
+
match.varMatch = match.varMatch.replace(`__REPLACED_${replacedIndex}__`, matches[replacedIndex].variable)
|
|
342
345
|
match.variable = match.variable.replace(`__REPLACED_${replacedIndex}__`, matches[replacedIndex].variable)
|
|
343
346
|
}
|
|
344
347
|
return match
|
|
@@ -3,96 +3,133 @@ const assert = require('uvu/assert');
|
|
|
3
3
|
const { findNestedVariables } = require('./find-nested-variables');
|
|
4
4
|
const deepLog = require('./deep-log')
|
|
5
5
|
|
|
6
|
+
// Import resolvers to build variableTypes array
|
|
7
|
+
const getValueFromEnv = require('../resolvers/valueFromEnv')
|
|
8
|
+
const getValueFromOptions = require('../resolvers/valueFromOptions')
|
|
9
|
+
const getValueFromGit = require('../resolvers/valueFromGit')
|
|
10
|
+
|
|
6
11
|
// Define the regex pattern as used in the main function
|
|
7
12
|
const regex = /\${((?!AWS|stageVariables)[ ~:a-zA-Z0-9=+!@#%*<>?._'",|\-\/\(\)\\]+?)}/g;
|
|
8
13
|
const variablesKnownTypes = /(^env:|^opt:|^self:|^file\((~?[\{\}\:\$a-zA-Z0-9._\-\/,'" ]+?)\)|^git:|(\${)?deep:\d+(\.[^}]+)*()}?)/
|
|
9
14
|
|
|
15
|
+
const fileRefSyntax = RegExp(/^file\((~?[@\{\}\:\$a-zA-Z0-9._\-\/,'" ]+?)\)/g)
|
|
16
|
+
const textRefSyntax = RegExp(/^text\((~?[@\{\}\:\$a-zA-Z0-9._\-\/,'" ]+?)\)/g)
|
|
17
|
+
const selfRefSyntax = RegExp(/^self:/g)
|
|
18
|
+
const deepRefSyntax = RegExp(/(\${)?deep:\d+(\.[^}]+)*()}?/)
|
|
19
|
+
|
|
20
|
+
// Build variableTypes array similar to main.js
|
|
21
|
+
const variableTypes = [
|
|
22
|
+
getValueFromEnv,
|
|
23
|
+
getValueFromOptions,
|
|
24
|
+
getValueFromGit,
|
|
25
|
+
{
|
|
26
|
+
type: 'self',
|
|
27
|
+
prefix: 'self',
|
|
28
|
+
match: selfRefSyntax,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
type: 'file',
|
|
32
|
+
prefix: 'file',
|
|
33
|
+
match: fileRefSyntax,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
type: 'text',
|
|
37
|
+
prefix: 'text',
|
|
38
|
+
match: textRefSyntax,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
type: 'deep',
|
|
42
|
+
prefix: 'deep',
|
|
43
|
+
match: deepRefSyntax,
|
|
44
|
+
},
|
|
45
|
+
]
|
|
46
|
+
|
|
10
47
|
test('findNestedVariables - simple variables', () => {
|
|
11
48
|
const input = '${simple}';
|
|
12
|
-
const result = findNestedVariables(input, regex, variablesKnownTypes, 'key');
|
|
49
|
+
const result = findNestedVariables(input, regex, variablesKnownTypes, 'key', variableTypes);
|
|
13
50
|
deepLog('result', result)
|
|
14
51
|
|
|
15
52
|
assert.equal(result.length, 1);
|
|
16
|
-
assert.equal(result[0].
|
|
53
|
+
assert.equal(result[0].varMatch, '${simple}');
|
|
17
54
|
assert.equal(result[0].variable, 'simple');
|
|
18
55
|
assert.equal(result[0].resolveOrder, 1, 'order should be 1');
|
|
19
56
|
});
|
|
20
57
|
|
|
21
58
|
test('findNestedVariables - complex variable with colon syntax', () => {
|
|
22
59
|
const input = '${opt:stage, dev}';
|
|
23
|
-
const result = findNestedVariables(input, regex, variablesKnownTypes);
|
|
60
|
+
const result = findNestedVariables(input, regex, variablesKnownTypes, undefined, variableTypes);
|
|
24
61
|
|
|
25
62
|
assert.equal(result.length, 1);
|
|
26
|
-
assert.equal(result[0].
|
|
63
|
+
assert.equal(result[0].varMatch, '${opt:stage, dev}');
|
|
27
64
|
assert.equal(result[0].variable, 'opt:stage, dev');
|
|
28
65
|
});
|
|
29
66
|
|
|
30
67
|
test('findNestedVariables - one level nesting', () => {
|
|
31
68
|
const input = '${file(./config.${stage}.json)}';
|
|
32
|
-
const result = findNestedVariables(input, regex, variablesKnownTypes);
|
|
69
|
+
const result = findNestedVariables(input, regex, variablesKnownTypes, undefined, variableTypes);
|
|
33
70
|
|
|
34
71
|
assert.equal(result.length, 2);
|
|
35
72
|
// The innermost variable should be found first
|
|
36
|
-
assert.equal(result[0].
|
|
73
|
+
assert.equal(result[0].varMatch, '${stage}');
|
|
37
74
|
assert.equal(result[0].variable, 'stage');
|
|
38
75
|
// Then the outer variable
|
|
39
|
-
assert.equal(result[1].
|
|
76
|
+
assert.equal(result[1].varMatch, '${file(./config.${stage}.json)}');
|
|
40
77
|
assert.equal(result[1].variable, 'file(./config.${stage}.json)');
|
|
41
78
|
});
|
|
42
79
|
|
|
43
80
|
test('findNestedVariables - two levels of nesting', () => {
|
|
44
81
|
const input = '${file(./config.${opt:stage, ${defaultStage}}.json):CREDS}';
|
|
45
|
-
const result = findNestedVariables(input, regex, variablesKnownTypes);
|
|
82
|
+
const result = findNestedVariables(input, regex, variablesKnownTypes, undefined, variableTypes);
|
|
46
83
|
|
|
47
84
|
assert.equal(result.length, 3);
|
|
48
85
|
// Innermost first
|
|
49
|
-
assert.equal(result[0].
|
|
86
|
+
assert.equal(result[0].varMatch, '${defaultStage}');
|
|
50
87
|
assert.equal(result[0].variable, 'defaultStage');
|
|
51
88
|
// Middle next
|
|
52
|
-
assert.equal(result[1].
|
|
89
|
+
assert.equal(result[1].varMatch, '${opt:stage, ${defaultStage}}');
|
|
53
90
|
assert.equal(result[1].variable, 'opt:stage, ${defaultStage}');
|
|
54
91
|
// Outermost last
|
|
55
|
-
assert.equal(result[2].
|
|
92
|
+
assert.equal(result[2].varMatch, '${file(./config.${opt:stage, ${defaultStage}}.json):CREDS}');
|
|
56
93
|
assert.equal(result[2].variable, 'file(./config.${opt:stage, ${defaultStage}}.json):CREDS');
|
|
57
94
|
});
|
|
58
95
|
|
|
59
96
|
test('findNestedVariables - multiple separate variables', () => {
|
|
60
97
|
const input = 'Hello ${name}, welcome to ${service}!';
|
|
61
|
-
const result = findNestedVariables(input, regex, variablesKnownTypes);
|
|
98
|
+
const result = findNestedVariables(input, regex, variablesKnownTypes, undefined, variableTypes);
|
|
62
99
|
|
|
63
100
|
assert.equal(result.length, 2);
|
|
64
|
-
assert.equal(result[0].
|
|
65
|
-
assert.equal(result[1].
|
|
101
|
+
assert.equal(result[0].varMatch, '${name}');
|
|
102
|
+
assert.equal(result[1].varMatch, '${service}');
|
|
66
103
|
});
|
|
67
104
|
|
|
68
105
|
test('findNestedVariables - complex mixed case', () => {
|
|
69
106
|
const input = '${db.${envOne}.host}:${db.${envTwo}.port} using ${credentials.${user.role}}';
|
|
70
|
-
const result = findNestedVariables(input, regex, variablesKnownTypes);
|
|
107
|
+
const result = findNestedVariables(input, regex, variablesKnownTypes, undefined, variableTypes);
|
|
71
108
|
console.log('result', result)
|
|
72
109
|
assert.equal(result.length, 6);
|
|
73
110
|
// Check the correct nesting order
|
|
74
|
-
assert.equal(result[0].
|
|
75
|
-
assert.equal(result[1].
|
|
76
|
-
assert.equal(result[2].
|
|
77
|
-
assert.equal(result[3].
|
|
78
|
-
assert.equal(result[4].
|
|
111
|
+
assert.equal(result[0].varMatch, '${envOne}');
|
|
112
|
+
assert.equal(result[1].varMatch, '${db.${envOne}.host}');
|
|
113
|
+
assert.equal(result[2].varMatch, '${envTwo}');
|
|
114
|
+
assert.equal(result[3].varMatch, '${db.${envTwo}.port}');
|
|
115
|
+
assert.equal(result[4].varMatch, '${user.role}');
|
|
79
116
|
});
|
|
80
117
|
|
|
81
118
|
test('findNestedVariables - empty string', () => {
|
|
82
119
|
const input = '';
|
|
83
|
-
const result = findNestedVariables(input, regex, variablesKnownTypes);
|
|
120
|
+
const result = findNestedVariables(input, regex, variablesKnownTypes, undefined, variableTypes);
|
|
84
121
|
assert.equal(result.length, 0);
|
|
85
122
|
});
|
|
86
123
|
|
|
87
124
|
test('findNestedVariables - string with no variables', () => {
|
|
88
125
|
const input = 'This is a string with no variables';
|
|
89
|
-
const result = findNestedVariables(input, regex, variablesKnownTypes);
|
|
126
|
+
const result = findNestedVariables(input, regex, variablesKnownTypes, undefined, variableTypes);
|
|
90
127
|
assert.equal(result.length, 0);
|
|
91
128
|
});
|
|
92
129
|
|
|
93
130
|
test('findNestedVariables - varString property for nested variables', () => {
|
|
94
131
|
const input = '${file(./config.${opt:stage, ${defaultStage}}.json)}';
|
|
95
|
-
const result = findNestedVariables(input, regex, variablesKnownTypes);
|
|
132
|
+
const result = findNestedVariables(input, regex, variablesKnownTypes, undefined, variableTypes);
|
|
96
133
|
deepLog('result', result)
|
|
97
134
|
// Check varString property for the outermost variable
|
|
98
135
|
assert.equal(result[2].variable, 'file(./config.${opt:stage, ${defaultStage}}.json)');
|
|
@@ -100,13 +137,60 @@ test('findNestedVariables - varString property for nested variables', () => {
|
|
|
100
137
|
|
|
101
138
|
test('findNestedVariables - mutliple fallback items', () => {
|
|
102
139
|
const input = '${file(./config.${opt:stage, ${opt:stageOne}, ${opt:stageTwo}, "three"}.json)}';
|
|
103
|
-
const result = findNestedVariables(input, regex, variablesKnownTypes);
|
|
140
|
+
const result = findNestedVariables(input, regex, variablesKnownTypes, undefined, variableTypes);
|
|
104
141
|
deepLog('result', result)
|
|
105
142
|
// Check varString property for the outermost variable
|
|
106
143
|
assert.equal(result[result.length - 1].variable, 'file(./config.${opt:stage, ${opt:stageOne}, ${opt:stageTwo}, "three"}.json)');
|
|
107
144
|
});
|
|
108
145
|
|
|
109
146
|
test('findNestedVariables - deep', () => {
|
|
147
|
+
const input =
|
|
148
|
+
'${file(./config.${opt:stage, ${opt:stageOne, ${env:foo}}, ${opt:stageTwo}, "three" }.json)}';
|
|
149
|
+
const result = findNestedVariables(input, regex, variablesKnownTypes, 'xyz', variableTypes);
|
|
150
|
+
deepLog('result', result)
|
|
151
|
+
|
|
152
|
+
// Should have 5 variables total
|
|
153
|
+
assert.equal(result.length, 5);
|
|
154
|
+
|
|
155
|
+
// Check the innermost variable
|
|
156
|
+
assert.equal(result[0].varMatch, '${env:foo}');
|
|
157
|
+
assert.equal(result[0].variable, 'env:foo');
|
|
158
|
+
assert.equal(result[0].variableType, 'env');
|
|
159
|
+
|
|
160
|
+
// Check opt:stageOne with env:foo fallback
|
|
161
|
+
assert.equal(result[1].varMatch, '${opt:stageOne, ${env:foo}}');
|
|
162
|
+
assert.equal(result[1].variable, 'opt:stageOne, ${env:foo}');
|
|
163
|
+
assert.equal(result[1].variableType, 'options');
|
|
164
|
+
assert.equal(result[1].hasFallback, true);
|
|
165
|
+
assert.equal(result[1].valueBeforeFallback, 'opt:stageOne');
|
|
166
|
+
assert.equal(result[1].fallbackValues.length, 1);
|
|
167
|
+
assert.equal(result[1].fallbackValues[0].isVariable, true);
|
|
168
|
+
assert.equal(result[1].fallbackValues[0].varMatch, '${env:foo}');
|
|
169
|
+
assert.equal(result[1].fallbackValues[0].variableType, 'env');
|
|
170
|
+
|
|
171
|
+
// Check opt:stageTwo
|
|
172
|
+
assert.equal(result[2].varMatch, '${opt:stageTwo}');
|
|
173
|
+
assert.equal(result[2].variable, 'opt:stageTwo');
|
|
174
|
+
|
|
175
|
+
// Check opt:stage with multiple fallbacks
|
|
176
|
+
assert.equal(result[3].varMatch, '${opt:stage, ${opt:stageOne, ${env:foo}}, ${opt:stageTwo}, "three" }');
|
|
177
|
+
assert.equal(result[3].variable, 'opt:stage, ${opt:stageOne, ${env:foo}}, ${opt:stageTwo}, "three"');
|
|
178
|
+
assert.equal(result[3].hasFallback, true);
|
|
179
|
+
assert.equal(result[3].valueBeforeFallback, 'opt:stage');
|
|
180
|
+
assert.equal(result[3].fallbackValues.length, 3);
|
|
181
|
+
assert.equal(result[3].fallbackValues[0].varMatch, '${opt:stageOne, ${env:foo}}');
|
|
182
|
+
assert.equal(result[3].fallbackValues[0].isVariable, true);
|
|
183
|
+
assert.equal(result[3].fallbackValues[1].varMatch, '${opt:stageTwo}');
|
|
184
|
+
assert.equal(result[3].fallbackValues[1].isVariable, true);
|
|
185
|
+
assert.equal(result[3].fallbackValues[2].varMatch, '"three"');
|
|
186
|
+
assert.equal(result[3].fallbackValues[2].isVariable, false);
|
|
187
|
+
assert.equal(result[3].fallbackValues[2].stringValue, 'three');
|
|
188
|
+
|
|
189
|
+
// Check outermost file variable
|
|
190
|
+
assert.equal(result[4].variable, 'file(./config.${opt:stage, ${opt:stageOne, ${env:foo}}, ${opt:stageTwo}, "three" }.json)');
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test('findNestedVariables - deep - no var types passed', () => {
|
|
110
194
|
const input =
|
|
111
195
|
'${file(./config.${opt:stage, ${opt:stageOne, ${env:foo}}, ${opt:stageTwo}, "three" }.json)}';
|
|
112
196
|
const result = findNestedVariables(input, regex, variablesKnownTypes, 'xyz');
|
|
@@ -116,36 +200,36 @@ test('findNestedVariables - deep', () => {
|
|
|
116
200
|
assert.equal(result.length, 5);
|
|
117
201
|
|
|
118
202
|
// Check the innermost variable
|
|
119
|
-
assert.equal(result[0].
|
|
203
|
+
assert.equal(result[0].varMatch, '${env:foo}');
|
|
120
204
|
assert.equal(result[0].variable, 'env:foo');
|
|
121
|
-
assert.equal(result[0].
|
|
205
|
+
assert.equal(result[0].variableType, 'env');
|
|
122
206
|
|
|
123
207
|
// Check opt:stageOne with env:foo fallback
|
|
124
|
-
assert.equal(result[1].
|
|
208
|
+
assert.equal(result[1].varMatch, '${opt:stageOne, ${env:foo}}');
|
|
125
209
|
assert.equal(result[1].variable, 'opt:stageOne, ${env:foo}');
|
|
126
|
-
assert.equal(result[1].
|
|
210
|
+
assert.equal(result[1].variableType, 'options');
|
|
127
211
|
assert.equal(result[1].hasFallback, true);
|
|
128
212
|
assert.equal(result[1].valueBeforeFallback, 'opt:stageOne');
|
|
129
213
|
assert.equal(result[1].fallbackValues.length, 1);
|
|
130
214
|
assert.equal(result[1].fallbackValues[0].isVariable, true);
|
|
131
|
-
assert.equal(result[1].fallbackValues[0].
|
|
132
|
-
assert.equal(result[1].fallbackValues[0].
|
|
215
|
+
assert.equal(result[1].fallbackValues[0].varMatch, '${env:foo}');
|
|
216
|
+
assert.equal(result[1].fallbackValues[0].variableType, 'env');
|
|
133
217
|
|
|
134
218
|
// Check opt:stageTwo
|
|
135
|
-
assert.equal(result[2].
|
|
219
|
+
assert.equal(result[2].varMatch, '${opt:stageTwo}');
|
|
136
220
|
assert.equal(result[2].variable, 'opt:stageTwo');
|
|
137
221
|
|
|
138
222
|
// Check opt:stage with multiple fallbacks
|
|
139
|
-
assert.equal(result[3].
|
|
223
|
+
assert.equal(result[3].varMatch, '${opt:stage, ${opt:stageOne, ${env:foo}}, ${opt:stageTwo}, "three" }');
|
|
140
224
|
assert.equal(result[3].variable, 'opt:stage, ${opt:stageOne, ${env:foo}}, ${opt:stageTwo}, "three"');
|
|
141
225
|
assert.equal(result[3].hasFallback, true);
|
|
142
226
|
assert.equal(result[3].valueBeforeFallback, 'opt:stage');
|
|
143
227
|
assert.equal(result[3].fallbackValues.length, 3);
|
|
144
|
-
assert.equal(result[3].fallbackValues[0].
|
|
228
|
+
assert.equal(result[3].fallbackValues[0].varMatch, '${opt:stageOne, ${env:foo}}');
|
|
145
229
|
assert.equal(result[3].fallbackValues[0].isVariable, true);
|
|
146
|
-
assert.equal(result[3].fallbackValues[1].
|
|
230
|
+
assert.equal(result[3].fallbackValues[1].varMatch, '${opt:stageTwo}');
|
|
147
231
|
assert.equal(result[3].fallbackValues[1].isVariable, true);
|
|
148
|
-
assert.equal(result[3].fallbackValues[2].
|
|
232
|
+
assert.equal(result[3].fallbackValues[2].varMatch, '"three"');
|
|
149
233
|
assert.equal(result[3].fallbackValues[2].isVariable, false);
|
|
150
234
|
assert.equal(result[3].fallbackValues[2].stringValue, 'three');
|
|
151
235
|
|
|
@@ -2,6 +2,8 @@ const os = require('os')
|
|
|
2
2
|
const fs = require('fs')
|
|
3
3
|
const path = require('path')
|
|
4
4
|
const findUp = require('find-up')
|
|
5
|
+
const trimSurroundingQuotes = require('./trimSurroundingQuotes')
|
|
6
|
+
const { resolveAlias } = require('./resolveAlias')
|
|
5
7
|
|
|
6
8
|
module.exports = function getFullPath(fileString, cwd) {
|
|
7
9
|
const configPath = cwd || process.cwd()
|
|
@@ -21,3 +23,39 @@ module.exports = function getFullPath(fileString, cwd) {
|
|
|
21
23
|
|
|
22
24
|
return fullFilePath
|
|
23
25
|
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Resolves a file path from a matched file string (e.g., from file() or text() syntax)
|
|
29
|
+
* @param {string} matchedFileString - The matched file string (e.g., "file(path/to/file.js)")
|
|
30
|
+
* @param {RegExp} syntax - The regex pattern used to match the file string (e.g., fileRefSyntax or textRefSyntax)
|
|
31
|
+
* @param {string} configPath - The base directory path for resolving relative paths
|
|
32
|
+
* @returns {{fullFilePath: string|null, resolvedPath: string}} - Object containing the resolved full file path and the resolved path (after alias resolution)
|
|
33
|
+
*/
|
|
34
|
+
function resolveFilePathFromMatch(matchedFileString, syntax, configPath) {
|
|
35
|
+
const relativePath = trimSurroundingQuotes(
|
|
36
|
+
matchedFileString.replace(syntax, (match, varName) => varName.trim()).replace('~', os.homedir()),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
// Resolve alias if the path contains alias syntax
|
|
40
|
+
const resolvedPath = resolveAlias(relativePath, configPath)
|
|
41
|
+
|
|
42
|
+
let fullFilePath = path.isAbsolute(resolvedPath) ? resolvedPath : path.join(configPath, resolvedPath)
|
|
43
|
+
|
|
44
|
+
if (fs.existsSync(fullFilePath)) {
|
|
45
|
+
// Get real path to handle potential symlinks (but don't fatal error)
|
|
46
|
+
fullFilePath = fs.realpathSync(fullFilePath)
|
|
47
|
+
|
|
48
|
+
// Only match files that are relative
|
|
49
|
+
} else if (resolvedPath.match(/\.\//)) {
|
|
50
|
+
// TODO test higher parent refs
|
|
51
|
+
const cleanName = path.basename(resolvedPath)
|
|
52
|
+
const findUpResult = findUp.sync(cleanName, { cwd: configPath })
|
|
53
|
+
if (findUpResult) {
|
|
54
|
+
fullFilePath = findUpResult
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return { fullFilePath, resolvedPath, relativePath }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports.resolveFilePathFromMatch = resolveFilePathFromMatch
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Utility to determine variable type from variable string using resolver definitions
|
|
2
|
+
|
|
3
|
+
const fallbackMap = {
|
|
4
|
+
opt: 'options',
|
|
5
|
+
file: 'file',
|
|
6
|
+
text: 'text',
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Determines the type of a variable by matching against resolver definitions
|
|
11
|
+
* @param {string} varString - The variable string (without ${})
|
|
12
|
+
* @param {Array} variableTypes - Array of variable type definitions with match regex and type
|
|
13
|
+
* @returns {string} The type field from the matching resolver, or 'dot.prop' as fallback
|
|
14
|
+
*/
|
|
15
|
+
function getVariableType(varString, variableTypes) {
|
|
16
|
+
if (!varString || !variableTypes) {
|
|
17
|
+
// if no variable types passed, try to guess the type from the variable string
|
|
18
|
+
if (!variableTypes) {
|
|
19
|
+
const unWrappedVarString = varString.replace(/^\$\{(.*)\}$/, '$1')
|
|
20
|
+
// if var:
|
|
21
|
+
if (unWrappedVarString.match(/^[a-zA-Z0-9._-]+:/)) {
|
|
22
|
+
const type = unWrappedVarString.split(':')[0]
|
|
23
|
+
return fallbackMap[type] || type
|
|
24
|
+
} else if (unWrappedVarString.match(/^[a-zA-Z0-9._-]+\(/)) {
|
|
25
|
+
const type = unWrappedVarString.split('(')[0]
|
|
26
|
+
return fallbackMap[type] || 'function'
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// console.log('getVariableType early return', { varString, hasVariableTypes: !!variableTypes })
|
|
30
|
+
return 'dot.prop'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (const variableType of variableTypes) {
|
|
34
|
+
if (!variableType.match) continue
|
|
35
|
+
|
|
36
|
+
// Handle both regex and function matchers
|
|
37
|
+
if (typeof variableType.match === 'function') {
|
|
38
|
+
if (variableType.match(varString)) {
|
|
39
|
+
return variableType.type
|
|
40
|
+
}
|
|
41
|
+
} else if (variableType.match.test) {
|
|
42
|
+
// Reset regex lastIndex to ensure clean matching
|
|
43
|
+
variableType.match.lastIndex = 0
|
|
44
|
+
if (variableType.match.test(varString)) {
|
|
45
|
+
return variableType.type
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Fallback to dot.prop for simple property references
|
|
51
|
+
// console.log('varString no match, fallback to dot.prop:', varString)
|
|
52
|
+
return 'dot.prop'
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = { getVariableType }
|
package/src/utils/logs.js
CHANGED
package/src/utils/parse.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const YAML = require('../parsers/yaml')
|
|
2
2
|
const TOML = require('../parsers/toml')
|
|
3
3
|
const INI = require('../parsers/ini')
|
|
4
|
+
const JSON5 = require('../parsers/json5')
|
|
4
5
|
const { executeTypeScriptFileSync } = require('../parsers/typescript')
|
|
5
6
|
const { executeESMFileSync } = require('../parsers/esm')
|
|
6
7
|
const cloudFormationSchema = require('./cloudformationSchema')
|
|
@@ -35,13 +36,14 @@ function parseFileContents(fileContents, fileType, filePath, varRegex, opts = {}
|
|
|
35
36
|
configObject = result.data
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
|
-
} else if (fileType.match(/\.(toml)/)) {
|
|
39
|
+
} else if (fileType.match(/\.(toml|tml)/)) {
|
|
39
40
|
configObject = TOML.parse(fileContents)
|
|
40
41
|
} else if (fileType.match(/\.(ini)/)) {
|
|
41
42
|
configObject = INI.parse(fileContents)
|
|
42
|
-
} else if (fileType.match(/\.(json)/)) {
|
|
43
|
-
configObject =
|
|
44
|
-
|
|
43
|
+
} else if (fileType.match(/\.(json|json5)/)) {
|
|
44
|
+
configObject = JSON5.parse(fileContents)
|
|
45
|
+
// TODO detect js syntax and use appropriate parser
|
|
46
|
+
} else if (fileType.match(/\.(js|cjs)/)) {
|
|
45
47
|
let jsFile
|
|
46
48
|
try {
|
|
47
49
|
jsFile = require(filePath)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
2
|
const fs = require('fs')
|
|
3
3
|
const findUp = require('find-up')
|
|
4
|
+
const JSON5 = require('../parsers/json5')
|
|
4
5
|
|
|
5
6
|
const DEBUG = false
|
|
6
7
|
const DEBUG_LOG = (message) => {
|
|
@@ -57,7 +58,7 @@ function resolveAlias(filePath, configDir) {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
// Read and parse config file
|
|
60
|
-
const config =
|
|
61
|
+
const config = JSON5.parse(fs.readFileSync(configPath, 'utf8'))
|
|
61
62
|
const { paths = {}, baseUrl = '.' } = config.compilerOptions || {}
|
|
62
63
|
|
|
63
64
|
// Extract the alias prefix and path
|
|
@@ -128,7 +129,7 @@ function getAliases(configDir) {
|
|
|
128
129
|
return { names: [], lookup: [] }
|
|
129
130
|
}
|
|
130
131
|
|
|
131
|
-
const config =
|
|
132
|
+
const config = JSON5.parse(fs.readFileSync(configPath, 'utf8'))
|
|
132
133
|
const { paths = {}, baseUrl = '.' } = config.compilerOptions || {}
|
|
133
134
|
|
|
134
135
|
const names = Object.keys(paths).map(key => key.replace('/*', ''))
|