@v19i/openapi-enum-arrays
Version:
A @hey-api/openapi-ts plugin that generates typed enum arrays with intelligent conflict resolution
205 lines (204 loc) • 7.63 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CodeGenerator = void 0;
class CodeGenerator {
generateEnumArrays(enums, options = {}) {
const { arrayPrefix = "" } = options;
const deduplicatedEnums = this.deduplicateEnums(enums);
const header = this.generateHeader();
const arrays = this.generateArrayConstants(deduplicatedEnums, arrayPrefix);
return [header, arrays].filter(Boolean).join("\n\n");
}
deduplicateEnums(enums) {
const enumMap = new Map();
const nameGroups = new Map();
for (const enumInfo of enums) {
if (!nameGroups.has(enumInfo.name)) {
nameGroups.set(enumInfo.name, []);
}
nameGroups.get(enumInfo.name).push(enumInfo);
}
const processedEnums = [];
for (const [_name, enumsWithSameName] of nameGroups) {
if (enumsWithSameName.length === 1) {
processedEnums.push(enumsWithSameName[0]);
}
else {
for (const enumInfo of enumsWithSameName) {
const contextualName = this.generateContextualName(enumInfo);
processedEnums.push({
...enumInfo,
name: contextualName,
});
}
}
}
const valueGroups = new Map();
for (const enumInfo of processedEnums) {
const sortedValues = [...enumInfo.values].sort().join("|");
if (!valueGroups.has(sortedValues)) {
valueGroups.set(sortedValues, []);
}
valueGroups.get(sortedValues).push(enumInfo);
}
for (const [valuesKey, enumsWithSameValues] of valueGroups) {
if (enumsWithSameValues.length === 1) {
const enumInfo = enumsWithSameValues[0];
const uniqueKey = `${enumInfo.name}:${valuesKey}`;
enumMap.set(uniqueKey, enumInfo);
}
else {
const bestEnum = this.chooseBestEnumForMerging(enumsWithSameValues);
const uniqueKey = `${bestEnum.name}:${valuesKey}`;
console.log(`Plugin: Merged ${enumsWithSameValues.length} duplicate enum arrays into ${bestEnum.name}`);
enumMap.set(uniqueKey, bestEnum);
}
}
return Array.from(enumMap.values());
}
chooseBestEnumForMerging(enums) {
const genericTerms = [
"data",
"response",
"request",
"query",
"body",
"params",
];
const scored = enums.map((enumInfo) => {
let score = 0;
const lowerName = enumInfo.name.toLowerCase();
score += 50 - enumInfo.name.length;
if (genericTerms.some((term) => lowerName.includes(term))) {
score -= 20;
}
if (lowerName.includes("request") ||
lowerName.includes("response") ||
lowerName.includes("query")) {
score += 10;
}
return { enumInfo, score };
});
scored.sort((a, b) => b.score - a.score);
return scored[0].enumInfo;
}
generateFullPathName(enumInfo) {
if (!enumInfo.originalTypePath) {
return enumInfo.name;
}
const cleaned = enumInfo.originalTypePath
.replace(/[^a-zA-Z0-9.]/g, "")
.split(".")
.map((part, index) => {
if (index === 0) {
return part.charAt(0).toLowerCase() + part.slice(1);
}
return part.charAt(0).toUpperCase() + part.slice(1);
})
.join("");
return cleaned || enumInfo.name;
}
generateContextualName(enumInfo) {
const context = this.extractContextForConflict(enumInfo.originalTypePath);
const fieldName = this.extractFieldName(enumInfo.originalTypePath);
if (context && fieldName) {
return `${context}${this.capitalizeFirst(fieldName)}`;
}
if (context) {
return `${context}${this.capitalizeFirst(enumInfo.name)}`;
}
return enumInfo.name;
}
extractContextForConflict(originalTypePath) {
if (originalTypePath.includes(".query.")) {
return "query";
}
if (originalTypePath.includes(".body.")) {
return "request";
}
if (originalTypePath.includes("ResponseData.")) {
return "response";
}
if (originalTypePath.startsWith("Get") &&
originalTypePath.includes("Data.")) {
return "query";
}
if ((originalTypePath.startsWith("Post") ||
originalTypePath.startsWith("Put")) &&
originalTypePath.includes("Data.")) {
return "request";
}
if (originalTypePath.includes("query")) {
return "query";
}
if (originalTypePath.includes("body") ||
originalTypePath.includes("Request")) {
return "request";
}
if (originalTypePath.includes("Response")) {
return "response";
}
const match = originalTypePath.match(/^(\w+)\./);
if (match) {
const objectName = match[1].toLowerCase();
if (objectName.includes("query"))
return "query";
if (objectName.includes("request") ||
objectName.includes("post") ||
objectName.includes("put"))
return "request";
if (objectName.includes("response") || objectName.includes("get"))
return "response";
}
return null;
}
extractFieldName(originalTypePath) {
const parts = originalTypePath.split(".");
return parts[parts.length - 1] || null;
}
generateHeader() {
return "// This file is auto-generated by openapi-enum-arrays";
}
generateArrayConstants(enums, arrayPrefix) {
return enums
.map((enumInfo) => {
const semanticName = this.toArrayName(enumInfo.name);
const arrayName = arrayPrefix + semanticName;
const values = enumInfo.values
.sort()
.map((v) => `'${v}'`)
.join(", ");
return `export const ${arrayName} = [${values}] as const`;
})
.join("\n");
}
toArrayName(typeName) {
// Convert to camelCase and add appropriate suffix
const camelCase = typeName.charAt(0).toLowerCase() + typeName.slice(1);
// Add pluralization based on semantic meaning
if (camelCase.endsWith("Status")) {
return camelCase.replace("Status", "Statuses");
}
if (camelCase.endsWith("Type")) {
return camelCase.replace("Type", "Types");
}
if (camelCase.endsWith("Model")) {
return camelCase.replace("Model", "Models");
}
if (camelCase.endsWith("Role")) {
return camelCase.replace("Role", "Roles");
}
if (camelCase.endsWith("Source")) {
return camelCase.replace("Source", "Sources");
}
if (camelCase.endsWith("Mode")) {
return camelCase.replace("Mode", "Modes");
}
// Default fallback - add 'Values' suffix
return `${camelCase}Values`;
}
capitalizeFirst(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
}
exports.CodeGenerator = CodeGenerator;