@shepherdjerred/helm-types 1.1.0 → 1.2.0-dev.893
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/README.md +11 -2
- package/dist/cli.js +4570 -3203
- package/dist/index.js +8 -20585
- package/package.json +5 -1
- package/src/chart-fetcher.ts +37 -16
- package/src/chart-info-parser.ts +54 -34
- package/src/cli.ts +76 -58
- package/src/code-generator.ts +57 -22
- package/src/comment-parser.ts +79 -55
- package/src/config.ts +12 -5
- package/src/helm-types.ts +16 -46
- package/src/index.ts +14 -1
- package/src/interface-generator.ts +58 -23
- package/src/schemas.ts +3 -1
- package/src/type-converter-helpers.ts +180 -0
- package/src/type-converter.ts +273 -300
- package/src/type-inference.ts +302 -194
- package/src/utils.ts +2 -2
- package/src/yaml-comment-filters.ts +103 -0
- package/src/yaml-comment-regex-parser.ts +150 -0
- package/src/yaml-comments.ts +216 -508
- package/src/yaml-preprocess.ts +235 -0
package/src/comment-parser.ts
CHANGED
|
@@ -1,72 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a line looks like an example/code block rather than documentation
|
|
3
|
+
*/
|
|
4
|
+
function isExampleLine(line: string): boolean {
|
|
5
|
+
return (
|
|
6
|
+
/^-{3,}/.test(line) ||
|
|
7
|
+
/^BEGIN .*(?:KEY|CERTIFICATE)/.test(line) ||
|
|
8
|
+
/^END .*(?:KEY|CERTIFICATE)/.test(line) ||
|
|
9
|
+
(line.startsWith("-") && (line.includes(":") || /^-\s+\|/.test(line))) ||
|
|
10
|
+
/^\w+:$/.test(line) ||
|
|
11
|
+
/^[\w-]+:\s*$/.test(line) ||
|
|
12
|
+
/^[\w.-]+:\s*\|/.test(line) || // YAML multiline indicator (e.g., "policy.csv: |")
|
|
13
|
+
line.startsWith("|") ||
|
|
14
|
+
line.includes("$ARGOCD_") ||
|
|
15
|
+
line.includes("$KUBE_") ||
|
|
16
|
+
/^\s{2,}/.test(line) ||
|
|
17
|
+
/^echo\s+/.test(line) ||
|
|
18
|
+
/^[pg],\s*/.test(line) // Policy rules like "p, role:..." or "g, subject, ..."
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Check if a line looks like normal prose (sentence case, punctuation)
|
|
24
|
+
*/
|
|
25
|
+
function looksLikeProse(line: string): boolean {
|
|
26
|
+
return (
|
|
27
|
+
/^[A-Z][\w\s]+[.!?]$/.test(line) ||
|
|
28
|
+
/^[A-Z][\w\s,'"-]+(?::\s*)?$/.test(line) ||
|
|
29
|
+
line.startsWith("Ref:") ||
|
|
30
|
+
line.startsWith("See:") ||
|
|
31
|
+
line.startsWith("http://") ||
|
|
32
|
+
line.startsWith("https://")
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Strip YAML comment markers from a single line
|
|
38
|
+
*/
|
|
39
|
+
function stripCommentMarkers(line: string): string {
|
|
40
|
+
let result = line.replace(/^#+\s*/, "");
|
|
41
|
+
result = result.replace(/^--\s*/, "");
|
|
42
|
+
result = result.replace(/^##\s*/, "");
|
|
43
|
+
return result.trim();
|
|
44
|
+
}
|
|
45
|
+
|
|
1
46
|
/**
|
|
2
47
|
* Clean up YAML comment text for use in JSDoc
|
|
3
48
|
*/
|
|
4
49
|
export function cleanYAMLComment(comment: string): string {
|
|
5
|
-
const lines = comment.split("\n").map((line) =>
|
|
6
|
-
// Remove leading # symbols
|
|
7
|
-
line = line.replace(/^#+\s*/, "");
|
|
8
|
-
// Remove common Helm chart comment markers
|
|
9
|
-
line = line.replace(/^--\s*/, "");
|
|
10
|
-
line = line.replace(/^##\s*/, "");
|
|
11
|
-
return line.trim();
|
|
12
|
-
});
|
|
50
|
+
const lines = comment.split("\n").map((line) => stripCommentMarkers(line));
|
|
13
51
|
|
|
14
52
|
// Filter and clean lines
|
|
15
53
|
const cleaned: string[] = [];
|
|
16
54
|
let inCodeBlock = false;
|
|
17
55
|
|
|
18
|
-
for (const
|
|
19
|
-
const currentLine = line;
|
|
20
|
-
|
|
21
|
-
// Skip empty lines
|
|
56
|
+
for (const currentLine of lines) {
|
|
22
57
|
if (currentLine.length === 0) {
|
|
23
|
-
if (inCodeBlock)
|
|
58
|
+
if (inCodeBlock) {
|
|
59
|
+
inCodeBlock = false;
|
|
60
|
+
}
|
|
24
61
|
continue;
|
|
25
62
|
}
|
|
26
63
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
/^-{3,}/.test(currentLine) ||
|
|
33
|
-
/^BEGIN .*(KEY|CERTIFICATE)/.test(currentLine) ||
|
|
34
|
-
/^END .*(KEY|CERTIFICATE)/.test(currentLine) ||
|
|
35
|
-
(currentLine.startsWith("-") && (currentLine.includes(":") || /^-\s+\|/.test(currentLine))) ||
|
|
36
|
-
/^\w+:$/.test(currentLine) ||
|
|
37
|
-
/^[\w-]+:\s*$/.test(currentLine) ||
|
|
38
|
-
/^[\w.-]+:\s*\|/.test(currentLine) || // YAML multiline indicator (e.g., "policy.csv: |")
|
|
39
|
-
currentLine.startsWith("|") ||
|
|
40
|
-
currentLine.includes("$ARGOCD_") ||
|
|
41
|
-
currentLine.includes("$KUBE_") ||
|
|
42
|
-
/^\s{2,}/.test(currentLine) ||
|
|
43
|
-
/^echo\s+/.test(currentLine) ||
|
|
44
|
-
/^[pg],\s*/.test(currentLine); // Policy rules like "p, role:..." or "g, subject, ..."
|
|
45
|
-
|
|
46
|
-
if (isExample) {
|
|
64
|
+
if (currentLine.startsWith("@default")) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (isExampleLine(currentLine)) {
|
|
47
69
|
inCodeBlock = true;
|
|
48
70
|
continue;
|
|
49
71
|
}
|
|
50
72
|
|
|
51
|
-
// If we're in a code block, skip until we hit normal prose
|
|
52
73
|
if (inCodeBlock) {
|
|
53
|
-
|
|
54
|
-
const looksLikeProse =
|
|
55
|
-
/^[A-Z][\w\s]+[.!?]$/.test(currentLine) ||
|
|
56
|
-
/^[A-Z][\w\s,'"-]+:?\s*$/.test(currentLine) ||
|
|
57
|
-
currentLine.startsWith("Ref:") ||
|
|
58
|
-
currentLine.startsWith("See:") ||
|
|
59
|
-
currentLine.startsWith("http://") ||
|
|
60
|
-
currentLine.startsWith("https://");
|
|
61
|
-
|
|
62
|
-
if (looksLikeProse) {
|
|
74
|
+
if (looksLikeProse(currentLine)) {
|
|
63
75
|
inCodeBlock = false;
|
|
64
76
|
} else {
|
|
65
77
|
continue;
|
|
66
78
|
}
|
|
67
79
|
}
|
|
68
80
|
|
|
69
|
-
// Keep the line
|
|
70
81
|
cleaned.push(currentLine);
|
|
71
82
|
}
|
|
72
83
|
|
|
@@ -96,7 +107,7 @@ export function parseYAMLComments(yamlContent: string): Map<string, string> {
|
|
|
96
107
|
|
|
97
108
|
// Check if line is a comment
|
|
98
109
|
if (trimmed.startsWith("#")) {
|
|
99
|
-
const comment = trimmed.
|
|
110
|
+
const comment = trimmed.slice(1).trim();
|
|
100
111
|
if (comment) {
|
|
101
112
|
// Track the indentation level of the comment
|
|
102
113
|
const commentIndent = currentLine.search(/\S/);
|
|
@@ -106,35 +117,48 @@ export function parseYAMLComments(yamlContent: string): Map<string, string> {
|
|
|
106
117
|
}
|
|
107
118
|
|
|
108
119
|
// Check if line has a key
|
|
109
|
-
const keyMatch = /^(\s*)([
|
|
120
|
+
const keyMatch = /^(\s*)([\w-]+)\s*:/.exec(currentLine);
|
|
110
121
|
if (keyMatch) {
|
|
111
122
|
const indent = keyMatch[1]?.length ?? 0;
|
|
112
123
|
const key = keyMatch[2];
|
|
113
|
-
if (
|
|
124
|
+
if (key == null || key === "") {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
114
127
|
|
|
115
128
|
// Update indent stack
|
|
116
|
-
const lastIndent = indentStack
|
|
117
|
-
while (
|
|
129
|
+
const lastIndent = indentStack.at(-1);
|
|
130
|
+
while (
|
|
131
|
+
indentStack.length > 0 &&
|
|
132
|
+
lastIndent &&
|
|
133
|
+
lastIndent.indent >= indent
|
|
134
|
+
) {
|
|
118
135
|
indentStack.pop();
|
|
119
136
|
}
|
|
120
137
|
|
|
121
138
|
// Build full key path
|
|
122
|
-
const keyPath =
|
|
139
|
+
const keyPath =
|
|
140
|
+
indentStack.length > 0
|
|
141
|
+
? `${indentStack.map((s) => s.key).join(".")}.${key}`
|
|
142
|
+
: key;
|
|
123
143
|
|
|
124
144
|
// Check for inline comment
|
|
125
|
-
const inlineCommentMatch = /#\s*(
|
|
126
|
-
if (inlineCommentMatch?.[1]) {
|
|
145
|
+
const inlineCommentMatch = /#\s*(\S.*)$/.exec(currentLine);
|
|
146
|
+
if (inlineCommentMatch?.[1] != null && inlineCommentMatch[1] !== "") {
|
|
127
147
|
pendingComments.push({ text: inlineCommentMatch[1].trim(), indent });
|
|
128
148
|
}
|
|
129
149
|
|
|
130
150
|
// Filter pending comments to only those at the same or shallower indent level as this key
|
|
131
151
|
// This prevents comments from deeper nested properties being associated with a shallower property
|
|
132
|
-
const relevantComments = pendingComments.filter(
|
|
152
|
+
const relevantComments = pendingComments.filter(
|
|
153
|
+
(c) => c.indent <= indent,
|
|
154
|
+
);
|
|
133
155
|
|
|
134
156
|
// Associate relevant comments with this key
|
|
135
157
|
if (relevantComments.length > 0) {
|
|
136
158
|
// Join and clean comments
|
|
137
|
-
const commentText = cleanYAMLComment(
|
|
159
|
+
const commentText = cleanYAMLComment(
|
|
160
|
+
relevantComments.map((c) => c.text).join("\n"),
|
|
161
|
+
);
|
|
138
162
|
if (commentText) {
|
|
139
163
|
comments.set(keyPath, commentText);
|
|
140
164
|
}
|
package/src/config.ts
CHANGED
|
@@ -17,7 +17,9 @@ export const K8S_RESOURCE_SPEC_PATTERN = {
|
|
|
17
17
|
* Check if a property name indicates a Kubernetes resource spec.
|
|
18
18
|
*/
|
|
19
19
|
export function isK8sResourceSpec(propertyName: string): boolean {
|
|
20
|
-
return K8S_RESOURCE_SPEC_PATTERN.resourceSpecNames.includes(
|
|
20
|
+
return K8S_RESOURCE_SPEC_PATTERN.resourceSpecNames.includes(
|
|
21
|
+
propertyName.toLowerCase(),
|
|
22
|
+
);
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
/**
|
|
@@ -86,7 +88,10 @@ export function shouldAllowArbitraryProps(
|
|
|
86
88
|
const patterns = EXTENSIBLE_TYPE_PATTERNS[chartName];
|
|
87
89
|
if (patterns) {
|
|
88
90
|
for (const pattern of patterns) {
|
|
89
|
-
if (
|
|
91
|
+
if (
|
|
92
|
+
pattern === keyPath ||
|
|
93
|
+
(pattern === "" && keyPath.split(".").length === 1)
|
|
94
|
+
) {
|
|
90
95
|
return true;
|
|
91
96
|
}
|
|
92
97
|
// Also match if keyPath starts with pattern
|
|
@@ -126,11 +131,13 @@ export function shouldAllowArbitraryProps(
|
|
|
126
131
|
}
|
|
127
132
|
|
|
128
133
|
// Check YAML comments for hints
|
|
129
|
-
if (yamlComment) {
|
|
134
|
+
if (yamlComment != null && yamlComment !== "") {
|
|
130
135
|
const commentLower = yamlComment.toLowerCase();
|
|
131
136
|
if (
|
|
132
|
-
/\b(arbitrary|custom|additional|extra|any)\s+(keys?|properties?|fields?|values?)\b/i.
|
|
133
|
-
|
|
137
|
+
/\b(?:arbitrary|custom|additional|extra|any)\s+(?:keys?|properties?|fields?|values?)\b/i.test(
|
|
138
|
+
commentLower,
|
|
139
|
+
) ||
|
|
140
|
+
/\bkey[\s-]?value\s+pairs?\b/i.test(commentLower)
|
|
134
141
|
) {
|
|
135
142
|
return true;
|
|
136
143
|
}
|
package/src/helm-types.ts
CHANGED
|
@@ -1,46 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
ErrorSchema,
|
|
18
|
-
StringBooleanSchema,
|
|
19
|
-
HelmValueSchema,
|
|
20
|
-
} from "./schemas.js";
|
|
21
|
-
|
|
22
|
-
// Configuration
|
|
23
|
-
export { EXTENSIBLE_TYPE_PATTERNS, shouldAllowArbitraryProps } from "./config.js";
|
|
24
|
-
|
|
25
|
-
// Chart info parsing
|
|
26
|
-
export { parseChartInfoFromVersions } from "./chart-info-parser.js";
|
|
27
|
-
|
|
28
|
-
// YAML comments
|
|
29
|
-
export { cleanYAMLComment, parseYAMLComments } from "./yaml-comments.js";
|
|
30
|
-
|
|
31
|
-
// Chart fetching
|
|
32
|
-
export { fetchHelmChart } from "./chart-fetcher.js";
|
|
33
|
-
|
|
34
|
-
// Type conversion
|
|
35
|
-
export {
|
|
36
|
-
jsonSchemaToTypeScript,
|
|
37
|
-
inferTypeFromValue,
|
|
38
|
-
typesAreCompatible,
|
|
39
|
-
convertToTypeScriptInterface,
|
|
40
|
-
} from "./type-converter.js";
|
|
41
|
-
|
|
42
|
-
// Code generation
|
|
43
|
-
export { generateTypeScriptCode } from "./interface-generator.js";
|
|
44
|
-
|
|
45
|
-
// Utilities
|
|
46
|
-
export { sanitizePropertyName, sanitizeTypeName, capitalizeFirst } from "./utils.js";
|
|
1
|
+
/**
|
|
2
|
+
* This file previously served as a barrel/re-export module.
|
|
3
|
+
* Import directly from submodules instead:
|
|
4
|
+
* - ./types.ts - Core types
|
|
5
|
+
* - ./schemas.ts - Zod schemas
|
|
6
|
+
* - ./config.ts - Configuration
|
|
7
|
+
* - ./chart-info-parser.ts - Chart info parsing
|
|
8
|
+
* - ./yaml-comments.ts - YAML comments
|
|
9
|
+
* - ./chart-fetcher.ts - Chart fetching
|
|
10
|
+
* - ./type-converter.ts - Type conversion
|
|
11
|
+
* - ./interface-generator.ts - Code generation
|
|
12
|
+
* - ./utils.ts - Utilities
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** Version marker for the helm-types module. */
|
|
16
|
+
export const HELM_TYPES_VERSION = "1.1.0";
|
package/src/index.ts
CHANGED
|
@@ -11,5 +11,18 @@
|
|
|
11
11
|
*
|
|
12
12
|
* This is a general-purpose library that can be used with any Helm chart.
|
|
13
13
|
* Application-specific logic should be kept in your application code.
|
|
14
|
+
*
|
|
15
|
+
* Import directly from submodules:
|
|
16
|
+
* - ./types.ts - Core types (ChartInfo, JSONSchemaProperty, TypeScriptInterface, TypeProperty)
|
|
17
|
+
* - ./schemas.ts - Zod schemas (HelmValueSchema, StringSchema, etc.)
|
|
18
|
+
* - ./config.ts - Configuration (EXTENSIBLE_TYPE_PATTERNS, shouldAllowArbitraryProps)
|
|
19
|
+
* - ./chart-info-parser.ts - Chart info parsing (parseChartInfoFromVersions)
|
|
20
|
+
* - ./yaml-comments.ts - YAML comments (cleanYAMLComment, parseYAMLComments)
|
|
21
|
+
* - ./chart-fetcher.ts - Chart fetching (fetchHelmChart)
|
|
22
|
+
* - ./type-converter.ts - Type conversion (jsonSchemaToTypeScript, inferTypeFromValue, etc.)
|
|
23
|
+
* - ./interface-generator.ts - Code generation (generateTypeScriptCode)
|
|
24
|
+
* - ./utils.ts - Utilities (sanitizePropertyName, sanitizeTypeName, capitalizeFirst)
|
|
14
25
|
*/
|
|
15
|
-
|
|
26
|
+
|
|
27
|
+
/** Version marker for the helm-types package. */
|
|
28
|
+
export const HELM_TYPES_PACKAGE_VERSION = "1.1.0";
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import type { TypeScriptInterface } from "./types.
|
|
2
|
-
import {
|
|
1
|
+
import type { TypeScriptInterface } from "./types.ts";
|
|
2
|
+
import {
|
|
3
|
+
ArraySchema,
|
|
4
|
+
RecordSchema,
|
|
5
|
+
StringSchema,
|
|
6
|
+
ActualNumberSchema,
|
|
7
|
+
ActualBooleanSchema,
|
|
8
|
+
} from "./schemas.ts";
|
|
3
9
|
|
|
4
10
|
/**
|
|
5
11
|
* Generate TypeScript code from interface definition
|
|
6
12
|
*/
|
|
7
|
-
export function generateTypeScriptCode(
|
|
13
|
+
export function generateTypeScriptCode(
|
|
14
|
+
mainInterface: TypeScriptInterface,
|
|
15
|
+
chartName: string,
|
|
16
|
+
): string {
|
|
8
17
|
const interfaces: TypeScriptInterface[] = [];
|
|
9
18
|
|
|
10
19
|
// Collect all nested interfaces
|
|
@@ -21,18 +30,20 @@ export function generateTypeScriptCode(mainInterface: TypeScriptInterface, chart
|
|
|
21
30
|
// Generate parameter type (flattened dot notation)
|
|
22
31
|
code += generateParameterType(mainInterface, chartName);
|
|
23
32
|
|
|
24
|
-
//
|
|
33
|
+
// Add header comment for generated files
|
|
25
34
|
if (code.includes(": any")) {
|
|
26
35
|
code = `// Generated TypeScript types for ${chartName} Helm chart
|
|
27
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
28
36
|
|
|
29
|
-
${code.
|
|
37
|
+
${code.slice(Math.max(0, code.indexOf("\n\n") + 2))}`;
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
return code;
|
|
33
41
|
}
|
|
34
42
|
|
|
35
|
-
function collectNestedInterfaces(
|
|
43
|
+
function collectNestedInterfaces(
|
|
44
|
+
iface: TypeScriptInterface,
|
|
45
|
+
collected: TypeScriptInterface[],
|
|
46
|
+
): void {
|
|
36
47
|
for (const prop of Object.values(iface.properties)) {
|
|
37
48
|
if (prop.nested) {
|
|
38
49
|
collected.push(prop.nested);
|
|
@@ -49,7 +60,7 @@ function collectNestedInterfaces(iface: TypeScriptInterface, collected: TypeScri
|
|
|
49
60
|
function generateInterfaceCode(iface: TypeScriptInterface): string {
|
|
50
61
|
const hasProperties = Object.keys(iface.properties).length > 0;
|
|
51
62
|
|
|
52
|
-
if (!hasProperties &&
|
|
63
|
+
if (!hasProperties && iface.allowArbitraryProps !== true) {
|
|
53
64
|
// Use 'object' for empty interfaces instead of '{}'
|
|
54
65
|
return `export type ${iface.name} = object;\n`;
|
|
55
66
|
}
|
|
@@ -57,7 +68,7 @@ function generateInterfaceCode(iface: TypeScriptInterface): string {
|
|
|
57
68
|
let code = `export type ${iface.name} = {\n`;
|
|
58
69
|
|
|
59
70
|
// Add index signature if this type allows arbitrary properties
|
|
60
|
-
if (iface.allowArbitraryProps) {
|
|
71
|
+
if (iface.allowArbitraryProps === true) {
|
|
61
72
|
code += ` /**\n`;
|
|
62
73
|
code += ` * This type allows arbitrary additional properties beyond those defined below.\n`;
|
|
63
74
|
code += ` * This is common for config maps, custom settings, and extensible configurations.\n`;
|
|
@@ -69,13 +80,19 @@ function generateInterfaceCode(iface: TypeScriptInterface): string {
|
|
|
69
80
|
const optional = prop.optional ? "?" : "";
|
|
70
81
|
|
|
71
82
|
// Generate JSDoc comment if we have description or default
|
|
72
|
-
if (
|
|
83
|
+
if (
|
|
84
|
+
(prop.description != null && prop.description !== "") ||
|
|
85
|
+
prop.default !== undefined
|
|
86
|
+
) {
|
|
73
87
|
code += ` /**\n`;
|
|
74
88
|
|
|
75
|
-
if (prop.description) {
|
|
89
|
+
if (prop.description != null && prop.description !== "") {
|
|
76
90
|
// Format multi-line descriptions properly with " * " prefix
|
|
77
91
|
// Escape */ sequences to prevent premature comment closure
|
|
78
|
-
const escapedDescription = prop.description.
|
|
92
|
+
const escapedDescription = prop.description.replaceAll(
|
|
93
|
+
"*/",
|
|
94
|
+
String.raw`*\/`,
|
|
95
|
+
);
|
|
79
96
|
const descLines = escapedDescription.split("\n");
|
|
80
97
|
for (const line of descLines) {
|
|
81
98
|
code += ` * ${line}\n`;
|
|
@@ -84,8 +101,12 @@ function generateInterfaceCode(iface: TypeScriptInterface): string {
|
|
|
84
101
|
|
|
85
102
|
if (prop.default !== undefined) {
|
|
86
103
|
const defaultStr = formatDefaultValue(prop.default);
|
|
87
|
-
|
|
88
|
-
|
|
104
|
+
const hasDescription =
|
|
105
|
+
prop.description != null && prop.description !== "";
|
|
106
|
+
if (defaultStr != null && defaultStr !== "" && hasDescription) {
|
|
107
|
+
code += ` *\n`;
|
|
108
|
+
}
|
|
109
|
+
if (defaultStr != null && defaultStr !== "") {
|
|
89
110
|
code += ` * @default ${defaultStr}\n`;
|
|
90
111
|
}
|
|
91
112
|
}
|
|
@@ -104,13 +125,19 @@ function generateInterfaceCode(iface: TypeScriptInterface): string {
|
|
|
104
125
|
* Format a default value for display in JSDoc
|
|
105
126
|
*/
|
|
106
127
|
function formatDefaultValue(value: unknown): string | null {
|
|
107
|
-
if (value === null)
|
|
108
|
-
|
|
128
|
+
if (value === null) {
|
|
129
|
+
return "null";
|
|
130
|
+
}
|
|
131
|
+
if (value === undefined) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
109
134
|
|
|
110
135
|
// Handle arrays
|
|
111
136
|
const arrayCheck = ArraySchema.safeParse(value);
|
|
112
137
|
if (arrayCheck.success) {
|
|
113
|
-
if (arrayCheck.data.length === 0)
|
|
138
|
+
if (arrayCheck.data.length === 0) {
|
|
139
|
+
return "[]";
|
|
140
|
+
}
|
|
114
141
|
if (arrayCheck.data.length <= 3) {
|
|
115
142
|
try {
|
|
116
143
|
return JSON.stringify(arrayCheck.data);
|
|
@@ -125,7 +152,9 @@ function formatDefaultValue(value: unknown): string | null {
|
|
|
125
152
|
const recordCheck = RecordSchema.safeParse(value);
|
|
126
153
|
if (recordCheck.success) {
|
|
127
154
|
const keys = Object.keys(recordCheck.data);
|
|
128
|
-
if (keys.length === 0)
|
|
155
|
+
if (keys.length === 0) {
|
|
156
|
+
return "{}";
|
|
157
|
+
}
|
|
129
158
|
if (keys.length <= 3) {
|
|
130
159
|
try {
|
|
131
160
|
return JSON.stringify(recordCheck.data);
|
|
@@ -141,7 +170,7 @@ function formatDefaultValue(value: unknown): string | null {
|
|
|
141
170
|
if (stringCheck.success) {
|
|
142
171
|
// Truncate long strings
|
|
143
172
|
if (stringCheck.data.length > 50) {
|
|
144
|
-
return `"${stringCheck.data.
|
|
173
|
+
return `"${stringCheck.data.slice(0, 47)}..."`;
|
|
145
174
|
}
|
|
146
175
|
return `"${stringCheck.data}"`;
|
|
147
176
|
}
|
|
@@ -165,10 +194,13 @@ function formatDefaultValue(value: unknown): string | null {
|
|
|
165
194
|
}
|
|
166
195
|
}
|
|
167
196
|
|
|
168
|
-
function generateParameterType(
|
|
197
|
+
function generateParameterType(
|
|
198
|
+
iface: TypeScriptInterface,
|
|
199
|
+
chartName: string,
|
|
200
|
+
): string {
|
|
169
201
|
const parameterKeys = flattenInterfaceKeys(iface);
|
|
170
202
|
|
|
171
|
-
const normalizedChartName = capitalizeFirst(chartName).
|
|
203
|
+
const normalizedChartName = capitalizeFirst(chartName).replaceAll("-", "");
|
|
172
204
|
let code = `export type ${normalizedChartName}HelmParameters = {\n`;
|
|
173
205
|
|
|
174
206
|
for (const key of parameterKeys) {
|
|
@@ -180,12 +212,15 @@ function generateParameterType(iface: TypeScriptInterface, chartName: string): s
|
|
|
180
212
|
return code;
|
|
181
213
|
}
|
|
182
214
|
|
|
183
|
-
function flattenInterfaceKeys(
|
|
215
|
+
function flattenInterfaceKeys(
|
|
216
|
+
iface: TypeScriptInterface,
|
|
217
|
+
prefix = "",
|
|
218
|
+
): string[] {
|
|
184
219
|
const keys: string[] = [];
|
|
185
220
|
|
|
186
221
|
for (const [key, prop] of Object.entries(iface.properties)) {
|
|
187
222
|
// Remove quotes from key for parameter names
|
|
188
|
-
const cleanKey = key.
|
|
223
|
+
const cleanKey = key.replaceAll('"', "");
|
|
189
224
|
const fullKey = prefix ? `${prefix}.${cleanKey}` : cleanKey;
|
|
190
225
|
|
|
191
226
|
if (prop.nested) {
|
package/src/schemas.ts
CHANGED
|
@@ -14,7 +14,9 @@ export const ErrorSchema = z.instanceof(Error);
|
|
|
14
14
|
// Custom boolean string parser - only matches actual boolean-like strings
|
|
15
15
|
export const StringBooleanSchema = z.string().refine((val) => {
|
|
16
16
|
const lower = val.toLowerCase();
|
|
17
|
-
return
|
|
17
|
+
return (
|
|
18
|
+
lower === "true" || lower === "false" || lower === "yes" || lower === "no"
|
|
19
|
+
);
|
|
18
20
|
}, "Not a boolean string");
|
|
19
21
|
|
|
20
22
|
// Zod schema for validating YAML values - recursive definition with more flexibility
|