timonel 2.14.0-beta.1 → 3.0.0-beta.1
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/CHANGELOG.md +185 -0
- package/README.md +133 -9
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/lib/helmChartWriter.js +5 -3
- package/dist/lib/rutter.js +10 -8
- package/dist/lib/utils/envVarsLoader.d.ts +19 -0
- package/dist/lib/utils/envVarsLoader.js +57 -0
- package/dist/lib/utils/helmControlStructures.d.ts +4 -5
- package/dist/lib/utils/helmControlStructures.js +12 -0
- package/dist/lib/utils/helmYamlSerializer.d.ts +2 -0
- package/dist/lib/utils/helmYamlSerializer.js +491 -15
- package/dist/lib/utils/valuesRef.d.ts +102 -0
- package/dist/lib/utils/valuesRef.js +264 -0
- package/package.json +1 -1
|
@@ -1,5 +1,33 @@
|
|
|
1
|
-
import { Document, Scalar, isMap, isScalar, visit } from 'yaml';
|
|
1
|
+
import { Document, Scalar, isMap, isScalar, visit, Pair } from 'yaml';
|
|
2
2
|
import { isHelmConstruct, isHelmExpression, createHelmExpression, } from './helmControlStructures.js';
|
|
3
|
+
import { isHelmValue, isHelmFieldConditional, isHelmRange, isHelmWith, } from './valuesRef.js';
|
|
4
|
+
const FIELD_CONDITIONAL = '__FIELD_CONDITIONAL__:';
|
|
5
|
+
const FIELD_WITH = '__FIELD_WITH__:';
|
|
6
|
+
const FIELD_WITH_MARKER = '__FIELD_WITH_MARKER__:';
|
|
7
|
+
function simpleHelmYaml(obj) {
|
|
8
|
+
function convertToPlain(value) {
|
|
9
|
+
if (isHelmValue(value)) {
|
|
10
|
+
return `{{ ${value.__path} }}`;
|
|
11
|
+
}
|
|
12
|
+
if (isHelmExpression(value)) {
|
|
13
|
+
return value.value;
|
|
14
|
+
}
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
return value.map(convertToPlain);
|
|
17
|
+
}
|
|
18
|
+
if (value !== null && typeof value === 'object') {
|
|
19
|
+
const result = {};
|
|
20
|
+
for (const [k, v] of Object.entries(value)) {
|
|
21
|
+
result[k] = convertToPlain(v);
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
const plain = convertToPlain(obj);
|
|
28
|
+
const doc = new Document(plain);
|
|
29
|
+
return doc.toString({ lineWidth: 0 });
|
|
30
|
+
}
|
|
3
31
|
function serializeElseIfChain(elseContent, openTag, closeTag) {
|
|
4
32
|
let result = '';
|
|
5
33
|
let currentElse = elseContent;
|
|
@@ -14,6 +42,9 @@ function serializeElseIfChain(elseContent, openTag, closeTag) {
|
|
|
14
42
|
return { result, remainingElse: currentElse };
|
|
15
43
|
}
|
|
16
44
|
function serializeHelmConstruct(construct) {
|
|
45
|
+
if (construct.type === 'fieldConditional') {
|
|
46
|
+
throw new Error('fieldConditional should not be serialized directly. It must be handled in preprocessHelmConstructs.');
|
|
47
|
+
}
|
|
17
48
|
const trimLeft = construct.options?.trimLeft ?? true;
|
|
18
49
|
const trimRight = construct.options?.trimRight ?? true;
|
|
19
50
|
const openTag = `{{${trimLeft ? '-' : ''} `;
|
|
@@ -21,22 +52,117 @@ function serializeHelmConstruct(construct) {
|
|
|
21
52
|
switch (construct.type) {
|
|
22
53
|
case 'if': {
|
|
23
54
|
const data = construct.data;
|
|
24
|
-
|
|
55
|
+
const isInline = construct.options?.inline ?? false;
|
|
56
|
+
const newline = isInline ? '' : '\n';
|
|
57
|
+
const space = isInline ? ' ' : '';
|
|
58
|
+
let result = `${openTag}if ${data.condition}${closeTag}${newline}`;
|
|
25
59
|
result += serializeHelmContent(data.then);
|
|
26
60
|
if (data.else !== undefined) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
61
|
+
if (isInline) {
|
|
62
|
+
result += `${space}${openTag}else${closeTag}${space}`;
|
|
63
|
+
result += serializeHelmContent(data.else);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
const { result: elseIfResult, remainingElse } = serializeElseIfChain(data.else, openTag, closeTag);
|
|
67
|
+
result += elseIfResult;
|
|
68
|
+
if (remainingElse !== undefined) {
|
|
69
|
+
result += `\n${openTag}else${closeTag}\n`;
|
|
70
|
+
result += serializeHelmContent(remainingElse);
|
|
71
|
+
}
|
|
32
72
|
}
|
|
33
73
|
}
|
|
34
|
-
result +=
|
|
74
|
+
result += `${newline}${openTag}end${closeTag}`;
|
|
35
75
|
return result;
|
|
36
76
|
}
|
|
37
77
|
case 'fragment': {
|
|
38
78
|
const data = construct.data;
|
|
39
|
-
|
|
79
|
+
const strings = [];
|
|
80
|
+
const objects = [];
|
|
81
|
+
for (const item of data) {
|
|
82
|
+
if (typeof item === 'object' &&
|
|
83
|
+
item !== null &&
|
|
84
|
+
!Array.isArray(item) &&
|
|
85
|
+
!isHelmConstruct(item) &&
|
|
86
|
+
!isHelmExpression(item)) {
|
|
87
|
+
objects.push(item);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
const serialized = serializeHelmContent(item);
|
|
91
|
+
if (serialized.trim().length > 0) {
|
|
92
|
+
strings.push(serialized);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (strings.length > 0 && objects.length > 0) {
|
|
97
|
+
const combinedObject = objects.reduce((acc, obj) => {
|
|
98
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
99
|
+
return { ...acc, ...obj };
|
|
100
|
+
}
|
|
101
|
+
return acc;
|
|
102
|
+
}, {});
|
|
103
|
+
const preprocessed = preprocessHelmConstructs(combinedObject);
|
|
104
|
+
const doc = new Document(preprocessed);
|
|
105
|
+
visit(doc, (_key, node) => {
|
|
106
|
+
if (isMap(node)) {
|
|
107
|
+
const isHelmExpr = node.items.some((pair) => isScalar(pair.key) &&
|
|
108
|
+
pair.key.value === '__helmExpression' &&
|
|
109
|
+
isScalar(pair.value) &&
|
|
110
|
+
pair.value.value === true);
|
|
111
|
+
if (isHelmExpr) {
|
|
112
|
+
const valuePair = node.items.find((pair) => isScalar(pair.key) && pair.key.value === 'value');
|
|
113
|
+
if (valuePair && isScalar(valuePair.value)) {
|
|
114
|
+
const value = String(valuePair.value.value);
|
|
115
|
+
const scalar = new Scalar(value);
|
|
116
|
+
if (value.trim().startsWith('{{') && value.includes('\n')) {
|
|
117
|
+
scalar.type = 'BLOCK_LITERAL';
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
scalar.type = 'QUOTE_DOUBLE';
|
|
121
|
+
}
|
|
122
|
+
return scalar;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return undefined;
|
|
127
|
+
});
|
|
128
|
+
let objectYaml = doc.toString({ lineWidth: 0 }).trim();
|
|
129
|
+
return [...strings, objectYaml].filter((s) => s.trim().length > 0).join('\n');
|
|
130
|
+
}
|
|
131
|
+
if (objects.length > 0) {
|
|
132
|
+
const combinedObject = objects.reduce((acc, obj) => {
|
|
133
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
134
|
+
return { ...acc, ...obj };
|
|
135
|
+
}
|
|
136
|
+
return acc;
|
|
137
|
+
}, {});
|
|
138
|
+
const preprocessed = preprocessHelmConstructs(combinedObject);
|
|
139
|
+
const doc = new Document(preprocessed);
|
|
140
|
+
visit(doc, (_key, node) => {
|
|
141
|
+
if (isMap(node)) {
|
|
142
|
+
const isHelmExpr = node.items.some((pair) => isScalar(pair.key) &&
|
|
143
|
+
pair.key.value === '__helmExpression' &&
|
|
144
|
+
isScalar(pair.value) &&
|
|
145
|
+
pair.value.value === true);
|
|
146
|
+
if (isHelmExpr) {
|
|
147
|
+
const valuePair = node.items.find((pair) => isScalar(pair.key) && pair.key.value === 'value');
|
|
148
|
+
if (valuePair && isScalar(valuePair.value)) {
|
|
149
|
+
const value = String(valuePair.value.value);
|
|
150
|
+
const scalar = new Scalar(value);
|
|
151
|
+
if (value.trim().startsWith('{{') && value.includes('\n')) {
|
|
152
|
+
scalar.type = 'BLOCK_LITERAL';
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
scalar.type = 'QUOTE_DOUBLE';
|
|
156
|
+
}
|
|
157
|
+
return scalar;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return undefined;
|
|
162
|
+
});
|
|
163
|
+
return doc.toString({ lineWidth: 0 }).trim();
|
|
164
|
+
}
|
|
165
|
+
return strings.filter((s) => s.trim().length > 0).join('\n');
|
|
40
166
|
}
|
|
41
167
|
case 'range': {
|
|
42
168
|
const data = construct.data;
|
|
@@ -58,7 +184,7 @@ function serializeHelmConstruct(construct) {
|
|
|
58
184
|
if (data.pipe) {
|
|
59
185
|
result += ` | ${data.pipe}`;
|
|
60
186
|
}
|
|
61
|
-
result +=
|
|
187
|
+
result += closeTag;
|
|
62
188
|
return result;
|
|
63
189
|
}
|
|
64
190
|
case 'define': {
|
|
@@ -104,15 +230,47 @@ function serializeHelmContent(content) {
|
|
|
104
230
|
return String(content);
|
|
105
231
|
}
|
|
106
232
|
if (typeof content === 'object') {
|
|
107
|
-
const
|
|
233
|
+
const preprocessed = preprocessHelmConstructs(content);
|
|
234
|
+
const doc = new Document(preprocessed);
|
|
108
235
|
let yaml = doc.toString({ lineWidth: 0 });
|
|
109
236
|
yaml = yaml.trim();
|
|
110
237
|
return yaml;
|
|
111
238
|
}
|
|
112
239
|
return '';
|
|
113
240
|
}
|
|
114
|
-
function preprocessHelmConstructs(obj) {
|
|
241
|
+
export function preprocessHelmConstructs(obj) {
|
|
242
|
+
if (isHelmValue(obj)) {
|
|
243
|
+
const value = obj;
|
|
244
|
+
return createHelmExpression(`{{ ${value.__path} }}`);
|
|
245
|
+
}
|
|
246
|
+
if (isHelmFieldConditional(obj)) {
|
|
247
|
+
return obj;
|
|
248
|
+
}
|
|
249
|
+
if (isHelmRange(obj)) {
|
|
250
|
+
const range = obj;
|
|
251
|
+
const sourcePath = range.source.__path;
|
|
252
|
+
const itemProxy = { __path: '$item' };
|
|
253
|
+
const indexProxy = { __path: '$index' };
|
|
254
|
+
const content = range.callback(itemProxy, indexProxy);
|
|
255
|
+
const processedContent = preprocessHelmConstructs(content);
|
|
256
|
+
const contentDoc = new Document(processedContent);
|
|
257
|
+
const contentStr = contentDoc.toString({ lineWidth: 0 }).trim();
|
|
258
|
+
return createHelmExpression(`{{- range ${sourcePath} }}\n${contentStr}\n{{- end }}`);
|
|
259
|
+
}
|
|
260
|
+
if (isHelmWith(obj)) {
|
|
261
|
+
return obj;
|
|
262
|
+
}
|
|
115
263
|
if (isHelmConstruct(obj)) {
|
|
264
|
+
if (obj.type === 'fieldConditional') {
|
|
265
|
+
return obj;
|
|
266
|
+
}
|
|
267
|
+
if (obj.type === 'if') {
|
|
268
|
+
const ifData = obj.data;
|
|
269
|
+
const isInline = obj.options?.inline ?? false;
|
|
270
|
+
if (!isInline && (ifData.else === undefined || !('else' in ifData))) {
|
|
271
|
+
return obj;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
116
274
|
const helmTemplate = serializeHelmConstruct(obj);
|
|
117
275
|
return createHelmExpression(helmTemplate);
|
|
118
276
|
}
|
|
@@ -125,6 +283,111 @@ function preprocessHelmConstructs(obj) {
|
|
|
125
283
|
}
|
|
126
284
|
const result = {};
|
|
127
285
|
for (const [key, value] of Object.entries(obj)) {
|
|
286
|
+
if (isHelmWith(value)) {
|
|
287
|
+
const withBlock = value;
|
|
288
|
+
const source = withBlock.source;
|
|
289
|
+
const sourcePath = source?.__path;
|
|
290
|
+
if (!sourcePath) {
|
|
291
|
+
throw new Error('HelmWith source must have a valid __path');
|
|
292
|
+
}
|
|
293
|
+
const ctxProxy = { __path: '.' };
|
|
294
|
+
const content = withBlock.callback(ctxProxy);
|
|
295
|
+
let contentStr;
|
|
296
|
+
if (isHelmExpression(content)) {
|
|
297
|
+
contentStr = content.value;
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
const processedContent = preprocessHelmConstructs(content);
|
|
301
|
+
const contentDoc = new Document(processedContent);
|
|
302
|
+
contentStr = contentDoc.toString({ lineWidth: 0 }).trim();
|
|
303
|
+
}
|
|
304
|
+
const template = `{{- with ${sourcePath} }}\n${key}:\n ${contentStr}\n{{- end }}`;
|
|
305
|
+
const marker = `__FIELD_WITH__:${key}:${template}`;
|
|
306
|
+
result[`__fieldWithTemplate_${key}`] = createHelmExpression(marker);
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
if (isHelmConstruct(value) && value.type === 'if') {
|
|
310
|
+
const ifData = value.data;
|
|
311
|
+
const isInline = value.options?.inline ?? false;
|
|
312
|
+
if (!isInline && (ifData.else === undefined || !('else' in ifData))) {
|
|
313
|
+
const preprocessedThen = preprocessHelmConstructs(ifData.then);
|
|
314
|
+
let thenValueStr = '';
|
|
315
|
+
let isMultilineObject = false;
|
|
316
|
+
if (isHelmExpression(preprocessedThen)) {
|
|
317
|
+
thenValueStr = preprocessedThen.value.trim();
|
|
318
|
+
isMultilineObject = thenValueStr.includes('\n');
|
|
319
|
+
}
|
|
320
|
+
else if (typeof preprocessedThen === 'string') {
|
|
321
|
+
thenValueStr = preprocessedThen.trim();
|
|
322
|
+
isMultilineObject = thenValueStr.includes('\n');
|
|
323
|
+
}
|
|
324
|
+
else if (Array.isArray(preprocessedThen)) {
|
|
325
|
+
const tempDoc = new Document(preprocessedThen);
|
|
326
|
+
thenValueStr = tempDoc.toString({ lineWidth: 0 }).trim();
|
|
327
|
+
isMultilineObject = true;
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
const tempDoc = new Document(preprocessedThen);
|
|
331
|
+
thenValueStr = tempDoc.toString({ lineWidth: 0 }).trim();
|
|
332
|
+
isMultilineObject = thenValueStr.includes('\n');
|
|
333
|
+
}
|
|
334
|
+
const constructOptions = isHelmConstruct(value) ? value.options : undefined;
|
|
335
|
+
const trimLeft = constructOptions?.trimLeft ?? true;
|
|
336
|
+
const openTag = `{{${trimLeft ? '-' : ''} `;
|
|
337
|
+
const closeTag = ` ${(constructOptions?.trimRight ?? true) ? '-' : ''}}}`;
|
|
338
|
+
let formattedContent;
|
|
339
|
+
if (isMultilineObject) {
|
|
340
|
+
const indentedValue = thenValueStr
|
|
341
|
+
.split('\n')
|
|
342
|
+
.map((line) => ' ' + line)
|
|
343
|
+
.join('\n');
|
|
344
|
+
formattedContent = `${key}:\n${indentedValue}`;
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
formattedContent = `${key}: ${thenValueStr}`;
|
|
348
|
+
}
|
|
349
|
+
const conditionalTemplate = `${openTag}if ${ifData.condition}${closeTag}\n ${formattedContent}\n${openTag}end${closeTag}`;
|
|
350
|
+
result[key] = createHelmExpression(`__FIELD_CONDITIONAL__:${key}:${conditionalTemplate}`);
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (isHelmFieldConditional(value)) {
|
|
355
|
+
const fieldCond = value;
|
|
356
|
+
const condition = fieldCond.condition.__condition;
|
|
357
|
+
const thenValueStr = simpleHelmYaml(fieldCond.thenValue).trim();
|
|
358
|
+
const isMultilineObject = thenValueStr.includes('\n');
|
|
359
|
+
const startsWithDash = thenValueStr.trimStart().startsWith('-');
|
|
360
|
+
let formattedContent;
|
|
361
|
+
if (isMultilineObject || startsWithDash) {
|
|
362
|
+
const lines = thenValueStr.split('\n');
|
|
363
|
+
const indentedValue = lines.map((line) => ' ' + line).join('\n');
|
|
364
|
+
formattedContent = `${key}:\n${indentedValue}`;
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
formattedContent = `${key}: ${thenValueStr}`;
|
|
368
|
+
}
|
|
369
|
+
const conditionalTemplate = `{{- if ${condition} }}\n${formattedContent}\n{{- end }}`;
|
|
370
|
+
result[key] = createHelmExpression(`__FIELD_CONDITIONAL__:${key}:${conditionalTemplate}`);
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
if (isHelmConstruct(value) && value.type === 'fieldConditional') {
|
|
374
|
+
const fieldData = value.data;
|
|
375
|
+
const preprocessedThen = preprocessHelmConstructs(fieldData.then);
|
|
376
|
+
let thenValueStr = '';
|
|
377
|
+
if (isHelmExpression(preprocessedThen)) {
|
|
378
|
+
thenValueStr = preprocessedThen.value;
|
|
379
|
+
}
|
|
380
|
+
else if (typeof preprocessedThen === 'string') {
|
|
381
|
+
thenValueStr = preprocessedThen;
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
const tempDoc = new Document(preprocessedThen);
|
|
385
|
+
thenValueStr = tempDoc.toString({ lineWidth: 0 }).trim();
|
|
386
|
+
}
|
|
387
|
+
const conditionalTemplate = `{{- if ${fieldData.condition} }}\n ${fieldData.fieldKey}: ${thenValueStr}\n{{- end }}`;
|
|
388
|
+
result[`__fieldConditionalTemplate_${fieldData.fieldKey}`] = conditionalTemplate;
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
128
391
|
result[key] = preprocessHelmConstructs(value);
|
|
129
392
|
}
|
|
130
393
|
return result;
|
|
@@ -144,10 +407,22 @@ export function dumpHelmAwareYaml(obj, options = {}) {
|
|
|
144
407
|
const valuePair = node.items.find((pair) => isScalar(pair.key) && pair.key.value === 'value');
|
|
145
408
|
if (valuePair && isScalar(valuePair.value)) {
|
|
146
409
|
const value = String(valuePair.value.value);
|
|
410
|
+
if (value.startsWith(FIELD_CONDITIONAL) ||
|
|
411
|
+
value.startsWith(FIELD_WITH) ||
|
|
412
|
+
value.startsWith(FIELD_WITH_MARKER)) {
|
|
413
|
+
const scalar = new Scalar(value);
|
|
414
|
+
scalar.type = 'BLOCK_LITERAL';
|
|
415
|
+
return scalar;
|
|
416
|
+
}
|
|
147
417
|
const scalar = new Scalar(value);
|
|
148
|
-
|
|
418
|
+
const isMultilineTemplate = value.includes('\n') && value.trim().startsWith('{{');
|
|
419
|
+
const isWithOrRange = value.trim().startsWith('{{- with ') || value.trim().startsWith('{{- range ');
|
|
420
|
+
if (isMultilineTemplate && !isWithOrRange) {
|
|
149
421
|
scalar.type = 'BLOCK_LITERAL';
|
|
150
422
|
}
|
|
423
|
+
else if (isWithOrRange) {
|
|
424
|
+
scalar.type = 'PLAIN';
|
|
425
|
+
}
|
|
151
426
|
else {
|
|
152
427
|
scalar.type = 'QUOTE_DOUBLE';
|
|
153
428
|
}
|
|
@@ -157,13 +432,214 @@ export function dumpHelmAwareYaml(obj, options = {}) {
|
|
|
157
432
|
}
|
|
158
433
|
return undefined;
|
|
159
434
|
});
|
|
160
|
-
const toStringOptions = {};
|
|
435
|
+
const toStringOptions = { lineWidth: 0 };
|
|
161
436
|
if (options.lineWidth !== undefined) {
|
|
162
437
|
toStringOptions.lineWidth = options.lineWidth;
|
|
163
438
|
}
|
|
439
|
+
const templatePairs = [];
|
|
440
|
+
visit(doc, (_key, node) => {
|
|
441
|
+
if (isMap(node)) {
|
|
442
|
+
for (let i = 0; i < node.items.length; i++) {
|
|
443
|
+
const pair = node.items[i];
|
|
444
|
+
if (pair && isScalar(pair.key)) {
|
|
445
|
+
const keyStr = String(pair.key.value);
|
|
446
|
+
if (keyStr.startsWith('__fieldConditionalTemplate_') && isScalar(pair.value)) {
|
|
447
|
+
const template = String(pair.value.value);
|
|
448
|
+
const fieldKey = keyStr.replace('__fieldConditionalTemplate_', '');
|
|
449
|
+
templatePairs.push({ parent: node, index: i, template, fieldKey });
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return undefined;
|
|
455
|
+
});
|
|
456
|
+
for (let i = templatePairs.length - 1; i >= 0; i--) {
|
|
457
|
+
const templatePair = templatePairs[i];
|
|
458
|
+
if (!templatePair)
|
|
459
|
+
continue;
|
|
460
|
+
const { parent, index, template } = templatePair;
|
|
461
|
+
parent.items.splice(index, 1);
|
|
462
|
+
const tempDoc = new Document({ __temp: template });
|
|
463
|
+
const _tempYaml = tempDoc.toString({ lineWidth: 0 });
|
|
464
|
+
const templateScalar = new Scalar(template);
|
|
465
|
+
templateScalar.type = 'BLOCK_LITERAL';
|
|
466
|
+
const specialKeyScalar = new Scalar('__fieldConditionalContent');
|
|
467
|
+
const templatePairObj = new Pair(specialKeyScalar, templateScalar);
|
|
468
|
+
parent.items.splice(index, 0, templatePairObj);
|
|
469
|
+
}
|
|
164
470
|
let result = doc.toString(toStringOptions);
|
|
471
|
+
while (result.includes('__fieldConditionalContent:')) {
|
|
472
|
+
const markerIndex = result.indexOf('__fieldConditionalContent:');
|
|
473
|
+
if (markerIndex === -1)
|
|
474
|
+
break;
|
|
475
|
+
const lineStart = result.lastIndexOf('\n', markerIndex);
|
|
476
|
+
const lineBeforeMarker = result.substring(lineStart + 1, markerIndex);
|
|
477
|
+
const indentMatch = lineBeforeMarker.match(/^(\s*)/);
|
|
478
|
+
const baseIndent = indentMatch && indentMatch[1] ? indentMatch[1] : '';
|
|
479
|
+
const afterMarker = result.substring(markerIndex);
|
|
480
|
+
let pipeLineMatch = afterMarker.match(/^__fieldConditionalContent:\s*(\|-?)\s*\n(\s*)/);
|
|
481
|
+
let pipeOnSameLine = true;
|
|
482
|
+
if (!pipeLineMatch) {
|
|
483
|
+
pipeLineMatch = afterMarker.match(/^__fieldConditionalContent:\s*\n(\s*)(\|-?)\s*\n/);
|
|
484
|
+
pipeOnSameLine = false;
|
|
485
|
+
}
|
|
486
|
+
if (!pipeLineMatch)
|
|
487
|
+
break;
|
|
488
|
+
const pipeChar = pipeOnSameLine ? pipeLineMatch[1] : pipeLineMatch[2];
|
|
489
|
+
const pipeIndent = pipeOnSameLine ? pipeLineMatch[2] || '' : pipeLineMatch[1] || '';
|
|
490
|
+
if (!pipeChar)
|
|
491
|
+
break;
|
|
492
|
+
const pipeIndex = markerIndex + pipeLineMatch[0].length;
|
|
493
|
+
const afterPipe = result.substring(pipeIndex);
|
|
494
|
+
const templateStartMatch = afterPipe.match(/^\n(\s*)(\{\{-)/);
|
|
495
|
+
if (!templateStartMatch) {
|
|
496
|
+
const templateStartMatchNoNewline = afterPipe.match(/^(\s*)(\{\{-)/);
|
|
497
|
+
if (!templateStartMatchNoNewline)
|
|
498
|
+
break;
|
|
499
|
+
const _templateIndent = templateStartMatchNoNewline[1] || '';
|
|
500
|
+
const templateStart = templateStartMatchNoNewline[2];
|
|
501
|
+
if (!templateStart)
|
|
502
|
+
break;
|
|
503
|
+
const templateStartIndex = pipeIndex + templateStartMatchNoNewline[0].length;
|
|
504
|
+
const templateSection = result.substring(templateStartIndex);
|
|
505
|
+
const endMatch = templateSection.match(/(\{\{-?\s*end\s*-?\}\})/);
|
|
506
|
+
if (!endMatch)
|
|
507
|
+
break;
|
|
508
|
+
const endIndex = endMatch.index;
|
|
509
|
+
if (endIndex === undefined)
|
|
510
|
+
break;
|
|
511
|
+
const templateEndIndex = templateStartIndex + endIndex + endMatch[0].length;
|
|
512
|
+
const templateContent = result.substring(templateStartIndex, templateEndIndex);
|
|
513
|
+
const beforeMarker = result.substring(0, lineStart + 1);
|
|
514
|
+
const afterTemplate = result.substring(templateEndIndex);
|
|
515
|
+
const pipeIndentEscaped = pipeIndent.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
516
|
+
const adjustedTemplate = templateContent.replace(new RegExp(`^${pipeIndentEscaped}`, 'gm'), baseIndent);
|
|
517
|
+
result = beforeMarker + adjustedTemplate + afterTemplate;
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
const _templateIndent = templateStartMatch[1] || '';
|
|
521
|
+
const templateStart = templateStartMatch[2];
|
|
522
|
+
if (!templateStart)
|
|
523
|
+
break;
|
|
524
|
+
const templateStartIndex = pipeIndex + templateStartMatch[0].length;
|
|
525
|
+
const templateSection = result.substring(templateStartIndex);
|
|
526
|
+
const endMatch = templateSection.match(/(\{\{-?\s*end\s*-?\}\})/);
|
|
527
|
+
if (!endMatch)
|
|
528
|
+
break;
|
|
529
|
+
const endIndex = endMatch.index;
|
|
530
|
+
if (endIndex === undefined)
|
|
531
|
+
break;
|
|
532
|
+
const templateEndIndex = templateStartIndex + endIndex + endMatch[0].length;
|
|
533
|
+
const templateContent = result.substring(templateStartIndex, templateEndIndex);
|
|
534
|
+
const beforeMarker = result.substring(0, lineStart + 1);
|
|
535
|
+
const afterTemplate = result.substring(templateEndIndex);
|
|
536
|
+
const pipeIndentEscaped = pipeIndent.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
537
|
+
const adjustedTemplate = templateContent.replace(new RegExp(`^${pipeIndentEscaped}`, 'gm'), baseIndent);
|
|
538
|
+
result = beforeMarker + adjustedTemplate + afterTemplate;
|
|
539
|
+
}
|
|
165
540
|
result = result.replace(/\\"/g, '"');
|
|
166
|
-
result = result.replace(
|
|
541
|
+
result = result.replace(/^(\s+)(\w+):\s+(\{\{-?\s*if\s+[^}]+\}\})\s*\n(\s+)(.+?)\n(\s+)(\{\{-?\s*end\s*-?\}\})/gm, (match, baseIndent, fieldKey, ifTag, contentIndent, content, endIndent, endTag) => {
|
|
542
|
+
const contentLines = content.split('\n');
|
|
543
|
+
const firstContentLine = contentLines[0]?.trim() || '';
|
|
544
|
+
if (firstContentLine.startsWith(`${fieldKey}:`)) {
|
|
545
|
+
contentLines[0] = contentLines[0].replace(new RegExp(`^\\s*${fieldKey}:\\s*`), '');
|
|
546
|
+
content = contentLines.join('\n');
|
|
547
|
+
}
|
|
548
|
+
const contentFormatted = content
|
|
549
|
+
? '\n' +
|
|
550
|
+
content
|
|
551
|
+
.split('\n')
|
|
552
|
+
.map((line) => baseIndent + ' ' + line.trimStart())
|
|
553
|
+
.join('\n')
|
|
554
|
+
: '';
|
|
555
|
+
return `${baseIndent}${ifTag}\n${baseIndent} ${fieldKey}:${contentFormatted}\n${baseIndent}${endTag}`;
|
|
556
|
+
});
|
|
557
|
+
result = postProcessFieldConditionals(result);
|
|
558
|
+
return result;
|
|
559
|
+
}
|
|
560
|
+
export function postProcessFieldConditionals(yaml) {
|
|
561
|
+
let result = yaml;
|
|
562
|
+
while (result.includes('__FIELD_WITH_MARKER__:')) {
|
|
563
|
+
const markerIndex = result.indexOf('__FIELD_WITH_MARKER__:');
|
|
564
|
+
if (markerIndex === -1)
|
|
565
|
+
break;
|
|
566
|
+
const beforeMarker = result.substring(0, markerIndex);
|
|
567
|
+
const lastNewline = beforeMarker.lastIndexOf('\n');
|
|
568
|
+
const secondLastNewline = beforeMarker.lastIndexOf('\n', lastNewline - 1);
|
|
569
|
+
const fieldKeyLine = result.substring(secondLastNewline + 1, lastNewline);
|
|
570
|
+
const fieldKeyMatch = fieldKeyLine.match(/^(\s*)(\w+):\s*|[-]?\s*$/);
|
|
571
|
+
if (!fieldKeyMatch || !fieldKeyMatch[2])
|
|
572
|
+
break;
|
|
573
|
+
const baseIndent = fieldKeyMatch[1] || '';
|
|
574
|
+
const fieldKey = fieldKeyMatch[2];
|
|
575
|
+
const afterMarker = result.substring(markerIndex);
|
|
576
|
+
const markerMatch = afterMarker.match(/^__FIELD_WITH_MARKER__:([^:]+):(.+?)$/m);
|
|
577
|
+
if (!markerMatch || !markerMatch[1] || !markerMatch[2])
|
|
578
|
+
break;
|
|
579
|
+
const path = markerMatch[1];
|
|
580
|
+
const content = markerMatch[2].trim();
|
|
581
|
+
const markerEnd = markerIndex + markerMatch[0].length;
|
|
582
|
+
const template = `${baseIndent}{{- with ${path} }}\n${baseIndent}${fieldKey}:\n${baseIndent} ${content}\n${baseIndent}{{- end }}`;
|
|
583
|
+
result = result.substring(0, secondLastNewline + 1) + template + result.substring(markerEnd);
|
|
584
|
+
}
|
|
585
|
+
while (result.includes('__FIELD_WITH__:')) {
|
|
586
|
+
const markerIndex = result.indexOf('__FIELD_WITH__:');
|
|
587
|
+
if (markerIndex === -1)
|
|
588
|
+
break;
|
|
589
|
+
const beforeMarker = result.substring(0, markerIndex);
|
|
590
|
+
const lastNewline = beforeMarker.lastIndexOf('\n');
|
|
591
|
+
const secondLastNewline = beforeMarker.lastIndexOf('\n', lastNewline - 1);
|
|
592
|
+
const fieldKeyLine = result.substring(secondLastNewline + 1, lastNewline);
|
|
593
|
+
const fieldKeyMatch = fieldKeyLine.match(/^(\s*)__fieldWithTemplate_\w+:\s*|[-]?\s*$/);
|
|
594
|
+
if (!fieldKeyMatch)
|
|
595
|
+
break;
|
|
596
|
+
const baseIndent = fieldKeyMatch[1];
|
|
597
|
+
const afterMarker = result.substring(markerIndex);
|
|
598
|
+
const markerMatch = afterMarker.match(/^__FIELD_WITH__:\w+:(.+?\{\{-?\s*end\s*-?\}\})/s);
|
|
599
|
+
if (!markerMatch || !markerMatch[1])
|
|
600
|
+
break;
|
|
601
|
+
const template = markerMatch[1];
|
|
602
|
+
const markerEnd = markerIndex + markerMatch[0].length;
|
|
603
|
+
const lines = template.split('\n');
|
|
604
|
+
const processed = lines
|
|
605
|
+
.map((line) => {
|
|
606
|
+
if (!line.trim())
|
|
607
|
+
return '';
|
|
608
|
+
const trimmed = line.trimStart();
|
|
609
|
+
return baseIndent + trimmed;
|
|
610
|
+
})
|
|
611
|
+
.join('\n');
|
|
612
|
+
result = result.substring(0, secondLastNewline + 1) + processed + result.substring(markerEnd);
|
|
613
|
+
}
|
|
614
|
+
while (result.includes('__FIELD_CONDITIONAL__:')) {
|
|
615
|
+
const markerIndex = result.indexOf('__FIELD_CONDITIONAL__:');
|
|
616
|
+
if (markerIndex === -1)
|
|
617
|
+
break;
|
|
618
|
+
const beforeMarker = result.substring(0, markerIndex);
|
|
619
|
+
const lastNewline = beforeMarker.lastIndexOf('\n');
|
|
620
|
+
const secondLastNewline = beforeMarker.lastIndexOf('\n', lastNewline - 1);
|
|
621
|
+
const fieldKeyLine = result.substring(secondLastNewline + 1, lastNewline);
|
|
622
|
+
const fieldKeyMatch = fieldKeyLine.match(/^(\s*)(\w+):\s*|[-]?\s*$/);
|
|
623
|
+
if (!fieldKeyMatch)
|
|
624
|
+
break;
|
|
625
|
+
const baseIndent = fieldKeyMatch[1];
|
|
626
|
+
const afterMarker = result.substring(markerIndex);
|
|
627
|
+
const markerMatch = afterMarker.match(/^__FIELD_CONDITIONAL__:\w+:(.+?\{\{-?\s*end\s*-?\}\})/s);
|
|
628
|
+
if (!markerMatch || !markerMatch[1])
|
|
629
|
+
break;
|
|
630
|
+
const template = markerMatch[1];
|
|
631
|
+
const markerEnd = markerIndex + markerMatch[0].length;
|
|
632
|
+
const lines = template.split('\n');
|
|
633
|
+
const processed = lines
|
|
634
|
+
.map((line) => {
|
|
635
|
+
if (!line.trim())
|
|
636
|
+
return '';
|
|
637
|
+
const trimmed = line.trimStart();
|
|
638
|
+
return baseIndent + trimmed;
|
|
639
|
+
})
|
|
640
|
+
.join('\n');
|
|
641
|
+
result = result.substring(0, secondLastNewline + 1) + processed + result.substring(markerEnd);
|
|
642
|
+
}
|
|
167
643
|
result = result.replace(/:\s*\|[-+]?\s*\n(\s*\{\{)/g, ':\n$1');
|
|
168
644
|
result = result.replace(/"(\{\{[\s\S]*?\}\})"/g, '$1');
|
|
169
645
|
return result;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { type HelmExpression } from './helmControlStructures.js';
|
|
2
|
+
declare const HELM_VALUE_SYMBOL: unique symbol;
|
|
3
|
+
export interface HelmValue<T = unknown> {
|
|
4
|
+
[HELM_VALUE_SYMBOL]: true;
|
|
5
|
+
__path: string;
|
|
6
|
+
__type?: T;
|
|
7
|
+
eq(value: HelmValue | string | number | boolean): HelmCondition;
|
|
8
|
+
ne(value: HelmValue | string | number | boolean): HelmCondition;
|
|
9
|
+
gt(value: HelmValue | number): HelmCondition;
|
|
10
|
+
ge(value: HelmValue | number): HelmCondition;
|
|
11
|
+
lt(value: HelmValue | number): HelmCondition;
|
|
12
|
+
le(value: HelmValue | number): HelmCondition;
|
|
13
|
+
not(): HelmCondition;
|
|
14
|
+
and(other: HelmCondition): HelmCondition;
|
|
15
|
+
or(other: HelmCondition): HelmCondition;
|
|
16
|
+
default(defaultValue: HelmValue | string | number | boolean): HelmValue<T>;
|
|
17
|
+
quote(): HelmValue<string>;
|
|
18
|
+
upper(): HelmValue<string>;
|
|
19
|
+
lower(): HelmValue<string>;
|
|
20
|
+
title(): HelmValue<string>;
|
|
21
|
+
trim(): HelmValue<string>;
|
|
22
|
+
trimPrefix(prefix: string): HelmValue<string>;
|
|
23
|
+
trimSuffix(suffix: string): HelmValue<string>;
|
|
24
|
+
replace(old: string, newStr: string): HelmValue<string>;
|
|
25
|
+
contains(substr: string): HelmCondition;
|
|
26
|
+
hasPrefix(prefix: string): HelmCondition;
|
|
27
|
+
hasSuffix(suffix: string): HelmCondition;
|
|
28
|
+
trunc(length: number): HelmValue<string>;
|
|
29
|
+
kindIs(kind: 'string' | 'slice' | 'map' | 'bool' | 'int' | 'float'): HelmCondition;
|
|
30
|
+
hasKey(key: string): HelmCondition;
|
|
31
|
+
toYaml(): HelmValue<string>;
|
|
32
|
+
toJson(): HelmValue<string>;
|
|
33
|
+
nindent(spaces: number): HelmValue<string>;
|
|
34
|
+
indent(spaces: number): HelmValue<string>;
|
|
35
|
+
toExpression(): HelmExpression;
|
|
36
|
+
if<V>(condition: HelmCondition, thenValue: V): HelmFieldConditional<V>;
|
|
37
|
+
ifElse<V>(condition: HelmCondition, thenValue: V, elseValue: V): HelmValue<V>;
|
|
38
|
+
range<V>(callback: (item: HelmValue<T extends (infer U)[] ? U : unknown>, index: HelmValue<number>) => V): HelmRange<V>;
|
|
39
|
+
with<V>(callback: (ctx: HelmValue<T>) => V): HelmWith<V>;
|
|
40
|
+
}
|
|
41
|
+
export interface HelmCondition {
|
|
42
|
+
[HELM_VALUE_SYMBOL]: true;
|
|
43
|
+
__condition: string;
|
|
44
|
+
not(): HelmCondition;
|
|
45
|
+
and(other: HelmCondition): HelmCondition;
|
|
46
|
+
or(other: HelmCondition): HelmCondition;
|
|
47
|
+
toString(): string;
|
|
48
|
+
}
|
|
49
|
+
export interface HelmFieldConditional<T> {
|
|
50
|
+
__helmFieldConditional: true;
|
|
51
|
+
condition: HelmCondition;
|
|
52
|
+
thenValue: T;
|
|
53
|
+
elseValue?: T;
|
|
54
|
+
}
|
|
55
|
+
export interface HelmRange<T> {
|
|
56
|
+
__helmRange: true;
|
|
57
|
+
source: HelmValue;
|
|
58
|
+
callback: (item: HelmValue, index: HelmValue<number>) => T;
|
|
59
|
+
}
|
|
60
|
+
export interface HelmWith<T> {
|
|
61
|
+
__helmWith: true;
|
|
62
|
+
source: HelmValue;
|
|
63
|
+
callback: (ctx: HelmValue) => T;
|
|
64
|
+
}
|
|
65
|
+
export interface HelmHelpers {
|
|
66
|
+
include(templateName: string, context?: '.' | HelmValue): HelmValue<string>;
|
|
67
|
+
printf(format: string, ...args: (HelmValue | string | number)[]): HelmValue<string>;
|
|
68
|
+
release: {
|
|
69
|
+
name: HelmValue<string>;
|
|
70
|
+
namespace: HelmValue<string>;
|
|
71
|
+
service: HelmValue<string>;
|
|
72
|
+
isUpgrade: HelmValue<boolean>;
|
|
73
|
+
isInstall: HelmValue<boolean>;
|
|
74
|
+
revision: HelmValue<number>;
|
|
75
|
+
};
|
|
76
|
+
chart: {
|
|
77
|
+
name: HelmValue<string>;
|
|
78
|
+
version: HelmValue<string>;
|
|
79
|
+
appVersion: HelmValue<string>;
|
|
80
|
+
type: HelmValue<string>;
|
|
81
|
+
};
|
|
82
|
+
capabilities: {
|
|
83
|
+
kubeVersion: {
|
|
84
|
+
version: HelmValue<string>;
|
|
85
|
+
major: HelmValue<string>;
|
|
86
|
+
minor: HelmValue<string>;
|
|
87
|
+
};
|
|
88
|
+
apiVersions: {
|
|
89
|
+
has(apiVersion: string): HelmCondition;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
rawCondition(condition: string): HelmCondition;
|
|
93
|
+
}
|
|
94
|
+
export declare function valuesRef<T extends Record<string, unknown>>(): HelmValue<T> & HelmHelpers;
|
|
95
|
+
export declare function isHelmValue(value: unknown): value is HelmValue;
|
|
96
|
+
export declare function isHelmCondition(value: unknown): value is HelmCondition;
|
|
97
|
+
export declare function isHelmFieldConditional(value: unknown): value is HelmFieldConditional<unknown>;
|
|
98
|
+
export declare function isHelmRange(value: unknown): value is HelmRange<unknown>;
|
|
99
|
+
export declare function isHelmWith(value: unknown): value is HelmWith<unknown>;
|
|
100
|
+
export declare function serializeHelmValue(value: HelmValue): string;
|
|
101
|
+
export declare function serializeHelmCondition(condition: HelmCondition): string;
|
|
102
|
+
export {};
|