@hubspot/project-parsing-lib 0.11.2 → 0.12.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/package.json +1 -1
- package/src/lib/profiles.js +68 -32
package/package.json
CHANGED
package/src/lib/profiles.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { PROFILE_FILE_PREFIX } from './constants.js';
|
|
2
2
|
import { errorMessages } from '../lang/copy.js';
|
|
3
3
|
const profileFilenameRegex = new RegExp(`^${PROFILE_FILE_PREFIX}\\.([^.]+)\\.json$`);
|
|
4
|
+
// Regex to match variable placeholders like ${variableName}
|
|
4
5
|
const profileInsertRegex = /\${(.*?)}/g;
|
|
6
|
+
// Helper to check if string is a single variable reference (e.g., "${port}" but not "http://${port}")
|
|
7
|
+
// Returns the variable key if it matches, null otherwise
|
|
8
|
+
function isSingleVariableReference(str) {
|
|
9
|
+
const match = str.match(/^\${([^}]+)}$/);
|
|
10
|
+
return match ? match[1] : null;
|
|
11
|
+
}
|
|
5
12
|
export function getIsProfileFile(filename) {
|
|
6
13
|
return profileFilenameRegex.test(filename);
|
|
7
14
|
}
|
|
@@ -20,56 +27,85 @@ export function getHsProfileVariables(hsProfileContents) {
|
|
|
20
27
|
}
|
|
21
28
|
export function validateProfileVariables(profileVariables, filename) {
|
|
22
29
|
const errors = [];
|
|
23
|
-
Object.entries(profileVariables)
|
|
30
|
+
for (const [variableKey, variableValue] of Object.entries(profileVariables)) {
|
|
24
31
|
try {
|
|
25
|
-
|
|
32
|
+
const variableType = typeof variableValue;
|
|
33
|
+
validateProfileVariable(variableType, variableKey, filename);
|
|
26
34
|
}
|
|
27
|
-
catch (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
errors.push(`${e}`);
|
|
33
|
-
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
37
|
+
errors.push(errorMessage);
|
|
34
38
|
}
|
|
35
|
-
}
|
|
39
|
+
}
|
|
36
40
|
return {
|
|
37
41
|
success: errors.length === 0,
|
|
38
42
|
errors,
|
|
39
43
|
};
|
|
40
44
|
}
|
|
41
|
-
function validateProfileVariable(
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
function validateProfileVariable(variableType, variableKey, filename) {
|
|
46
|
+
const allowedTypes = ['string', 'number', 'boolean'];
|
|
47
|
+
if (variableType === 'undefined') {
|
|
48
|
+
throw new Error(errorMessages.profile.missingValue(variableKey, filename));
|
|
44
49
|
}
|
|
45
|
-
if (!
|
|
46
|
-
throw new Error(errorMessages.profile.invalidValue(
|
|
50
|
+
if (!allowedTypes.includes(variableType)) {
|
|
51
|
+
throw new Error(errorMessages.profile.invalidValue(variableKey));
|
|
47
52
|
}
|
|
48
53
|
}
|
|
49
54
|
function interpolate(file, template, profileVariables) {
|
|
50
|
-
return template.replace(profileInsertRegex, (
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
return template.replace(profileInsertRegex, (_fullMatch, variableKey) => {
|
|
56
|
+
const variableValue = profileVariables[variableKey];
|
|
57
|
+
const variableType = typeof variableValue;
|
|
58
|
+
validateProfileVariable(variableType, variableKey, file);
|
|
59
|
+
return String(variableValue);
|
|
54
60
|
});
|
|
55
61
|
}
|
|
62
|
+
function replaceVariablesInValue(value, file, profileVariables) {
|
|
63
|
+
// Handle string values with variable substitution
|
|
64
|
+
if (typeof value === 'string') {
|
|
65
|
+
// If the entire value is a single variable (e.g., "${port}"),
|
|
66
|
+
// return the typed value to preserve number/boolean types
|
|
67
|
+
const variableKey = isSingleVariableReference(value);
|
|
68
|
+
if (variableKey) {
|
|
69
|
+
const variableValue = profileVariables[variableKey];
|
|
70
|
+
const variableType = typeof variableValue;
|
|
71
|
+
validateProfileVariable(variableType, variableKey, file);
|
|
72
|
+
return variableValue;
|
|
73
|
+
}
|
|
74
|
+
// Otherwise, perform string interpolation (no-op if no variables found)
|
|
75
|
+
// For strings with multiple variables (e.g., "http://${host}:${port}"),
|
|
76
|
+
// this converts all values to strings
|
|
77
|
+
return interpolate(file, value, profileVariables);
|
|
78
|
+
}
|
|
79
|
+
// Recursively handle arrays
|
|
80
|
+
if (Array.isArray(value)) {
|
|
81
|
+
return value.map(item => replaceVariablesInValue(item, file, profileVariables));
|
|
82
|
+
}
|
|
83
|
+
// Recursively handle objects
|
|
84
|
+
if (value !== null && typeof value === 'object') {
|
|
85
|
+
const result = {};
|
|
86
|
+
for (const [key, val] of Object.entries(value)) {
|
|
87
|
+
result[key] = replaceVariablesInValue(val, file, profileVariables);
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
// Return primitive values as-is
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
56
94
|
export function applyHsProfileVariables(fileParseResults, hsProfileContents) {
|
|
57
95
|
const profileVariables = getHsProfileVariables(hsProfileContents);
|
|
58
96
|
return fileParseResults.map(fileParseResult => {
|
|
59
97
|
const { content, file } = fileParseResult;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (interpolatedConfig) {
|
|
64
|
-
return {
|
|
65
|
-
...fileParseResult,
|
|
66
|
-
content: {
|
|
67
|
-
...content,
|
|
68
|
-
config: interpolatedConfig,
|
|
69
|
-
},
|
|
70
|
-
};
|
|
71
|
-
}
|
|
98
|
+
// Only process files that have content with a config section
|
|
99
|
+
if (!content?.config) {
|
|
100
|
+
return fileParseResult;
|
|
72
101
|
}
|
|
73
|
-
|
|
102
|
+
const configWithVariablesReplaced = replaceVariablesInValue(content.config, file, profileVariables);
|
|
103
|
+
return {
|
|
104
|
+
...fileParseResult,
|
|
105
|
+
content: {
|
|
106
|
+
...content,
|
|
107
|
+
config: configWithVariablesReplaced,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
74
110
|
});
|
|
75
111
|
}
|