From 9e6e409f0ae236bd6d04d0a137af839dea432682 Mon Sep 17 00:00:00 2001 From: Lubos Date: Wed, 15 May 2024 02:48:20 +0100 Subject: [PATCH] fix: deduplicate inlined enums --- .changeset/mean-phones-beg.md | 5 ++++ .../src/openApi/v3/parser/getModel.ts | 10 ++----- .../v3/parser/getOperationParameter.ts | 10 ++----- packages/openapi-ts/src/utils/enum.ts | 27 ++++++++++++++++++- packages/openapi-ts/src/utils/write/types.ts | 5 ++-- packages/openapi-ts/test/sample.cjs | 2 +- 6 files changed, 38 insertions(+), 21 deletions(-) create mode 100644 .changeset/mean-phones-beg.md diff --git a/.changeset/mean-phones-beg.md b/.changeset/mean-phones-beg.md new file mode 100644 index 000000000..d8d92a593 --- /dev/null +++ b/.changeset/mean-phones-beg.md @@ -0,0 +1,5 @@ +--- +"@hey-api/openapi-ts": patch +--- + +fix: deduplicate inlined enums diff --git a/packages/openapi-ts/src/openApi/v3/parser/getModel.ts b/packages/openapi-ts/src/openApi/v3/parser/getModel.ts index c3d553aef..62cf3af1a 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/getModel.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/getModel.ts @@ -1,9 +1,8 @@ -import { unescapeName } from '../../../utils/escape'; +import { enumMeta } from '../../../utils/enum'; import type { Model, ModelMeta } from '../../common/interfaces/client'; import { getDefault } from '../../common/parser/getDefault'; import { getEnums } from '../../common/parser/getEnums'; import { getPattern } from '../../common/parser/getPattern'; -import { ensureValidTypeScriptJavaScriptIdentifier } from '../../common/parser/sanitize'; import { getType } from '../../common/parser/type'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiSchema } from '../interfaces/OpenApiSchema'; @@ -99,12 +98,7 @@ export const getModel = ({ model.type = 'string'; model.default = getDefault(definition, model); if (!model.meta) { - model.meta = { - $ref: `enum/${model.name}`, - name: ensureValidTypeScriptJavaScriptIdentifier( - unescapeName(model.name), - ), - }; + model.meta = enumMeta(model); } return model; } diff --git a/packages/openapi-ts/src/openApi/v3/parser/getOperationParameter.ts b/packages/openapi-ts/src/openApi/v3/parser/getOperationParameter.ts index 98789f0fb..e842a5840 100644 --- a/packages/openapi-ts/src/openApi/v3/parser/getOperationParameter.ts +++ b/packages/openapi-ts/src/openApi/v3/parser/getOperationParameter.ts @@ -1,10 +1,9 @@ -import { unescapeName } from '../../../utils/escape'; +import { enumMeta } from '../../../utils/enum'; import type { OperationParameter } from '../../common/interfaces/client'; import { getDefault } from '../../common/parser/getDefault'; import { getPattern } from '../../common/parser/getPattern'; import { getRef } from '../../common/parser/getRef'; import { getOperationParameterName } from '../../common/parser/operation'; -import { ensureValidTypeScriptJavaScriptIdentifier } from '../../common/parser/sanitize'; import { getType } from '../../common/parser/type'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiParameter } from '../interfaces/OpenApiParameter'; @@ -110,12 +109,7 @@ export const getOperationParameter = ( (operationParameter.enum.length || operationParameter.enums.length) && !operationParameter.meta ) { - operationParameter.meta = { - $ref: `enum/${operationParameter.name}`, - name: ensureValidTypeScriptJavaScriptIdentifier( - unescapeName(operationParameter.name), - ), - }; + operationParameter.meta = enumMeta(operationParameter); } operationParameter.default = model.default; return operationParameter; diff --git a/packages/openapi-ts/src/utils/enum.ts b/packages/openapi-ts/src/utils/enum.ts index d0a36cfa9..1139cbd2c 100644 --- a/packages/openapi-ts/src/utils/enum.ts +++ b/packages/openapi-ts/src/utils/enum.ts @@ -1,4 +1,7 @@ -import type { Enum } from '../openApi'; +import type { Enum, Model } from '../openApi'; +import { ensureValidTypeScriptJavaScriptIdentifier } from '../openApi/common/parser/sanitize'; +import { unescapeName } from './escape'; +import { sort } from './sort'; import { unique } from './unique'; /** @@ -49,3 +52,25 @@ export const enumValue = (value?: string | number, union: boolean = false) => { } return value; }; + +export const enumEntry = (enumerator: Enum) => { + const key = enumKey(enumerator.value, enumerator.customName); + const value = enumValue(enumerator.value); + return { key, value }; +}; + +/** + * Represent enum in `meta` object for deduplication + */ +export const enumMeta = (model: Model): Required['meta'] => { + // serialize enum values in namespace for quick lookup + const serialized = model.enum + .map((enumerator) => enumEntry(enumerator)) + .sort((a, b) => sort(a.key, b.key)) + .map((enumerator) => `${enumerator.key}=${enumerator.value}`) + .join('&'); + return { + $ref: `enum/${model.name}/${serialized}`, + name: ensureValidTypeScriptJavaScriptIdentifier(unescapeName(model.name)), + }; +}; diff --git a/packages/openapi-ts/src/utils/write/types.ts b/packages/openapi-ts/src/utils/write/types.ts index 59982b84f..cf631d2e0 100644 --- a/packages/openapi-ts/src/utils/write/types.ts +++ b/packages/openapi-ts/src/utils/write/types.ts @@ -7,7 +7,7 @@ import { import type { Model, OperationParameter, Service } from '../../openApi'; import type { Client } from '../../types/client'; import { getConfig } from '../config'; -import { enumKey, enumUnionType, enumValue } from '../enum'; +import { enumEntry, enumUnionType } from '../enum'; import { escapeComment } from '../escape'; import { sortByName, sorterByName } from '../sort'; import { operationDataTypeName, operationResponseTypeName } from './services'; @@ -103,8 +103,7 @@ const processEnum = (client: Client, model: Model, onNode: OnNode) => { const properties: Record = {}; const comments: Record = {}; model.enum.forEach((enumerator) => { - const key = enumKey(enumerator.value, enumerator.customName); - const value = enumValue(enumerator.value); + const { key, value } = enumEntry(enumerator); properties[key] = value; const comment = enumerator.customDescription || enumerator.description; if (comment) { diff --git a/packages/openapi-ts/test/sample.cjs b/packages/openapi-ts/test/sample.cjs index cf2c3136c..ab06fa5c0 100644 --- a/packages/openapi-ts/test/sample.cjs +++ b/packages/openapi-ts/test/sample.cjs @@ -15,7 +15,7 @@ const main = async () => { // export: false, }, types: { - // enums: 'javascript', + enums: 'typescript', // include: '^NestedAnyOfArraysNullable', // name: 'PascalCase', },