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
- return {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-openapi-gen",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "Automatically generate OpenAPI 3.0 documentation from Next.js projects, with support for TypeScript types and Zod schemas.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",