next-openapi-gen 0.5.2 → 0.5.4
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.
|
@@ -109,7 +109,7 @@ export class RouteProcessor {
|
|
|
109
109
|
const routePath = this.getRoutePath(filePath);
|
|
110
110
|
const rootPath = capitalize(routePath.split("/")[1]);
|
|
111
111
|
const operationId = getOperationId(routePath, method);
|
|
112
|
-
const { tag, summary, description, auth, isOpenApi } = dataTypes;
|
|
112
|
+
const { tag, summary, description, auth, isOpenApi, deprecated, bodyDescription, responseDescription, } = dataTypes;
|
|
113
113
|
if (this.config.includeOpenApiRoutes && !isOpenApi) {
|
|
114
114
|
// If flag is enabled and there is no @openapi tag, then skip path
|
|
115
115
|
return;
|
|
@@ -125,6 +125,9 @@ export class RouteProcessor {
|
|
|
125
125
|
tags: [tag || rootPath],
|
|
126
126
|
parameters: [],
|
|
127
127
|
};
|
|
128
|
+
if (deprecated) {
|
|
129
|
+
definition.deprecated = true;
|
|
130
|
+
}
|
|
128
131
|
// Add auth
|
|
129
132
|
if (auth) {
|
|
130
133
|
definition.security = [
|
|
@@ -157,12 +160,11 @@ export class RouteProcessor {
|
|
|
157
160
|
}
|
|
158
161
|
// Add request body
|
|
159
162
|
if (MUTATION_HTTP_METHODS.includes(method.toUpperCase())) {
|
|
160
|
-
definition.requestBody =
|
|
161
|
-
this.schemaProcessor.createRequestBodySchema(body);
|
|
163
|
+
definition.requestBody = this.schemaProcessor.createRequestBodySchema(body, bodyDescription);
|
|
162
164
|
}
|
|
163
165
|
// Add responses
|
|
164
166
|
definition.responses = responses
|
|
165
|
-
? this.schemaProcessor.createResponseSchema(responses)
|
|
167
|
+
? this.schemaProcessor.createResponseSchema(responses, responseDescription)
|
|
166
168
|
: {};
|
|
167
169
|
this.swaggerPaths[routePath][method] = definition;
|
|
168
170
|
}
|
|
@@ -555,19 +555,23 @@ export class SchemaProcessor {
|
|
|
555
555
|
}
|
|
556
556
|
return queryParams;
|
|
557
557
|
}
|
|
558
|
-
createRequestBodySchema(body) {
|
|
559
|
-
|
|
558
|
+
createRequestBodySchema(body, description) {
|
|
559
|
+
const schema = {
|
|
560
560
|
content: {
|
|
561
561
|
"application/json": {
|
|
562
562
|
schema: body,
|
|
563
563
|
},
|
|
564
564
|
},
|
|
565
565
|
};
|
|
566
|
+
if (description) {
|
|
567
|
+
schema.description = description;
|
|
568
|
+
}
|
|
569
|
+
return schema;
|
|
566
570
|
}
|
|
567
|
-
createResponseSchema(responses) {
|
|
571
|
+
createResponseSchema(responses, description) {
|
|
568
572
|
return {
|
|
569
573
|
200: {
|
|
570
|
-
description: "Successful response",
|
|
574
|
+
description: description || "Successful response",
|
|
571
575
|
content: {
|
|
572
576
|
"application/json": {
|
|
573
577
|
schema: responses,
|
package/dist/lib/utils.js
CHANGED
|
@@ -25,10 +25,30 @@ export function extractJSDocComments(path) {
|
|
|
25
25
|
let responseType = "";
|
|
26
26
|
let auth = "";
|
|
27
27
|
let isOpenApi = false;
|
|
28
|
+
let deprecated = false;
|
|
29
|
+
let bodyDescription = "";
|
|
30
|
+
let responseDescription = "";
|
|
28
31
|
if (comments) {
|
|
29
32
|
comments.forEach((comment) => {
|
|
30
33
|
const commentValue = cleanComment(comment.value);
|
|
31
34
|
isOpenApi = commentValue.includes("@openapi");
|
|
35
|
+
if (commentValue.includes("@deprecated")) {
|
|
36
|
+
deprecated = true;
|
|
37
|
+
}
|
|
38
|
+
if (commentValue.includes("@bodyDescription")) {
|
|
39
|
+
const regex = /@bodyDescription\s*(.*)/;
|
|
40
|
+
const match = commentValue.match(regex);
|
|
41
|
+
if (match && match[1]) {
|
|
42
|
+
bodyDescription = match[1].trim();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (commentValue.includes("@responseDescription")) {
|
|
46
|
+
const regex = /@responseDescription\s*(.*)/;
|
|
47
|
+
const match = commentValue.match(regex);
|
|
48
|
+
if (match && match[1]) {
|
|
49
|
+
responseDescription = match[1].trim();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
32
52
|
if (!summary) {
|
|
33
53
|
summary = commentValue.split("\n")[0];
|
|
34
54
|
}
|
|
@@ -82,6 +102,9 @@ export function extractJSDocComments(path) {
|
|
|
82
102
|
bodyType,
|
|
83
103
|
responseType,
|
|
84
104
|
isOpenApi,
|
|
105
|
+
deprecated,
|
|
106
|
+
bodyDescription,
|
|
107
|
+
responseDescription,
|
|
85
108
|
};
|
|
86
109
|
}
|
|
87
110
|
export function extractTypeFromComment(commentValue, tag) {
|
|
@@ -138,6 +138,12 @@ export class ZodSchemaConverter {
|
|
|
138
138
|
if (!content.includes(schemaName)) {
|
|
139
139
|
return;
|
|
140
140
|
}
|
|
141
|
+
// Pre-process all schemas in file
|
|
142
|
+
this.preprocessAllSchemasInFile(filePath);
|
|
143
|
+
// Return it, if the schema has already been processed during pre-processing
|
|
144
|
+
if (this.zodSchemas[schemaName]) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
141
147
|
// Parse the file
|
|
142
148
|
const ast = parse(content, {
|
|
143
149
|
sourceType: "module",
|
|
@@ -593,6 +599,44 @@ export class ZodSchemaConverter {
|
|
|
593
599
|
console.log(`Skipping property ${index} - unsupported key type`);
|
|
594
600
|
return; // Skip if key is not identifier or string literal
|
|
595
601
|
}
|
|
602
|
+
if (t.isCallExpression(prop.value) &&
|
|
603
|
+
t.isMemberExpression(prop.value.callee) &&
|
|
604
|
+
t.isIdentifier(prop.value.callee.object)) {
|
|
605
|
+
const schemaName = prop.value.callee.object.name;
|
|
606
|
+
// @ts-ignore
|
|
607
|
+
const methodName = prop.value.callee.property.name;
|
|
608
|
+
// Process base schema first
|
|
609
|
+
if (!this.zodSchemas[schemaName]) {
|
|
610
|
+
this.convertZodSchemaToOpenApi(schemaName);
|
|
611
|
+
}
|
|
612
|
+
// For describe method, use reference with description
|
|
613
|
+
if (methodName === "describe" && this.zodSchemas[schemaName]) {
|
|
614
|
+
if (prop.value.arguments.length > 0 &&
|
|
615
|
+
t.isStringLiteral(prop.value.arguments[0])) {
|
|
616
|
+
properties[propName] = {
|
|
617
|
+
allOf: [{ $ref: `#/components/schemas/${schemaName}` }],
|
|
618
|
+
description: prop.value.arguments[0].value,
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
else {
|
|
622
|
+
properties[propName] = {
|
|
623
|
+
$ref: `#/components/schemas/${schemaName}`,
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
required.push(propName);
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
// For other methods, process normally
|
|
630
|
+
const processedSchema = this.processZodNode(prop.value);
|
|
631
|
+
if (processedSchema) {
|
|
632
|
+
properties[propName] = processedSchema;
|
|
633
|
+
const isOptional = this.isOptional(prop.value) || this.hasOptionalMethod(prop.value);
|
|
634
|
+
if (!isOptional) {
|
|
635
|
+
required.push(propName);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
596
640
|
// Check if the property value is an identifier (reference to another schema)
|
|
597
641
|
if (t.isIdentifier(prop.value)) {
|
|
598
642
|
const referencedSchemaName = prop.value.name;
|
|
@@ -833,6 +877,9 @@ export class ZodSchemaConverter {
|
|
|
833
877
|
schema.description = node.arguments[0].value;
|
|
834
878
|
}
|
|
835
879
|
break;
|
|
880
|
+
case "deprecated":
|
|
881
|
+
schema.deprecated = true;
|
|
882
|
+
break;
|
|
836
883
|
case "min":
|
|
837
884
|
if (node.arguments.length > 0 &&
|
|
838
885
|
t.isNumericLiteral(node.arguments[0])) {
|
|
@@ -1181,4 +1228,59 @@ export class ZodSchemaConverter {
|
|
|
1181
1228
|
console.error(`Error scanning directory ${dir} for type mappings:`, error);
|
|
1182
1229
|
}
|
|
1183
1230
|
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Pre-process all Zod schemas in a file
|
|
1233
|
+
*/
|
|
1234
|
+
preprocessAllSchemasInFile(filePath) {
|
|
1235
|
+
try {
|
|
1236
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
1237
|
+
const ast = parse(content, {
|
|
1238
|
+
sourceType: "module",
|
|
1239
|
+
plugins: ["typescript", "decorators-legacy"],
|
|
1240
|
+
});
|
|
1241
|
+
// Collect all exported Zod schemas
|
|
1242
|
+
traverse.default(ast, {
|
|
1243
|
+
ExportNamedDeclaration: (path) => {
|
|
1244
|
+
if (t.isVariableDeclaration(path.node.declaration)) {
|
|
1245
|
+
path.node.declaration.declarations.forEach((declaration) => {
|
|
1246
|
+
if (t.isIdentifier(declaration.id) && declaration.init) {
|
|
1247
|
+
const schemaName = declaration.id.name;
|
|
1248
|
+
// Check if is Zos schema
|
|
1249
|
+
if (this.isZodSchema(declaration.init) &&
|
|
1250
|
+
!this.zodSchemas[schemaName]) {
|
|
1251
|
+
console.log(`Pre-processing Zod schema: ${schemaName}`);
|
|
1252
|
+
this.processingSchemas.add(schemaName);
|
|
1253
|
+
const schema = this.processZodNode(declaration.init);
|
|
1254
|
+
if (schema) {
|
|
1255
|
+
this.zodSchemas[schemaName] = schema;
|
|
1256
|
+
}
|
|
1257
|
+
this.processingSchemas.delete(schemaName);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
},
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
catch (error) {
|
|
1266
|
+
console.error(`Error pre-processing file ${filePath}:`, error);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* Check if node is Zod schema
|
|
1271
|
+
*/
|
|
1272
|
+
isZodSchema(node) {
|
|
1273
|
+
if (t.isCallExpression(node)) {
|
|
1274
|
+
if (t.isMemberExpression(node.callee) &&
|
|
1275
|
+
t.isIdentifier(node.callee.object) &&
|
|
1276
|
+
node.callee.object.name === "z") {
|
|
1277
|
+
return true;
|
|
1278
|
+
}
|
|
1279
|
+
if (t.isMemberExpression(node.callee) &&
|
|
1280
|
+
t.isCallExpression(node.callee.object)) {
|
|
1281
|
+
return this.isZodSchema(node.callee.object);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
return false;
|
|
1285
|
+
}
|
|
1184
1286
|
}
|
package/package.json
CHANGED