@shepherdjerred/helm-types 0.0.0-dev.706
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 +209 -0
- package/dist/cli.js +22085 -0
- package/dist/index.js +23 -0
- package/package.json +66 -0
- package/src/chart-fetcher.ts +171 -0
- package/src/chart-info-parser.ts +72 -0
- package/src/cli.ts +215 -0
- package/src/code-generator.ts +226 -0
- package/src/comment-parser.ts +180 -0
- package/src/config.ts +147 -0
- package/src/helm-types.ts +16 -0
- package/src/index.ts +28 -0
- package/src/interface-generator.ts +238 -0
- package/src/reset.d.ts +1 -0
- package/src/schemas.ts +39 -0
- package/src/type-converter-helpers.ts +180 -0
- package/src/type-converter.ts +509 -0
- package/src/type-inference.ts +548 -0
- package/src/types.ts +38 -0
- package/src/utils.ts +76 -0
- package/src/yaml-comment-filters.ts +103 -0
- package/src/yaml-comment-regex-parser.ts +150 -0
- package/src/yaml-comments.ts +507 -0
- package/src/yaml-preprocess.ts +235 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
4
|
+
var __returnValue = (v) => v;
|
|
5
|
+
function __exportSetter(name, newValue) {
|
|
6
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
7
|
+
}
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, {
|
|
11
|
+
get: all[name],
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
set: __exportSetter.bind(all, name)
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
18
|
+
|
|
19
|
+
// src/index.ts
|
|
20
|
+
var HELM_TYPES_PACKAGE_VERSION = "1.1.0";
|
|
21
|
+
export {
|
|
22
|
+
HELM_TYPES_PACKAGE_VERSION
|
|
23
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shepherdjerred/helm-types",
|
|
3
|
+
"version": "0.0.0-dev.706",
|
|
4
|
+
"description": "Generate TypeScript types from Helm chart values.yaml and values.schema.json",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "src/index.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./src/index.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"helm-types": "dist/cli.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"src",
|
|
20
|
+
"!src/**/*.test.ts",
|
|
21
|
+
"!src/__snapshots__"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "bun build src/index.ts src/cli.ts --outdir dist --target node",
|
|
25
|
+
"generate": "bun run src/cli.ts",
|
|
26
|
+
"test": "bun test",
|
|
27
|
+
"test:helm-types": "bun test src/helm-types.test.ts",
|
|
28
|
+
"test:cli": "bun test src/cli.test.ts",
|
|
29
|
+
"lint": "bunx eslint .",
|
|
30
|
+
"lint:fix": "bunx eslint . --fix",
|
|
31
|
+
"typecheck": "bunx tsc --noEmit",
|
|
32
|
+
"prepublishOnly": "bun run build"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"helm",
|
|
36
|
+
"kubernetes",
|
|
37
|
+
"typescript",
|
|
38
|
+
"types",
|
|
39
|
+
"codegen",
|
|
40
|
+
"k8s"
|
|
41
|
+
],
|
|
42
|
+
"author": "Jerred Shepherd",
|
|
43
|
+
"license": "GPL-3.0",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "https://github.com/shepherdjerred/homelab.git",
|
|
47
|
+
"directory": "src/helm-types"
|
|
48
|
+
},
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/shepherdjerred/homelab/issues"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://github.com/shepherdjerred/homelab/tree/main/src/helm-types#readme",
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=18"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"yaml": "^2.8.1",
|
|
61
|
+
"zod": "^4.1.11"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"jiti": "^2.6.1"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// Using Bun.$ for path operations instead of node:path
|
|
2
|
+
import { parse as yamlParse } from "yaml";
|
|
3
|
+
import type { ChartInfo, JSONSchemaProperty } from "./types.ts";
|
|
4
|
+
import { HelmValueSchema, RecordSchema, ErrorSchema } from "./schemas.ts";
|
|
5
|
+
import type { HelmValue } from "./schemas.ts";
|
|
6
|
+
import { parseYAMLComments } from "./yaml-comments.ts";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Load JSON schema if it exists in the chart
|
|
10
|
+
*/
|
|
11
|
+
async function loadJSONSchema(
|
|
12
|
+
chartPath: string,
|
|
13
|
+
): Promise<JSONSchemaProperty | null> {
|
|
14
|
+
try {
|
|
15
|
+
const schemaPath = `${chartPath}/values.schema.json`;
|
|
16
|
+
const schemaContent = await Bun.file(schemaPath).text();
|
|
17
|
+
const parsed: unknown = JSON.parse(schemaContent);
|
|
18
|
+
// Validate that parsed is an object
|
|
19
|
+
const recordCheck = RecordSchema.safeParse(parsed);
|
|
20
|
+
if (!recordCheck.success) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
// Note: JSONSchemaProperty is a structural type
|
|
24
|
+
const schema: JSONSchemaProperty = recordCheck.data;
|
|
25
|
+
console.log(` 📋 Loaded values.schema.json`);
|
|
26
|
+
return schema;
|
|
27
|
+
} catch {
|
|
28
|
+
// Schema doesn't exist or couldn't be parsed - that's okay
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Run a command and return its output using Bun
|
|
35
|
+
*/
|
|
36
|
+
async function runCommand(command: string, args: string[]): Promise<string> {
|
|
37
|
+
try {
|
|
38
|
+
const proc = Bun.spawn([command, ...args], {
|
|
39
|
+
stdout: "pipe",
|
|
40
|
+
stderr: "inherit",
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const output = await new Response(proc.stdout).text();
|
|
44
|
+
const exitCode = await proc.exited;
|
|
45
|
+
|
|
46
|
+
if (exitCode === 0) {
|
|
47
|
+
return output;
|
|
48
|
+
} else {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`Command "${command} ${args.join(" ")}" failed with code ${exitCode.toString()}`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
const parseResult = ErrorSchema.safeParse(error);
|
|
55
|
+
const errorMessage = parseResult.success
|
|
56
|
+
? parseResult.data.message
|
|
57
|
+
: String(error);
|
|
58
|
+
throw new Error(
|
|
59
|
+
`Failed to spawn command "${command} ${args.join(" ")}": ${errorMessage}`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Fetch a Helm chart and extract its values.yaml and optional schema
|
|
66
|
+
*/
|
|
67
|
+
export async function fetchHelmChart(chart: ChartInfo): Promise<{
|
|
68
|
+
values: HelmValue;
|
|
69
|
+
schema: JSONSchemaProperty | null;
|
|
70
|
+
yamlComments: Map<string, string>;
|
|
71
|
+
}> {
|
|
72
|
+
const pwd = Bun.env["PWD"] ?? process.cwd();
|
|
73
|
+
const tempDir = `${pwd}/temp/helm-${chart.name}`;
|
|
74
|
+
const repoName = `temp-repo-${chart.name}-${String(Date.now())}`;
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
// Ensure temp directory exists
|
|
78
|
+
await Bun.$`mkdir -p ${tempDir}`.quiet();
|
|
79
|
+
|
|
80
|
+
console.log(` 📦 Adding Helm repo: ${chart.repoUrl}`);
|
|
81
|
+
// Add the helm repo
|
|
82
|
+
await runCommand("helm", ["repo", "add", repoName, chart.repoUrl]);
|
|
83
|
+
|
|
84
|
+
console.log(` 🔄 Updating Helm repos...`);
|
|
85
|
+
// Update repo
|
|
86
|
+
await runCommand("helm", ["repo", "update"]);
|
|
87
|
+
|
|
88
|
+
console.log(` ⬇️ Pulling chart ${chart.chartName}:${chart.version}...`);
|
|
89
|
+
// Pull the chart
|
|
90
|
+
await runCommand("helm", [
|
|
91
|
+
"pull",
|
|
92
|
+
`${repoName}/${chart.chartName}`,
|
|
93
|
+
"--version",
|
|
94
|
+
chart.version,
|
|
95
|
+
"--destination",
|
|
96
|
+
tempDir,
|
|
97
|
+
"--untar",
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
// Read values.yaml
|
|
101
|
+
const valuesPath = `${tempDir}/${chart.chartName}/values.yaml`;
|
|
102
|
+
console.log(` 📖 Reading values.yaml from ${valuesPath}`);
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const valuesContent = await Bun.file(valuesPath).text();
|
|
106
|
+
|
|
107
|
+
// Parse YAML comments
|
|
108
|
+
const yamlComments = parseYAMLComments(valuesContent);
|
|
109
|
+
console.log(
|
|
110
|
+
` 💬 Extracted ${String(yamlComments.size)} comments from values.yaml`,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// Parse YAML using yaml package
|
|
114
|
+
const parsedValues = yamlParse(valuesContent) as unknown;
|
|
115
|
+
console.log(` ✅ Successfully parsed values.yaml`);
|
|
116
|
+
const recordParseResult = RecordSchema.safeParse(parsedValues);
|
|
117
|
+
if (recordParseResult.success) {
|
|
118
|
+
console.log(
|
|
119
|
+
` 🔍 Parsed values keys: ${Object.keys(recordParseResult.data)
|
|
120
|
+
.slice(0, 10)
|
|
121
|
+
.join(
|
|
122
|
+
", ",
|
|
123
|
+
)}${Object.keys(recordParseResult.data).length > 10 ? "..." : ""}`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Check if parsedValues is a valid object using Zod before validation
|
|
128
|
+
if (!recordParseResult.success) {
|
|
129
|
+
console.warn(
|
|
130
|
+
` ⚠️ Parsed values is not a valid record object: ${String(parsedValues)}`,
|
|
131
|
+
);
|
|
132
|
+
return { values: {}, schema: null, yamlComments: new Map() };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Validate and parse with Zod for runtime type safety
|
|
136
|
+
const parseResult = HelmValueSchema.safeParse(recordParseResult.data);
|
|
137
|
+
|
|
138
|
+
// Try to load JSON schema
|
|
139
|
+
const chartPath = `${tempDir}/${chart.chartName}`;
|
|
140
|
+
const schema = await loadJSONSchema(chartPath);
|
|
141
|
+
|
|
142
|
+
if (parseResult.success) {
|
|
143
|
+
console.log(` ✅ Zod validation successful`);
|
|
144
|
+
return { values: parseResult.data, schema, yamlComments };
|
|
145
|
+
} else {
|
|
146
|
+
console.warn(` ⚠️ Zod validation failed for ${chart.name}:`);
|
|
147
|
+
console.warn(
|
|
148
|
+
` First few errors:`,
|
|
149
|
+
parseResult.error.issues.slice(0, 3),
|
|
150
|
+
);
|
|
151
|
+
console.warn(
|
|
152
|
+
` ⚠️ Falling back to unvalidated object for type generation`,
|
|
153
|
+
);
|
|
154
|
+
// Return the validated record data from the successful parse result
|
|
155
|
+
return { values: recordParseResult.data, schema, yamlComments };
|
|
156
|
+
}
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.warn(` ⚠️ Failed to read/parse values.yaml: ${String(error)}`);
|
|
159
|
+
return { values: {}, schema: null, yamlComments: new Map() };
|
|
160
|
+
}
|
|
161
|
+
} finally {
|
|
162
|
+
// Cleanup
|
|
163
|
+
try {
|
|
164
|
+
console.log(` 🧹 Cleaning up...`);
|
|
165
|
+
await runCommand("helm", ["repo", "remove", repoName]);
|
|
166
|
+
await Bun.$`rm -rf ${tempDir}`.quiet();
|
|
167
|
+
} catch (cleanupError) {
|
|
168
|
+
console.warn(`Cleanup failed for ${chart.name}:`, String(cleanupError));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { ChartInfo } from "./types.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parse chart information from versions.ts comments and values
|
|
5
|
+
*/
|
|
6
|
+
export async function parseChartInfoFromVersions(
|
|
7
|
+
versionsPath = "src/versions.ts",
|
|
8
|
+
): Promise<ChartInfo[]> {
|
|
9
|
+
const content = await Bun.file(versionsPath).text();
|
|
10
|
+
const lines = content.split("\n");
|
|
11
|
+
const charts: ChartInfo[] = [];
|
|
12
|
+
|
|
13
|
+
for (let i = 0; i < lines.length; i++) {
|
|
14
|
+
const line = lines[i];
|
|
15
|
+
const nextLine = lines[i + 1];
|
|
16
|
+
|
|
17
|
+
// Look for renovate comments that indicate Helm charts
|
|
18
|
+
if (
|
|
19
|
+
line == null ||
|
|
20
|
+
!line.includes("renovate: datasource=helm") ||
|
|
21
|
+
nextLine == null ||
|
|
22
|
+
nextLine === ""
|
|
23
|
+
) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const repoUrlMatch = /registryUrl=(\S+)/.exec(line);
|
|
28
|
+
const versionKeyMatch = /^\s*"?([^":\s]+)"?:/.exec(nextLine);
|
|
29
|
+
if (!repoUrlMatch || !versionKeyMatch) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const repoUrl = repoUrlMatch[1];
|
|
34
|
+
const versionKey = versionKeyMatch[1];
|
|
35
|
+
if (
|
|
36
|
+
repoUrl == null ||
|
|
37
|
+
repoUrl === "" ||
|
|
38
|
+
versionKey == null ||
|
|
39
|
+
versionKey === ""
|
|
40
|
+
) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Extract version value
|
|
45
|
+
const versionMatch = /:\s*"([^"]+)"/.exec(nextLine);
|
|
46
|
+
if (!versionMatch) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const version = versionMatch[1];
|
|
51
|
+
if (version == null || version === "") {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Try to determine chart name from the version key or URL
|
|
56
|
+
let chartName = versionKey;
|
|
57
|
+
|
|
58
|
+
// Handle special cases like "argo-cd" vs "argocd"
|
|
59
|
+
if (versionKey === "argo-cd") {
|
|
60
|
+
chartName = "argo-cd";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
charts.push({
|
|
64
|
+
name: versionKey,
|
|
65
|
+
repoUrl: repoUrl.replace(/\/$/, ""), // Remove trailing slash
|
|
66
|
+
version,
|
|
67
|
+
chartName,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return charts;
|
|
72
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
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 == null || arg === "") {
|
|
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 != null && value !== "") {
|
|
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
|
+
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
|
+
void main();
|