diff --git a/.changeset/preserve-mixed-case-enum-keys.md b/.changeset/preserve-mixed-case-enum-keys.md new file mode 100644 index 000000000..44b65b2df --- /dev/null +++ b/.changeset/preserve-mixed-case-enum-keys.md @@ -0,0 +1,5 @@ +--- +"swagger-typescript-api": patch +--- + +Fix enum key formatting to preserve mixed case names with underscores (e.g., `_123ValCamelCase`, `Val_Snake_Case`, `_Val_12_CamelCase`) while still transforming pure lowercase snake_case to PascalCase (e.g., `local_only` → `LocalOnly`) diff --git a/src/type-name-formatter.ts b/src/type-name-formatter.ts index 58743a863..4030c6e66 100644 --- a/src/type-name-formatter.ts +++ b/src/type-name-formatter.ts @@ -36,6 +36,17 @@ export class TypeNameFormatter { return lodash.compact([typePrefix, name, typeSuffix]).join("_"); } + // for enum keys, preserve mixed case names with underscores like Val_Snake_Case, _Val_12_CamelCase + // but transform pure lowercase snake_case like local_only to PascalCase + if ( + schemaType === "enum-key" && + /^(?!\d)([A-Za-z0-9_]{1,})$/g.test(name) && + name.includes("_") && + /[A-Z]/.test(name) + ) { + return lodash.compact([typePrefix, name, typeSuffix]).join("_"); + } + if (this.formattedModelNamesMap.has(hashKey)) { return this.formattedModelNamesMap.get(hashKey); } diff --git a/tests/spec/enumIncludesNumber/__snapshots__/basic.test.ts.snap b/tests/spec/enumIncludesNumber/__snapshots__/basic.test.ts.snap index 571ebdb11..cda046887 100644 --- a/tests/spec/enumIncludesNumber/__snapshots__/basic.test.ts.snap +++ b/tests/spec/enumIncludesNumber/__snapshots__/basic.test.ts.snap @@ -1,5 +1,89 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`basic > edge case with prefix and suffix 1`] = ` +"/* eslint-disable */ +/* tslint:disable */ +// @ts-nocheck +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +/** Pure lowercase snake_case values that should be transformed to PascalCase */ +export enum LowercaseSnakeCaseEnum { + VALLocalOnlyCaSeCamelCase = "local_only", + VALPhoneNumberCaSeCamelCase = "phone_number", + VALWaitDependencyCaSeCamelCase = "wait_dependency", + VALAuthFailedCaSeCamelCase = "auth_failed", + VALWaitingToPushCaSeCamelCase = "waiting_to_push", +} + +export enum StringEnumIncludesNumbersAndUnderscore { + /** Foo */ + V_aL_VAL_1_Ca_Se_CamelCase = "VAL_1", + /** Bar */ + V_aL_VAL_2_Ca_Se_CamelCase = "VAL_2", + /** Baz */ + V_aL_VAL_3_Ca_Se_CamelCase = "VAL_3", + V_aL__1_VAL_Ca_Se_CamelCase = "_1_VAL", + V_aL_A_1_B_2_C_3_Ca_Se_CamelCase = "A_1_B_2_C_3", + V_aL__A_1_B_2_C_3_Ca_Se_CamelCase = "_A_1_B_2_C_3", + V_aL__1_A_2_B_3_C_Ca_Se_CamelCase = "_1_A_2_B_3_C", + V_aL__123ValCamelCase_Ca_Se_CamelCase = "_123ValCamelCase", + V_aL_Val_Snake_Case_Ca_Se_CamelCase = "Val_Snake_Case", + V_aL_Val_12_CamelCase_Ca_Se_CamelCase = "Val_12_CamelCase", + V_aL__Val_12_Snake_Case_Ca_Se_CamelCase = "_Val_12_Snake_Case", + V_aL__Val_12_CamelCase_Ca_Se_CamelCase = "_Val_12_CamelCase", +} +" +`; + +exports[`basic > transform pure lowercase snake_case to PascalCase 1`] = ` +"/* eslint-disable */ +/* tslint:disable */ +// @ts-nocheck +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +/** Pure lowercase snake_case values that should be transformed to PascalCase */ +export enum LowercaseSnakeCaseEnum { + LocalOnly = "local_only", + PhoneNumber = "phone_number", + WaitDependency = "wait_dependency", + AuthFailed = "auth_failed", + WaitingToPush = "waiting_to_push", +} + +export enum StringEnumIncludesNumbersAndUnderscore { + /** Foo */ + VAL_1 = "VAL_1", + /** Bar */ + VAL_2 = "VAL_2", + /** Baz */ + VAL_3 = "VAL_3", + _1_VAL = "_1_VAL", + A_1_B_2_C_3 = "A_1_B_2_C_3", + _A_1_B_2_C_3 = "_A_1_B_2_C_3", + _1_A_2_B_3_C = "_1_A_2_B_3_C", + _123ValCamelCase = "_123ValCamelCase", + Val_Snake_Case = "Val_Snake_Case", + Val_12_CamelCase = "Val_12_CamelCase", + _Val_12_Snake_Case = "_Val_12_Snake_Case", + _Val_12_CamelCase = "_Val_12_CamelCase", +} +" +`; + exports[`basic > use x-enumNames as the key for enum 1`] = ` "/* eslint-disable */ /* tslint:disable */ @@ -13,6 +97,15 @@ exports[`basic > use x-enumNames as the key for enum 1`] = ` * --------------------------------------------------------------- */ +/** Pure lowercase snake_case values that should be transformed to PascalCase */ +export enum LowercaseSnakeCaseEnum { + LocalOnly = "local_only", + PhoneNumber = "phone_number", + WaitDependency = "wait_dependency", + AuthFailed = "auth_failed", + WaitingToPush = "waiting_to_push", +} + export enum StringEnumIncludesNumbersAndUnderscore { /** Foo */ VAL_1 = "VAL_1", @@ -24,6 +117,11 @@ export enum StringEnumIncludesNumbersAndUnderscore { A_1_B_2_C_3 = "A_1_B_2_C_3", _A_1_B_2_C_3 = "_A_1_B_2_C_3", _1_A_2_B_3_C = "_1_A_2_B_3_C", + _123ValCamelCase = "_123ValCamelCase", + Val_Snake_Case = "Val_Snake_Case", + Val_12_CamelCase = "Val_12_CamelCase", + _Val_12_Snake_Case = "_Val_12_Snake_Case", + _Val_12_CamelCase = "_Val_12_CamelCase", } " `; diff --git a/tests/spec/enumIncludesNumber/basic.test.ts b/tests/spec/enumIncludesNumber/basic.test.ts index 404e7688a..de44025fe 100644 --- a/tests/spec/enumIncludesNumber/basic.test.ts +++ b/tests/spec/enumIncludesNumber/basic.test.ts @@ -31,4 +31,41 @@ describe("basic", async () => { expect(content).toMatchSnapshot(); }); + + test("edge case with prefix and suffix", async () => { + await generateApi({ + fileName: "schema", + input: path.resolve(import.meta.dirname, "schema.json"), + output: tmpdir, + silent: true, + enumNamesAsValues: false, + generateClient: false, + enumKeyPrefix: "V_aL", + enumKeySuffix: "Ca_Se_CamelCase", + }); + + const content = await fs.readFile(path.join(tmpdir, "schema.ts"), { + encoding: "utf8", + }); + + expect(content).toMatchSnapshot(); + }); + + test("transform pure lowercase snake_case to PascalCase", async () => { + await generateApi({ + fileName: "schema", + input: path.resolve(import.meta.dirname, "schema.json"), + output: tmpdir, + silent: true, + enumNamesAsValues: false, + generateClient: false, + }); + + const content = await fs.readFile(path.join(tmpdir, "schema.ts"), { + encoding: "utf8", + }); + + // Check that pure lowercase snake_case values are transformed to PascalCase + expect(content).toMatchSnapshot(); + }); }); diff --git a/tests/spec/enumIncludesNumber/schema.json b/tests/spec/enumIncludesNumber/schema.json index 324298f10..623fffbbe 100644 --- a/tests/spec/enumIncludesNumber/schema.json +++ b/tests/spec/enumIncludesNumber/schema.json @@ -13,9 +13,25 @@ "_1_VAL", "A_1_B_2_C_3", "_A_1_B_2_C_3", - "_1_A_2_B_3_C" + "_1_A_2_B_3_C", + "_123ValCamelCase", + "Val_Snake_Case", + "Val_12_CamelCase", + "_Val_12_Snake_Case", + "_Val_12_CamelCase" ], "x-enum-descriptions": ["Foo", "Bar", "Baz"] + }, + "LowercaseSnakeCaseEnum": { + "type": "string", + "enum": [ + "local_only", + "phone_number", + "wait_dependency", + "auth_failed", + "waiting_to_push" + ], + "description": "Pure lowercase snake_case values that should be transformed to PascalCase" } } }