barehttp 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +185 -28
- package/lib/context/execution.d.ts +7 -0
- package/lib/context/execution.js +14 -0
- package/lib/context/index.d.ts +10 -0
- package/lib/context/index.js +46 -0
- package/lib/env.d.ts +5 -0
- package/lib/env.js +5 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +3 -0
- package/lib/logger/index.d.ts +16 -0
- package/lib/logger/index.js +26 -0
- package/lib/logger/serializers.d.ts +28 -0
- package/lib/logger/serializers.js +78 -0
- package/lib/middlewares/cookies/cookie-manager.d.ts +25 -0
- package/lib/middlewares/cookies/cookie-manager.js +68 -0
- package/lib/middlewares/cookies/signer.d.ts +8 -0
- package/lib/middlewares/cookies/signer.js +25 -0
- package/lib/middlewares/cors/cors.d.ts +38 -0
- package/lib/middlewares/cors/cors.js +164 -0
- package/lib/request.d.ts +84 -0
- package/lib/request.js +260 -0
- package/lib/schemas/custom-schema.d.ts +32 -0
- package/lib/schemas/custom-schema.js +62 -0
- package/lib/schemas/dirty-tsm.d.ts +1 -0
- package/lib/schemas/dirty-tsm.js +199 -0
- package/lib/schemas/generator.d.ts +7 -0
- package/lib/schemas/generator.js +179 -0
- package/lib/schemas/helpers.d.ts +27 -0
- package/lib/schemas/helpers.js +40 -0
- package/lib/schemas/json-schema.d.ts +2 -0
- package/lib/schemas/json-schema.js +48 -0
- package/lib/schemas/openami-schema.d.ts +2 -0
- package/lib/schemas/openami-schema.js +59 -0
- package/lib/schemas/project.d.ts +1 -0
- package/lib/schemas/project.js +1 -0
- package/lib/server.d.ts +154 -0
- package/lib/server.js +396 -0
- package/lib/utils/content-type.d.ts +54 -0
- package/lib/utils/content-type.js +54 -0
- package/lib/utils/http-methods.d.ts +11 -0
- package/lib/utils/http-methods.js +9 -0
- package/lib/utils/index.d.ts +4 -0
- package/lib/utils/index.js +4 -0
- package/lib/utils/safe-json.d.ts +2 -0
- package/lib/utils/safe-json.js +18 -0
- package/lib/utils/status-codes.d.ts +339 -0
- package/lib/utils/status-codes.js +339 -0
- package/lib/utils/status-phrases.d.ts +338 -0
- package/lib/utils/status-phrases.js +339 -0
- package/lib/websocket.d.ts +36 -0
- package/lib/websocket.js +176 -0
- package/package.json +64 -32
- package/.eslintrc.js +0 -47
- package/.github/workflows/release.yml +0 -27
- package/.jest-setup.js +0 -1
- package/jest.config.js +0 -8
- package/prettier.config.js +0 -6
- package/src/context/context.test.ts +0 -30
- package/src/context/execution.ts +0 -17
- package/src/context/index.ts +0 -61
- package/src/env.ts +0 -5
- package/src/examples/bare-http.ts +0 -36
- package/src/examples/express.ts +0 -11
- package/src/examples/fastify.ts +0 -18
- package/src/index.ts +0 -4
- package/src/logger/index.ts +0 -67
- package/src/logger/serializers.test.ts +0 -186
- package/src/logger/serializers.ts +0 -109
- package/src/middlewares/cookies/cookie-manager.ts +0 -86
- package/src/middlewares/cookies/signer.ts +0 -30
- package/src/report.ts +0 -25
- package/src/request.test.ts +0 -143
- package/src/request.ts +0 -277
- package/src/server.integration.test.ts +0 -296
- package/src/server.middlewares.test.ts +0 -93
- package/src/server.routes.test.ts +0 -71
- package/src/server.ts +0 -450
- package/src/utils/content-type.ts +0 -59
- package/src/utils/index.ts +0 -2
- package/src/utils/safe-json.ts +0 -17
- package/src/utils/status-codes.ts +0 -339
- package/src/utils/status-phrases.ts +0 -339
- package/tsconfig.json +0 -24
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import find from 'lodash/find.js';
|
|
2
|
+
import { getApparentTypeName, getTypeGenericText, helpers, isFinalType, isNullType, } from './helpers.js';
|
|
3
|
+
export const generateCustomSchema = (t) => {
|
|
4
|
+
if (isFinalType(t)) {
|
|
5
|
+
return { type: getTypeGenericText(t), nullable: false };
|
|
6
|
+
}
|
|
7
|
+
if (t.isUnion()) {
|
|
8
|
+
const nulled = t.getUnionTypes().some((nt) => isNullType(nt));
|
|
9
|
+
const cleanTypes = helpers.cleanNullableTypes(t.getUnionTypes());
|
|
10
|
+
let returning = {
|
|
11
|
+
nullable: false,
|
|
12
|
+
type: 'union',
|
|
13
|
+
};
|
|
14
|
+
const transformed = cleanTypes.reduce((acc, ut) => {
|
|
15
|
+
const regenerated = generateCustomSchema(ut);
|
|
16
|
+
if (find(acc, regenerated))
|
|
17
|
+
return acc;
|
|
18
|
+
return acc.concat(regenerated);
|
|
19
|
+
}, []);
|
|
20
|
+
if (transformed.length > 1) {
|
|
21
|
+
returning.anyOf = transformed;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
returning = transformed[0];
|
|
25
|
+
}
|
|
26
|
+
if (nulled) {
|
|
27
|
+
returning.nullable = true;
|
|
28
|
+
}
|
|
29
|
+
return returning;
|
|
30
|
+
}
|
|
31
|
+
if (t.isIntersection()) {
|
|
32
|
+
return t.getIntersectionTypes().reduce((acc, it) => {
|
|
33
|
+
const generatedSchema = generateCustomSchema(it);
|
|
34
|
+
if (Object.keys(acc).length === 0) {
|
|
35
|
+
acc = generatedSchema;
|
|
36
|
+
return acc;
|
|
37
|
+
}
|
|
38
|
+
if (generatedSchema.type === acc.type && acc.type === 'object') {
|
|
39
|
+
acc.properties = { ...acc.properties, ...generatedSchema.properties };
|
|
40
|
+
}
|
|
41
|
+
return acc;
|
|
42
|
+
}, {});
|
|
43
|
+
}
|
|
44
|
+
if (t.isArray()) {
|
|
45
|
+
return {
|
|
46
|
+
type: 'array',
|
|
47
|
+
items: generateCustomSchema(t.getArrayElementType()),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (t.isInterface() || t.isObject()) {
|
|
51
|
+
const result = t.getProperties().reduce((acc, ci) => {
|
|
52
|
+
const val = ci.getValueDeclaration();
|
|
53
|
+
acc.properties = { ...acc.properties, [ci.getName()]: generateCustomSchema(val.getType()) };
|
|
54
|
+
return acc;
|
|
55
|
+
}, { type: 'object', properties: {} });
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
type: getApparentTypeName(t),
|
|
60
|
+
nullable: false,
|
|
61
|
+
};
|
|
62
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { Project, ts } from 'ts-morph';
|
|
2
|
+
import { generateCustomSchema } from './custom-schema.js';
|
|
3
|
+
import { isFinalType, logInternals } from './helpers.js';
|
|
4
|
+
const project = new Project({ tsConfigFilePath: 'tsconfig.json' });
|
|
5
|
+
project.enableLogging();
|
|
6
|
+
const sourceFile = project.getSourceFile('server.ts');
|
|
7
|
+
const tp = sourceFile?.getClass('BareServer')?.getMember('route');
|
|
8
|
+
const isHandler = (c) => c.getSymbol()?.getName() === 'handler';
|
|
9
|
+
const isRoute = (c) => c.getSymbol()?.getName() === 'route';
|
|
10
|
+
function returnFinder(route, base) {
|
|
11
|
+
if (!base) {
|
|
12
|
+
throw new Error('No project been allocated, theres some issue');
|
|
13
|
+
}
|
|
14
|
+
const refsAcrossProject = base
|
|
15
|
+
.getChildrenOfKind(ts.SyntaxKind.Identifier)[0]
|
|
16
|
+
.findReferences()[0]
|
|
17
|
+
.getReferences()
|
|
18
|
+
?.filter((re) => re.compilerObject.fileName.includes(route));
|
|
19
|
+
if (!refsAcrossProject?.length) {
|
|
20
|
+
console.log('There are no routes declarations across the project');
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
const extractedReturns = refsAcrossProject.map((ref) => {
|
|
24
|
+
return ref
|
|
25
|
+
.getNode()
|
|
26
|
+
.getAncestors()
|
|
27
|
+
.find((n) => n.getKind() === ts.SyntaxKind.CallExpression)
|
|
28
|
+
?.getChildren()
|
|
29
|
+
.find((n) => n.getKind() === ts.SyntaxKind.SyntaxList)
|
|
30
|
+
?.getFirstChild()
|
|
31
|
+
?.getChildSyntaxList()
|
|
32
|
+
?.getChildren()
|
|
33
|
+
.filter((c) => {
|
|
34
|
+
return c.getKind() === ts.SyntaxKind.PropertyAssignment && (isHandler(c) || isRoute(c));
|
|
35
|
+
})
|
|
36
|
+
.map((c) => {
|
|
37
|
+
if (isHandler(c)) {
|
|
38
|
+
return {
|
|
39
|
+
type: 'handler',
|
|
40
|
+
syntaxList: c
|
|
41
|
+
.getChildren()
|
|
42
|
+
.find((n) => n.getKind() === ts.SyntaxKind.ArrowFunction ||
|
|
43
|
+
n.getKind() === ts.SyntaxKind.FunctionExpression)
|
|
44
|
+
?.getLastChild()
|
|
45
|
+
?.getChildSyntaxList(),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
type: 'route',
|
|
50
|
+
value: c
|
|
51
|
+
.getNodeProperty('initializer')
|
|
52
|
+
.getText()
|
|
53
|
+
?.replaceAll("'", ''),
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
const perRoute = extractedReturns
|
|
58
|
+
.map((routeCombination) => {
|
|
59
|
+
return routeCombination.reduce((acc, curr) => {
|
|
60
|
+
if (curr.type === 'handler') {
|
|
61
|
+
return {
|
|
62
|
+
...acc,
|
|
63
|
+
handler: curr.syntaxList,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
return {
|
|
68
|
+
...acc,
|
|
69
|
+
route: curr.value,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}, {});
|
|
73
|
+
})
|
|
74
|
+
.map((routeCombination) => ({
|
|
75
|
+
...routeCombination,
|
|
76
|
+
handler: getReturnStatements(routeCombination.handler),
|
|
77
|
+
}));
|
|
78
|
+
const schemas = perRoute.map(({ handler, route }) => {
|
|
79
|
+
const schemas = handler.map((t) => generateCustomSchema(t));
|
|
80
|
+
let finalSchema = schemas[0];
|
|
81
|
+
if (schemas.length > 1) {
|
|
82
|
+
finalSchema = {
|
|
83
|
+
type: 'union',
|
|
84
|
+
nullable: false,
|
|
85
|
+
anyOf: schemas,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
route,
|
|
90
|
+
schemas,
|
|
91
|
+
finalSchema,
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
return schemas;
|
|
95
|
+
}
|
|
96
|
+
// const res = tp
|
|
97
|
+
// ?.getChildrenOfKind(ts.SyntaxKind.Identifier)[0]
|
|
98
|
+
// .findReferences()[0]
|
|
99
|
+
// .getReferences()
|
|
100
|
+
// .filter((ref) => !ref.compilerObject.fileName.includes('server.ts'))[0]
|
|
101
|
+
// .getNode()
|
|
102
|
+
// .getAncestors()
|
|
103
|
+
// .filter((n) => n.getKind() === ts.SyntaxKind.CallExpression)[0]
|
|
104
|
+
// .getAncestors()[0]
|
|
105
|
+
// .getChildrenOfKind(ts.SyntaxKind.CallExpression)[0]
|
|
106
|
+
// .getChildrenOfKind(ts.SyntaxKind.SyntaxList)[0]
|
|
107
|
+
// .getChildren()[0]
|
|
108
|
+
// .getChildrenOfKind(ts.SyntaxKind.SyntaxList)[0]
|
|
109
|
+
// .getChildrenOfKind(ts.SyntaxKind.PropertyAssignment)
|
|
110
|
+
// .find((node) =>
|
|
111
|
+
// node
|
|
112
|
+
// .getChildren()
|
|
113
|
+
// .find(
|
|
114
|
+
// (n) =>
|
|
115
|
+
// n.getKind() === ts.SyntaxKind.ArrowFunction ||
|
|
116
|
+
// n.getKind() === ts.SyntaxKind.FunctionExpression,
|
|
117
|
+
// ),
|
|
118
|
+
// )
|
|
119
|
+
// ?.getChildren()
|
|
120
|
+
// ?.find((c) => c.getKind() === ts.SyntaxKind.FunctionExpression)
|
|
121
|
+
// ?.getLastChild()
|
|
122
|
+
// ?.getChildSyntaxList()
|
|
123
|
+
// ?.getChildren()
|
|
124
|
+
// .filter((c) => c.getKind() === ts.SyntaxKind.IfStatement)[0]
|
|
125
|
+
// .getChildren()
|
|
126
|
+
// .find((c) => c.getKind() === ts.SyntaxKind.Block)
|
|
127
|
+
// ?.getChildSyntaxList();
|
|
128
|
+
const extractReturnStatements = (accumulator, n) => {
|
|
129
|
+
if (!n)
|
|
130
|
+
return;
|
|
131
|
+
if (ts.SyntaxKind.IfStatement === n.getKind()) {
|
|
132
|
+
const thenProp = n.getNodeProperty('thenStatement');
|
|
133
|
+
const elseProp = n.getNodeProperty('elseStatement');
|
|
134
|
+
const thenSyntax = thenProp?.getChildSyntaxList();
|
|
135
|
+
const elseSyntax = elseProp?.getChildSyntaxList();
|
|
136
|
+
extractReturnStatements(accumulator, thenSyntax);
|
|
137
|
+
extractReturnStatements(accumulator, elseSyntax);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (n.getChildren().length) {
|
|
141
|
+
const cleanChildren = n.getChildren().filter((c) => typeof c.getKind === 'function');
|
|
142
|
+
const findReturn = cleanChildren.find((c) => c.getKind() === ts.SyntaxKind.ReturnStatement);
|
|
143
|
+
const thereIf = cleanChildren.find((c) => c.getKind() === ts.SyntaxKind.IfStatement);
|
|
144
|
+
const thereWhile = cleanChildren.find((c) => c.getKind() === ts.SyntaxKind.WhileKeyword);
|
|
145
|
+
const thereFor = cleanChildren.find((c) => c.getKind() === ts.SyntaxKind.ForStatement);
|
|
146
|
+
const syntaxList = n.getChildSyntaxList();
|
|
147
|
+
if (findReturn) {
|
|
148
|
+
accumulator.push(findReturn);
|
|
149
|
+
}
|
|
150
|
+
extractReturnStatements(accumulator, thereIf);
|
|
151
|
+
extractReturnStatements(accumulator, thereWhile);
|
|
152
|
+
extractReturnStatements(accumulator, thereFor);
|
|
153
|
+
extractReturnStatements(accumulator, syntaxList);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
const getReturnStatements = (n) => {
|
|
157
|
+
if (!n)
|
|
158
|
+
return [];
|
|
159
|
+
const accumulator = [];
|
|
160
|
+
extractReturnStatements(accumulator, n);
|
|
161
|
+
return accumulator
|
|
162
|
+
.map((r) => r.getChildren().find((c) => {
|
|
163
|
+
const type = c.getType();
|
|
164
|
+
return type.isObject() || isFinalType(type);
|
|
165
|
+
}))
|
|
166
|
+
.filter((n) => n)
|
|
167
|
+
.map((acc) => acc.getType());
|
|
168
|
+
// console.log({ accumulator });
|
|
169
|
+
// let baseChildren = n?.getChildren()?.filter((c) => typeof c.getKind === 'function') ?? [];
|
|
170
|
+
// baseChildren = baseChildren.flat(5).filter((c) => typeof c.getKind === 'function');
|
|
171
|
+
// if (thereIf || thereBlock || thereWhile || thereFor) {
|
|
172
|
+
// baseChildren?.push(
|
|
173
|
+
// getReturnStatements(thereIf) as any,
|
|
174
|
+
// getReturnStatements(thereWhile) as any,
|
|
175
|
+
// getReturnStatements(thereBlock) as any,
|
|
176
|
+
// getReturnStatements(thereFor) as any,
|
|
177
|
+
// );
|
|
178
|
+
// }
|
|
179
|
+
// baseChildren = baseChildren.flat(5).filter((c) => typeof c.getKind === 'function');
|
|
180
|
+
// return baseChildren
|
|
181
|
+
// .filter((c) => typeof c.getKind === 'function')
|
|
182
|
+
// .filter((c) => c.getKind() === ts.SyntaxKind.ReturnStatement)
|
|
183
|
+
// .map((r) =>
|
|
184
|
+
// r.getChildren().find((c) => {
|
|
185
|
+
// const type = c.getType();
|
|
186
|
+
// return type.isObject() || isFinalType(type);
|
|
187
|
+
// }),
|
|
188
|
+
// )
|
|
189
|
+
// .filter((v) => v)
|
|
190
|
+
// .map((v) => v!.getType());
|
|
191
|
+
};
|
|
192
|
+
// returnFinder('examples', tp);
|
|
193
|
+
logInternals(returnFinder('examples', tp));
|
|
194
|
+
// logInternals(returnFinder('examples', tp).map((s) => convertToJsonSchema(s)));
|
|
195
|
+
// console.log(tp);
|
|
196
|
+
// logInternals(getReturnStatements(res!)?.map((t) => regenerateTypeSchema(t!)));
|
|
197
|
+
// regenerateTypeSchema(res![0].getType());
|
|
198
|
+
// logInternals(regenerateTypeSchema(res![0].getType()));
|
|
199
|
+
// console.log(regenerateTypeSchema(res![0].getType()));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const generateRouteSchema: (fileRouteToDeclarations: string) => {
|
|
2
|
+
route: string;
|
|
3
|
+
methodName: "get" | "options" | "post" | "put" | "delete" | "patch" | "head";
|
|
4
|
+
schemas: import("./custom-schema.js").CustomSchema[];
|
|
5
|
+
finalSchema: import("./custom-schema.js").CustomSchema;
|
|
6
|
+
jsonSchema: any;
|
|
7
|
+
}[];
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { Project, ts } from 'ts-morph';
|
|
2
|
+
import { generateCustomSchema } from './custom-schema.js';
|
|
3
|
+
import { isFinalType, isHandler, isRoute } from './helpers.js';
|
|
4
|
+
import { convertToJsonSchema } from './json-schema.js';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { existsSync } from 'fs';
|
|
7
|
+
const project = new Project({ tsConfigFilePath: path.join(process.cwd(), '/tsconfig.json') });
|
|
8
|
+
const nodeModulesFile = path.join(process.cwd(), 'node_modules', 'barehttp');
|
|
9
|
+
const isInstalledPackage = existsSync(nodeModulesFile);
|
|
10
|
+
if (isInstalledPackage)
|
|
11
|
+
project.addSourceFileAtPath(nodeModulesFile + '/lib/server.d.ts');
|
|
12
|
+
const serverSourceFile = project.getSourceFile('server.d.ts');
|
|
13
|
+
const routes = serverSourceFile?.getClass('BareServer')?.getMember('route');
|
|
14
|
+
const runtimeRoutes = serverSourceFile?.getClass('BareServer')?.getMember('runtimeRoute');
|
|
15
|
+
// const requestSourceFile = project.getSourceFile('request.ts');
|
|
16
|
+
// const flowJson = requestSourceFile?.getClass('BareRequest')?.getMember('json');
|
|
17
|
+
// const flowSend = requestSourceFile?.getClass('BareRequest')?.getMember('send');
|
|
18
|
+
const acceptedPropertyNames = ['get', 'post', 'put', 'delete', 'options', 'head', 'patch'];
|
|
19
|
+
const getReferences = (fileRoute, target) => {
|
|
20
|
+
if (!target)
|
|
21
|
+
return [];
|
|
22
|
+
return target
|
|
23
|
+
?.getChildrenOfKind(ts.SyntaxKind.Identifier)[0]
|
|
24
|
+
.findReferences()[0]
|
|
25
|
+
.getReferences()
|
|
26
|
+
?.filter((re) => {
|
|
27
|
+
return re.compilerObject.fileName.includes(fileRoute);
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
// needed for future (extract call functions)
|
|
31
|
+
// const getFlowNodes = (n?: ClassMemberTypes) => {
|
|
32
|
+
// if (!n) return [];
|
|
33
|
+
// return n
|
|
34
|
+
// .getChildrenOfKind(ts.SyntaxKind.Identifier)[0]
|
|
35
|
+
// .findReferences()[0]
|
|
36
|
+
// .getReferences()
|
|
37
|
+
// .map((r) => r.getNode().getParent()?.getParent())
|
|
38
|
+
// .filter((p) => p?.getKind() === ts.SyntaxKind.CallExpression)
|
|
39
|
+
// .map((p) => p?.getNodeProperty('arguments' as any));
|
|
40
|
+
// };
|
|
41
|
+
export const generateRouteSchema = (fileRouteToDeclarations) => {
|
|
42
|
+
if (!routes && !runtimeRoutes) {
|
|
43
|
+
throw new Error('No project been allocated, theres some issue');
|
|
44
|
+
}
|
|
45
|
+
const allReferences = [
|
|
46
|
+
...getReferences(fileRouteToDeclarations, routes),
|
|
47
|
+
...getReferences(fileRouteToDeclarations, runtimeRoutes),
|
|
48
|
+
];
|
|
49
|
+
const extractedReturns = allReferences.map((ref) => {
|
|
50
|
+
const methodName = ref
|
|
51
|
+
.getNode()
|
|
52
|
+
.getAncestors()
|
|
53
|
+
.map((n) => n.getSymbol()?.getName())
|
|
54
|
+
.filter((param) => acceptedPropertyNames.includes(param))
|
|
55
|
+
.pop();
|
|
56
|
+
if (!methodName) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
return ref
|
|
60
|
+
.getNode()
|
|
61
|
+
.getAncestors()
|
|
62
|
+
.find((n) => n.getKind() === ts.SyntaxKind.CallExpression)
|
|
63
|
+
?.getChildren()
|
|
64
|
+
.find((n) => n.getKind() === ts.SyntaxKind.SyntaxList)
|
|
65
|
+
?.getFirstChild()
|
|
66
|
+
?.getChildSyntaxList()
|
|
67
|
+
?.getChildren()
|
|
68
|
+
.filter((c) => {
|
|
69
|
+
return c.getKind() === ts.SyntaxKind.PropertyAssignment && (isHandler(c) || isRoute(c));
|
|
70
|
+
})
|
|
71
|
+
.map((c) => {
|
|
72
|
+
if (isHandler(c)) {
|
|
73
|
+
return {
|
|
74
|
+
type: 'handler',
|
|
75
|
+
methodName,
|
|
76
|
+
syntaxList: c
|
|
77
|
+
.getChildren()
|
|
78
|
+
.find((n) => n.getKind() === ts.SyntaxKind.ArrowFunction ||
|
|
79
|
+
n.getKind() === ts.SyntaxKind.FunctionExpression)
|
|
80
|
+
?.getLastChild()
|
|
81
|
+
?.getChildSyntaxList(),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
type: 'route',
|
|
86
|
+
methodName,
|
|
87
|
+
value: c
|
|
88
|
+
.getNodeProperty('initializer')
|
|
89
|
+
.getText()
|
|
90
|
+
?.replaceAll("'", ''),
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
const perRoute = extractedReturns
|
|
95
|
+
.map((routeCombination) => {
|
|
96
|
+
return routeCombination.reduce((acc, curr) => {
|
|
97
|
+
acc.methodName = curr.methodName;
|
|
98
|
+
if (curr.type === 'handler') {
|
|
99
|
+
return {
|
|
100
|
+
...acc,
|
|
101
|
+
handler: curr.syntaxList,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
return {
|
|
106
|
+
...acc,
|
|
107
|
+
route: curr.value,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}, {});
|
|
111
|
+
})
|
|
112
|
+
.map((routeCombination) => ({
|
|
113
|
+
...routeCombination,
|
|
114
|
+
handler: getReturnStatements(routeCombination.handler),
|
|
115
|
+
}));
|
|
116
|
+
const schemas = perRoute
|
|
117
|
+
.filter((pr) => pr.route && pr.handler.length)
|
|
118
|
+
.map(({ handler, route, methodName }) => {
|
|
119
|
+
const schemas = handler.map((t) => generateCustomSchema(t));
|
|
120
|
+
let finalSchema = schemas[0];
|
|
121
|
+
if (schemas.length > 1) {
|
|
122
|
+
finalSchema = {
|
|
123
|
+
type: 'union',
|
|
124
|
+
nullable: false,
|
|
125
|
+
anyOf: schemas,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
route,
|
|
130
|
+
methodName,
|
|
131
|
+
schemas,
|
|
132
|
+
finalSchema,
|
|
133
|
+
jsonSchema: convertToJsonSchema(finalSchema),
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
return [...schemas];
|
|
137
|
+
};
|
|
138
|
+
const extractReturnStatements = (accumulator, n) => {
|
|
139
|
+
if (!n)
|
|
140
|
+
return;
|
|
141
|
+
if (ts.SyntaxKind.IfStatement === n.getKind()) {
|
|
142
|
+
const thenProp = n.getNodeProperty('thenStatement');
|
|
143
|
+
const elseProp = n.getNodeProperty('elseStatement');
|
|
144
|
+
const thenSyntax = thenProp?.getChildSyntaxList();
|
|
145
|
+
const elseSyntax = elseProp?.getChildSyntaxList();
|
|
146
|
+
extractReturnStatements(accumulator, thenSyntax);
|
|
147
|
+
extractReturnStatements(accumulator, elseSyntax);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (n.getChildren().length) {
|
|
151
|
+
const cleanChildren = n.getChildren().filter((c) => typeof c.getKind === 'function');
|
|
152
|
+
const findReturn = cleanChildren.find((c) => c.getKind() === ts.SyntaxKind.ReturnStatement);
|
|
153
|
+
const thereIf = cleanChildren.find((c) => c.getKind() === ts.SyntaxKind.IfStatement);
|
|
154
|
+
const thereWhile = cleanChildren.find((c) => c.getKind() === ts.SyntaxKind.WhileKeyword);
|
|
155
|
+
const thereFor = cleanChildren.find((c) => c.getKind() === ts.SyntaxKind.ForStatement);
|
|
156
|
+
const syntaxList = n.getChildSyntaxList();
|
|
157
|
+
if (findReturn) {
|
|
158
|
+
accumulator.push(findReturn);
|
|
159
|
+
}
|
|
160
|
+
extractReturnStatements(accumulator, thereIf);
|
|
161
|
+
extractReturnStatements(accumulator, thereWhile);
|
|
162
|
+
extractReturnStatements(accumulator, thereFor);
|
|
163
|
+
extractReturnStatements(accumulator, syntaxList);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
const getTypes = (nodes) => nodes
|
|
167
|
+
.map((r) => r.getChildren().find((c) => {
|
|
168
|
+
const type = c.getType();
|
|
169
|
+
return type.isObject() || isFinalType(type);
|
|
170
|
+
}))
|
|
171
|
+
.filter((n) => n && typeof n.getType === 'function')
|
|
172
|
+
.map((acc) => acc.getType());
|
|
173
|
+
const getReturnStatements = (n) => {
|
|
174
|
+
if (!n)
|
|
175
|
+
return [];
|
|
176
|
+
const accumulator = [];
|
|
177
|
+
extractReturnStatements(accumulator, n);
|
|
178
|
+
return getTypes(accumulator);
|
|
179
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Node, PropertyAssignment, ts, Type } from 'ts-morph';
|
|
2
|
+
export declare const helpers: {
|
|
3
|
+
findCallExpressionFromChildren: (property: PropertyAssignment) => Node<ts.Node>[];
|
|
4
|
+
findCallExpression: (n: Node<ts.Node>[]) => Node<ts.Node> | undefined;
|
|
5
|
+
findSyntaxList: (property: PropertyAssignment | Node<ts.Node>) => Node<ts.Node> | undefined;
|
|
6
|
+
findFunction: (property: PropertyAssignment) => Node<ts.Node> | undefined;
|
|
7
|
+
findReturnStatement: (property: PropertyAssignment) => Node<ts.Node> | undefined;
|
|
8
|
+
findIdentifier: (property: PropertyAssignment) => Node<ts.Node> | undefined;
|
|
9
|
+
findObjectLiteralExpressionFromChildren: (property: PropertyAssignment) => Node<ts.Node> | undefined;
|
|
10
|
+
findObjectLiteralExpression: (n: Node<ts.Node>[]) => Node<ts.Node> | undefined;
|
|
11
|
+
filterPropertyAssignmentFromChildren: (property: PropertyAssignment) => Node<ts.Node>[];
|
|
12
|
+
findPropertyAssignmentFromChildren: (property: PropertyAssignment) => Node<ts.Node> | undefined;
|
|
13
|
+
findPropertyAssignment: (n: Node<ts.Node>[]) => Node<ts.Node> | undefined;
|
|
14
|
+
findUnionTypeNodeFromChildren: (n: Node<ts.Node>) => Node<ts.Node> | undefined;
|
|
15
|
+
findNullableTypeFromChildren: (n: Node<ts.Node>) => Node<ts.Node> | undefined;
|
|
16
|
+
filterNullableTypeFromChildren: (n: Node<ts.Node>) => Node<ts.Node>[];
|
|
17
|
+
cleanNullableTypes: (t: Type<ts.Type>[]) => Type<ts.Type>[];
|
|
18
|
+
};
|
|
19
|
+
export declare const isFinalType: (t: Type<ts.Type>) => boolean;
|
|
20
|
+
export declare const isNullType: (t: Type<ts.Type>) => boolean;
|
|
21
|
+
type ResolvedBasicTypes = 'string' | 'number' | 'boolean' | 'array' | 'object';
|
|
22
|
+
export declare const isHandler: (c: Node<ts.Node>) => boolean;
|
|
23
|
+
export declare const isRoute: (c: Node<ts.Node>) => boolean;
|
|
24
|
+
export declare const getTypeGenericText: (t: Type<ts.Type>) => ResolvedBasicTypes;
|
|
25
|
+
export declare const getApparentTypeName: (t: Type<ts.Type>) => string;
|
|
26
|
+
export declare function logInternals(data: any): void;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ts } from 'ts-morph';
|
|
2
|
+
import { inspect } from 'util';
|
|
3
|
+
export const helpers = {
|
|
4
|
+
findCallExpressionFromChildren: (property) => property.getChildren(),
|
|
5
|
+
findCallExpression: (n) => n.find((x) => x.getKind() === ts.SyntaxKind.CallExpression),
|
|
6
|
+
findSyntaxList: (property) => property.getChildren().find((x) => x.getKind() === ts.SyntaxKind.SyntaxList),
|
|
7
|
+
findFunction: (property) => property
|
|
8
|
+
.getChildren()
|
|
9
|
+
.find((x) => x.getKind() === ts.SyntaxKind.ArrowFunction ||
|
|
10
|
+
x.getKind() === ts.SyntaxKind.FunctionExpression),
|
|
11
|
+
findReturnStatement: (property) => property.getChildren().find((x) => x.getKind() === ts.SyntaxKind.ReturnStatement),
|
|
12
|
+
findIdentifier: (property) => property.getChildren().find((x) => x.getKind() === ts.SyntaxKind.ReturnStatement),
|
|
13
|
+
findObjectLiteralExpressionFromChildren: (property) => property.getChildren().find((x) => x.getKind() === ts.SyntaxKind.ObjectLiteralExpression),
|
|
14
|
+
findObjectLiteralExpression: (n) => n.find((x) => x.getKind() === ts.SyntaxKind.ObjectLiteralExpression),
|
|
15
|
+
filterPropertyAssignmentFromChildren: (property) => property.getChildren().filter((x) => x.getKind() === ts.SyntaxKind.PropertyAssignment),
|
|
16
|
+
findPropertyAssignmentFromChildren: (property) => property.getChildren().find((x) => x.getKind() === ts.SyntaxKind.PropertyAssignment),
|
|
17
|
+
findPropertyAssignment: (n) => n.find((x) => x.getKind() === ts.SyntaxKind.PropertyAssignment),
|
|
18
|
+
findUnionTypeNodeFromChildren: (n) => n.getChildren().find((x) => x.getKind() === ts.SyntaxKind.UnionType),
|
|
19
|
+
findNullableTypeFromChildren: (n) => n.getChildren().find((x) => isNullType(x.getType())),
|
|
20
|
+
filterNullableTypeFromChildren: (n) => n.getChildren().filter((x) => !isNullType(x.getType())),
|
|
21
|
+
cleanNullableTypes: (t) => t.filter((x) => !isNullType(x)),
|
|
22
|
+
}; //Prop
|
|
23
|
+
export const isFinalType = (t) => t.isNumber() || t.isString() || t.isBoolean() || t.isLiteral();
|
|
24
|
+
export const isNullType = (t) => t.isNull() || t.isUndefined();
|
|
25
|
+
export const isHandler = (c) => c.getSymbol()?.getName() === 'handler';
|
|
26
|
+
export const isRoute = (c) => c.getSymbol()?.getName() === 'route';
|
|
27
|
+
export const getTypeGenericText = (t) => {
|
|
28
|
+
if (t.isStringLiteral() || t.isNumberLiteral() || t.isBooleanLiteral()) {
|
|
29
|
+
return t.getBaseTypeOfLiteralType().getText();
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
return t.getText();
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
export const getApparentTypeName = (t) => {
|
|
36
|
+
return t.getApparentType().getText().toLowerCase();
|
|
37
|
+
};
|
|
38
|
+
export function logInternals(data) {
|
|
39
|
+
console.log(inspect(data, false, null, true));
|
|
40
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const convertToJsonSchema = (schema) => {
|
|
2
|
+
if (schema.type === 'string') {
|
|
3
|
+
return {
|
|
4
|
+
type: 'string',
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
if (schema.type === 'number') {
|
|
8
|
+
return {
|
|
9
|
+
type: 'number',
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
if (schema.type === 'boolean') {
|
|
13
|
+
return {
|
|
14
|
+
type: 'boolean',
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
if (schema.type === 'array') {
|
|
18
|
+
const reSchema = schema;
|
|
19
|
+
return {
|
|
20
|
+
type: 'array',
|
|
21
|
+
items: convertToJsonSchema(reSchema.items),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if (schema.type === 'object') {
|
|
25
|
+
const reSchema = schema;
|
|
26
|
+
const objectJsonedProperties = Object.keys(reSchema.properties).reduce((acc, key) => {
|
|
27
|
+
acc[key] = convertToJsonSchema(reSchema.properties[key]);
|
|
28
|
+
return acc;
|
|
29
|
+
}, {});
|
|
30
|
+
const required = Object.entries(reSchema.properties).reduce((acc, [key, value]) => {
|
|
31
|
+
if (!value.nullable) {
|
|
32
|
+
acc.push(key);
|
|
33
|
+
}
|
|
34
|
+
return acc;
|
|
35
|
+
}, []);
|
|
36
|
+
return {
|
|
37
|
+
required,
|
|
38
|
+
type: 'object',
|
|
39
|
+
properties: objectJsonedProperties,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (schema.type === 'union') {
|
|
43
|
+
const reSchema = schema;
|
|
44
|
+
return {
|
|
45
|
+
anyOf: reSchema.anyOf.map((item) => convertToJsonSchema(item)),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const createRouteSchema = (route, method, openApiSchema) => ({
|
|
2
|
+
[route]: {
|
|
3
|
+
[method]: {
|
|
4
|
+
description: 'Autogenerated',
|
|
5
|
+
responses: {
|
|
6
|
+
'200': openApiSchema,
|
|
7
|
+
},
|
|
8
|
+
},
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
export const convertToOpenApiSchema = (schema, route, method) => {
|
|
12
|
+
if (schema.type === 'string') {
|
|
13
|
+
return {
|
|
14
|
+
type: 'string',
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
if (schema.type === 'number') {
|
|
18
|
+
return {
|
|
19
|
+
type: 'number',
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
if (schema.type === 'boolean') {
|
|
23
|
+
return {
|
|
24
|
+
type: 'boolean',
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (schema.type === 'array') {
|
|
28
|
+
const reSchema = schema;
|
|
29
|
+
return {
|
|
30
|
+
type: 'array',
|
|
31
|
+
items: convertToOpenApiSchema(reSchema.items),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (schema.type === 'object') {
|
|
35
|
+
const reSchema = schema;
|
|
36
|
+
const objectJsonedProperties = Object.keys(reSchema.properties).reduce((acc, key) => {
|
|
37
|
+
acc[key] = convertToOpenApiSchema(reSchema.properties[key]);
|
|
38
|
+
return acc;
|
|
39
|
+
}, {});
|
|
40
|
+
const required = Object.entries(reSchema.properties).reduce((acc, [key, value]) => {
|
|
41
|
+
if (!value.nullable) {
|
|
42
|
+
acc.push(key);
|
|
43
|
+
}
|
|
44
|
+
return acc;
|
|
45
|
+
}, []);
|
|
46
|
+
return {
|
|
47
|
+
required,
|
|
48
|
+
type: 'object',
|
|
49
|
+
properties: objectJsonedProperties,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (schema.type === 'union') {
|
|
53
|
+
const reSchema = schema;
|
|
54
|
+
return {
|
|
55
|
+
anyOf: reSchema.anyOf.map((item) => convertToOpenApiSchema(item)),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return createRouteSchema(route, method, schema);
|
|
59
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|