next-openapi-gen 0.6.1 → 0.6.2

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 CHANGED
@@ -154,6 +154,7 @@ export async function GET(
154
154
  | `@bodyDescription` | Request body description |
155
155
  | `@response` | Response type/schema |
156
156
  | `@responseDescription` | Response description |
157
+ | `@contentType` | Request body content type (`application/json`, `multipart/form-data`) |
157
158
  | `@auth` | Authorization type (`bearer`, `basic`, `apikey`) |
158
159
  | `@tag` | Custom tag |
159
160
  | `@deprecated` | Marks the route as deprecated |
@@ -344,6 +345,34 @@ export async function GET() {
344
345
  }
345
346
  ```
346
347
 
348
+ ### File Uploads / Multipart Form Data
349
+
350
+ ```typescript
351
+ // src/app/api/upload/route.ts
352
+
353
+ // TypeScript
354
+ type FileUploadFormData = {
355
+ file: File;
356
+ description?: string;
357
+ category: string;
358
+ };
359
+
360
+ // Or Zod
361
+ const FileUploadSchema = z.object({
362
+ file: z.custom<File>().describe("Image file (PNG/JPG)"),
363
+ description: z.string().optional().describe("File description"),
364
+ category: z.string().describe("File category"),
365
+ });
366
+
367
+ /**
368
+ * @body FileUploadSchema
369
+ * @contentType multipart/form-data
370
+ */
371
+ export async function POST() {
372
+ // ...
373
+ }
374
+ ```
375
+
347
376
  ## Advanced Usage
348
377
 
349
378
  ### Automatic Path Parameter Detection
@@ -160,7 +160,7 @@ export class RouteProcessor {
160
160
  }
161
161
  // Add request body
162
162
  if (MUTATION_HTTP_METHODS.includes(method.toUpperCase())) {
163
- definition.requestBody = this.schemaProcessor.createRequestBodySchema(body, bodyDescription);
163
+ definition.requestBody = this.schemaProcessor.createRequestBodySchema(body, bodyDescription, dataTypes.contentType);
164
164
  }
165
165
  // Add responses
166
166
  definition.responses = responses
@@ -501,6 +501,44 @@ export class SchemaProcessor {
501
501
  return "example";
502
502
  }
503
503
  }
504
+ detectContentType(bodyType, explicitContentType) {
505
+ if (explicitContentType) {
506
+ return explicitContentType;
507
+ }
508
+ // Automatic detection based on type name
509
+ if (bodyType &&
510
+ (bodyType.toLowerCase().includes("formdata") ||
511
+ bodyType.toLowerCase().includes("fileupload") ||
512
+ bodyType.toLowerCase().includes("multipart"))) {
513
+ return "multipart/form-data";
514
+ }
515
+ return "application/json";
516
+ }
517
+ createFormDataSchema(body) {
518
+ if (!body.properties) {
519
+ return body;
520
+ }
521
+ const formDataProperties = {};
522
+ Object.entries(body.properties).forEach(([key, value]) => {
523
+ // Convert File types to binary format
524
+ if (value.type === "object" &&
525
+ (key.toLowerCase().includes("file") ||
526
+ value.description?.toLowerCase().includes("file"))) {
527
+ formDataProperties[key] = {
528
+ type: "string",
529
+ format: "binary",
530
+ description: value.description,
531
+ };
532
+ }
533
+ else {
534
+ formDataProperties[key] = value;
535
+ }
536
+ });
537
+ return {
538
+ ...body,
539
+ properties: formDataProperties,
540
+ };
541
+ }
504
542
  /**
505
543
  * Create a default schema for path parameters when no schema is defined
506
544
  */
@@ -558,18 +596,24 @@ export class SchemaProcessor {
558
596
  }
559
597
  return queryParams;
560
598
  }
561
- createRequestBodySchema(body, description) {
562
- const schema = {
599
+ createRequestBodySchema(body, description, contentType) {
600
+ const detectedContentType = this.detectContentType(body?.type || "", contentType);
601
+ let schema = body;
602
+ // If it is multipart/form-data, convert schema
603
+ if (detectedContentType === "multipart/form-data") {
604
+ schema = this.createFormDataSchema(body);
605
+ }
606
+ const requestBody = {
563
607
  content: {
564
- "application/json": {
565
- schema: body,
608
+ [detectedContentType]: {
609
+ schema: schema,
566
610
  },
567
611
  },
568
612
  };
569
613
  if (description) {
570
- schema.description = description;
614
+ requestBody.description = description;
571
615
  }
572
- return schema;
616
+ return requestBody;
573
617
  }
574
618
  createResponseSchema(responses, description) {
575
619
  return {
package/dist/lib/utils.js CHANGED
@@ -28,6 +28,7 @@ export function extractJSDocComments(path) {
28
28
  let deprecated = false;
29
29
  let bodyDescription = "";
30
30
  let responseDescription = "";
31
+ let contentType = "";
31
32
  if (comments) {
32
33
  comments.forEach((comment) => {
33
34
  const commentValue = cleanComment(comment.value);
@@ -90,6 +91,13 @@ export function extractJSDocComments(path) {
90
91
  if (commentValue.includes("@response")) {
91
92
  responseType = extractTypeFromComment(commentValue, "@response");
92
93
  }
94
+ if (commentValue.includes("@contentType")) {
95
+ const regex = /@contentType\s*(.*)/;
96
+ const match = commentValue.match(regex);
97
+ if (match && match[1]) {
98
+ contentType = match[1].trim();
99
+ }
100
+ }
93
101
  });
94
102
  }
95
103
  return {
@@ -105,6 +113,7 @@ export function extractJSDocComments(path) {
105
113
  deprecated,
106
114
  bodyDescription,
107
115
  responseDescription,
116
+ contentType,
108
117
  };
109
118
  }
110
119
  export function extractTypeFromComment(commentValue, tag) {
@@ -846,6 +846,22 @@ export class ZodSchemaConverter {
846
846
  !t.isIdentifier(node.callee.property)) {
847
847
  return { type: "string" };
848
848
  }
849
+ if (t.isMemberExpression(node.callee) &&
850
+ t.isIdentifier(node.callee.property)) {
851
+ const zodType = node.callee.property.name;
852
+ // Custom() support for FormData
853
+ if (zodType === "custom" && node.arguments.length > 0) {
854
+ // Check if it is FormData
855
+ if (t.isArrowFunctionExpression(node.arguments[0])) {
856
+ // Assume custom FormData validation
857
+ return {
858
+ type: "object",
859
+ additionalProperties: true,
860
+ description: "Form data object",
861
+ };
862
+ }
863
+ }
864
+ }
849
865
  const zodType = node.callee.property.name;
850
866
  let schema = {};
851
867
  // Basic type mapping
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-openapi-gen",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
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",