Skip to content

Commit

Permalink
fix(plugin): revamp types resolution, fix empty summary in ctrl bug
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilmysliwiec committed Jun 26, 2023
1 parent 0ae98f5 commit c1295b6
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 57 deletions.
30 changes: 19 additions & 11 deletions lib/plugin/utils/plugin-utils.ts
Expand Up @@ -161,7 +161,7 @@ export function replaceImportPath(
} catch (_error) {
const from = options?.readonly
? options.pathToSource
: posix.dirname(fileName);
: posix.dirname(convertPath(fileName));

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

typeReference = typeReference.replace(importPath, relativePath);

if (options.readonly) {
const { typeName, typeImportStatement } =
convertToAsyncImport(typeReference);
return {
typeReference: typeImportStatement,
typeName,
importPath: relativePath
};
}
return {
typeReference: options.readonly
? convertToAsyncImport(typeReference)
: typeReference.replace('import', 'require'),
typeReference: typeReference.replace('import', 'require'),
importPath: relativePath
};
}
Expand All @@ -205,14 +212,15 @@ function convertToAsyncImport(typeReference: string) {

if (match?.length >= 2) {
const importPos = typeReference.indexOf(match[0]);
typeReference = typeReference.replace(
match[1],
`then((f) => f.${match[1]})`
);
return insertAt(typeReference, importPos, 'await ');
typeReference = typeReference.replace(`.${match[1]}`, '');

return {
typeImportStatement: insertAt(typeReference, importPos, 'await '),
typeName: match[1]
};
}

return typeReference;
return { typeImportStatement: typeReference };
}

