Skip to content

Commit c1295b6

Browse files
committedJun 26, 2023
fix(plugin): revamp types resolution, fix empty summary in ctrl bug
1 parent 0ae98f5 commit c1295b6

File tree

6 files changed

+114
-57
lines changed

6 files changed

+114
-57
lines changed
 

‎lib/plugin/utils/plugin-utils.ts

+19-11
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ export function replaceImportPath(
161161
} catch (_error) {
162162
const from = options?.readonly
163163
? options.pathToSource
164-
: posix.dirname(fileName);
164+
: posix.dirname(convertPath(fileName));
165165

166166
let relativePath = posix.relative(from, importPath);
167167
relativePath = relativePath[0] !== '.' ? './' + relativePath : relativePath;
@@ -190,10 +190,17 @@ export function replaceImportPath(
190190

191191
typeReference = typeReference.replace(importPath, relativePath);
192192

193+
if (options.readonly) {
194+
const { typeName, typeImportStatement } =
195+
convertToAsyncImport(typeReference);
196+
return {
197+
typeReference: typeImportStatement,
198+
typeName,
199+
importPath: relativePath
200+
};
201+
}
193202
return {
194-
typeReference: options.readonly
195-
? convertToAsyncImport(typeReference)
196-
: typeReference.replace('import', 'require'),
203+
typeReference: typeReference.replace('import', 'require'),
197204
importPath: relativePath
198205
};
199206
}
@@ -205,14 +212,15 @@ function convertToAsyncImport(typeReference: string) {
205212

206213
if (match?.length >= 2) {
207214
const importPos = typeReference.indexOf(match[0]);
208-
typeReference = typeReference.replace(
209-
match[1],
210-
`then((f) => f.${match[1]})`
211-
);
212-
return insertAt(typeReference, importPos, 'await ');
215+
typeReference = typeReference.replace(`.${match[1]}`, '');
216+
217+
return {
218+
typeImportStatement: insertAt(typeReference, importPos, 'await '),
219+
typeName: match[1]
220+
};
213221
}
214222

215-
return typeReference;
223+
return { typeImportStatement: typeReference };
216224
}
217225

218226
export function insertAt(string: string, index: number, substring: string) {
@@ -326,7 +334,7 @@ function isOptionalBoolean(text: string) {
326334
* Converts Windows specific file paths to posix
327335
* @param windowsPath
328336
*/
329-
function convertPath(windowsPath: string) {
337+
export function convertPath(windowsPath: string) {
330338
return windowsPath
331339
.replace(/^\\\\\?\\/, '')
332340
.replace(/\\/g, '/')

‎lib/plugin/visitors/controller-class.visitor.ts

+45-24
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
getTsDocTagsOfNode
1313
} from '../utils/ast-utils';
1414
import {
15+
convertPath,
1516
getDecoratorOrUndefinedByNames,
1617
getTypeReferenceAsString,
1718
hasPropertyKey,
@@ -204,12 +205,19 @@ export class ControllerClassVisitor extends AbstractFileVisitor {
204205
decorators,
205206
factory
206207
);
207-
const apiOperationExpr: ts.ObjectLiteralExpression | undefined =
208-
apiOperationDecorator &&
209-
head(getDecoratorArguments(apiOperationDecorator));
210-
const apiOperationExprProperties =
211-
apiOperationExpr &&
212-
(apiOperationExpr.properties as ts.NodeArray<ts.PropertyAssignment>);
208+
let apiOperationExistingProps:
209+
| ts.NodeArray<ts.PropertyAssignment>
210+
| undefined = undefined;
211+
212+
if (apiOperationDecorator && !options.readonly) {
213+
const apiOperationExpr = head(
214+
getDecoratorArguments(apiOperationDecorator)
215+
);
216+
if (apiOperationExpr) {
217+
apiOperationExistingProps =
218+
apiOperationExpr.properties as ts.NodeArray<ts.PropertyAssignment>;
219+
}
220+
}
213221

214222
const extractedComments = getMainCommentOfNode(node, sourceFile);
215223
if (!extractedComments) {
@@ -222,12 +230,12 @@ export class ControllerClassVisitor extends AbstractFileVisitor {
222230
keyToGenerate,
223231
factory.createStringLiteral(extractedComments)
224232
),
225-
...(apiOperationExprProperties ?? factory.createNodeArray())
233+
...(apiOperationExistingProps ?? factory.createNodeArray())
226234
];
227235

228236
const hasDeprecatedKey = hasPropertyKey(
229237
'deprecated',
230-
factory.createNodeArray(apiOperationExprProperties)
238+
factory.createNodeArray(apiOperationExistingProps)
231239
);
232240
if (!hasDeprecatedKey && tags.deprecated) {
233241
const deprecatedPropertyAssignment = factory.createPropertyAssignment(
@@ -345,16 +353,15 @@ export class ControllerClassVisitor extends AbstractFileVisitor {
345353
if (!type) {
346354
return undefined;
347355
}
348-
const { typeName, isArray } = getTypeReferenceAsString(type, typeChecker);
349-
if (!typeName) {
356+
const typeReferenceDescriptor = getTypeReferenceAsString(type, typeChecker);
357+
if (!typeReferenceDescriptor.typeName) {
350358
return undefined;
351359
}
352-
if (typeName.includes('node_modules')) {
360+
if (typeReferenceDescriptor.typeName.includes('node_modules')) {
353361
return undefined;
354362
}
355363
const identifier = this.typeReferenceStringToIdentifier(
356-
typeName,
357-
isArray,
364+
typeReferenceDescriptor,
358365
hostFilename,
359366
options,
360367
factory
@@ -399,20 +406,23 @@ export class ControllerClassVisitor extends AbstractFileVisitor {
399406
}
400407

401408
private normalizeImportPath(pathToSource: string, path: string) {
402-
let relativePath = posix.relative(pathToSource, path);
409+
let relativePath = posix.relative(pathToSource, convertPath(path));
403410
relativePath = relativePath[0] !== '.' ? './' + relativePath : relativePath;
404411
return relativePath;
405412
}
406413

407414
private typeReferenceStringToIdentifier(
408-
_typeReference: string,
409-
isArray: boolean,
415+
typeReferenceDescriptor: {
416+
typeName: string;
417+
isArray?: boolean;
418+
arrayDepth?: number;
419+
},
410420
hostFilename: string,
411421
options: PluginOptions,
412422
factory: ts.NodeFactory
413423
) {
414-
const { typeReference, importPath } = replaceImportPath(
415-
_typeReference,
424+
const { typeReference, importPath, typeName } = replaceImportPath(
425+
typeReferenceDescriptor.typeName,
416426
hostFilename,
417427
options
418428
);
@@ -423,14 +433,25 @@ export class ControllerClassVisitor extends AbstractFileVisitor {
423433
this._typeImports[importPath] = typeReference;
424434
}
425435

426-
identifier = factory.createIdentifier(
427-
isArray ? `[t["${importPath}"]]` : `t["${importPath}"]`
428-
);
436+
let ref = `t["${importPath}"].${typeName}`;
437+
if (typeReferenceDescriptor.isArray) {
438+
ref = this.wrapTypeInArray(ref, typeReferenceDescriptor.arrayDepth);
439+
}
440+
identifier = factory.createIdentifier(ref);
429441
} else {
430-
identifier = factory.createIdentifier(
431-
isArray ? `[${typeReference}]` : typeReference
432-
);
442+
let ref = typeReference;
443+
if (typeReferenceDescriptor.isArray) {
444+
ref = this.wrapTypeInArray(ref, typeReferenceDescriptor.arrayDepth);
445+
}
446+
identifier = factory.createIdentifier(ref);
433447
}
434448
return identifier;
435449
}
450+
451+
private wrapTypeInArray(typeRef: string, arrayDepth: number) {
452+
for (let i = 0; i < arrayDepth; i++) {
453+
typeRef = `[${typeRef}]`;
454+
}
455+
return typeRef;
456+
}
436457
}

‎lib/plugin/visitors/model-class.visitor.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
isEnum
1717
} from '../utils/ast-utils';
1818
import {
19+
convertPath,
1920
extractTypeArgumentIfArray,
2021
getDecoratorOrUndefinedByNames,
2122
getTypeReferenceAsString,
@@ -725,7 +726,7 @@ export class ModelClassVisitor extends AbstractFileVisitor {
725726
}
726727

727728
private normalizeImportPath(pathToSource: string, path: string) {
728-
let relativePath = posix.relative(pathToSource, path);
729+
let relativePath = posix.relative(pathToSource, convertPath(path));
729730
relativePath = relativePath[0] !== '.' ? './' + relativePath : relativePath;
730731
return relativePath;
731732
}
@@ -740,7 +741,7 @@ export class ModelClassVisitor extends AbstractFileVisitor {
740741
options: PluginOptions,
741742
factory: ts.NodeFactory
742743
) {
743-
const { typeReference, importPath } = replaceImportPath(
744+
const { typeReference, importPath, typeName } = replaceImportPath(
744745
typeReferenceDescriptor.typeName,
745746
hostFilename,
746747
options
@@ -752,7 +753,7 @@ export class ModelClassVisitor extends AbstractFileVisitor {
752753
this._typeImports[importPath] = typeReference;
753754
}
754755

755-
let ref = `t["${importPath}"]`;
756+
let ref = `t["${importPath}"].${typeName}`;
756757
if (typeReferenceDescriptor.isArray) {
757758
ref = this.wrapTypeInArray(ref, typeReferenceDescriptor.arrayDepth);
758759
}

‎test/plugin/fixtures/project/app.controller.ts

+5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Controller, Get } from '@nestjs/common';
2+
import { ApiOperation } from '../../../../lib/decorators';
23

34
@Controller()
45
export class AppController {
@@ -21,6 +22,10 @@ export class AppController {
2122
return 'Hello world!';
2223
}
2324

25+
/**
26+
* Returns information about the application
27+
*/
28+
@ApiOperation({ summary: 'Returns Hello World' })
2429
@Get('fastify::colon::another/:prop')
2530
withColonFastify(): string {
2631
return 'Hello world!';

‎test/plugin/fixtures/project/cats/dto/create-cat.dto.ts

+11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ import { ExtraModel } from './extra-model.dto';
33
import { LettersEnum } from './pagination-query.dto';
44
import { TagDto } from './tag.dto';
55

6+
export enum CategoryState {
7+
OK = 'OK',
8+
DEPRECATED = 'DEPRECATED'
9+
}
10+
611
@ApiExtraModels(ExtraModel)
712
export class CreateCatDto {
813
@ApiProperty()
@@ -48,6 +53,12 @@ export class CreateCatDto {
4853
})
4954
readonly enum: LettersEnum;
5055

56+
/**
57+
* Available language in the application
58+
* @example FR
59+
*/
60+
state?: CategoryState;
61+
5162
@ApiProperty({
5263
enum: LettersEnum,
5364
enumName: 'LettersEnum',

‎test/plugin/fixtures/serialized-meta.fixture.ts

+30-19
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ export default async () => {
33
const t = {
44
['./cats/dto/pagination-query.dto']: await import(
55
'./cats/dto/pagination-query.dto'
6-
).then((f) => f.LettersEnum),
7-
['./cats/dto/tag.dto']: await import('./cats/dto/tag.dto').then(
8-
(f) => f.TagDto
9-
)
6+
),
7+
['./cats/dto/create-cat.dto']: await import('./cats/dto/create-cat.dto'),
8+
['./cats/dto/tag.dto']: await import('./cats/dto/tag.dto')
109
};
1110
return {
1211
'@nestjs/swagger': {
@@ -20,16 +19,16 @@ export default async () => {
2019
limit: { required: true, type: () => Number },
2120
enum: {
2221
required: true,
23-
enum: t['./cats/dto/pagination-query.dto']
22+
enum: t['./cats/dto/pagination-query.dto'].LettersEnum
2423
},
2524
enumArr: {
2625
required: true,
27-
enum: t['./cats/dto/pagination-query.dto'],
26+
enum: t['./cats/dto/pagination-query.dto'].LettersEnum,
2827
isArray: true
2928
},
3029
letters: {
3130
required: true,
32-
enum: t['./cats/dto/pagination-query.dto'],
31+
enum: t['./cats/dto/pagination-query.dto'].LettersEnum,
3332
isArray: true
3433
},
3534
beforeDate: { required: true, type: () => Date },
@@ -59,11 +58,11 @@ export default async () => {
5958
options: { required: false, type: () => [Object] },
6059
enum: {
6160
required: true,
62-
enum: t['./cats/dto/pagination-query.dto']
61+
enum: t['./cats/dto/pagination-query.dto'].LettersEnum
6362
},
6463
enumArr: {
6564
required: true,
66-
enum: t['./cats/dto/pagination-query.dto']
65+
enum: t['./cats/dto/pagination-query.dto'].LettersEnum
6766
}
6867
}
6968
}
@@ -94,21 +93,30 @@ export default async () => {
9493
options: { required: false, type: () => [Object] },
9594
enum: {
9695
required: true,
97-
enum: t['./cats/dto/pagination-query.dto']
96+
enum: t['./cats/dto/pagination-query.dto'].LettersEnum
97+
},
98+
state: {
99+
required: false,
100+
description: 'Available language in the application',
101+
example: 'FR',
102+
enum: t['./cats/dto/create-cat.dto'].CategoryState
98103
},
99104
enumArr: {
100105
required: true,
101-
enum: t['./cats/dto/pagination-query.dto']
106+
enum: t['./cats/dto/pagination-query.dto'].LettersEnum
102107
},
103108
enumArr2: {
104109
required: true,
105-
enum: t['./cats/dto/pagination-query.dto'],
110+
enum: t['./cats/dto/pagination-query.dto'].LettersEnum,
106111
isArray: true
107112
},
108-
tag: { required: true, type: () => t['./cats/dto/tag.dto'] },
113+
tag: {
114+
required: true,
115+
type: () => t['./cats/dto/tag.dto'].TagDto
116+
},
109117
multipleTags: {
110118
required: true,
111-
type: () => [t['./cats/dto/tag.dto']]
119+
type: () => [t['./cats/dto/tag.dto'].TagDto]
112120
},
113121
nested: {
114122
required: true,
@@ -133,19 +141,22 @@ export default async () => {
133141
},
134142
withAliases: { type: String },
135143
withColonExpress: { type: String },
136-
withColonFastify: { type: String }
144+
withColonFastify: {
145+
description: 'Returns information about the application',
146+
type: String
147+
}
137148
}
138149
}
139150
],
140151
[
141152
import('./cats/cats.controller'),
142153
{
143154
CatsController: {
144-
create: { type: t['./cats/classes/cat.class'] },
145-
findOne: { type: t['./cats/classes/cat.class'] },
155+
create: { type: t['./cats/classes/cat.class'].Cat },
156+
findOne: { type: t['./cats/classes/cat.class'].Cat },
146157
findAll: {},
147-
createBulk: { type: t['./cats/classes/cat.class'] },
148-
createAsFormData: { type: t['./cats/classes/cat.class'] },
158+
createBulk: { type: t['./cats/classes/cat.class'].Cat },
159+
createAsFormData: { type: t['./cats/classes/cat.class'].Cat },
149160
getWithEnumParam: {},
150161
getWithRandomQuery: {}
151162
}

0 commit comments

Comments
 (0)
Please sign in to comment.