Skip to content

Commit

Permalink
chore: lint
Browse files Browse the repository at this point in the history
  • Loading branch information
Ahmed Hekal committed Jul 3, 2024
1 parent bf1c0fd commit cfb3cda
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 90 deletions.
13 changes: 10 additions & 3 deletions packages/cli/src/metadataGeneration/parameterGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import { TypeResolver } from './typeResolver';
import { getHeaderType } from '../utils/headerTypeHelpers';

export class ParameterGenerator {
constructor(private readonly parameter: ts.ParameterDeclaration, private readonly method: string, private readonly path: string, private readonly current: MetadataGenerator) {}
constructor(
private readonly parameter: ts.ParameterDeclaration,
private readonly method: string,
private readonly path: string,
private readonly current: MetadataGenerator,
) {}

public Generate(): Tsoa.Parameter[] {
const decoratorName = getNodeFirstDecoratorName(this.parameter, identifier => this.supportParameterDecorator(identifier.text));
Expand Down Expand Up @@ -414,7 +419,7 @@ export class ParameterGenerator {
const exampleLabels: Array<string | undefined> = [];
const examples = getJSDocTags(node.parent, tag => {
const comment = commentToString(tag.comment);
const isExample = (tag.tagName.text === 'example' || tag.tagName.escapedText === 'example') && !!tag.comment && comment?.startsWith(parameterName);
const isExample = (tag.tagName.text === 'example' || (tag.tagName.escapedText as string) === 'example') && !!tag.comment && comment?.startsWith(parameterName);

if (isExample) {
const hasExampleLabel = (comment?.split(' ')[0].indexOf('.') || -1) > 0;
Expand Down Expand Up @@ -447,7 +452,9 @@ export class ParameterGenerator {
}

private supportParameterDecorator(decoratorName: string) {
return ['header', 'query', 'queries', 'path', 'body', 'bodyprop', 'request', 'requestprop', 'res', 'inject', 'uploadedfile', 'uploadedfiles', 'formfield'].some(d => d === decoratorName.toLocaleLowerCase());
return ['header', 'query', 'queries', 'path', 'body', 'bodyprop', 'request', 'requestprop', 'res', 'inject', 'uploadedfile', 'uploadedfiles', 'formfield'].some(
d => d === decoratorName.toLocaleLowerCase(),
);
}

private supportPathDataType(parameterType: Tsoa.Type): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ export class ReferenceTransformer extends Transformer {
}

if (referenceTypes.every(refType => refType.dataType === 'refEnum')) {
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
return EnumTransformer.mergeMany(referenceTypes as Tsoa.RefEnumType[]);
}

if (referenceTypes.every(refType => refType.dataType === 'refObject')) {
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
return this.mergeManyRefObj(referenceTypes as Tsoa.RefObjectType[]);
}

Expand Down
111 changes: 25 additions & 86 deletions packages/cli/src/metadataGeneration/typeResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,11 @@ export class TypeResolver {
let additionalType: Tsoa.Type | undefined;

if (indexMember) {
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
const indexSignatureDeclaration = indexMember as ts.IndexSignatureDeclaration;
const indexType = new TypeResolver(indexSignatureDeclaration.parameters[0].type as ts.TypeNode, this.current, this.parentNode, this.context).resolve();

throwUnless(
indexType.dataType === 'string',
new GenerateMetadataError(`Only string indexers are supported.`, this.typeNode),
);
throwUnless(indexType.dataType === 'string', new GenerateMetadataError(`Only string indexers are supported.`, this.typeNode));

additionalType = new TypeResolver(indexSignatureDeclaration.type, this.current, this.parentNode, this.context).resolve();
}
Expand Down Expand Up @@ -216,7 +214,7 @@ export class TypeResolver {
const parent = getOneOrigDeclaration(property); //If there are more declarations, we need to get one of them, from where we want to recognize jsDoc
const type = new TypeResolver(typeNode, this.current, parent, this.context, propertyType).resolve();

const required = !(this.hasFlag(property, ts.SymbolFlags.Optional));
const required = !this.hasFlag(property, ts.SymbolFlags.Optional);

const comments = property.getDocumentationComment(this.current.typeChecker);
const description = comments.length ? ts.displayPartsToString(comments) : undefined;
Expand Down Expand Up @@ -319,22 +317,12 @@ export class TypeResolver {
return new TypeResolver(this.typeNode.type, this.current, this.typeNode, this.context, this.referencer).resolve();
}

throwUnless(
this.typeNode.kind === ts.SyntaxKind.TypeReference,
new GenerateMetadataError(`Unknown type: ${ts.SyntaxKind[this.typeNode.kind]}`, this.typeNode),
);
throwUnless(this.typeNode.kind === ts.SyntaxKind.TypeReference, new GenerateMetadataError(`Unknown type: ${ts.SyntaxKind[this.typeNode.kind]}`, this.typeNode));

return this.resolveTypeReferenceNode(this.typeNode as ts.TypeReferenceNode, this.current, this.context, this.parentNode);
}

private resolveTypeOperatorNode(
typeNode: ts.TypeOperatorNode,
typeChecker: ts.TypeChecker,
current: MetadataGenerator,
context: Context,
parentNode?: ts.Node,
referencer?: ts.Type,
): Tsoa.Type {
private resolveTypeOperatorNode(typeNode: ts.TypeOperatorNode, typeChecker: ts.TypeChecker, current: MetadataGenerator, context: Context, parentNode?: ts.Node, referencer?: ts.Type): Tsoa.Type {
switch (typeNode.operator) {
case ts.SyntaxKind.KeyOfKeyword: {
// keyof
Expand All @@ -344,10 +332,7 @@ export class TypeResolver {
const symbol = type.type.getSymbol();
if (symbol && symbol.getFlags() & ts.TypeFlags.TypeParameter) {
const typeName = symbol.getEscapedName();
throwUnless(
typeof typeName === 'string',
new GenerateMetadataError(`typeName is not string, but ${typeof typeName}`, typeNode),
);
throwUnless(typeof typeName === 'string', new GenerateMetadataError(`typeName is not string, but ${typeof typeName}`, typeNode));

if (context[typeName]) {
const subResult = new TypeResolver(context[typeName].type, current, parentNode, context).resolve();
Expand All @@ -358,10 +343,7 @@ export class TypeResolver {
};
}
const properties = (subResult as Tsoa.RefObjectType).properties?.map(v => v.name);
throwUnless(
properties,
new GenerateMetadataError(`TypeOperator 'keyof' on node which have no properties`, context[typeName].type),
);
throwUnless(properties, new GenerateMetadataError(`TypeOperator 'keyof' on node which have no properties`, context[typeName].type));

return {
dataType: 'enum',
Expand Down Expand Up @@ -459,22 +441,13 @@ export class TypeResolver {
const isNumberIndexType = indexType.kind === ts.SyntaxKind.NumberKeyword;
const typeOfObjectType = typeChecker.getTypeFromTypeNode(objectType);
const type = isNumberIndexType ? typeOfObjectType.getNumberIndexType() : typeOfObjectType.getStringIndexType();
throwUnless(
type,
new GenerateMetadataError(`Could not determine ${isNumberIndexType ? 'number' : 'string'} index on ${typeChecker.typeToString(typeOfObjectType)}`, typeNode),
);
throwUnless(type, new GenerateMetadataError(`Could not determine ${isNumberIndexType ? 'number' : 'string'} index on ${typeChecker.typeToString(typeOfObjectType)}`, typeNode));
return new TypeResolver(typeChecker.typeToTypeNode(type, objectType, ts.NodeBuilderFlags.NoTruncation)!, current, typeNode, context).resolve();
} else if (ts.isLiteralTypeNode(indexType) && (ts.isStringLiteral(indexType.literal) || ts.isNumericLiteral(indexType.literal))) {
// Indexed by literal
const hasType = (node: ts.Node | undefined): node is ts.HasType => node !== undefined && Object.prototype.hasOwnProperty.call(node, 'type');
const symbol = typeChecker.getPropertyOfType(typeChecker.getTypeFromTypeNode(objectType), indexType.literal.text);
throwUnless(
symbol,
new GenerateMetadataError(
`Could not determine the keys on ${typeChecker.typeToString(typeChecker.getTypeFromTypeNode(objectType))}`,
typeNode,
),
);
throwUnless(symbol, new GenerateMetadataError(`Could not determine the keys on ${typeChecker.typeToString(typeChecker.getTypeFromTypeNode(objectType))}`, typeNode));
if (hasType(symbol.valueDeclaration) && symbol.valueDeclaration.type) {
return new TypeResolver(symbol.valueDeclaration.type, current, typeNode, context).resolve();
}
Expand All @@ -483,9 +456,7 @@ export class TypeResolver {
return new TypeResolver(typeChecker.typeToTypeNode(declaration, objectType, ts.NodeBuilderFlags.NoTruncation)!, current, typeNode, context).resolve();
} catch {
throw new GenerateMetadataError(
`Could not determine the keys on ${typeChecker.typeToString(
typeChecker.getTypeFromTypeNode(typeChecker.typeToTypeNode(declaration, undefined, ts.NodeBuilderFlags.NoTruncation)!),
)}`,
`Could not determine the keys on ${typeChecker.typeToString(typeChecker.getTypeFromTypeNode(typeChecker.typeToTypeNode(declaration, undefined, ts.NodeBuilderFlags.NoTruncation)!))}`,
typeNode,
);
}
Expand All @@ -504,27 +475,22 @@ export class TypeResolver {
throw new GenerateMetadataError(`Unknown type: ${ts.SyntaxKind[typeNode.kind]}`, typeNode);
}

private resolveTypeReferenceNode(
typeNode: ts.TypeReferenceNode,
current: MetadataGenerator,
context: Context,
parentNode?: ts.Node,
): Tsoa.Type {
private resolveTypeReferenceNode(typeNode: ts.TypeReferenceNode, current: MetadataGenerator, context: Context, parentNode?: ts.Node): Tsoa.Type {
const { typeName, typeArguments } = typeNode;

if (typeName.kind !== ts.SyntaxKind.Identifier) {
return this.getReferenceType(typeNode);
}

switch(typeName.text) {
switch (typeName.text) {
case 'Date':
return new DateTransformer(this).transform(parentNode);
case 'Buffer':
case 'Readable':
return { dataType: 'buffer' };
case 'Array':
if (typeArguments && typeArguments.length === 1) {
return {
return {
dataType: 'array',
elementType: new TypeResolver(typeArguments[0], current, parentNode, context).resolve(),
};
Expand Down Expand Up @@ -559,10 +525,7 @@ export class TypeResolver {
case ts.SyntaxKind.NullKeyword:
return null;
default:
throwUnless(
Object.prototype.hasOwnProperty.call(typeNode.literal, 'text'),
new GenerateMetadataError(`Couldn't resolve literal node: ${typeNode.literal.getText()}`),
);
throwUnless(Object.prototype.hasOwnProperty.call(typeNode.literal, 'text'), new GenerateMetadataError(`Couldn't resolve literal node: ${typeNode.literal.getText()}`));
return (typeNode.literal as ts.LiteralExpression).text;
}
}
Expand All @@ -578,15 +541,13 @@ export class TypeResolver {
return nodes;
}

throwUnless(
designatedNodes.length === 1,
new GenerateMetadataError(`Multiple models for ${typeName} marked with '@tsoaModel'; '@tsoaModel' should only be applied to one model.`),
);
throwUnless(designatedNodes.length === 1, new GenerateMetadataError(`Multiple models for ${typeName} marked with '@tsoaModel'; '@tsoaModel' should only be applied to one model.`));

return designatedNodes;
}

private hasFlag(type: ts.Type | ts.Symbol | ts.Declaration, flag: ts.TypeFlags | ts.NodeFlags | ts.SymbolFlags) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison
return (type.flags & flag) === flag;
}

Expand Down Expand Up @@ -650,10 +611,7 @@ export class TypeResolver {

while (!ts.isSourceFile(actNode)) {
if (!(isFirst && ts.isEnumDeclaration(actNode)) && !ts.isModuleBlock(actNode)) {
throwUnless(
ts.isModuleDeclaration(actNode),
new GenerateMetadataError(`This node kind is unknown: ${actNode.kind}`, type),
);
throwUnless(ts.isModuleDeclaration(actNode), new GenerateMetadataError(`This node kind is unknown: ${actNode.kind}`, type));

if (!isGlobalDeclaration(actNode)) {
const moduleName = actNode.name.text;
Expand Down Expand Up @@ -729,30 +687,23 @@ export class TypeResolver {
return resolvedType;
}
if (ts.isTypeReferenceNode(arg) || ts.isExpressionWithTypeArguments(arg)) {
const [_, name] = this.calcTypeReferenceTypeName(arg);
return name;
return this.calcTypeReferenceTypeName(arg)[1];
} else if (ts.isTypeLiteralNode(arg)) {
const members = arg.members.map(member => {
if (ts.isPropertySignature(member)) {
const name = (member.name as ts.Identifier).text;
const typeText = this.calcTypeName(member.type as ts.TypeNode);
return `"${name}"${member.questionToken ? '?' : ''}${this.calcMemberJsDocProperties(member)}: ${typeText}`;
} else if (ts.isIndexSignatureDeclaration(member)) {
throwUnless(
member.parameters.length === 1,
new GenerateMetadataError(`Index signature parameters length != 1`, member),
);
throwUnless(member.parameters.length === 1, new GenerateMetadataError(`Index signature parameters length != 1`, member));

const indexType = member.parameters[0];
throwUnless(
// now we can't reach this part of code
ts.isParameter(indexType),
new GenerateMetadataError(`indexSignature declaration parameter kind is not SyntaxKind.Parameter`, indexType),
);
throwUnless(
!indexType.questionToken,
new GenerateMetadataError(`Question token has found for an indexSignature declaration`, indexType),
);
throwUnless(!indexType.questionToken, new GenerateMetadataError(`Question token has found for an indexSignature declaration`, indexType));

const typeText = this.calcTypeName(member.type);
const indexName = (indexType.name as ts.Identifier).text;
Expand Down Expand Up @@ -886,10 +837,7 @@ export class TypeResolver {
const deprecated = isExistJSDocTag(modelType, tag => tag.tagName.text === 'deprecated') || isDecorator(modelType, identifier => identifier.text === 'Deprecated');

// Handle toJSON methods
throwUnless(
modelType.name,
new GenerateMetadataError("Can't get Symbol from anonymous class", modelType),
);
throwUnless(modelType.name, new GenerateMetadataError("Can't get Symbol from anonymous class", modelType));

const type = this.current.typeChecker.getTypeAtLocation(modelType.name);
const toJSON = this.current.typeChecker.getPropertyOfType(type, 'toJSON');
Expand Down Expand Up @@ -995,23 +943,17 @@ export class TypeResolver {
}
const declarations = symbol?.getDeclarations();

throwUnless(
symbol && declarations,
new GenerateMetadataError(`No declarations found for referenced type ${typeName}.`),
);
throwUnless(symbol && declarations, new GenerateMetadataError(`No declarations found for referenced type ${typeName}.`));

if (symbol.escapedName !== typeName && symbol.escapedName !== 'default') {
if ((symbol.escapedName as string) !== typeName && (symbol.escapedName as string) !== 'default') {
typeName = symbol.escapedName as string;
}

let modelTypes = declarations.filter((node): node is UsableDeclarationWithoutPropertySignature => {
return this.nodeIsUsable(node) && node.name?.getText() === typeName;
});

throwUnless(
modelTypes.length,
new GenerateMetadataError(`No matching model found for referenced type ${typeName}.`),
);
throwUnless(modelTypes.length, new GenerateMetadataError(`No matching model found for referenced type ${typeName}.`));

if (modelTypes.length > 1) {
// remove types that are from typescript e.g. 'Account'
Expand Down Expand Up @@ -1041,10 +983,7 @@ export class TypeResolver {

const indexSignatureDeclaration = indexMember as ts.IndexSignatureDeclaration;
const indexType = new TypeResolver(indexSignatureDeclaration.parameters[0].type as ts.TypeNode, this.current, this.parentNode, this.context).resolve();
throwUnless(
indexType.dataType === 'string',
new GenerateMetadataError(`Only string indexers are supported.`, this.typeNode),
);
throwUnless(indexType.dataType === 'string', new GenerateMetadataError(`Only string indexers are supported.`, this.typeNode));

return new TypeResolver(indexSignatureDeclaration.type, this.current, this.parentNode, this.context).resolve();
}
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/swagger/specGenerator2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ export class SpecGenerator2 extends SpecGenerator {
if (typesWithoutUndefined.every(subType => subType.dataType === 'enum')) {
const mergedEnum: Tsoa.EnumType = { dataType: 'enum', enums: [] };
typesWithoutUndefined.forEach(t => {
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
mergedEnum.enums = [...mergedEnum.enums, ...(t as Tsoa.EnumType).enums];
});
return this.getSwaggerTypeForEnumType(mergedEnum);
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/swagger/specGenerator3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export class SpecGenerator3 extends SpecGenerator {
type: 'http',
} as Swagger.BasicSecurity3;
} else if (definitions[key].type === 'oauth2') {
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
const definition = definitions[key] as
| Swagger.OAuth2PasswordSecurity
| Swagger.OAuth2ApplicationSecurity
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/utils/jsDocUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function getJSDocComment(node: ts.Node, tagName: string) {
}

export function getJSDocComments(node: ts.Node, tagName: string) {
const tags = getJSDocTags(node, tag => tag.tagName.text === tagName || tag.tagName.escapedText === tagName);
const tags = getJSDocTags(node, tag => tag.tagName.text === tagName || (tag.tagName.escapedText as string) === tagName);
if (tags.length === 0) {
return;
}
Expand Down

0 comments on commit cfb3cda

Please sign in to comment.