@@ -11,6 +11,34 @@ import transformRequestBodyObject from "./request-body-object.js";
1111import transformResponseObject from "./response-object.js" ;
1212import transformSchemaObject from "./schema-object.js" ;
1313
14+ /**
15+ * Determines if a schema object represents an enum type to prevent duplicate exports
16+ * when using --root-types and --enum flags together.
17+ *
18+ * When both flags are enabled:
19+ * - --enum flag generates TypeScript enums at the bottom of the file
20+ * - --root-types flag would normally also export these as root type aliases
21+ * - This results in duplicate exports (both enum and type alias for the same schema)
22+ *
23+ * This function identifies enum schemas so they can be excluded from root type generation,
24+ * allowing only the TypeScript enum to be generated.
25+ *
26+ * @param schema The schema object to check
27+ * @returns true if the schema represents an enum type
28+ */
29+ export function isEnumSchema ( schema : unknown ) : boolean {
30+ return (
31+ typeof schema === "object" &&
32+ schema !== null &&
33+ ! Array . isArray ( schema ) &&
34+ "enum" in schema &&
35+ Array . isArray ( ( schema as any ) . enum ) &&
36+ ( ! ( "type" in schema ) || ( schema as any ) . type !== "object" ) &&
37+ ! ( "properties" in schema ) &&
38+ ! ( "additionalProperties" in schema )
39+ ) ;
40+ }
41+
1442type ComponentTransforms = keyof Omit < ComponentsObject , "examples" | "securitySchemes" | "links" | "callbacks" > ;
1543
1644const transformers : Record < ComponentTransforms , ( node : any , options : TransformNodeOptions ) => ts . TypeNode > = {
@@ -68,27 +96,32 @@ export default function transformComponentsObject(componentsObject: ComponentsOb
6896 items . push ( property ) ;
6997
7098 if ( ctx . rootTypes ) {
71- const componentKey = changeCase . pascalCase ( singularizeComponentKey ( key ) ) ;
72- let aliasName = ` ${ componentKey } ${ changeCase . pascalCase ( name ) } ` ;
99+ // Skip enum schemas when generating root types to prevent duplication (only when --enum flag is enabled)
100+ const shouldSkipEnumSchema = ctx . enum && key === "schemas" && isEnumSchema ( item ) ;
73101
74- // Add counter suffix (e.g. "_2") if conflict in name
75- let conflictCounter = 1 ;
102+ if ( ! shouldSkipEnumSchema ) {
103+ const componentKey = changeCase . pascalCase ( singularizeComponentKey ( key ) ) ;
104+ let aliasName = `${ componentKey } ${ changeCase . pascalCase ( name ) } ` ;
76105
77- while ( rootTypeAliases [ aliasName ] !== undefined ) {
78- conflictCounter ++ ;
79- aliasName = `${ componentKey } ${ changeCase . pascalCase ( name ) } _${ conflictCounter } ` ;
80- }
81- const ref = ts . factory . createTypeReferenceNode ( `components['${ key } ']['${ name } ']` ) ;
82- if ( ctx . rootTypesNoSchemaPrefix && key === "schemas" ) {
83- aliasName = aliasName . replace ( componentKey , "" ) ;
106+ // Add counter suffix (e.g. "_2") if conflict in name
107+ let conflictCounter = 1 ;
108+
109+ while ( rootTypeAliases [ aliasName ] !== undefined ) {
110+ conflictCounter ++ ;
111+ aliasName = `${ componentKey } ${ changeCase . pascalCase ( name ) } _${ conflictCounter } ` ;
112+ }
113+ const ref = ts . factory . createTypeReferenceNode ( `components['${ key } ']['${ name } ']` ) ;
114+ if ( ctx . rootTypesNoSchemaPrefix && key === "schemas" ) {
115+ aliasName = aliasName . replace ( componentKey , "" ) ;
116+ }
117+ const typeAlias = ts . factory . createTypeAliasDeclaration (
118+ /* modifiers */ tsModifiers ( { export : true } ) ,
119+ /* name */ aliasName ,
120+ /* typeParameters */ undefined ,
121+ /* type */ ref ,
122+ ) ;
123+ rootTypeAliases [ aliasName ] = typeAlias ;
84124 }
85- const typeAlias = ts . factory . createTypeAliasDeclaration (
86- /* modifiers */ tsModifiers ( { export : true } ) ,
87- /* name */ aliasName ,
88- /* typeParameters */ undefined ,
89- /* type */ ref ,
90- ) ;
91- rootTypeAliases [ aliasName ] = typeAlias ;
92125 }
93126 }
94127 }
0 commit comments