UNPKG

@nestjs/swagger

Version:

Nest - modern, fast, powerful node.js web framework (@swagger)

156 lines (155 loc) 7.55 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const lodash_1 = require("lodash"); const ts = require("typescript"); const decorators_1 = require("../../decorators"); const plugin_constants_1 = require("../plugin-constants"); const ast_utils_1 = require("../utils/ast-utils"); const plugin_utils_1 = require("../utils/plugin-utils"); const abstract_visitor_1 = require("./abstract.visitor"); class ModelClassVisitor extends abstract_visitor_1.AbstractFileVisitor { visit(sourceFile, ctx, program, options) { const typeChecker = program.getTypeChecker(); sourceFile = this.updateImports(sourceFile); const visitNode = (node) => { if (ts.isPropertyDeclaration(node)) { const decorators = node.decorators; const hidePropertyDecorator = plugin_utils_1.getDecoratorOrUndefinedByNames([decorators_1.ApiHideProperty.name], decorators); if (hidePropertyDecorator) { return node; } const propertyDecorator = plugin_utils_1.getDecoratorOrUndefinedByNames([decorators_1.ApiProperty.name, decorators_1.ApiPropertyOptional.name], decorators); if (!propertyDecorator) { return this.addDecoratorToNode(node, typeChecker, options, sourceFile.fileName); } return this.addPropertiesToExisitingDecorator(propertyDecorator, node, typeChecker, options, sourceFile.fileName); } return ts.visitEachChild(node, visitNode, ctx); }; return ts.visitNode(sourceFile, visitNode); } addDecoratorToNode(compilerNode, typeChecker, options, hostFilename) { const { pos, end } = compilerNode.decorators || ts.createNodeArray(); compilerNode.decorators = Object.assign([ ...(compilerNode.decorators || ts.createNodeArray()), ts.createDecorator(ts.createCall(ts.createIdentifier(`${plugin_constants_1.OPENAPI_NAMESPACE}.${decorators_1.ApiProperty.name}`), undefined, [ this.createDecoratorObjectLiteralExpr(compilerNode, typeChecker, [], options, hostFilename) ])) ], { pos, end }); return compilerNode; } addPropertiesToExisitingDecorator(compilerNode, originalNode, typeChecker, options, hostFilename) { const callExpr = compilerNode.expression; if (!callExpr) { return originalNode; } const callArgs = callExpr.arguments; if (!callArgs) { return originalNode; } const { pos, end } = callArgs; const decoratorArgument = lodash_1.head(callArgs); if (!decoratorArgument) { callExpr.arguments = Object.assign([ this.createDecoratorObjectLiteralExpr(originalNode, typeChecker, [], options, hostFilename) ], { pos, end }); } const decoratorProperties = (decoratorArgument && decoratorArgument.properties) || []; callExpr.arguments = Object.assign([ this.createDecoratorObjectLiteralExpr(originalNode, typeChecker, decoratorProperties, options, hostFilename) ], { pos, end }); return originalNode; } createDecoratorObjectLiteralExpr(node, typeChecker, existingProperties = [], options = {}, hostFilename = '') { const isRequired = !node.questionToken; let properties = [ ...existingProperties, !plugin_utils_1.hasPropertyKey('required', existingProperties) && ts.createPropertyAssignment('required', ts.createLiteral(isRequired)), this.createTypePropertyAssignment(node, typeChecker, existingProperties, hostFilename), this.createDefaultPropertyAssignment(node, existingProperties), this.createEnumPropertyAssignment(node, typeChecker, existingProperties, hostFilename) ]; if (options.classValidatorShim) { properties = properties.concat(this.createValidationPropertyAssignments(node)); } return ts.createObjectLiteral(lodash_1.compact(lodash_1.flatten(properties))); } createTypePropertyAssignment(node, typeChecker, existingProperties, hostFilename) { const key = 'type'; if (plugin_utils_1.hasPropertyKey(key, existingProperties)) { return undefined; } const type = typeChecker.getTypeAtLocation(node); if (!type) { return undefined; } let typeReference = plugin_utils_1.getTypeReferenceAsString(type, typeChecker); if (!typeReference) { return undefined; } typeReference = plugin_utils_1.replaceImportPath(typeReference, hostFilename); return ts.createPropertyAssignment(key, ts.createArrowFunction(undefined, undefined, [], undefined, undefined, ts.createIdentifier(typeReference))); } createEnumPropertyAssignment(node, typeChecker, existingProperties, hostFilename) { const key = 'enum'; if (plugin_utils_1.hasPropertyKey(key, existingProperties)) { return undefined; } let type = typeChecker.getTypeAtLocation(node); if (!type) { return undefined; } let isArrayType = false; if (ast_utils_1.isArray(type)) { type = ast_utils_1.getTypeArguments(type)[0]; isArrayType = true; if (!type) { return undefined; } } if (!ast_utils_1.isEnum(type)) { return undefined; } const enumRef = plugin_utils_1.replaceImportPath(ast_utils_1.getText(type, typeChecker), hostFilename); const enumProperty = ts.createPropertyAssignment(key, ts.createIdentifier(enumRef)); if (isArrayType) { const isArrayKey = 'isArray'; const isArrayProperty = ts.createPropertyAssignment(isArrayKey, ts.createIdentifier('true')); return [enumProperty, isArrayProperty]; } return enumProperty; } createDefaultPropertyAssignment(node, existingProperties) { const key = 'default'; if (plugin_utils_1.hasPropertyKey(key, existingProperties)) { return undefined; } const initializer = node.initializer; if (!initializer) { return undefined; } return ts.createPropertyAssignment(key, ts.createIdentifier(initializer.getText())); } createValidationPropertyAssignments(node) { const assignments = []; const decorators = node.decorators; this.addPropertyByValidationDecorator('Min', 'minimum', decorators, assignments); this.addPropertyByValidationDecorator('Max', 'maximum', decorators, assignments); this.addPropertyByValidationDecorator('MinLength', 'minLength', decorators, assignments); this.addPropertyByValidationDecorator('MaxLength', 'maxLength', decorators, assignments); return assignments; } addPropertyByValidationDecorator(decoratorName, propertyKey, decorators, assignments) { const decoratorRef = plugin_utils_1.getDecoratorOrUndefinedByNames([decoratorName], decorators); if (!decoratorRef) { return; } const argument = lodash_1.head(ast_utils_1.getDecoratorArguments(decoratorRef)); assignments.push(ts.createPropertyAssignment(propertyKey, ts.createIdentifier(argument && argument.getText()))); } } exports.ModelClassVisitor = ModelClassVisitor;