@extrahorizon/exh-cli 1.12.0-dev-152-fc8569c → 1.12.0-dev-154-7343f7f

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.
@@ -44,6 +44,10 @@
44
44
  "outputs": {
45
45
  "type": "object",
46
46
  "additionalProperties": { "type": "string" }
47
+ },
48
+ "variables": {
49
+ "type": "object",
50
+ "additionalProperties": { "type": "string" }
47
51
  }
48
52
  }
49
53
  },
@@ -17,10 +17,14 @@ async function buildTemplate(name, templateFilesByName, callChain = []) {
17
17
  if (callChain.includes(name)) {
18
18
  throw new Error(`Circular extension detected. ${renderCallChain(newCallChain)}`);
19
19
  }
20
- const templateFile = templateFilesByName[name];
21
- if (!templateFile) {
20
+ if (!templateFilesByName[name]) {
22
21
  throw new Error(`Template file dependency ${name} not found!`);
23
22
  }
23
+ const { variables, ...templateFile } = templateFilesByName[name];
24
+ if (variables && templateFile.outputs) {
25
+ templateFile.outputs = replaceVariables(templateFile.outputs, variables);
26
+ templateFilesByName[name] = templateFile;
27
+ }
24
28
  let templateConfig;
25
29
  if (templateFile.extends_template) {
26
30
  templateConfig = await extendV1Template(name, templateFilesByName, newCallChain);
@@ -31,8 +35,8 @@ async function buildTemplate(name, templateFilesByName, callChain = []) {
31
35
  else {
32
36
  templateConfig = { ...templateFile, name };
33
37
  }
34
- if (!(0, utils_1.isV1Template)(templateConfig) && Object.keys(templateConfig.outputs || {}).length < 1) {
35
- throw new Error(`Template '${name}' must have at least one output defined!`);
38
+ if (!(0, utils_1.isV1Template)(templateConfig)) {
39
+ validateV2Template(templateConfig);
36
40
  }
37
41
  return templateConfig;
38
42
  }
@@ -94,8 +98,55 @@ async function extendV2Template(name, templateFilesByName, callChain) {
94
98
  outputs: newOutputs,
95
99
  };
96
100
  }
101
+ function replaceVariables(outputs, variables) {
102
+ const replaced = {};
103
+ const resolvedVariables = {};
104
+ for (const [varName, varValue] of Object.entries(variables)) {
105
+ resolvedVariables[varName] = resolveVariableValue(varValue);
106
+ }
107
+ for (const [key, value] of Object.entries(outputs)) {
108
+ let replacedValue = value;
109
+ for (const [varName, varValue] of Object.entries(resolvedVariables)) {
110
+ replacedValue = replacedValue
111
+ .replaceAll(`\${${varName}}`, varValue)
112
+ .replaceAll(`$${varName}`, varValue);
113
+ }
114
+ replaced[key] = replacedValue;
115
+ }
116
+ return replaced;
117
+ }
118
+ function resolveVariableValue(value) {
119
+ if (!value.startsWith('$')) {
120
+ return value;
121
+ }
122
+ const envVar = value.startsWith('${') && value.endsWith('}') ?
123
+ value.slice(2, -1) :
124
+ value.slice(1);
125
+ const validEnvPattern = /^[A-Z][A-Z0-9_]*$/;
126
+ if (!validEnvPattern.test(envVar)) {
127
+ throw new Error(`Invalid environment variable name ${envVar}. Environment variable names must contain only uppercase letters, numbers, and underscores and must start with a letter.`);
128
+ }
129
+ const resolved = process.env[envVar];
130
+ if (resolved === undefined) {
131
+ throw new Error(`Variable ${envVar} not found in environment`);
132
+ }
133
+ return resolved;
134
+ }
135
+ function validateV2Template(template) {
136
+ if (!/^[A-Za-z][A-Za-z0-9_-]{0,49}$/.test(template.name)) {
137
+ throw new Error(`Template name '${template.name}' is invalid. Template names must start with a letter and can only contain letters, numbers, underscores and hyphens, and be at most 50 characters long.`);
138
+ }
139
+ if (Object.keys(template.outputs || {}).length < 1) {
140
+ throw new Error(`Template '${template.name}' must have at least one output defined.`);
141
+ }
142
+ for (const outputName of Object.keys(template.outputs)) {
143
+ if (!/^[A-Za-z][A-Za-z0-9_-]{0,49}$/.test(outputName)) {
144
+ throw new Error(`Output name '${outputName}' is invalid. Output names must start with a letter and can only contain letters, numbers, underscores and hyphens, and be at most 50 characters long.`);
145
+ }
146
+ }
147
+ }
97
148
  function v1ExtendingV2Error(extendsTemplate, callChain) {
98
- return new Error(`You cannot extend a v2 template ('${extendsTemplate}') in a v1 template!` +
149
+ return new Error(`You cannot extend a v2 template ('${extendsTemplate}') in a v1 template.` +
99
150
  `In ${renderCallChain(callChain)}`);
100
151
  }
101
152
  function v1VariableNotFoundError(variableName, extendsTemplate, callChain) {
@@ -104,7 +155,7 @@ function v1VariableNotFoundError(variableName, extendsTemplate, callChain) {
104
155
  `in ${renderCallChain(callChain)}`);
105
156
  }
106
157
  function v2ExtendingV1Error(extendsTemplate, callChain) {
107
- return new Error(`You cannot extend a v1 template ('${extendsTemplate}') in a v2 template!` +
158
+ return new Error(`You cannot extend a v1 template ('${extendsTemplate}') in a v2 template.` +
108
159
  `In ${renderCallChain(callChain)}`);
109
160
  }
110
161
  function v2VariableNotFoundError(variableName, extendsTemplate, callChain) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@extrahorizon/exh-cli",
3
- "version": "1.12.0-dev-152-fc8569c",
3
+ "version": "1.12.0-dev-154-7343f7f",
4
4
  "main": "build/index.js",
5
5
  "exports": "./build/index.js",
6
6
  "license": "MIT",