1 | "use strict";
|
2 | var _a;
|
3 | Object.defineProperty(exports, "__esModule", { value: true });
|
4 | exports.ControllerClassVisitor = void 0;
|
5 | const lodash_1 = require("lodash");
|
6 | const ts = require("typescript");
|
7 | const decorators_1 = require("../../decorators");
|
8 | const plugin_constants_1 = require("../plugin-constants");
|
9 | const ast_utils_1 = require("../utils/ast-utils");
|
10 | const plugin_utils_1 = require("../utils/plugin-utils");
|
11 | const abstract_visitor_1 = require("./abstract.visitor");
|
12 | const [tsVersionMajor, tsVersionMinor] = (_a = ts.versionMajorMinor) === null || _a === void 0 ? void 0 : _a.split('.').map((x) => +x);
|
13 | const isInUpdatedAstContext = tsVersionMinor >= 8 || tsVersionMajor > 4;
|
14 | class ControllerClassVisitor extends abstract_visitor_1.AbstractFileVisitor {
|
15 | visit(sourceFile, ctx, program, options) {
|
16 | const typeChecker = program.getTypeChecker();
|
17 | sourceFile = this.updateImports(sourceFile, ctx.factory, program);
|
18 | const visitNode = (node) => {
|
19 | if (ts.isMethodDeclaration(node)) {
|
20 | try {
|
21 | return this.addDecoratorToNode(ctx.factory, node, typeChecker, options, sourceFile.fileName, sourceFile);
|
22 | }
|
23 | catch (_a) {
|
24 | return node;
|
25 | }
|
26 | }
|
27 | return ts.visitEachChild(node, visitNode, ctx);
|
28 | };
|
29 | return ts.visitNode(sourceFile, visitNode);
|
30 | }
|
31 | addDecoratorToNode(factory, compilerNode, typeChecker, options, hostFilename, sourceFile) {
|
32 | var _a;
|
33 | const decorators = ts.canHaveDecorators
|
34 | ? ts.getDecorators(compilerNode)
|
35 | : compilerNode.decorators;
|
36 | if (!decorators) {
|
37 | return compilerNode;
|
38 | }
|
39 | const apiOperationDecoratorsArray = this.createApiOperationDecorator(factory, compilerNode, decorators, options, sourceFile, typeChecker);
|
40 | const removeExistingApiOperationDecorator = apiOperationDecoratorsArray.length > 0;
|
41 | const existingDecorators = removeExistingApiOperationDecorator
|
42 | ? decorators.filter((item) => (0, ast_utils_1.getDecoratorName)(item) !== decorators_1.ApiOperation.name)
|
43 | : decorators;
|
44 | const modifiers = (_a = (isInUpdatedAstContext
|
45 | ? ts.getModifiers(compilerNode)
|
46 | : compilerNode.modifiers)) !== null && _a !== void 0 ? _a : [];
|
47 | const updatedDecorators = [
|
48 | ...apiOperationDecoratorsArray,
|
49 | ...existingDecorators,
|
50 | factory.createDecorator(factory.createCallExpression(factory.createIdentifier(`${plugin_constants_1.OPENAPI_NAMESPACE}.${decorators_1.ApiResponse.name}`), undefined, [
|
51 | this.createDecoratorObjectLiteralExpr(factory, compilerNode, typeChecker, factory.createNodeArray(), hostFilename)
|
52 | ]))
|
53 | ];
|
54 | return isInUpdatedAstContext
|
55 | ? factory.updateMethodDeclaration(compilerNode, [...updatedDecorators, ...modifiers], compilerNode.asteriskToken, compilerNode.name, compilerNode.questionToken, compilerNode.typeParameters, compilerNode.parameters, compilerNode.type, compilerNode.body)
|
56 | : factory.updateMethodDeclaration(compilerNode, updatedDecorators, modifiers, compilerNode.asteriskToken, compilerNode.name, compilerNode.questionToken, compilerNode.typeParameters, compilerNode.parameters, compilerNode.type, compilerNode.body);
|
57 | }
|
58 | createApiOperationDecorator(factory, node, nodeArray, options, sourceFile, typeChecker) {
|
59 | if (!options.introspectComments) {
|
60 | return [];
|
61 | }
|
62 | const keyToGenerate = options.controllerKeyOfComment;
|
63 | const apiOperationDecorator = (0, plugin_utils_1.getDecoratorOrUndefinedByNames)([decorators_1.ApiOperation.name], nodeArray, factory);
|
64 | const apiOperationExpr = apiOperationDecorator &&
|
65 | (0, lodash_1.head)((0, ast_utils_1.getDecoratorArguments)(apiOperationDecorator));
|
66 | const apiOperationExprProperties = apiOperationExpr &&
|
67 | apiOperationExpr.properties;
|
68 | const extractedComments = (0, ast_utils_1.getMainCommentOfNode)(node, sourceFile);
|
69 | const tags = (0, ast_utils_1.getTsDocTagsOfNode)(node, sourceFile, typeChecker);
|
70 | const properties = [
|
71 | factory.createPropertyAssignment(keyToGenerate, factory.createStringLiteral(extractedComments)),
|
72 | ...(apiOperationExprProperties !== null && apiOperationExprProperties !== void 0 ? apiOperationExprProperties : factory.createNodeArray())
|
73 | ];
|
74 | const hasDeprecatedKey = (0, plugin_utils_1.hasPropertyKey)('deprecated', factory.createNodeArray(apiOperationExprProperties));
|
75 | if (!hasDeprecatedKey && tags.deprecated) {
|
76 | const deprecatedPropertyAssignment = factory.createPropertyAssignment('deprecated', (0, ast_utils_1.createLiteralFromAnyValue)(factory, tags.deprecated));
|
77 | properties.push(deprecatedPropertyAssignment);
|
78 | }
|
79 | const apiOperationDecoratorArguments = factory.createNodeArray([
|
80 | factory.createObjectLiteralExpression((0, lodash_1.compact)(properties))
|
81 | ]);
|
82 | if (apiOperationDecorator) {
|
83 | const expr = apiOperationDecorator.expression;
|
84 | const updatedCallExpr = factory.updateCallExpression(expr, expr.expression, undefined, apiOperationDecoratorArguments);
|
85 | return [factory.updateDecorator(apiOperationDecorator, updatedCallExpr)];
|
86 | }
|
87 | else {
|
88 | return [
|
89 | factory.createDecorator(factory.createCallExpression(factory.createIdentifier(`${plugin_constants_1.OPENAPI_NAMESPACE}.${decorators_1.ApiOperation.name}`), undefined, apiOperationDecoratorArguments))
|
90 | ];
|
91 | }
|
92 | }
|
93 | createDecoratorObjectLiteralExpr(factory, node, typeChecker, existingProperties = factory.createNodeArray(), hostFilename) {
|
94 | const properties = [
|
95 | ...existingProperties,
|
96 | this.createStatusPropertyAssignment(factory, node, existingProperties),
|
97 | this.createTypePropertyAssignment(factory, node, typeChecker, existingProperties, hostFilename)
|
98 | ];
|
99 | return factory.createObjectLiteralExpression((0, lodash_1.compact)(properties));
|
100 | }
|
101 | createTypePropertyAssignment(factory, node, typeChecker, existingProperties, hostFilename) {
|
102 | if ((0, plugin_utils_1.hasPropertyKey)('type', existingProperties)) {
|
103 | return undefined;
|
104 | }
|
105 | const signature = typeChecker.getSignatureFromDeclaration(node);
|
106 | const type = typeChecker.getReturnTypeOfSignature(signature);
|
107 | if (!type) {
|
108 | return undefined;
|
109 | }
|
110 | let typeReference = (0, plugin_utils_1.getTypeReferenceAsString)(type, typeChecker);
|
111 | if (!typeReference) {
|
112 | return undefined;
|
113 | }
|
114 | if (typeReference.includes('node_modules')) {
|
115 | return undefined;
|
116 | }
|
117 | typeReference = (0, plugin_utils_1.replaceImportPath)(typeReference, hostFilename);
|
118 | return factory.createPropertyAssignment('type', factory.createIdentifier(typeReference));
|
119 | }
|
120 | createStatusPropertyAssignment(factory, node, existingProperties) {
|
121 | if ((0, plugin_utils_1.hasPropertyKey)('status', existingProperties)) {
|
122 | return undefined;
|
123 | }
|
124 | const statusNode = this.getStatusCodeIdentifier(factory, node);
|
125 | return factory.createPropertyAssignment('status', statusNode);
|
126 | }
|
127 | getStatusCodeIdentifier(factory, node) {
|
128 | const decorators = ts.canHaveDecorators
|
129 | ? ts.getDecorators(node)
|
130 | : node.decorators;
|
131 | const httpCodeDecorator = (0, plugin_utils_1.getDecoratorOrUndefinedByNames)(['HttpCode'], decorators, factory);
|
132 | if (httpCodeDecorator) {
|
133 | const argument = (0, lodash_1.head)((0, ast_utils_1.getDecoratorArguments)(httpCodeDecorator));
|
134 | if (argument) {
|
135 | return argument;
|
136 | }
|
137 | }
|
138 | const postDecorator = (0, plugin_utils_1.getDecoratorOrUndefinedByNames)(['Post'], decorators, factory);
|
139 | if (postDecorator) {
|
140 | return factory.createIdentifier('201');
|
141 | }
|
142 | return factory.createIdentifier('200');
|
143 | }
|
144 | }
|
145 | exports.ControllerClassVisitor = ControllerClassVisitor;
|