-
-
Notifications
You must be signed in to change notification settings - Fork 40
Expand file tree
/
Copy pathservice.mts
More file actions
109 lines (98 loc) · 3.22 KB
/
service.mts
File metadata and controls
109 lines (98 loc) · 3.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import type { ClassDeclaration, Project, SourceFile } from "ts-morph";
import ts from "typescript";
import {
type MethodDescription,
getClassNameFromClassNode,
getClassesFromService,
} from "./common.mjs";
import { serviceFileName } from "./constants.mjs";
export type Service = {
node: SourceFile;
klasses: Array<{
className: string;
klass: ClassDeclaration;
methods: Array<MethodDescription>;
}>;
};
export async function getServices(project: Project): Promise<Service> {
const node = project
.getSourceFiles()
.find((sourceFile) => sourceFile.getFilePath().includes(serviceFileName));
if (!node) {
throw new Error("No service node found");
}
const klasses = getClassesFromService(node);
return {
klasses: klasses.map(({ klass, className }) => ({
className,
klass,
methods: getMethodsFromService(node, klass),
})),
node,
} satisfies Service;
}
function getMethodsFromService(node: SourceFile, klass: ClassDeclaration) {
const methods = klass.getMethods();
if (!methods.length) {
throw new Error("No methods found");
}
return methods.map((method) => {
const methodBlockNode = method.compilerNode
.getChildren(node.compilerNode)
.find((child) => child.kind === ts.SyntaxKind.Block);
if (!methodBlockNode) {
throw new Error("Method block not found");
}
const methodBlock = methodBlockNode as ts.Block;
const foundReturnStatement = methodBlock.statements.find(
(s) => s.kind === ts.SyntaxKind.ReturnStatement,
);
if (!foundReturnStatement) {
throw new Error("Return statement not found");
}
const returnStatement = foundReturnStatement as ts.ReturnStatement;
const foundCallExpression = returnStatement.expression;
if (!foundCallExpression) {
throw new Error("Call expression not found");
}
const callExpression = foundCallExpression as ts.CallExpression;
const properties = (
callExpression.arguments[1] as ts.ObjectLiteralExpression
).properties as unknown as ts.PropertyAssignment[];
const httpMethodName = properties
.find((p) => p.name?.getText(node.compilerNode) === "method")
?.initializer?.getText(node.compilerNode);
if (!httpMethodName) {
throw new Error("httpMethodName not found");
}
const getAllChildren = (tsNode: ts.Node): Array<ts.Node> => {
const childItems = tsNode.getChildren(node.compilerNode);
if (childItems.length) {
const allChildren = childItems.map(getAllChildren);
return [tsNode].concat(allChildren.flat());
}
return [tsNode];
};
const children = getAllChildren(method.compilerNode);
// get all JSDoc comments
// this should be an array of 1 or 0
const jsDocs = children
.filter((c) => c.kind === ts.SyntaxKind.JSDoc)
.map((c) => c.getText(node.compilerNode));
// get the first JSDoc comment
const jsDoc = jsDocs?.[0];
const isDeprecated = children.some(
(c) => c.kind === ts.SyntaxKind.JSDocDeprecatedTag,
);
const className = getClassNameFromClassNode(klass);
return {
className,
node,
method,
methodBlock,
httpMethodName,
jsDoc,
isDeprecated,
} satisfies MethodDescription;
});
}