Skip to content

Commit 4c2daf5

Browse files
maxholmanclaude
andcommitted
feat: emit runtime enum values as const arrays
Consumers need iterable enum values at runtime (e.g. for dropdown options) but TypeScript union types are erased. Generate an enums.ts file with `as const` arrays for each enum schema. Skip writing the file when no enums exist. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ab01c11 commit 4c2daf5

3 files changed

Lines changed: 68 additions & 2 deletions

File tree

lib/build.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,22 @@ export async function build(
2121
*
2222
*/`.trim();
2323

24-
const { commandsFile, typesFile, mainFile, valibotFile, honoValibotFile } =
25-
await processOpenApiDocument(outputDir, apischema.default, tags);
24+
const {
25+
commandsFile,
26+
typesFile,
27+
mainFile,
28+
valibotFile,
29+
honoValibotFile,
30+
enumsFile,
31+
} = await processOpenApiDocument(outputDir, apischema.default, tags);
2632

2733
const files = [
2834
commandsFile,
2935
typesFile,
3036
mainFile,
3137
valibotFile,
3238
honoValibotFile,
39+
...(enumsFile.getStatements().length > 0 ? [enumsFile] : []),
3340
];
3441

3542
commandsFile.insertStatements(0, "/** eslint-disable max-classes */");

lib/process-document.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
} from "./hono-valibot.ts";
2626
import { registerTypesFromSchema, schemaToType } from "./process-schema.ts";
2727
import {
28+
camelCase,
2829
castToValidJsIdentifier,
2930
getDependents,
3031
iife,
@@ -108,6 +109,13 @@ export async function processOpenApiDocument(
108109
overwrite: true,
109110
});
110111

112+
// Enums file for runtime enum values
113+
const enumsFile = project.createSourceFile(
114+
join(outputDir, "enums.ts"),
115+
"",
116+
{ overwrite: true },
117+
);
118+
111119
// Validators file for Valibot schemas
112120
const valibotFile = createValibotFile(project, outputDir);
113121

@@ -243,6 +251,52 @@ export async function processOpenApiDocument(
243251
schemaName,
244252
schemaObject,
245253
);
254+
255+
// Add enum values to enums file
256+
if (
257+
!("$ref" in schemaObject) &&
258+
"enum" in schemaObject &&
259+
Array.isArray(schemaObject.enum)
260+
) {
261+
const values = schemaObject.enum.filter(
262+
(v): v is string => v !== null,
263+
);
264+
265+
if (values.length > 0) {
266+
enumsFile.addVariableStatement({
267+
isExported: true,
268+
declarationKind: VariableDeclarationKind.Const,
269+
docs: schemaObject.description
270+
? [
271+
{
272+
description: wordWrap(schemaObject.description),
273+
tags: [
274+
...(schemaObject.deprecated
275+
? [{ tagName: "deprecated" }]
276+
: []),
277+
].filter(Boolean),
278+
},
279+
]
280+
: [],
281+
declarations: [
282+
{
283+
name: camelCase(schemaName),
284+
initializer: Writers.assertion(
285+
(writer) => {
286+
writer.write("[");
287+
values.forEach((value, index) => {
288+
writer.write(JSON.stringify(value));
289+
if (index < values.length - 1) writer.write(", ");
290+
});
291+
writer.write("]");
292+
},
293+
"const",
294+
),
295+
},
296+
],
297+
});
298+
}
299+
}
246300
}
247301

248302
for (const [path, pathItemObject] of Object.entries<oas31.PathItemObject>(
@@ -1073,5 +1127,6 @@ export async function processOpenApiDocument(
10731127
mainFile,
10741128
valibotFile,
10751129
honoValibotFile,
1130+
enumsFile,
10761131
};
10771132
}

lib/utils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ export function refToName(ref: string): string {
6969
return name;
7070
}
7171

72+
export function camelCase(...str: string[]): string {
73+
return camelcase(str.flatMap((s) => s.split("/")));
74+
}
75+
7276
export function pascalCase(...str: string[]): string {
7377
return camelcase(
7478
str.flatMap((s) => s.split("/")),

0 commit comments

Comments
 (0)