Skip to content

Commit 29ec2e9

Browse files
Merge remote-tracking branch 'upstream/master'
2 parents f93e26c + c50fc6d commit 29ec2e9

File tree

90 files changed

+2502
-1539
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+2502
-1539
lines changed

.eslintrc.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ module.exports = {
1818
default: 'array-simple',
1919
},
2020
],
21-
'@typescript-eslint/ban-types': 'off',
2221
'@typescript-eslint/naming-convention': [
2322
'error',
2423
{
@@ -32,7 +31,6 @@ module.exports = {
3231
],
3332
'@typescript-eslint/no-explicit-any': 'off',
3433
'@typescript-eslint/no-unsafe-assignment': 'off',
35-
'@typescript-eslint/no-unsafe-call': 'off',
3634
'@typescript-eslint/no-unsafe-argument': 'off',
3735
'@typescript-eslint/no-unsafe-member-access': 'off',
3836
'@typescript-eslint/no-unsafe-return': 'off',

.github/workflows/closeStaleIssuesAndPRs.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
repo-token: ${{ secrets.GITHUB_TOKEN }}
1313
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days'
1414
stale-pr-message: 'This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days'
15-
days-before-stale: 30
16-
days-before-close: 5
15+
days-before-stale: 150
16+
days-before-close: 30
1717
exempt-issue-labels: 'help wanted,Pending feedback'
1818
exempt-pr-labels: 'help wanted,breaking change'

lerna.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "6.4.0",
2+
"version": "6.5.1",
33
"npmClient": "yarn",
44
"command": {
55
"version": {

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@
4949
"eslint": "^8.56.0",
5050
"eslint-config-prettier": "^9.1.0",
5151
"husky": "^4.3.0",
52-
"lerna": "^8.0.1",
53-
"lint-staged": "^15.2.0",
54-
"prettier": "^3.1.1",
52+
"lerna": "^8.1.8",
53+
"lint-staged": "^15.2.10",
54+
"prettier": "^3.3.3",
5555
"ts-node": "^10.9.2",
56-
"typedoc": "^0.25.12"
56+
"typedoc": "^0.26.7"
5757
},
5858
"resolutions": {
5959
"typescript": "^5.2.2"

packages/cli/package.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@tsoa/cli",
33
"description": "Build swagger-compliant REST APIs using TypeScript and Node",
4-
"version": "6.4.0",
4+
"version": "6.5.1",
55
"main": "./dist/index.js",
66
"typings": "./dist/index.d.ts",
77
"files": [
@@ -30,25 +30,25 @@
3030
"author": "Luke Autry <[email protected]> (http://www.lukeautry.com)",
3131
"license": "MIT",
3232
"dependencies": {
33-
"@tsoa/runtime": "^6.4.0",
34-
"@types/multer": "^1.4.11",
33+
"@tsoa/runtime": "^6.5.1",
34+
"@types/multer": "^1.4.12",
3535
"fs-extra": "^11.2.0",
3636
"glob": "^10.3.10",
3737
"handlebars": "^4.7.8",
3838
"merge-anything": "^5.1.4",
3939
"minimatch": "^9.0.1",
40-
"ts-deepmerge": "^7.0.0",
41-
"typescript": "^5.3.3",
42-
"validator": "^13.11.0",
43-
"yaml": "^2.4.1",
40+
"ts-deepmerge": "^7.0.1",
41+
"typescript": "^5.6.2",
42+
"validator": "^13.12.0",
43+
"yaml": "^2.5.1",
4444
"yargs": "^17.7.1"
4545
},
4646
"devDependencies": {
4747
"@types/glob": "^8.1.0",
4848
"@types/minimatch": "^5.1.0",
4949
"@types/node": "^18.0.0",
50-
"@types/validator": "^13.11.7",
51-
"@types/yargs": "^17.0.32",
50+
"@types/validator": "^13.12.2",
51+
"@types/yargs": "^17.0.33",
5252
"copyfiles": "^2.4.1",
5353
"rimraf": "^5.0.5"
5454
},

packages/cli/src/cli.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,3 +397,11 @@ export async function generateSpecAndRoutes(args: SwaggerArgs, metadata?: Tsoa.M
397397
throw err;
398398
}
399399
}
400+
export type RouteGeneratorModule<Config extends ExtendedRoutesConfig> = {
401+
default: new (
402+
metadata: Tsoa.Metadata,
403+
routesConfig: Config,
404+
) => {
405+
GenerateCustomRoutes: () => Promise<void>;
406+
};
407+
};

packages/cli/src/metadataGeneration/extension.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@ export function getExtensions(decorators: ts.Identifier[], metadataGenerator: Me
1212

1313
const [decoratorKeyArg, decoratorValueArg] = extensionDecorator.parent.arguments;
1414

15-
if (!ts.isStringLiteral(decoratorKeyArg)) {
15+
if (!ts.isStringLiteral(decoratorKeyArg) && !ts.isIdentifier(decoratorKeyArg)) {
1616
throw new Error('The first argument of @Extension must be a string');
1717
}
1818

19-
const attributeKey = decoratorKeyArg.text;
19+
const attributeKey = ts.isIdentifier(decoratorKeyArg) ? getInitializerValue(decoratorKeyArg, metadataGenerator.typeChecker) : decoratorKeyArg.text;
20+
21+
if (typeof attributeKey !== 'string') {
22+
throw new Error('The first argument of @Extension must be a string');
23+
}
2024

2125
if (!decoratorValueArg) {
2226
throw new Error(`Extension '${attributeKey}' must contain a value`);

packages/cli/src/metadataGeneration/methodGenerator.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,11 @@ export class MethodGenerator {
197197

198198
return decorators.map(decorator => {
199199
const [name, description, example, produces] = getDecoratorValues(decorator, this.current.typeChecker);
200-
responseExamplesByName[name] = responseExamplesByName[name] ? [...responseExamplesByName[name], example] : [example];
200+
201+
if (example !== undefined) {
202+
responseExamplesByName[name] = responseExamplesByName[name] ? [...responseExamplesByName[name], example] : [example];
203+
}
204+
201205
return {
202206
description: description || '',
203207
examples: responseExamplesByName[name] || undefined,

packages/cli/src/metadataGeneration/parameterGenerator.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,16 +86,36 @@ export class ParameterGenerator {
8686
};
8787
}
8888

89+
private extractTsoaResponse(typeNode: ts.TypeNode | undefined): ts.TypeReferenceNode | undefined {
90+
if (!typeNode || !ts.isTypeReferenceNode(typeNode)) {
91+
return undefined;
92+
}
93+
if (typeNode.typeName.getText() === 'TsoaResponse') {
94+
return typeNode;
95+
}
96+
97+
const symbol = this.current.typeChecker.getTypeAtLocation(typeNode).aliasSymbol;
98+
if (!symbol || !symbol.declarations) {
99+
return undefined;
100+
}
101+
const declaration = symbol.declarations[0];
102+
if (!ts.isTypeAliasDeclaration(declaration) || !ts.isTypeReferenceNode(declaration.type)) {
103+
return undefined;
104+
}
105+
106+
return declaration.type.typeName.getText() === 'TsoaResponse' ? declaration.type : undefined;
107+
}
108+
89109
private getResParameters(parameter: ts.ParameterDeclaration): Tsoa.ResParameter[] {
90110
const parameterName = (parameter.name as ts.Identifier).text;
91111
const decorator = getNodeFirstDecoratorValue(this.parameter, this.current.typeChecker, ident => ident.text === 'Res') || parameterName;
92112
if (!decorator) {
93113
throw new GenerateMetadataError('Could not find Decorator', parameter);
94114
}
95115

96-
const typeNode = parameter.type;
116+
const typeNode = this.extractTsoaResponse(parameter.type);
97117

98-
if (!typeNode || !ts.isTypeReferenceNode(typeNode) || typeNode.typeName.getText() !== 'TsoaResponse') {
118+
if (!typeNode) {
99119
throw new GenerateMetadataError('@Res() requires the type to be TsoaResponse<HTTPStatusCode, ResBody>', parameter);
100120
}
101121

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
};

0 commit comments

Comments
 (0)