@shepherdjerred/helm-types 1.7.0 → 2.0.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/CHANGELOG.md +78 -0
- package/LICENSE +674 -0
- package/README.md +82 -63
- package/demos/argo-cd-cli.cast +50 -0
- package/demos/record-cli-demo.sh +132 -0
- package/dist/chart-fetcher.d.ts +14 -0
- package/dist/chart-fetcher.d.ts.map +1 -0
- package/dist/chart-fetcher.js +105 -0
- package/dist/chart-fetcher.js.map +1 -0
- package/dist/chart-info-parser.d.ts +6 -0
- package/dist/chart-info-parser.d.ts.map +1 -0
- package/dist/chart-info-parser.js +50 -0
- package/dist/chart-info-parser.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +140 -22841
- package/dist/cli.js.map +1 -0
- package/dist/comment-parser.d.ts +10 -0
- package/dist/comment-parser.d.ts.map +1 -0
- package/dist/comment-parser.js +150 -0
- package/dist/comment-parser.js.map +1 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +169 -0
- package/dist/config.js.map +1 -0
- package/dist/helm-types.d.ts +16 -0
- package/dist/helm-types.d.ts.map +1 -0
- package/{src/helm-types.ts → dist/helm-types.js} +1 -1
- package/dist/helm-types.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -23
- package/dist/index.js.map +1 -0
- package/dist/interface-generator.d.ts +6 -0
- package/dist/interface-generator.d.ts.map +1 -0
- package/dist/interface-generator.js +181 -0
- package/dist/interface-generator.js.map +1 -0
- package/dist/schemas.d.ts +13 -0
- package/dist/schemas.d.ts.map +1 -0
- package/{src/schemas.ts → dist/schemas.js} +12 -23
- package/dist/schemas.js.map +1 -0
- package/dist/type-converter-helpers.d.ts +20 -0
- package/dist/type-converter-helpers.d.ts.map +1 -0
- package/dist/type-converter-helpers.js +75 -0
- package/dist/type-converter-helpers.js.map +1 -0
- package/dist/type-converter.d.ts +26 -0
- package/dist/type-converter.d.ts.map +1 -0
- package/dist/type-converter.js +378 -0
- package/dist/type-converter.js.map +1 -0
- package/dist/types.d.ts +35 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +17 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +69 -0
- package/dist/utils.js.map +1 -0
- package/dist/yaml-comment-filters.d.ts +7 -0
- package/dist/yaml-comment-filters.d.ts.map +1 -0
- package/dist/yaml-comment-filters.js +91 -0
- package/dist/yaml-comment-filters.js.map +1 -0
- package/dist/yaml-comment-regex-parser.d.ts +8 -0
- package/dist/yaml-comment-regex-parser.d.ts.map +1 -0
- package/dist/yaml-comment-regex-parser.js +120 -0
- package/dist/yaml-comment-regex-parser.js.map +1 -0
- package/dist/yaml-comments.d.ts +74 -0
- package/dist/yaml-comments.d.ts.map +1 -0
- package/dist/yaml-comments.js +406 -0
- package/dist/yaml-comments.js.map +1 -0
- package/dist/yaml-preprocess.d.ts +15 -0
- package/dist/yaml-preprocess.d.ts.map +1 -0
- package/dist/yaml-preprocess.js +170 -0
- package/dist/yaml-preprocess.js.map +1 -0
- package/examples/argo-cd/generate.mjs +23 -0
- package/package.json +17 -12
- package/src/__fixtures__/sample-chart/Chart.yaml +0 -6
- package/src/__fixtures__/sample-chart/values.schema.json +0 -32
- package/src/__fixtures__/sample-chart/values.yaml +0 -22
- package/src/chart-fetcher.ts +0 -221
- package/src/chart-info-parser.ts +0 -64
- package/src/cli.ts +0 -217
- package/src/code-generator.ts +0 -226
- package/src/comment-parser.ts +0 -180
- package/src/config.ts +0 -195
- package/src/index.ts +0 -28
- package/src/interface-generator.ts +0 -238
- package/src/reset.d.ts +0 -1
- package/src/type-converter-helpers.ts +0 -108
- package/src/type-converter.ts +0 -520
- package/src/type-inference.ts +0 -548
- package/src/types.ts +0 -41
- package/src/utils.ts +0 -76
- package/src/yaml-comment-filters.ts +0 -103
- package/src/yaml-comment-regex-parser.ts +0 -150
- package/src/yaml-comments.ts +0 -507
- package/src/yaml-preprocess.ts +0 -235
package/src/cli.ts
DELETED
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* CLI for @homelab/helm-types
|
|
4
|
-
*
|
|
5
|
-
* Generate TypeScript types from Helm charts
|
|
6
|
-
*/
|
|
7
|
-
import { z } from "zod";
|
|
8
|
-
import { fetchHelmChart } from "./chart-fetcher.ts";
|
|
9
|
-
import { convertToTypeScriptInterface } from "./type-converter.ts";
|
|
10
|
-
import { generateTypeScriptCode } from "./interface-generator.ts";
|
|
11
|
-
import type { ChartInfo } from "./types.ts";
|
|
12
|
-
|
|
13
|
-
const ErrorSchema = z.object({
|
|
14
|
-
message: z.string(),
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
const HELP_TEXT = String.raw`
|
|
18
|
-
helm-types - Generate TypeScript types from Helm charts
|
|
19
|
-
|
|
20
|
-
USAGE:
|
|
21
|
-
bunx @homelab/helm-types [options]
|
|
22
|
-
|
|
23
|
-
OPTIONS:
|
|
24
|
-
--name, -n Unique identifier for the chart (required)
|
|
25
|
-
--chart, -c Chart name in the repository (defaults to --name)
|
|
26
|
-
--repo, -r Helm repository URL (required)
|
|
27
|
-
--version, -v Chart version (required)
|
|
28
|
-
--output, -o Output file path (defaults to stdout)
|
|
29
|
-
--interface, -i Interface name (auto-generated from chart name if not provided)
|
|
30
|
-
--help, -h Show this help message
|
|
31
|
-
|
|
32
|
-
EXAMPLES:
|
|
33
|
-
# Generate types for ArgoCD and print to stdout
|
|
34
|
-
bunx @homelab/helm-types \
|
|
35
|
-
--name argo-cd \
|
|
36
|
-
--repo https://argoproj.github.io/argo-helm \
|
|
37
|
-
--version 8.3.1
|
|
38
|
-
|
|
39
|
-
# Generate types with custom output file
|
|
40
|
-
bunx @homelab/helm-types \
|
|
41
|
-
--name argo-cd \
|
|
42
|
-
--repo https://argoproj.github.io/argo-helm \
|
|
43
|
-
--version 8.3.1 \
|
|
44
|
-
--output argo-cd.types.ts
|
|
45
|
-
|
|
46
|
-
# Generate types with custom chart name and interface name
|
|
47
|
-
bunx @homelab/helm-types \
|
|
48
|
-
--name argocd \
|
|
49
|
-
--chart argo-cd \
|
|
50
|
-
--repo https://argoproj.github.io/argo-helm \
|
|
51
|
-
--version 8.3.1 \
|
|
52
|
-
--interface ArgocdHelmValues \
|
|
53
|
-
--output argocd.types.ts
|
|
54
|
-
`;
|
|
55
|
-
|
|
56
|
-
type CliArgs = {
|
|
57
|
-
name?: string;
|
|
58
|
-
chart?: string;
|
|
59
|
-
repo?: string;
|
|
60
|
-
version?: string;
|
|
61
|
-
output?: string;
|
|
62
|
-
interface?: string;
|
|
63
|
-
help?: boolean;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
/** String-valued flag names (excludes boolean 'help') */
|
|
67
|
-
type StringCliArgsKey = Exclude<keyof CliArgs, "help">;
|
|
68
|
-
|
|
69
|
-
/** Map from flag name to CliArgs key */
|
|
70
|
-
const FLAG_MAP: Record<string, StringCliArgsKey> = {
|
|
71
|
-
"--name": "name",
|
|
72
|
-
"-n": "name",
|
|
73
|
-
"--chart": "chart",
|
|
74
|
-
"-c": "chart",
|
|
75
|
-
"--repo": "repo",
|
|
76
|
-
"-r": "repo",
|
|
77
|
-
"--version": "version",
|
|
78
|
-
"-v": "version",
|
|
79
|
-
"--output": "output",
|
|
80
|
-
"-o": "output",
|
|
81
|
-
"--interface": "interface",
|
|
82
|
-
"-i": "interface",
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const HELP_FLAGS = new Set(["--help", "-h"]);
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Simple argument parser for Bun CLI
|
|
89
|
-
*/
|
|
90
|
-
function parseCliArgs(args: string[]): CliArgs {
|
|
91
|
-
const result: CliArgs = {};
|
|
92
|
-
|
|
93
|
-
for (let i = 0; i < args.length; i++) {
|
|
94
|
-
const arg = args[i];
|
|
95
|
-
if (arg === "" || arg == null) {
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (HELP_FLAGS.has(arg)) {
|
|
100
|
-
result.help = true;
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const key = FLAG_MAP[arg];
|
|
105
|
-
if (key != null) {
|
|
106
|
-
const value = args[i + 1];
|
|
107
|
-
if (value !== "" && value != null) {
|
|
108
|
-
result[key] = value;
|
|
109
|
-
i += 1;
|
|
110
|
-
}
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (arg.startsWith("-")) {
|
|
115
|
-
throw new Error(`Unknown argument: ${arg}`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return result;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async function main() {
|
|
123
|
-
try {
|
|
124
|
-
const args = parseCliArgs(Bun.argv.slice(2));
|
|
125
|
-
|
|
126
|
-
// Show help
|
|
127
|
-
if (args.help === true) {
|
|
128
|
-
console.log(HELP_TEXT);
|
|
129
|
-
process.exit(0);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Validate required arguments
|
|
133
|
-
if (
|
|
134
|
-
args.name == null ||
|
|
135
|
-
args.name === "" ||
|
|
136
|
-
args.repo == null ||
|
|
137
|
-
args.repo === "" ||
|
|
138
|
-
args.version == null ||
|
|
139
|
-
args.version === ""
|
|
140
|
-
) {
|
|
141
|
-
console.error("Error: Missing required arguments");
|
|
142
|
-
console.error("Required: --name, --repo, --version");
|
|
143
|
-
console.error("\nRun with --help for usage information");
|
|
144
|
-
process.exit(1);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Build chart info
|
|
148
|
-
const chartInfo: ChartInfo = {
|
|
149
|
-
name: args.name,
|
|
150
|
-
chartName: args.chart ?? args.name,
|
|
151
|
-
repoUrl: args.repo,
|
|
152
|
-
version: args.version,
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
// Generate interface name from chart name if not provided
|
|
156
|
-
const interfaceName =
|
|
157
|
-
args.interface ?? `${toPascalCase(args.name)}HelmValues`;
|
|
158
|
-
|
|
159
|
-
console.error(
|
|
160
|
-
`Fetching chart: ${chartInfo.chartName}@${chartInfo.version}`,
|
|
161
|
-
);
|
|
162
|
-
console.error(`Repository: ${chartInfo.repoUrl}`);
|
|
163
|
-
console.error("");
|
|
164
|
-
|
|
165
|
-
// Fetch chart
|
|
166
|
-
const { values, schema, yamlComments } = await fetchHelmChart(chartInfo);
|
|
167
|
-
|
|
168
|
-
console.error("");
|
|
169
|
-
console.error(`Converting to TypeScript interface: ${interfaceName}`);
|
|
170
|
-
|
|
171
|
-
// Convert to TypeScript interface
|
|
172
|
-
const tsInterface = convertToTypeScriptInterface({
|
|
173
|
-
values,
|
|
174
|
-
interfaceName,
|
|
175
|
-
schema,
|
|
176
|
-
yamlComments,
|
|
177
|
-
chartName: args.name,
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
// Generate TypeScript code
|
|
181
|
-
const code = generateTypeScriptCode(tsInterface, args.name);
|
|
182
|
-
|
|
183
|
-
// Write to file or stdout
|
|
184
|
-
if (args.output != null && args.output !== "") {
|
|
185
|
-
await Bun.write(args.output, code);
|
|
186
|
-
console.error("");
|
|
187
|
-
console.error(`✅ Types written to: ${args.output}`);
|
|
188
|
-
} else {
|
|
189
|
-
// Write to stdout (so it can be piped)
|
|
190
|
-
console.log(code);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
process.exit(0);
|
|
194
|
-
} catch (error) {
|
|
195
|
-
const parseResult = ErrorSchema.safeParse(error);
|
|
196
|
-
if (parseResult.success) {
|
|
197
|
-
console.error(`Error: ${parseResult.data.message}`);
|
|
198
|
-
} else {
|
|
199
|
-
console.error(`Error: ${String(error)}`);
|
|
200
|
-
}
|
|
201
|
-
process.exit(1);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Convert a string to PascalCase
|
|
207
|
-
*/
|
|
208
|
-
export function toPascalCase(str: string): string {
|
|
209
|
-
return str
|
|
210
|
-
.split(/[-_\s]+/)
|
|
211
|
-
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
212
|
-
.join("");
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (import.meta.main) {
|
|
216
|
-
void main();
|
|
217
|
-
}
|
package/src/code-generator.ts
DELETED
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import type { TypeScriptInterface } from "./types.ts";
|
|
2
|
-
import {
|
|
3
|
-
StringSchema,
|
|
4
|
-
ArraySchema,
|
|
5
|
-
RecordSchema,
|
|
6
|
-
ActualNumberSchema,
|
|
7
|
-
ActualBooleanSchema,
|
|
8
|
-
} from "./schemas.ts";
|
|
9
|
-
import { capitalizeFirst } from "./utils.ts";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Generate TypeScript code from interface definition
|
|
13
|
-
*/
|
|
14
|
-
export function generateTypeScriptCode(
|
|
15
|
-
mainInterface: TypeScriptInterface,
|
|
16
|
-
chartName: string,
|
|
17
|
-
): string {
|
|
18
|
-
const interfaces: TypeScriptInterface[] = [];
|
|
19
|
-
|
|
20
|
-
// Collect all nested interfaces
|
|
21
|
-
collectNestedInterfaces(mainInterface, interfaces);
|
|
22
|
-
|
|
23
|
-
let code = `// Generated TypeScript types for ${chartName} Helm chart\n\n`;
|
|
24
|
-
|
|
25
|
-
// Generate all interfaces
|
|
26
|
-
for (const iface of interfaces) {
|
|
27
|
-
code += generateInterfaceCode(iface);
|
|
28
|
-
code += "\n";
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Generate parameter type (flattened dot notation)
|
|
32
|
-
code += generateParameterType(mainInterface, chartName);
|
|
33
|
-
|
|
34
|
-
// Add header comment for generated files
|
|
35
|
-
if (code.includes(": any")) {
|
|
36
|
-
code = `// Generated TypeScript types for ${chartName} Helm chart
|
|
37
|
-
|
|
38
|
-
${code.slice(Math.max(0, code.indexOf("\n\n") + 2))}`;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return code;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function collectNestedInterfaces(
|
|
45
|
-
iface: TypeScriptInterface,
|
|
46
|
-
collected: TypeScriptInterface[],
|
|
47
|
-
): void {
|
|
48
|
-
for (const prop of Object.values(iface.properties)) {
|
|
49
|
-
if (prop.nested) {
|
|
50
|
-
collected.push(prop.nested);
|
|
51
|
-
collectNestedInterfaces(prop.nested, collected);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Add main interface last so dependencies come first
|
|
56
|
-
if (!collected.some((i) => i.name === iface.name)) {
|
|
57
|
-
collected.push(iface);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function generateInterfaceCode(iface: TypeScriptInterface): string {
|
|
62
|
-
const hasProperties = Object.keys(iface.properties).length > 0;
|
|
63
|
-
|
|
64
|
-
if (!hasProperties) {
|
|
65
|
-
// Use 'object' for empty interfaces instead of '{}'
|
|
66
|
-
return `export type ${iface.name} = object;\n`;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
let code = `export type ${iface.name} = {\n`;
|
|
70
|
-
|
|
71
|
-
for (const [key, prop] of Object.entries(iface.properties)) {
|
|
72
|
-
const optional = prop.optional ? "?" : "";
|
|
73
|
-
|
|
74
|
-
// Generate JSDoc comment if we have description or default
|
|
75
|
-
if (
|
|
76
|
-
(prop.description != null && prop.description !== "") ||
|
|
77
|
-
prop.default !== undefined
|
|
78
|
-
) {
|
|
79
|
-
code += ` /**\n`;
|
|
80
|
-
|
|
81
|
-
if (prop.description != null && prop.description !== "") {
|
|
82
|
-
// Format multi-line descriptions properly with " * " prefix
|
|
83
|
-
// Escape */ sequences to prevent premature comment closure
|
|
84
|
-
const escapedDescription = prop.description.replaceAll(
|
|
85
|
-
"*/",
|
|
86
|
-
String.raw`*\/`,
|
|
87
|
-
);
|
|
88
|
-
const descLines = escapedDescription.split("\n");
|
|
89
|
-
for (const line of descLines) {
|
|
90
|
-
code += ` * ${line}\n`;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (prop.default !== undefined) {
|
|
95
|
-
const defaultStr = formatDefaultValue(prop.default);
|
|
96
|
-
const hasDescription =
|
|
97
|
-
prop.description != null && prop.description !== "";
|
|
98
|
-
if (defaultStr !== "" && hasDescription && defaultStr != null) {
|
|
99
|
-
code += ` *\n`;
|
|
100
|
-
}
|
|
101
|
-
if (defaultStr !== "" && defaultStr != null) {
|
|
102
|
-
code += ` * @default ${defaultStr}\n`;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
code += ` */\n`;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
code += ` ${key}${optional}: ${prop.type};\n`;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
code += "};\n";
|
|
113
|
-
return code;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Format a default value for display in JSDoc
|
|
118
|
-
*/
|
|
119
|
-
function formatDefaultValue(value: unknown): string | null {
|
|
120
|
-
if (value === null) {
|
|
121
|
-
return "null";
|
|
122
|
-
}
|
|
123
|
-
if (value === undefined) {
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Handle arrays
|
|
128
|
-
const arrayCheck = ArraySchema.safeParse(value);
|
|
129
|
-
if (arrayCheck.success) {
|
|
130
|
-
if (arrayCheck.data.length === 0) {
|
|
131
|
-
return "[]";
|
|
132
|
-
}
|
|
133
|
-
if (arrayCheck.data.length <= 3) {
|
|
134
|
-
try {
|
|
135
|
-
return JSON.stringify(arrayCheck.data);
|
|
136
|
-
} catch {
|
|
137
|
-
return "[...]";
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return `[...] (${String(arrayCheck.data.length)} items)`;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Handle objects
|
|
144
|
-
const recordCheck = RecordSchema.safeParse(value);
|
|
145
|
-
if (recordCheck.success) {
|
|
146
|
-
const keys = Object.keys(recordCheck.data);
|
|
147
|
-
if (keys.length === 0) {
|
|
148
|
-
return "{}";
|
|
149
|
-
}
|
|
150
|
-
if (keys.length <= 3) {
|
|
151
|
-
try {
|
|
152
|
-
return JSON.stringify(recordCheck.data);
|
|
153
|
-
} catch {
|
|
154
|
-
return "{...}";
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return `{...} (${String(keys.length)} keys)`;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Primitives
|
|
161
|
-
const stringCheck = StringSchema.safeParse(value);
|
|
162
|
-
if (stringCheck.success) {
|
|
163
|
-
// Truncate long strings
|
|
164
|
-
if (stringCheck.data.length > 50) {
|
|
165
|
-
return `"${stringCheck.data.slice(0, 47)}..."`;
|
|
166
|
-
}
|
|
167
|
-
return `"${stringCheck.data}"`;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Handle other primitives (numbers, booleans, etc.)
|
|
171
|
-
const numberCheck = ActualNumberSchema.safeParse(value);
|
|
172
|
-
if (numberCheck.success) {
|
|
173
|
-
return String(numberCheck.data);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const booleanCheck = ActualBooleanSchema.safeParse(value);
|
|
177
|
-
if (booleanCheck.success) {
|
|
178
|
-
return String(booleanCheck.data);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Fallback for unknown types - try JSON.stringify
|
|
182
|
-
try {
|
|
183
|
-
return JSON.stringify(value);
|
|
184
|
-
} catch {
|
|
185
|
-
return "unknown";
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function generateParameterType(
|
|
190
|
-
iface: TypeScriptInterface,
|
|
191
|
-
chartName: string,
|
|
192
|
-
): string {
|
|
193
|
-
const parameterKeys = flattenInterfaceKeys(iface);
|
|
194
|
-
|
|
195
|
-
const normalizedChartName = capitalizeFirst(chartName).replaceAll("-", "");
|
|
196
|
-
let code = `export type ${normalizedChartName}HelmParameters = {\n`;
|
|
197
|
-
|
|
198
|
-
for (const key of parameterKeys) {
|
|
199
|
-
code += ` "${key}"?: string;\n`;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
code += "};\n";
|
|
203
|
-
|
|
204
|
-
return code;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function flattenInterfaceKeys(
|
|
208
|
-
iface: TypeScriptInterface,
|
|
209
|
-
prefix = "",
|
|
210
|
-
): string[] {
|
|
211
|
-
const keys: string[] = [];
|
|
212
|
-
|
|
213
|
-
for (const [key, prop] of Object.entries(iface.properties)) {
|
|
214
|
-
// Remove quotes from key for parameter names
|
|
215
|
-
const cleanKey = key.replaceAll('"', "");
|
|
216
|
-
const fullKey = prefix ? `${prefix}.${cleanKey}` : cleanKey;
|
|
217
|
-
|
|
218
|
-
if (prop.nested) {
|
|
219
|
-
keys.push(...flattenInterfaceKeys(prop.nested, fullKey));
|
|
220
|
-
} else {
|
|
221
|
-
keys.push(fullKey);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return keys;
|
|
226
|
-
}
|
package/src/comment-parser.ts
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
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
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Clean up YAML comment text for use in JSDoc
|
|
48
|
-
*/
|
|
49
|
-
export function cleanYAMLComment(comment: string): string {
|
|
50
|
-
const lines = comment.split("\n").map((line) => stripCommentMarkers(line));
|
|
51
|
-
|
|
52
|
-
// Filter and clean lines
|
|
53
|
-
const cleaned: string[] = [];
|
|
54
|
-
let inCodeBlock = false;
|
|
55
|
-
|
|
56
|
-
for (const currentLine of lines) {
|
|
57
|
-
if (currentLine.length === 0) {
|
|
58
|
-
if (inCodeBlock) {
|
|
59
|
-
inCodeBlock = false;
|
|
60
|
-
}
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (currentLine.startsWith("@default")) {
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (isExampleLine(currentLine)) {
|
|
69
|
-
inCodeBlock = true;
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (inCodeBlock) {
|
|
74
|
-
if (looksLikeProse(currentLine)) {
|
|
75
|
-
inCodeBlock = false;
|
|
76
|
-
} else {
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
cleaned.push(currentLine);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return cleaned.join("\n").trim();
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Parse YAML comments and associate them with keys
|
|
89
|
-
* Exported for testing purposes
|
|
90
|
-
*/
|
|
91
|
-
export function parseYAMLComments(yamlContent: string): Map<string, string> {
|
|
92
|
-
const lines = yamlContent.split("\n");
|
|
93
|
-
const comments = new Map<string, string>();
|
|
94
|
-
let pendingComments: { text: string; indent: number }[] = [];
|
|
95
|
-
const indentStack: { indent: number; key: string }[] = [];
|
|
96
|
-
|
|
97
|
-
for (const line of lines) {
|
|
98
|
-
const currentLine = line;
|
|
99
|
-
const trimmed = currentLine.trim();
|
|
100
|
-
|
|
101
|
-
// Skip empty lines
|
|
102
|
-
if (!trimmed) {
|
|
103
|
-
// Empty lines break comment association only if we already have comments
|
|
104
|
-
// This allows comments separated by blank lines to still be associated
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Check if line is a comment
|
|
109
|
-
if (trimmed.startsWith("#")) {
|
|
110
|
-
const comment = trimmed.slice(1).trim();
|
|
111
|
-
if (comment) {
|
|
112
|
-
// Track the indentation level of the comment
|
|
113
|
-
const commentIndent = currentLine.search(/\S/);
|
|
114
|
-
pendingComments.push({ text: comment, indent: commentIndent });
|
|
115
|
-
}
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Check if line has a key
|
|
120
|
-
const keyMatch = /^(\s*)([\w-]+)\s*:/.exec(currentLine);
|
|
121
|
-
if (keyMatch) {
|
|
122
|
-
const indent = keyMatch[1]?.length ?? 0;
|
|
123
|
-
const key = keyMatch[2];
|
|
124
|
-
if (key === "" || key == null) {
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Update indent stack
|
|
129
|
-
const lastIndent = indentStack.at(-1);
|
|
130
|
-
while (
|
|
131
|
-
lastIndent &&
|
|
132
|
-
indentStack.length > 0 &&
|
|
133
|
-
lastIndent.indent >= indent
|
|
134
|
-
) {
|
|
135
|
-
indentStack.pop();
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Build full key path
|
|
139
|
-
const keyPath =
|
|
140
|
-
indentStack.length > 0
|
|
141
|
-
? `${indentStack.map((s) => s.key).join(".")}.${key}`
|
|
142
|
-
: key;
|
|
143
|
-
|
|
144
|
-
// Check for inline comment
|
|
145
|
-
const inlineCommentMatch = /#\s*(\S.*)$/.exec(currentLine);
|
|
146
|
-
if (inlineCommentMatch?.[1] != null && inlineCommentMatch[1] !== "") {
|
|
147
|
-
pendingComments.push({ text: inlineCommentMatch[1].trim(), indent });
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Filter pending comments to only those at the same or shallower indent level as this key
|
|
151
|
-
// This prevents comments from deeper nested properties being associated with a shallower property
|
|
152
|
-
const relevantComments = pendingComments.filter(
|
|
153
|
-
(c) => c.indent <= indent,
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
// Associate relevant comments with this key
|
|
157
|
-
if (relevantComments.length > 0) {
|
|
158
|
-
// Join and clean comments
|
|
159
|
-
const commentText = cleanYAMLComment(
|
|
160
|
-
relevantComments.map((c) => c.text).join("\n"),
|
|
161
|
-
);
|
|
162
|
-
if (commentText) {
|
|
163
|
-
comments.set(keyPath, commentText);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Clear pending comments after processing
|
|
168
|
-
pendingComments = [];
|
|
169
|
-
|
|
170
|
-
// Add to indent stack
|
|
171
|
-
indentStack.push({ indent, key });
|
|
172
|
-
} else {
|
|
173
|
-
// If we encounter a non-comment, non-key line, clear pending comments
|
|
174
|
-
// This handles list items and other YAML structures
|
|
175
|
-
pendingComments = [];
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return comments;
|
|
180
|
-
}
|