Skip to content

Commit

Permalink
Merge branch 'features/enhanced-controller-comments' into features/en…
Browse files Browse the repository at this point in the history
…hanced-comments
  • Loading branch information
lit26 committed Feb 7, 2024
2 parents 500a718 + dc48ef1 commit 90447b1
Show file tree
Hide file tree
Showing 6 changed files with 8,352 additions and 223 deletions.
27 changes: 26 additions & 1 deletion lib/plugin/utils/ast-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
UnionTypeNode
} from 'typescript';
import { isDynamicallyAdded } from './plugin-utils';
import { DocComment, DocExcerpt, DocNode, ParserContext, TSDocParser } from '@microsoft/tsdoc';

export function isArray(type: Type) {
const symbol = type.getSymbol();
Expand Down Expand Up @@ -117,7 +118,31 @@ export function getDefaultTypeFormatFlags(enclosingNode: Node) {
return formatFlags;
}

export function getMainCommentOfNode(
export function getNodeDocs(
node: Node
): DocComment {
const tsdocParser: TSDocParser = new TSDocParser();
const parserContext: ParserContext = tsdocParser.parseString(node.getFullText());
return parserContext.docComment;
}

export function docNodeToString(docNode: DocNode): string {
let result = '';

if (docNode) {
if (docNode instanceof DocExcerpt) {
result += docNode.content.toString();
}

for(const childNode of docNode.getChildNodes()) {
result += docNodeToString(childNode);
}
}

return result.trim();
}

export function getMainCommentAndExamplesOfNode(
node: Node,
sourceFile: SourceFile
): string {
Expand Down
134 changes: 58 additions & 76 deletions lib/plugin/visitors/controller-class.visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ import { ApiOperation, ApiResponse } from '../../decorators';
import { PluginOptions } from '../merge-options';
import { OPENAPI_NAMESPACE } from '../plugin-constants';
import {
createLiteralFromAnyValue,
docNodeToString,
getDecoratorArguments,
getDecoratorName,
getMainCommentOfNode,
getTsDocTagsOfNode
getMainCommentAndExamplesOfNode, getNodeDocs
} from '../utils/ast-utils';
import {
convertPath,
Expand Down Expand Up @@ -205,85 +203,69 @@ export class ControllerClassVisitor extends AbstractFileVisitor {
decorators,
factory
);
let apiOperationExistingProps:
| ts.NodeArray<ts.PropertyAssignment>
| undefined = undefined;
const apiOperationExpr: ts.ObjectLiteralExpression | undefined =
apiOperationDecorator &&
head(getDecoratorArguments(apiOperationDecorator));
const apiOperationExprProperties =
apiOperationExpr &&
(apiOperationExpr.properties as ts.NodeArray<ts.PropertyAssignment>);

if (apiOperationDecorator && !options.readonly) {
const apiOperationExpr = head(
getDecoratorArguments(apiOperationDecorator)
);
if (apiOperationExpr) {
apiOperationExistingProps =
apiOperationExpr.properties as ts.NodeArray<ts.PropertyAssignment>;
}
}
if (
!apiOperationDecorator ||
!apiOperationExpr ||
!apiOperationExprProperties ||
!hasPropertyKey(keyToGenerate, apiOperationExprProperties)
) {
const properties = [];

const extractedComments = getMainCommentOfNode(node, sourceFile);
if (!extractedComments) {
return [];
}
const tags = getTsDocTagsOfNode(node, sourceFile, typeChecker);

const properties = [
factory.createPropertyAssignment(
keyToGenerate,
factory.createStringLiteral(extractedComments)
),
...(apiOperationExistingProps ?? factory.createNodeArray())
];
if (keyToGenerate) {
const [extractedComments] = getMainCommentAndExamplesOfNode(
node,
sourceFile,
typeChecker
);

const hasDeprecatedKey = hasPropertyKey(
'deprecated',
factory.createNodeArray(apiOperationExistingProps)
);
if (!hasDeprecatedKey && tags.deprecated) {
const deprecatedPropertyAssignment = factory.createPropertyAssignment(
'deprecated',
createLiteralFromAnyValue(factory, tags.deprecated)
);
properties.push(deprecatedPropertyAssignment);
}
if (!extractedComments) {
// Node does not have any comments
return [];
}

const objectLiteralExpr = factory.createObjectLiteralExpression(
compact(properties)
);
const apiOperationDecoratorArguments: ts.NodeArray<ts.Expression> =
factory.createNodeArray([objectLiteralExpr]);
properties.push(ts.createPropertyAssignment(keyToGenerate, ts.createLiteral(extractedComments)));
} else {
const docs = getNodeDocs(node);

const methodKey = node.name.getText();
if (metadata[methodKey]) {
const existingObjectLiteralExpr = metadata[methodKey];
const existingProperties = existingObjectLiteralExpr.properties;
const updatedProperties = factory.createNodeArray([
...existingProperties,
...compact(properties)
]);
const updatedObjectLiteralExpr =
factory.createObjectLiteralExpression(updatedProperties);
metadata[methodKey] = updatedObjectLiteralExpr;
} else {
metadata[methodKey] = objectLiteralExpr;
}
if (!docs) {
return [];
}

if (apiOperationDecorator) {
const expr = apiOperationDecorator.expression as any as ts.CallExpression;
const updatedCallExpr = factory.updateCallExpression(
expr,
expr.expression,
undefined,
apiOperationDecoratorArguments
const summary = docNodeToString(docs.summarySection);
if (summary && (!apiOperationExprProperties || !hasPropertyKey("summary", apiOperationExprProperties))) {
properties.push(ts.createPropertyAssignment("summary", ts.createLiteral(summary)));
}

const remarks = docNodeToString(docs.remarksBlock.content);
if (remarks && (!apiOperationExprProperties || !hasPropertyKey("description", apiOperationExprProperties))) {
properties.push(ts.createPropertyAssignment("description", ts.createLiteral(remarks)));
}
}

const apiOperationDecoratorArguments: ts.NodeArray<ts.Expression> = ts.createNodeArray(
[ts.createObjectLiteral(compact([
...properties,
...(apiOperationExprProperties ?? ts.createNodeArray())
]))]
);
return [factory.updateDecorator(apiOperationDecorator, updatedCallExpr)];
} else {
return [
factory.createDecorator(
factory.createCallExpression(
factory.createIdentifier(
`${OPENAPI_NAMESPACE}.${ApiOperation.name}`
),
undefined,
apiOperationDecoratorArguments

if (apiOperationDecorator) {
((apiOperationDecorator.expression as ts.CallExpression) as any).arguments = apiOperationDecoratorArguments;
} else {
return [
ts.createDecorator(
ts.createCall(
ts.createIdentifier(`${OPENAPI_NAMESPACE}.${ApiOperation.name}`),
undefined,
apiOperationDecoratorArguments
)
)
)
];
Expand Down
Loading

0 comments on commit 90447b1

Please sign in to comment.