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.
@@ -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
- varType: undefined,
59
+ variableType: undefined,
56
60
  location,
57
- value: input,
58
- fullMatch: match[0],
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].varType = matches[i].varString.match(variablesKnownTypes)[1]
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 = 'fullMatch') {
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.fullMatch.includes('__VAR_') && !currentMatch.variable.includes('__VAR_')) {
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.fullMatch = replaceAllPlaceholders(item.fullMatch, matches, 'fullMatch')
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.fullMatch = replaceAllPlaceholders(currentMatch.fullMatch, matches)
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].varType = matches[i].varString.match(variablesKnownTypes)[1]
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
- fullMatch: item,
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
- const varType = innerContent.match(variablesKnownTypes)[1]
202
- fallbackData.varType = varType
203
- // if (varType === 'self:') {
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.varType = 'dot.prop'
209
+ // fallbackData.variableType = 'dot.prop'
207
210
  // }
208
211
  }
209
212
  return fallbackData
210
213
  })
211
214
  }
212
- } else if (typeof matches[i].varType === 'undefined') {
213
- matches[i].varType = 'dot.prop'
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.varType === 'undefined') {
222
+ if (typeof m.variableType === 'undefined') {
220
223
  /*
221
224
  {
222
- varType: 'dot.prop',
225
+ variableType: 'dot.prop',
223
226
  location: 'resolvedDomainName',
224
- value: '${domainByStage.${opt:stage, ${defaultStage}}}',
225
- fullMatch: '${domainByStage.${opt:stage, ${defaultStage}}}',
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
- varType: 'dot.prop',
236
+ variableType: 'dot.prop',
234
237
  location: 'resolvedDomainName',
235
- value: '${domainByStage.${opt:stage, ${defaultStage}}}',
236
- fullMatch: '${defaultStage}',
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.varType === 'dot.prop') {
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(`Full: ${m.fullMatch}`)
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.fullMatch.match(/__REPLACED_(\d+)__/)
341
+ const indexOfReplaced = match.varMatch.match(/__REPLACED_(\d+)__/)
339
342
  if (indexOfReplaced) {
340
343
  const replacedIndex = parseInt(indexOfReplaced[1])
341
- match.fullMatch = match.fullMatch.replace(`__REPLACED_${replacedIndex}__`, matches[replacedIndex].variable)
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].fullMatch, '${simple}');
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].fullMatch, '${opt:stage, dev}');
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].fullMatch, '${stage}');
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].fullMatch, '${file(./config.${stage}.json)}');
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].fullMatch, '${defaultStage}');
86
+ assert.equal(result[0].varMatch, '${defaultStage}');
50
87
  assert.equal(result[0].variable, 'defaultStage');
51
88
  // Middle next
52
- assert.equal(result[1].fullMatch, '${opt:stage, ${defaultStage}}');
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].fullMatch, '${file(./config.${opt:stage, ${defaultStage}}.json):CREDS}');
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].fullMatch, '${name}');
65
- assert.equal(result[1].fullMatch, '${service}');
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].fullMatch, '${envOne}');
75
- assert.equal(result[1].fullMatch, '${db.${envOne}.host}');
76
- assert.equal(result[2].fullMatch, '${envTwo}');
77
- assert.equal(result[3].fullMatch, '${db.${envTwo}.port}');
78
- assert.equal(result[4].fullMatch, '${user.role}');
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].fullMatch, '${env:foo}');
203
+ assert.equal(result[0].varMatch, '${env:foo}');
120
204
  assert.equal(result[0].variable, 'env:foo');
121
- assert.equal(result[0].varType, 'env:');
205
+ assert.equal(result[0].variableType, 'env');
122
206
 
123
207
  // Check opt:stageOne with env:foo fallback
124
- assert.equal(result[1].fullMatch, '${opt:stageOne, ${env:foo}}');
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].varType, 'opt:');
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].fullMatch, '${env:foo}');
132
- assert.equal(result[1].fallbackValues[0].varType, 'env:');
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].fullMatch, '${opt:stageTwo}');
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].fullMatch, '${opt:stage, ${opt:stageOne, ${env:foo}}, ${opt:stageTwo}, "three" }');
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].fullMatch, '${opt:stageOne, ${env:foo}}');
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].fullMatch, '${opt:stageTwo}');
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].fullMatch, '"three"');
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
@@ -2,7 +2,7 @@ const { makeHeader } = require('@davidwells/box-logger')
2
2
 
3
3
  function logHeader(message) {
4
4
  console.log(makeHeader({
5
- text: message,
5
+ content: message,
6
6
  rightBorder: true,
7
7
  minWidth: 80,
8
8
  textStyle: 'bold',
@@ -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 = JSON.parse(fileContents)
44
- } else if (fileType.match(/\.(js)/)) {
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 = JSON.parse(fs.readFileSync(configPath, 'utf8'))
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 = JSON.parse(fs.readFileSync(configPath, 'utf8'))
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('/*', ''))