ts-class-to-openapi 1.1.3 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.esm.js +187 -39
- package/dist/index.js +187 -39
- package/dist/run.js +191 -44
- package/dist/types.d.ts +3 -0
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -113,6 +113,7 @@ class SchemaTransformer {
|
|
|
113
113
|
const isPrimitive = this.isPrimitiveType(type);
|
|
114
114
|
const isClassType = this.isClassType(member);
|
|
115
115
|
const isArray = this.isArrayProperty(member);
|
|
116
|
+
const isTypeLiteral = this.isTypeLiteral(member);
|
|
116
117
|
const property = {
|
|
117
118
|
name: propertyName,
|
|
118
119
|
type,
|
|
@@ -124,6 +125,7 @@ class SchemaTransformer {
|
|
|
124
125
|
isClassType,
|
|
125
126
|
isArray,
|
|
126
127
|
isRef: false,
|
|
128
|
+
isTypeLiteral: isTypeLiteral,
|
|
127
129
|
};
|
|
128
130
|
// Check for self-referencing properties to mark as $ref
|
|
129
131
|
if (property.isClassType) {
|
|
@@ -136,8 +138,25 @@ class SchemaTransformer {
|
|
|
136
138
|
property.isRef = true;
|
|
137
139
|
}
|
|
138
140
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
+
}
|
|
142
|
+
if (property.isTypeLiteral &&
|
|
143
|
+
property.originalProperty.type &&
|
|
144
|
+
property.originalProperty.type
|
|
145
|
+
.typeArguments?.length === 1) {
|
|
146
|
+
const typeArguments = property.originalProperty.type.typeArguments;
|
|
147
|
+
if (typeArguments && typeArguments[0]) {
|
|
148
|
+
const firstTypeArg = typeArguments[0];
|
|
149
|
+
if (ts.isTypeReferenceNode(firstTypeArg)) {
|
|
150
|
+
const type = this.checker.getTypeAtLocation(firstTypeArg);
|
|
151
|
+
const symbol = type.getSymbol();
|
|
152
|
+
if (symbol && symbol.declarations) {
|
|
153
|
+
const classDeclaration = symbol.declarations.find(decl => ts.isClassDeclaration(decl));
|
|
154
|
+
if (classDeclaration &&
|
|
155
|
+
ts.isClassDeclaration(classDeclaration)) {
|
|
156
|
+
property.typeLiteralClassReference = classDeclaration;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
141
160
|
}
|
|
142
161
|
}
|
|
143
162
|
properties.push(property);
|
|
@@ -462,43 +481,113 @@ class SchemaTransformer {
|
|
|
462
481
|
return false;
|
|
463
482
|
}
|
|
464
483
|
// Check if the original property type is an array type
|
|
465
|
-
if (this.isArrayProperty(propertyDeclaration)
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
484
|
+
if (this.isArrayProperty(propertyDeclaration)) {
|
|
485
|
+
const arrayType = propertyDeclaration.type;
|
|
486
|
+
const elementType = arrayType.elementType;
|
|
487
|
+
// Special handling for utility types with type arguments (e.g., PayloadEntity<Person>)
|
|
488
|
+
if (ts.isTypeReferenceNode(elementType) &&
|
|
489
|
+
elementType.typeArguments &&
|
|
490
|
+
elementType.typeArguments.length > 0) {
|
|
491
|
+
// Check the first type argument - it might be the actual class
|
|
492
|
+
const firstTypeArg = elementType.typeArguments[0];
|
|
493
|
+
if (firstTypeArg) {
|
|
494
|
+
const argType = this.checker.getTypeAtLocation(firstTypeArg);
|
|
495
|
+
const argSymbol = argType.getSymbol();
|
|
496
|
+
if (argSymbol && argSymbol.declarations) {
|
|
497
|
+
const hasClass = argSymbol.declarations.some(decl => ts.isClassDeclaration(decl));
|
|
498
|
+
if (hasClass)
|
|
499
|
+
return true;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
// Get the type from the element, regardless of its syntaxkind
|
|
504
|
+
const type = this.checker.getTypeAtLocation(elementType);
|
|
469
505
|
const symbol = type.getSymbol();
|
|
470
506
|
if (symbol && symbol.declarations) {
|
|
471
507
|
return symbol.declarations.some(decl => ts.isClassDeclaration(decl));
|
|
472
508
|
}
|
|
509
|
+
return false;
|
|
473
510
|
}
|
|
474
|
-
|
|
511
|
+
// Check non-array types
|
|
512
|
+
else {
|
|
513
|
+
// Special handling for utility types with type arguments (e.g., PayloadEntity<Branch>)
|
|
514
|
+
if (ts.isTypeReferenceNode(propertyDeclaration.type) &&
|
|
515
|
+
propertyDeclaration.type.typeArguments &&
|
|
516
|
+
propertyDeclaration.type.typeArguments.length > 0) {
|
|
517
|
+
// Check the first type argument - it might be the actual class
|
|
518
|
+
const firstTypeArg = propertyDeclaration.type.typeArguments[0];
|
|
519
|
+
if (firstTypeArg) {
|
|
520
|
+
const argType = this.checker.getTypeAtLocation(firstTypeArg);
|
|
521
|
+
const argSymbol = argType.getSymbol();
|
|
522
|
+
if (argSymbol && argSymbol.declarations) {
|
|
523
|
+
const hasClass = argSymbol.declarations.some(decl => ts.isClassDeclaration(decl));
|
|
524
|
+
if (hasClass)
|
|
525
|
+
return true;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
475
529
|
const type = this.checker.getTypeAtLocation(propertyDeclaration.type);
|
|
476
530
|
const symbol = type.getSymbol();
|
|
477
531
|
if (symbol && symbol.declarations) {
|
|
478
532
|
return symbol.declarations.some(decl => ts.isClassDeclaration(decl));
|
|
479
533
|
}
|
|
534
|
+
return false;
|
|
480
535
|
}
|
|
481
|
-
return false;
|
|
482
536
|
}
|
|
483
537
|
getDeclarationProperty(property) {
|
|
484
538
|
if (!property.originalProperty.type) {
|
|
485
539
|
return undefined;
|
|
486
540
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
const
|
|
541
|
+
// Handle array types - get the element type
|
|
542
|
+
if (ts.isArrayTypeNode(property.originalProperty.type)) {
|
|
543
|
+
const elementType = property.originalProperty.type.elementType;
|
|
544
|
+
// Check if it's a utility type with type arguments (e.g., PayloadEntity<Branch>[])
|
|
545
|
+
if (ts.isTypeReferenceNode(elementType) &&
|
|
546
|
+
elementType.typeArguments &&
|
|
547
|
+
elementType.typeArguments.length > 0) {
|
|
548
|
+
const firstTypeArg = elementType.typeArguments[0];
|
|
549
|
+
if (firstTypeArg) {
|
|
550
|
+
const argType = this.checker.getTypeAtLocation(firstTypeArg);
|
|
551
|
+
const argSymbol = argType.getSymbol();
|
|
552
|
+
if (argSymbol && argSymbol.declarations) {
|
|
553
|
+
const classDecl = argSymbol.declarations.find(decl => ts.isClassDeclaration(decl));
|
|
554
|
+
if (classDecl)
|
|
555
|
+
return classDecl;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
const type = this.checker.getTypeAtLocation(elementType);
|
|
490
560
|
const symbol = type.getSymbol();
|
|
491
561
|
if (symbol && symbol.declarations) {
|
|
492
|
-
|
|
562
|
+
// Return the first class declaration found
|
|
563
|
+
const classDecl = symbol.declarations.find(decl => ts.isClassDeclaration(decl));
|
|
564
|
+
return classDecl || symbol.declarations[0];
|
|
493
565
|
}
|
|
566
|
+
return undefined;
|
|
494
567
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
568
|
+
// Handle non-array types
|
|
569
|
+
// Check if it's a utility type with type arguments (e.g., PayloadEntity<Branch>)
|
|
570
|
+
if (ts.isTypeReferenceNode(property.originalProperty.type) &&
|
|
571
|
+
property.originalProperty.type.typeArguments &&
|
|
572
|
+
property.originalProperty.type.typeArguments.length > 0) {
|
|
573
|
+
const firstTypeArg = property.originalProperty.type.typeArguments[0];
|
|
574
|
+
if (firstTypeArg) {
|
|
575
|
+
const argType = this.checker.getTypeAtLocation(firstTypeArg);
|
|
576
|
+
const argSymbol = argType.getSymbol();
|
|
577
|
+
if (argSymbol && argSymbol.declarations) {
|
|
578
|
+
const classDecl = argSymbol.declarations.find(decl => ts.isClassDeclaration(decl));
|
|
579
|
+
if (classDecl)
|
|
580
|
+
return classDecl;
|
|
581
|
+
}
|
|
500
582
|
}
|
|
501
583
|
}
|
|
584
|
+
const type = this.checker.getTypeAtLocation(property.originalProperty.type);
|
|
585
|
+
const symbol = type.getSymbol();
|
|
586
|
+
if (symbol && symbol.declarations) {
|
|
587
|
+
// Return the first class declaration found
|
|
588
|
+
const classDecl = symbol.declarations.find(decl => ts.isClassDeclaration(decl));
|
|
589
|
+
return classDecl || symbol.declarations[0];
|
|
590
|
+
}
|
|
502
591
|
return undefined;
|
|
503
592
|
}
|
|
504
593
|
isArrayProperty(propertyDeclaration) {
|
|
@@ -534,34 +623,60 @@ class SchemaTransformer {
|
|
|
534
623
|
schema = this.getSchemaFromPrimitive(property);
|
|
535
624
|
}
|
|
536
625
|
else if (property.isClassType) {
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
626
|
+
schema = this.buildSchemaFromClass({
|
|
627
|
+
property,
|
|
628
|
+
classDeclaration,
|
|
629
|
+
visitedClass,
|
|
630
|
+
transformedSchema,
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
else if (property.isTypeLiteral && property.typeLiteralClassReference) {
|
|
634
|
+
schema = this.buildSchemaFromClass({
|
|
635
|
+
property,
|
|
636
|
+
classDeclaration: property.typeLiteralClassReference,
|
|
637
|
+
visitedClass,
|
|
638
|
+
transformedSchema,
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
schema = { type: 'object', properties: {}, additionalProperties: true };
|
|
643
|
+
}
|
|
644
|
+
this.applyDecorators(property, schema);
|
|
645
|
+
return schema;
|
|
646
|
+
}
|
|
647
|
+
buildSchemaFromClass({ property, classDeclaration, visitedClass, transformedSchema, }) {
|
|
648
|
+
const declaration = this.getDeclarationProperty(property);
|
|
649
|
+
let schema = {};
|
|
650
|
+
if (property.isRef && classDeclaration.name) {
|
|
651
|
+
// Self-referencing property, handle as a reference to avoid infinite recursion
|
|
652
|
+
if (property.isArray) {
|
|
653
|
+
schema.type = 'array';
|
|
654
|
+
schema.items = {
|
|
655
|
+
$ref: `#/components/schemas/${classDeclaration.name.text}`,
|
|
656
|
+
};
|
|
551
657
|
}
|
|
552
658
|
else {
|
|
553
|
-
schema =
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
transformedSchema,
|
|
557
|
-
declaration,
|
|
558
|
-
});
|
|
659
|
+
schema = {
|
|
660
|
+
$ref: `#/components/schemas/${classDeclaration.name.text}`,
|
|
661
|
+
};
|
|
559
662
|
}
|
|
560
663
|
}
|
|
664
|
+
else if (property.isTypeLiteral && property.typeLiteralClassReference) {
|
|
665
|
+
schema = this.getSchemaFromClass({
|
|
666
|
+
isArray: property.isArray,
|
|
667
|
+
visitedClass,
|
|
668
|
+
transformedSchema,
|
|
669
|
+
declaration: property.typeLiteralClassReference,
|
|
670
|
+
});
|
|
671
|
+
}
|
|
561
672
|
else {
|
|
562
|
-
schema = {
|
|
673
|
+
schema = this.getSchemaFromClass({
|
|
674
|
+
isArray: property.isArray,
|
|
675
|
+
visitedClass,
|
|
676
|
+
transformedSchema,
|
|
677
|
+
declaration,
|
|
678
|
+
});
|
|
563
679
|
}
|
|
564
|
-
this.applyDecorators(property, schema);
|
|
565
680
|
return schema;
|
|
566
681
|
}
|
|
567
682
|
getSchemaFromClass({ transformedSchema = new Map(), visitedClass = new Set(), declaration, isArray, }) {
|
|
@@ -664,6 +779,39 @@ class SchemaTransformer {
|
|
|
664
779
|
}
|
|
665
780
|
return propertySchema;
|
|
666
781
|
}
|
|
782
|
+
isTypeLiteral(property) {
|
|
783
|
+
if (!property.type)
|
|
784
|
+
return false;
|
|
785
|
+
if (ts.isTypeReferenceNode(property.type)) {
|
|
786
|
+
const symbol = this.checker.getSymbolAtLocation(property.type.typeName);
|
|
787
|
+
if (symbol) {
|
|
788
|
+
const declarations = symbol.getDeclarations();
|
|
789
|
+
if (declarations && declarations.length > 0) {
|
|
790
|
+
const typeAliasDecl = declarations.find(decl => ts.isTypeAliasDeclaration(decl));
|
|
791
|
+
if (typeAliasDecl && typeAliasDecl.type) {
|
|
792
|
+
return this.isLiteralTypeNode(typeAliasDecl.type);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
*
|
|
801
|
+
* @param typeNode
|
|
802
|
+
* @returns boolean - true si el typeNode representa un tipo literal complejo
|
|
803
|
+
*/
|
|
804
|
+
isLiteralTypeNode(typeNode) {
|
|
805
|
+
return (ts.isIntersectionTypeNode(typeNode) || // {} & Omit<T, ...>
|
|
806
|
+
ts.isUnionTypeNode(typeNode) || // string | number
|
|
807
|
+
ts.isMappedTypeNode(typeNode) || // { [K in keyof T]: ... }
|
|
808
|
+
ts.isTypeLiteralNode(typeNode) || // { foo: string }
|
|
809
|
+
ts.isConditionalTypeNode(typeNode) || // T extends U ? X : Y
|
|
810
|
+
ts.isIndexedAccessTypeNode(typeNode) || // T['key']
|
|
811
|
+
ts.isTypeOperatorNode(typeNode) || // keyof T, readonly T
|
|
812
|
+
ts.isTypeReferenceNode(typeNode) // Omit, Pick, Partial, etc.
|
|
813
|
+
);
|
|
814
|
+
}
|
|
667
815
|
//Todo: implement properly
|
|
668
816
|
applyEnumDecorator(decorator, schema) { }
|
|
669
817
|
applyDecorators(property, schema) {
|
package/dist/index.js
CHANGED
|
@@ -115,6 +115,7 @@ class SchemaTransformer {
|
|
|
115
115
|
const isPrimitive = this.isPrimitiveType(type);
|
|
116
116
|
const isClassType = this.isClassType(member);
|
|
117
117
|
const isArray = this.isArrayProperty(member);
|
|
118
|
+
const isTypeLiteral = this.isTypeLiteral(member);
|
|
118
119
|
const property = {
|
|
119
120
|
name: propertyName,
|
|
120
121
|
type,
|
|
@@ -126,6 +127,7 @@ class SchemaTransformer {
|
|
|
126
127
|
isClassType,
|
|
127
128
|
isArray,
|
|
128
129
|
isRef: false,
|
|
130
|
+
isTypeLiteral: isTypeLiteral,
|
|
129
131
|
};
|
|
130
132
|
// Check for self-referencing properties to mark as $ref
|
|
131
133
|
if (property.isClassType) {
|
|
@@ -138,8 +140,25 @@ class SchemaTransformer {
|
|
|
138
140
|
property.isRef = true;
|
|
139
141
|
}
|
|
140
142
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
+
}
|
|
144
|
+
if (property.isTypeLiteral &&
|
|
145
|
+
property.originalProperty.type &&
|
|
146
|
+
property.originalProperty.type
|
|
147
|
+
.typeArguments?.length === 1) {
|
|
148
|
+
const typeArguments = property.originalProperty.type.typeArguments;
|
|
149
|
+
if (typeArguments && typeArguments[0]) {
|
|
150
|
+
const firstTypeArg = typeArguments[0];
|
|
151
|
+
if (ts.isTypeReferenceNode(firstTypeArg)) {
|
|
152
|
+
const type = this.checker.getTypeAtLocation(firstTypeArg);
|
|
153
|
+
const symbol = type.getSymbol();
|
|
154
|
+
if (symbol && symbol.declarations) {
|
|
155
|
+
const classDeclaration = symbol.declarations.find(decl => ts.isClassDeclaration(decl));
|
|
156
|
+
if (classDeclaration &&
|
|
157
|
+
ts.isClassDeclaration(classDeclaration)) {
|
|
158
|
+
property.typeLiteralClassReference = classDeclaration;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
143
162
|
}
|
|
144
163
|
}
|
|
145
164
|
properties.push(property);
|
|
@@ -464,43 +483,113 @@ class SchemaTransformer {
|
|
|
464
483
|
return false;
|
|
465
484
|
}
|
|
466
485
|
// Check if the original property type is an array type
|
|
467
|
-
if (this.isArrayProperty(propertyDeclaration)
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
486
|
+
if (this.isArrayProperty(propertyDeclaration)) {
|
|
487
|
+
const arrayType = propertyDeclaration.type;
|
|
488
|
+
const elementType = arrayType.elementType;
|
|
489
|
+
// Special handling for utility types with type arguments (e.g., PayloadEntity<Person>)
|
|
490
|
+
if (ts.isTypeReferenceNode(elementType) &&
|
|
491
|
+
elementType.typeArguments &&
|
|
492
|
+
elementType.typeArguments.length > 0) {
|
|
493
|
+
// Check the first type argument - it might be the actual class
|
|
494
|
+
const firstTypeArg = elementType.typeArguments[0];
|
|
495
|
+
if (firstTypeArg) {
|
|
496
|
+
const argType = this.checker.getTypeAtLocation(firstTypeArg);
|
|
497
|
+
const argSymbol = argType.getSymbol();
|
|
498
|
+
if (argSymbol && argSymbol.declarations) {
|
|
499
|
+
const hasClass = argSymbol.declarations.some(decl => ts.isClassDeclaration(decl));
|
|
500
|
+
if (hasClass)
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
// Get the type from the element, regardless of its syntaxkind
|
|
506
|
+
const type = this.checker.getTypeAtLocation(elementType);
|
|
471
507
|
const symbol = type.getSymbol();
|
|
472
508
|
if (symbol && symbol.declarations) {
|
|
473
509
|
return symbol.declarations.some(decl => ts.isClassDeclaration(decl));
|
|
474
510
|
}
|
|
511
|
+
return false;
|
|
475
512
|
}
|
|
476
|
-
|
|
513
|
+
// Check non-array types
|
|
514
|
+
else {
|
|
515
|
+
// Special handling for utility types with type arguments (e.g., PayloadEntity<Branch>)
|
|
516
|
+
if (ts.isTypeReferenceNode(propertyDeclaration.type) &&
|
|
517
|
+
propertyDeclaration.type.typeArguments &&
|
|
518
|
+
propertyDeclaration.type.typeArguments.length > 0) {
|
|
519
|
+
// Check the first type argument - it might be the actual class
|
|
520
|
+
const firstTypeArg = propertyDeclaration.type.typeArguments[0];
|
|
521
|
+
if (firstTypeArg) {
|
|
522
|
+
const argType = this.checker.getTypeAtLocation(firstTypeArg);
|
|
523
|
+
const argSymbol = argType.getSymbol();
|
|
524
|
+
if (argSymbol && argSymbol.declarations) {
|
|
525
|
+
const hasClass = argSymbol.declarations.some(decl => ts.isClassDeclaration(decl));
|
|
526
|
+
if (hasClass)
|
|
527
|
+
return true;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
477
531
|
const type = this.checker.getTypeAtLocation(propertyDeclaration.type);
|
|
478
532
|
const symbol = type.getSymbol();
|
|
479
533
|
if (symbol && symbol.declarations) {
|
|
480
534
|
return symbol.declarations.some(decl => ts.isClassDeclaration(decl));
|
|
481
535
|
}
|
|
536
|
+
return false;
|
|
482
537
|
}
|
|
483
|
-
return false;
|
|
484
538
|
}
|
|
485
539
|
getDeclarationProperty(property) {
|
|
486
540
|
if (!property.originalProperty.type) {
|
|
487
541
|
return undefined;
|
|
488
542
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
const
|
|
543
|
+
// Handle array types - get the element type
|
|
544
|
+
if (ts.isArrayTypeNode(property.originalProperty.type)) {
|
|
545
|
+
const elementType = property.originalProperty.type.elementType;
|
|
546
|
+
// Check if it's a utility type with type arguments (e.g., PayloadEntity<Branch>[])
|
|
547
|
+
if (ts.isTypeReferenceNode(elementType) &&
|
|
548
|
+
elementType.typeArguments &&
|
|
549
|
+
elementType.typeArguments.length > 0) {
|
|
550
|
+
const firstTypeArg = elementType.typeArguments[0];
|
|
551
|
+
if (firstTypeArg) {
|
|
552
|
+
const argType = this.checker.getTypeAtLocation(firstTypeArg);
|
|
553
|
+
const argSymbol = argType.getSymbol();
|
|
554
|
+
if (argSymbol && argSymbol.declarations) {
|
|
555
|
+
const classDecl = argSymbol.declarations.find(decl => ts.isClassDeclaration(decl));
|
|
556
|
+
if (classDecl)
|
|
557
|
+
return classDecl;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
const type = this.checker.getTypeAtLocation(elementType);
|
|
492
562
|
const symbol = type.getSymbol();
|
|
493
563
|
if (symbol && symbol.declarations) {
|
|
494
|
-
|
|
564
|
+
// Return the first class declaration found
|
|
565
|
+
const classDecl = symbol.declarations.find(decl => ts.isClassDeclaration(decl));
|
|
566
|
+
return classDecl || symbol.declarations[0];
|
|
495
567
|
}
|
|
568
|
+
return undefined;
|
|
496
569
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
570
|
+
// Handle non-array types
|
|
571
|
+
// Check if it's a utility type with type arguments (e.g., PayloadEntity<Branch>)
|
|
572
|
+
if (ts.isTypeReferenceNode(property.originalProperty.type) &&
|
|
573
|
+
property.originalProperty.type.typeArguments &&
|
|
574
|
+
property.originalProperty.type.typeArguments.length > 0) {
|
|
575
|
+
const firstTypeArg = property.originalProperty.type.typeArguments[0];
|
|
576
|
+
if (firstTypeArg) {
|
|
577
|
+
const argType = this.checker.getTypeAtLocation(firstTypeArg);
|
|
578
|
+
const argSymbol = argType.getSymbol();
|
|
579
|
+
if (argSymbol && argSymbol.declarations) {
|
|
580
|
+
const classDecl = argSymbol.declarations.find(decl => ts.isClassDeclaration(decl));
|
|
581
|
+
if (classDecl)
|
|
582
|
+
return classDecl;
|
|
583
|
+
}
|
|
502
584
|
}
|
|
503
585
|
}
|
|
586
|
+
const type = this.checker.getTypeAtLocation(property.originalProperty.type);
|
|
587
|
+
const symbol = type.getSymbol();
|
|
588
|
+
if (symbol && symbol.declarations) {
|
|
589
|
+
// Return the first class declaration found
|
|
590
|
+
const classDecl = symbol.declarations.find(decl => ts.isClassDeclaration(decl));
|
|
591
|
+
return classDecl || symbol.declarations[0];
|
|
592
|
+
}
|
|
504
593
|
return undefined;
|
|
505
594
|
}
|
|
506
595
|
isArrayProperty(propertyDeclaration) {
|
|
@@ -536,34 +625,60 @@ class SchemaTransformer {
|
|
|
536
625
|
schema = this.getSchemaFromPrimitive(property);
|
|
537
626
|
}
|
|
538
627
|
else if (property.isClassType) {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
628
|
+
schema = this.buildSchemaFromClass({
|
|
629
|
+
property,
|
|
630
|
+
classDeclaration,
|
|
631
|
+
visitedClass,
|
|
632
|
+
transformedSchema,
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
else if (property.isTypeLiteral && property.typeLiteralClassReference) {
|
|
636
|
+
schema = this.buildSchemaFromClass({
|
|
637
|
+
property,
|
|
638
|
+
classDeclaration: property.typeLiteralClassReference,
|
|
639
|
+
visitedClass,
|
|
640
|
+
transformedSchema,
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
else {
|
|
644
|
+
schema = { type: 'object', properties: {}, additionalProperties: true };
|
|
645
|
+
}
|
|
646
|
+
this.applyDecorators(property, schema);
|
|
647
|
+
return schema;
|
|
648
|
+
}
|
|
649
|
+
buildSchemaFromClass({ property, classDeclaration, visitedClass, transformedSchema, }) {
|
|
650
|
+
const declaration = this.getDeclarationProperty(property);
|
|
651
|
+
let schema = {};
|
|
652
|
+
if (property.isRef && classDeclaration.name) {
|
|
653
|
+
// Self-referencing property, handle as a reference to avoid infinite recursion
|
|
654
|
+
if (property.isArray) {
|
|
655
|
+
schema.type = 'array';
|
|
656
|
+
schema.items = {
|
|
657
|
+
$ref: `#/components/schemas/${classDeclaration.name.text}`,
|
|
658
|
+
};
|
|
553
659
|
}
|
|
554
660
|
else {
|
|
555
|
-
schema =
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
transformedSchema,
|
|
559
|
-
declaration,
|
|
560
|
-
});
|
|
661
|
+
schema = {
|
|
662
|
+
$ref: `#/components/schemas/${classDeclaration.name.text}`,
|
|
663
|
+
};
|
|
561
664
|
}
|
|
562
665
|
}
|
|
666
|
+
else if (property.isTypeLiteral && property.typeLiteralClassReference) {
|
|
667
|
+
schema = this.getSchemaFromClass({
|
|
668
|
+
isArray: property.isArray,
|
|
669
|
+
visitedClass,
|
|
670
|
+
transformedSchema,
|
|
671
|
+
declaration: property.typeLiteralClassReference,
|
|
672
|
+
});
|
|
673
|
+
}
|
|
563
674
|
else {
|
|
564
|
-
schema = {
|
|
675
|
+
schema = this.getSchemaFromClass({
|
|
676
|
+
isArray: property.isArray,
|
|
677
|
+
visitedClass,
|
|
678
|
+
transformedSchema,
|
|
679
|
+
declaration,
|
|
680
|
+
});
|
|
565
681
|
}
|
|
566
|
-
this.applyDecorators(property, schema);
|
|
567
682
|
return schema;
|
|
568
683
|
}
|
|
569
684
|
getSchemaFromClass({ transformedSchema = new Map(), visitedClass = new Set(), declaration, isArray, }) {
|
|
@@ -666,6 +781,39 @@ class SchemaTransformer {
|
|
|
666
781
|
}
|
|
667
782
|
return propertySchema;
|
|
668
783
|
}
|
|
784
|
+
isTypeLiteral(property) {
|
|
785
|
+
if (!property.type)
|
|
786
|
+
return false;
|
|
787
|
+
if (ts.isTypeReferenceNode(property.type)) {
|
|
788
|
+
const symbol = this.checker.getSymbolAtLocation(property.type.typeName);
|
|
789
|
+
if (symbol) {
|
|
790
|
+
const declarations = symbol.getDeclarations();
|
|
791
|
+
if (declarations && declarations.length > 0) {
|
|
792
|
+
const typeAliasDecl = declarations.find(decl => ts.isTypeAliasDeclaration(decl));
|
|
793
|
+
if (typeAliasDecl && typeAliasDecl.type) {
|
|
794
|
+
return this.isLiteralTypeNode(typeAliasDecl.type);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
return false;
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
*
|
|
803
|
+
* @param typeNode
|
|
804
|
+
* @returns boolean - true si el typeNode representa un tipo literal complejo
|
|
805
|
+
*/
|
|
806
|
+
isLiteralTypeNode(typeNode) {
|
|
807
|
+
return (ts.isIntersectionTypeNode(typeNode) || // {} & Omit<T, ...>
|
|
808
|
+
ts.isUnionTypeNode(typeNode) || // string | number
|
|
809
|
+
ts.isMappedTypeNode(typeNode) || // { [K in keyof T]: ... }
|
|
810
|
+
ts.isTypeLiteralNode(typeNode) || // { foo: string }
|
|
811
|
+
ts.isConditionalTypeNode(typeNode) || // T extends U ? X : Y
|
|
812
|
+
ts.isIndexedAccessTypeNode(typeNode) || // T['key']
|
|
813
|
+
ts.isTypeOperatorNode(typeNode) || // keyof T, readonly T
|
|
814
|
+
ts.isTypeReferenceNode(typeNode) // Omit, Pick, Partial, etc.
|
|
815
|
+
);
|
|
816
|
+
}
|
|
669
817
|
//Todo: implement properly
|
|
670
818
|
applyEnumDecorator(decorator, schema) { }
|
|
671
819
|
applyDecorators(property, schema) {
|