Skip to content

Commit c011c02

Browse files
committed
refactor(resolve): decouple TypeResolver with abstract Transformer class
1 parent 8d86fdb commit c011c02

File tree

6 files changed

+50
-78
lines changed

6 files changed

+50
-78
lines changed

packages/cli/src/metadataGeneration/transformer/enumTransformer.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Tsoa } from '@tsoa/runtime';
44

55
import { Transformer } from './transformer';
66
import { isExistJSDocTag } from '../../utils/jsDocUtils';
7+
import { TypeResolver } from '../typeResolver';
78

89
export class EnumTransformer extends Transformer {
910
public static mergeMany(many: Tsoa.RefEnumType[]): Tsoa.RefEnumType {
@@ -40,36 +41,36 @@ export class EnumTransformer extends Transformer {
4041
return isEnumDeclaration(declaration) || isEnumMember(declaration);
4142
}
4243

43-
public transform(declaration: EnumDeclaration | EnumMember, enumName: string): Tsoa.RefEnumType {
44+
public transform(resolver: TypeResolver, declaration: EnumDeclaration | EnumMember, enumName: string): Tsoa.RefEnumType {
4445
if (isEnumDeclaration(declaration)) {
45-
return this.transformDeclaration(declaration, enumName);
46+
return this.transformDeclaration(resolver, declaration, enumName);
4647
}
47-
return this.transformMember(declaration, enumName);
48+
return this.transformMember(resolver, declaration, enumName);
4849
}
4950

50-
private transformDeclaration(declaration: EnumDeclaration, enumName: string): Tsoa.RefEnumType {
51+
private transformDeclaration(resolver: TypeResolver, declaration: EnumDeclaration, enumName: string): Tsoa.RefEnumType {
5152
const isNotUndefined = <T>(item: T): item is Exclude<T, undefined> => {
5253
return item === undefined ? false : true;
5354
};
54-
const enums = declaration.members.map(e => this.resolver.current.typeChecker.getConstantValue(e)).filter(isNotUndefined);
55+
const enums = declaration.members.map(e => resolver.current.typeChecker.getConstantValue(e)).filter(isNotUndefined);
5556
const enumVarnames = declaration.members.map(e => e.name.getText()).filter(isNotUndefined);
5657

5758
return {
5859
dataType: 'refEnum',
59-
description: this.resolver.getNodeDescription(declaration),
60-
example: this.resolver.getNodeExample(declaration),
60+
description: resolver.getNodeDescription(declaration),
61+
example: resolver.getNodeExample(declaration),
6162
enums,
6263
enumVarnames,
6364
refName: enumName,
6465
deprecated: isExistJSDocTag(declaration, tag => tag.tagName.text === 'deprecated'),
6566
};
6667
}
6768

68-
private transformMember(declaration: EnumMember, enumName: string): Tsoa.RefEnumType {
69+
private transformMember(resolver: TypeResolver, declaration: EnumMember, enumName: string): Tsoa.RefEnumType {
6970
return {
7071
dataType: 'refEnum',
7172
refName: enumName,
72-
enums: [this.resolver.current.typeChecker.getConstantValue(declaration)!],
73+
enums: [resolver.current.typeChecker.getConstantValue(declaration)!],
7374
enumVarnames: [declaration.name.getText()],
7475
deprecated: isExistJSDocTag(declaration, tag => tag.tagName.text === 'deprecated'),
7576
};

packages/cli/src/metadataGeneration/transformer/primitiveTransformer.ts

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
import type { TypeNode, Node } from 'typescript';
2-
import { SyntaxKind } from 'typescript';
1+
import { type TypeNode, SyntaxKind } from 'typescript';
32
import { Tsoa, assertNever } from '@tsoa/runtime';
43

54
import { Transformer } from './transformer';
6-
import { getJSDocTagNames } from '../../utils/jsDocUtils';
75

86
export class PrimitiveTransformer extends Transformer {
97
public static resolveKindToPrimitive(syntaxKind: SyntaxKind): ResolvesToPrimitive {
@@ -25,17 +23,15 @@ export class PrimitiveTransformer extends Transformer {
2523
}
2624
}
2725

28-
public transform(typeNode: TypeNode, parentNode?: Node): Tsoa.Type | undefined {
26+
public transform(defaultNumberType: DefaultNumberType, typeNode: TypeNode, partentJsDocTagNames?: string[]): Tsoa.Type | undefined {
2927
const resolvedType = PrimitiveTransformer.resolveKindToPrimitive(typeNode.kind);
3028
if (!resolvedType) {
3129
return;
3230
}
3331

34-
const defaultNumberType = this.resolver.current.defaultNumberType;
35-
3632
switch (resolvedType) {
3733
case 'number':
38-
return this.transformNumber(defaultNumberType, parentNode);
34+
return this.transformNumber(defaultNumberType, partentJsDocTagNames);
3935
case 'string':
4036
case 'boolean':
4137
case 'void':
@@ -51,17 +47,14 @@ export class PrimitiveTransformer extends Transformer {
5147
}
5248
}
5349

54-
private transformNumber(defaultNumberType: NonNullable<'double' | 'float' | 'integer' | 'long' | undefined>, parentNode?: Node): Tsoa.PrimitiveType {
55-
if (!parentNode) {
50+
private transformNumber(defaultNumberType: DefaultNumberType, partentJsDocTagNames?: string[]): Tsoa.PrimitiveType {
51+
if (!partentJsDocTagNames || partentJsDocTagNames.length === 0) {
5652
return { dataType: defaultNumberType };
5753
}
5854

59-
const tags = getJSDocTagNames(parentNode).filter(name => {
55+
const tags = partentJsDocTagNames.filter(name => {
6056
return ['isInt', 'isLong', 'isFloat', 'isDouble'].some(m => m === name);
6157
});
62-
if (tags.length === 0) {
63-
return { dataType: defaultNumberType };
64-
}
6558

6659
switch (tags[0]) {
6760
case 'isInt':
@@ -78,4 +71,5 @@ export class PrimitiveTransformer extends Transformer {
7871
}
7972
}
8073

74+
type DefaultNumberType = NonNullable<'double' | 'float' | 'integer' | 'long' | undefined>;
8175
type ResolvesToPrimitive = 'number' | 'string' | 'boolean' | 'void' | 'undefined' | 'null' | undefined;

packages/cli/src/metadataGeneration/transformer/propertyTransformer.ts

Lines changed: 22 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,5 @@
1-
import type {
2-
Token,
3-
InterfaceDeclaration,
4-
ClassDeclaration,
5-
PropertyDeclaration,
6-
ParameterDeclaration,
7-
ConstructorDeclaration,
8-
TypeElement,
9-
ClassElement,
10-
PropertySignature,
11-
} from 'typescript';
12-
import {
13-
NodeFlags,
14-
NodeBuilderFlags,
15-
SyntaxKind,
16-
isInterfaceDeclaration,
17-
isPropertyDeclaration,
18-
isConstructorDeclaration,
19-
isPropertySignature,
20-
} from 'typescript';
1+
import type { Token, InterfaceDeclaration, ClassDeclaration, PropertyDeclaration, ParameterDeclaration, ConstructorDeclaration, TypeElement, ClassElement, PropertySignature } from 'typescript';
2+
import { NodeFlags, NodeBuilderFlags, SyntaxKind, isInterfaceDeclaration, isPropertyDeclaration, isConstructorDeclaration, isPropertySignature } from 'typescript';
213
import { Tsoa } from '@tsoa/runtime';
224

235
import { Transformer } from './transformer';
@@ -32,7 +14,7 @@ import { throwUnless } from '../../utils/flowUtils';
3214
type OverrideToken = Token<SyntaxKind.QuestionToken> | Token<SyntaxKind.PlusToken> | Token<SyntaxKind.MinusToken> | undefined;
3315

3416
export class PropertyTransformer extends Transformer {
35-
public transform(node: InterfaceDeclaration | ClassDeclaration, overrideToken?: OverrideToken): Tsoa.Property[] {
17+
public transform(resolver: TypeResolver, node: InterfaceDeclaration | ClassDeclaration, overrideToken?: OverrideToken): Tsoa.Property[] {
3618
const isIgnored = (e: TypeElement | ClassElement) => {
3719
let ignore = isExistJSDocTag(e, tag => tag.tagName.text === 'ignore');
3820
ignore = ignore || (e.flags & NodeFlags.ThisNodeHasError) > 0;
@@ -43,7 +25,7 @@ export class PropertyTransformer extends Transformer {
4325
if (isInterfaceDeclaration(node)) {
4426
return node.members
4527
.filter((member): member is PropertySignature => !isIgnored(member) && isPropertySignature(member))
46-
.map((member: PropertySignature) => this.propertyFromSignature(member, overrideToken));
28+
.map((member: PropertySignature) => this.propertyFromSignature(resolver, member, overrideToken));
4729
}
4830

4931
const properties: Array<PropertyDeclaration | ParameterDeclaration> = [];
@@ -61,10 +43,10 @@ export class PropertyTransformer extends Transformer {
6143
properties.push(...constructorProperties);
6244
}
6345

64-
return properties.map(property => this.propertyFromDeclaration(property, overrideToken));
46+
return properties.map(property => this.propertyFromDeclaration(resolver, property, overrideToken));
6547
}
6648

67-
private propertyFromSignature(propertySignature: PropertySignature, overrideToken?: OverrideToken): Tsoa.Property {
49+
private propertyFromSignature(resolver: TypeResolver, propertySignature: PropertySignature, overrideToken?: OverrideToken): Tsoa.Property {
6850
throwUnless(propertySignature.type, new GenerateMetadataError(`No valid type found for property declaration.`));
6951

7052
let required = !propertySignature.questionToken;
@@ -78,54 +60,54 @@ export class PropertyTransformer extends Transformer {
7860

7961
const property: Tsoa.Property = {
8062
default: def,
81-
description: this.resolver.getNodeDescription(propertySignature),
82-
example: this.resolver.getNodeExample(propertySignature),
83-
format: this.resolver.getNodeFormat(propertySignature),
84-
name: this.resolver.getPropertyName(propertySignature),
63+
description: resolver.getNodeDescription(propertySignature),
64+
example: resolver.getNodeExample(propertySignature),
65+
format: resolver.getNodeFormat(propertySignature),
66+
name: resolver.getPropertyName(propertySignature),
8567
required,
86-
type: new TypeResolver(propertySignature.type, this.resolver.current, propertySignature.type.parent, this.resolver.context).resolve(),
68+
type: new TypeResolver(propertySignature.type, resolver.current, propertySignature.type.parent, resolver.context).resolve(),
8769
validators: getPropertyValidators(propertySignature) || {},
8870
deprecated: isExistJSDocTag(propertySignature, tag => tag.tagName.text === 'deprecated'),
89-
extensions: this.resolver.getNodeExtension(propertySignature),
71+
extensions: resolver.getNodeExtension(propertySignature),
9072
};
9173
return property;
9274
}
9375

94-
private propertyFromDeclaration(propertyDeclaration: PropertyDeclaration | ParameterDeclaration, overrideToken?: OverrideToken): Tsoa.Property {
76+
private propertyFromDeclaration(resolver: TypeResolver, propertyDeclaration: PropertyDeclaration | ParameterDeclaration, overrideToken?: OverrideToken): Tsoa.Property {
9577
let typeNode = propertyDeclaration.type;
9678

97-
const tsType = this.resolver.current.typeChecker.getTypeAtLocation(propertyDeclaration);
79+
const tsType = resolver.current.typeChecker.getTypeAtLocation(propertyDeclaration);
9880

9981
if (!typeNode) {
10082
// Type is from initializer
101-
typeNode = this.resolver.current.typeChecker.typeToTypeNode(tsType, undefined, NodeBuilderFlags.NoTruncation)!;
83+
typeNode = resolver.current.typeChecker.typeToTypeNode(tsType, undefined, NodeBuilderFlags.NoTruncation)!;
10284
}
10385

104-
const type = new TypeResolver(typeNode, this.resolver.current, propertyDeclaration, this.resolver.context, tsType).resolve();
86+
const type = new TypeResolver(typeNode, resolver.current, propertyDeclaration, resolver.context, tsType).resolve();
10587

10688
let required = !propertyDeclaration.questionToken && !propertyDeclaration.initializer;
10789
if (overrideToken && overrideToken.kind === SyntaxKind.MinusToken) {
10890
required = true;
10991
} else if (overrideToken && overrideToken.kind === SyntaxKind.QuestionToken) {
11092
required = false;
11193
}
112-
let def = getInitializerValue(propertyDeclaration.initializer, this.resolver.current.typeChecker);
94+
let def = getInitializerValue(propertyDeclaration.initializer, resolver.current.typeChecker);
11395
if (def === undefined) {
11496
def = TypeResolver.getDefault(propertyDeclaration);
11597
}
11698

11799
const property: Tsoa.Property = {
118100
default: def,
119-
description: this.resolver.getNodeDescription(propertyDeclaration),
120-
example: this.resolver.getNodeExample(propertyDeclaration),
121-
format: this.resolver.getNodeFormat(propertyDeclaration),
122-
name: this.resolver.getPropertyName(propertyDeclaration),
101+
description: resolver.getNodeDescription(propertyDeclaration),
102+
example: resolver.getNodeExample(propertyDeclaration),
103+
format: resolver.getNodeFormat(propertyDeclaration),
104+
name: resolver.getPropertyName(propertyDeclaration),
123105
required,
124106
type,
125107
validators: getPropertyValidators(propertyDeclaration) || {},
126108
// class properties and constructor parameters may be deprecated either via jsdoc annotation or decorator
127109
deprecated: isExistJSDocTag(propertyDeclaration, tag => tag.tagName.text === 'deprecated') || isDecorator(propertyDeclaration, identifier => identifier.text === 'Deprecated'),
128-
extensions: this.resolver.getNodeExtension(propertyDeclaration),
110+
extensions: resolver.getNodeExtension(propertyDeclaration),
129111
};
130112
return property;
131113
}

packages/cli/src/metadataGeneration/transformer/referenceTransformer.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,16 @@ export class ReferenceTransformer extends Transformer {
6868
return result;
6969
}
7070

71-
public transform(declaration: TypeAliasDeclaration, refTypeName: string, referencer?: Type): Tsoa.ReferenceType {
72-
const example = this.resolver.getNodeExample(declaration);
71+
public transform(declaration: TypeAliasDeclaration, refTypeName: string, resolver: TypeResolver, referencer?: Type): Tsoa.ReferenceType {
72+
const example = resolver.getNodeExample(declaration);
7373

7474
const referenceType: Tsoa.ReferenceType = {
7575
dataType: 'refAlias',
7676
default: TypeResolver.getDefault(declaration),
77-
description: this.resolver.getNodeDescription(declaration),
77+
description: resolver.getNodeDescription(declaration),
7878
refName: refTypeName,
79-
format: this.resolver.getNodeFormat(declaration),
80-
type: new TypeResolver(declaration.type, this.resolver.current, declaration, this.resolver.context, this.resolver.referencer || referencer).resolve(),
79+
format: resolver.getNodeFormat(declaration),
80+
type: new TypeResolver(declaration.type, resolver.current, declaration, resolver.context, resolver.referencer || referencer).resolve(),
8181
validators: getPropertyValidators(declaration) || {},
8282
...(example && { example }),
8383
};

packages/cli/src/metadataGeneration/transformer/transformer.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
11
import { SyntaxKind, getModifiers, type HasModifiers } from 'typescript';
22

3-
import { TypeResolver } from '../typeResolver';
4-
53
/**
64
* Transformer responsible to transforming native ts node into tsoa type.
75
*/
86
export abstract class Transformer {
9-
constructor(
10-
protected readonly resolver: TypeResolver,
11-
) {}
12-
137
protected hasPublicModifier(node: HasModifiers): boolean {
148
return (
159
!node.modifiers ||

packages/cli/src/metadataGeneration/typeResolver.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ export class TypeResolver {
4848
}
4949

5050
public resolve(): Tsoa.Type {
51-
const primitiveType = new PrimitiveTransformer(this).transform(this.typeNode, this.parentNode);
51+
const partentJsDocTagNames = this.parentNode ? getJSDocTagNames(this.parentNode) : undefined;
52+
const primitiveType = new PrimitiveTransformer().transform(this.current.defaultNumberType, this.typeNode, partentJsDocTagNames);
5253
if (primitiveType) {
5354
return primitiveType;
5455
}
@@ -469,7 +470,7 @@ export class TypeResolver {
469470

470471
switch (typeName.text) {
471472
case 'Date':
472-
return new DateTransformer(this).transform(parentNode);
473+
return new DateTransformer().transform(parentNode);
473474
case 'Buffer':
474475
case 'Readable':
475476
return { dataType: 'buffer' };
@@ -782,9 +783,9 @@ export class TypeResolver {
782783
for (const declaration of declarations) {
783784
if (ts.isTypeAliasDeclaration(declaration)) {
784785
const referencer = node.pos !== -1 ? this.current.typeChecker.getTypeFromTypeNode(node) : undefined;
785-
referenceTypes.push(new ReferenceTransformer(this).transform(declaration, refTypeName, referencer));
786+
referenceTypes.push(new ReferenceTransformer().transform(declaration, refTypeName, this, referencer));
786787
} else if (EnumTransformer.transformable(declaration)) {
787-
referenceTypes.push(new EnumTransformer(this).transform(declaration, refTypeName));
788+
referenceTypes.push(new EnumTransformer().transform(this, declaration, refTypeName));
788789
} else {
789790
referenceTypes.push(this.getModelReference(declaration, refTypeName));
790791
}
@@ -846,7 +847,7 @@ export class TypeResolver {
846847
return referenceType;
847848
}
848849

849-
const properties = new PropertyTransformer(this).transform(modelType);
850+
const properties = new PropertyTransformer().transform(this, modelType);
850851
const additionalProperties = this.getModelAdditionalProperties(modelType);
851852
const inheritedProperties = this.getModelInheritedProperties(modelType) || [];
852853

0 commit comments

Comments
 (0)