export function insertAt(string: string, index: number, substring: string) {
Expand Down Expand Up @@ -326,7 +334,7 @@ function isOptionalBoolean(text: string) {
* Converts Windows specific file paths to posix
* @param windowsPath
*/
function convertPath(windowsPath: string) {
export function convertPath(windowsPath: string) {
return windowsPath
.replace(/^\\\\\?\\/, '')
.replace(/\\/g, '/')
Expand Down
69 changes: 45 additions & 24 deletions lib/plugin/visitors/controller-class.visitor.ts
Expand Up @@ -12,6 +12,7 @@ import {
getTsDocTagsOfNode
} from '../utils/ast-utils';
import {
convertPath,
getDecoratorOrUndefinedByNames,
getTypeReferenceAsString,
hasPropertyKey,
Expand Down Expand Up @@ -204,12 +205,19 @@ export class ControllerClassVisitor extends AbstractFileVisitor {
decorators,
factory
);
const apiOperationExpr: ts.ObjectLiteralExpression | undefined =
apiOperationDecorator &&
head(getDecoratorArguments(apiOperationDecorator));
const apiOperationExprProperties =
apiOperationExpr &&
(apiOperationExpr.properties as ts.NodeArray<ts.PropertyAssignment>);
let apiOperationExistingProps:
| ts.NodeArray<ts.PropertyAssignment>
| undefined = undefined;

if (apiOperationDecorator && !options.readonly) {
const apiOperationExpr = head(
getDecoratorArguments(apiOperationDecorator)
);
if (apiOperationExpr) {
apiOperationExistingProps =
apiOperationExpr.properties as ts.NodeArray<ts.PropertyAssignment>;
}
}

const extractedComments = getMainCommentOfNode(node, sourceFile);
if (!extractedComments) {
Expand All @@ -222,12 +230,12 @@ export class ControllerClassVisitor extends AbstractFileVisitor {
keyToGenerate,
factory.createStringLiteral(extractedComments)
),
...(apiOperationExprProperties ?? factory.createNodeArray())
...(apiOperationExistingProps ?? factory.createNodeArray())
];

const hasDeprecatedKey = hasPropertyKey(
'deprecated',
factory.createNodeArray(apiOperationExprProperties)
factory.createNodeArray(apiOperationExistingProps)
);
if (!hasDeprecatedKey && tags.deprecated) {
const deprecatedPropertyAssignment = factory.createPropertyAssignment(
Expand Down Expand Up @@ -345,16 +353,15 @@ export class ControllerClassVisitor extends AbstractFileVisitor {
if (!type) {
return undefined;
}
const { typeName, isArray } = getTypeReferenceAsString(type, typeChecker);
if (!typeName) {
const typeReferenceDescriptor = getTypeReferenceAsString(type, typeChecker);
if (!typeReferenceDescriptor.typeName) {
return undefined;
}
if (typeName.includes('node_modules')) {
if (typeReferenceDescriptor.typeName.includes('node_modules')) {
return undefined;
}
const identifier = this.typeReferenceStringToIdentifier(
typeName,
isArray,
typeReferenceDescriptor,
hostFilename,
options,
factory
Expand Down Expand Up @@ -399,20 +406,23 @@ export class ControllerClassVisitor extends AbstractFileVisitor {
}

private normalizeImportPath(pathToSource: string, path: string) {
let relativePath = posix.relative(pathToSource, path);
let relativePath = posix.relative(pathToSource, convertPath(path));
relativePath = relativePath[0] !== '.' ? './' + relativePath : relativePath;
return relativePath;
}

private typeReferenceStringToIdentifier(
_typeReference: string,
isArray: boolean,
typeReferenceDescriptor: {
typeName: string;
isArray?: boolean;
arrayDepth?: number;
},
hostFilename: string,
options: PluginOptions,
factory: ts.NodeFactory
) {
const { typeReference, importPath } = replaceImportPath(
_typeReference,
const { typeReference, importPath, typeName } = replaceImportPath(
typeReferenceDescriptor.typeName,
hostFilename,
options
);
Expand All @@ -423,14 +433,25 @@ export class ControllerClassVisitor extends AbstractFileVisitor {
this._typeImports[importPath] = typeReference;
}

identifier = factory.createIdentifier(
isArray ? `[t["${importPath}"]]` : `t["${importPath}"]`
);
let ref = `t["${importPath}"].${typeName}`;
if (typeReferenceDescriptor.isArray) {
ref = this.wrapTypeInArray(ref, typeReferenceDescriptor.arrayDepth);
}
identifier = factory.createIdentifier(ref);
} else {
identifier = factory.createIdentifier(
isArray ? `[${typeReference}]` : typeReference
);
let ref = typeReference;
if (typeReferenceDescriptor.isArray) {
ref = this.wrapTypeInArray(ref, typeReferenceDescriptor.arrayDepth);
}
identifier = factory.createIdentifier(ref);
}
return identifier;
}

private wrapTypeInArray(typeRef: string, arrayDepth: number) {
for (let i = 0; i < arrayDepth; i++) {
typeRef = `[${typeRef}]`;
}
return typeRef;
}
}
7 changes: 4 additions & 3 deletions lib/plugin/visitors/model-class.visitor.ts
Expand Up @@ -16,6 +16,7 @@ import {
isEnum
} from '../utils/ast-utils';
import {
convertPath,
extractTypeArgumentIfArray,
getDecoratorOrUndefinedByNames,
getTypeReferenceAsString,
Expand Down Expand Up @@ -725,7 +726,7 @@ export class ModelClassVisitor extends AbstractFileVisitor {
}

private normalizeImportPath(pathToSource: string, path: string) {
let relativePath = posix.relative(pathToSource, path);
let relativePath = posix.relative(pathToSource, convertPath(path));
relativePath = relativePath[0] !== '.' ? './' + relativePath : relativePath;
return relativePath;
}
Expand All @@ -740,7 +741,7 @@ export class ModelClassVisitor extends AbstractFileVisitor {
options: PluginOptions,
factory: ts.NodeFactory
) {
const { typeReference, importPath } = replaceImportPath(
const { typeReference, importPath, typeName } = replaceImportPath(
typeReferenceDescriptor.typeName,
hostFilename,
options
Expand All @@ -752,7 +753,7 @@ export class ModelClassVisitor extends AbstractFileVisitor {
this._typeImports[importPath] = typeReference;
}

let ref = `t["${importPath}"]`;
let ref = `t["${importPath}"].${typeName}`;
if (typeReferenceDescriptor.isArray) {
ref = this.wrapTypeInArray(ref, typeReferenceDescriptor.arrayDepth);
}
Expand Down
5 changes: 5 additions & 0 deletions test/plugin/fixtures/project/app.controller.ts
@@ -1,4 +1,5 @@
import { Controller, Get } from '@nestjs/common';
import { ApiOperation } from '../../../../lib/decorators';

@Controller()
export class AppController {
Expand All @@ -21,6 +22,10 @@ export class AppController {
return 'Hello world!';
}

/**
* Returns information about the application
*/
@ApiOperation({ summary: 'Returns Hello World' })
@Get('fastify::colon::another/:prop')
withColonFastify(): string {
return 'Hello world!';
Expand Down
11 changes: 11 additions & 0 deletions test/plugin/fixtures/project/cats/dto/create-cat.dto.ts
Expand Up @@ -3,6 +3,11 @@ import { ExtraModel } from './extra-model.dto';
import { LettersEnum } from './pagination-query.dto';
import { TagDto } from './tag.dto';

export enum CategoryState {
OK = 'OK',
DEPRECATED = 'DEPRECATED'
}

@ApiExtraModels(ExtraModel)
export class CreateCatDto {
@ApiProperty()
Expand Down Expand Up @@ -48,6 +53,12 @@ export class CreateCatDto {
})
readonly enum: LettersEnum;

/**
* Available language in the application
* @example FR
*/
state?: CategoryState;

@ApiProperty({
enum: LettersEnum,
enumName: 'LettersEnum',
Expand Down
49 changes: 30 additions & 19 deletions test/plugin/fixtures/serialized-meta.fixture.ts
Expand Up @@ -3,10 +3,9 @@ export default async () => {
const t = {
['./cats/dto/pagination-query.dto']: await import(
'./cats/dto/pagination-query.dto'
).then((f) => f.LettersEnum),
['./cats/dto/tag.dto']: await import('./cats/dto/tag.dto').then(
(f) => f.TagDto
)
),
['./cats/dto/create-cat.dto']: await import('./cats/dto/create-cat.dto'),
['./cats/dto/tag.dto']: await import('./cats/dto/tag.dto')
};
return {
'@nestjs/swagger': {
Expand All @@ -20,16 +19,16 @@ export default async () => {
limit: { required: true, type: () => Number },
enum: {
required: true,
enum: t['./cats/dto/pagination-query.dto']
enum: t['./cats/dto/pagination-query.dto'].LettersEnum
},
enumArr: {
required: true,
enum: t['./cats/dto/pagination-query.dto'],
enum: t['./cats/dto/pagination-query.dto'].LettersEnum,
isArray: true
},
letters: {
required: true,
enum: t['./cats/dto/pagination-query.dto'],
enum: t['./cats/dto/pagination-query.dto'].LettersEnum,
isArray: true
},
beforeDate: { required: true, type: () => Date },
Expand Down Expand Up @@ -59,11 +58,11 @@ export default async () => {
options: { required: false, type: () => [Object] },
enum: {
required: true,
enum: t['./cats/dto/pagination-query.dto']
enum: t['./cats/dto/pagination-query.dto'].LettersEnum
},
enumArr: {
required: true,
enum: t['./cats/dto/pagination-query.dto']
enum: t['./cats/dto/pagination-query.dto'].LettersEnum
}
}
}
Expand Down Expand Up @@ -94,21 +93,30 @@ export default async () => {
options: { required: false, type: () => [Object] },
enum: {
required: true,
enum: t['./cats/dto/pagination-query.dto']
enum: t['./cats/dto/pagination-query.dto'].LettersEnum
},
state: {
required: false,
description: 'Available language in the application',
example: 'FR',
enum: t['./cats/dto/create-cat.dto'].CategoryState
},
enumArr: {
required: true,
enum: t['./cats/dto/pagination-query.dto']
enum: t['./cats/dto/pagination-query.dto'].LettersEnum
},
enumArr2: {
required: true,
enum: t['./cats/dto/pagination-query.dto'],
enum: t['./cats/dto/pagination-query.dto'].LettersEnum,
isArray: true
},
tag: { required: true, type: () => t['./cats/dto/tag.dto'] },
tag: {
required: true,
type: () => t['./cats/dto/tag.dto'].TagDto
},
multipleTags: {
required: true,
type: () => [t['./cats/dto/tag.dto']]
type: () => [t['./cats/dto/tag.dto'].TagDto]
},
nested: {
required: true,
Expand All @@ -133,19 +141,22 @@ export default async () => {
},
withAliases: { type: String },
withColonExpress: { type: String },
withColonFastify: { type: String }
withColonFastify: {
description: 'Returns information about the application',
type: String
}
}
}
],
[
import('./cats/cats.controller'),
{
CatsController: {
create: { type: t['./cats/classes/cat.class'] },
findOne: { type: t['./cats/classes/cat.class'] },
create: { type: t['./cats/classes/cat.class'].Cat },
findOne: { type: t['./cats/classes/cat.class'].Cat },
findAll: {},
createBulk: { type: t['./cats/classes/cat.class'] },
createAsFormData: { type: t['./cats/classes/cat.class'] },
createBulk: { type: t['./cats/classes/cat.class'].Cat },
createAsFormData: { type: t['./cats/classes/cat.class'].Cat },
getWithEnumParam: {},
getWithRandomQuery: {}
}
Expand Down

0 comments on commit c1295b6

Please sign in to comment.