@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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/lib/profiles.js +68 -32
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/project-parsing-lib",
3
- "version": "0.11.2",
3
+ "version": "0.12.0",
4
4
  "description": "Parsing library for converting projects directory structures to their intermediate representation",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -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).forEach(([key, value]) => {
30
+ for (const [variableKey, variableValue] of Object.entries(profileVariables)) {
24
31
  try {
25
- validateProfileVariable(typeof value, key, filename);
32
+ const variableType = typeof variableValue;
33
+ validateProfileVariable(variableType, variableKey, filename);
26
34
  }
27
- catch (e) {
28
- if (e instanceof Error) {
29
- errors.push(e.message);
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(profileVariableType, key, file) {
42
- if (profileVariableType === 'undefined') {
43
- throw new Error(errorMessages.profile.missingValue(key, file));
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 (!['string', 'number', 'boolean'].includes(profileVariableType)) {
46
- throw new Error(errorMessages.profile.invalidValue(key));
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, (__match, key) => {
51
- const profileVariableType = typeof profileVariables[key];
52
- validateProfileVariable(profileVariableType, key, file);
53
- return String(profileVariables[key]);
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
- if (content && content.config) {
61
- let interpolatedConfig = content.config;
62
- interpolatedConfig = JSON.parse(interpolate(file, JSON.stringify(content.config), profileVariables));
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
- return fileParseResult;
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
  }