next-openapi-gen 0.8.5 → 0.8.7
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 +5 -16
- package/dist/lib/route-processor.js +27 -3
- package/dist/lib/schema-processor.js +23 -10
- package/dist/lib/utils.js +2 -2
- package/dist/lib/zod-converter.js +12 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -842,29 +842,18 @@ Custom schema files support YAML/JSON in OpenAPI 3.0 format. See **[next15-app-m
|
|
|
842
842
|
|
|
843
843
|
## Examples
|
|
844
844
|
|
|
845
|
-
|
|
845
|
+
Explore complete demo projects in the **[examples](./examples/)** directory, covering integrations with Zod, TypeScript, Drizzle and documentation tools like Scalar and Swagger.
|
|
846
846
|
|
|
847
|
-
###
|
|
848
|
-
|
|
849
|
-
| Example | Description | Features |
|
|
850
|
-
| --------------------------------------------------------------- | ----------------------- | ----------------------------------------------- |
|
|
851
|
-
| **[next15-app-zod](./examples/next15-app-zod)** | Zod schemas example | Users, Products, Orders API with Zod validation |
|
|
852
|
-
| **[next15-app-drizzle-zod](./examples/next15-app-drizzle-zod)** | Drizzle-Zod integration | Blog API with Drizzle ORM + drizzle-zod |
|
|
853
|
-
| **[next15-app-mixed-schemas](./examples/next15-app-mixed-schemas)** 🆕 | Multiple schema types | Zod + TypeScript + Custom YAML schemas combined |
|
|
854
|
-
| **[next15-app-typescript](./examples/next15-app-typescript)** | TypeScript types | API with pure TypeScript type definitions |
|
|
855
|
-
| **[next15-app-scalar](./examples/next15-app-scalar)** | Scalar UI | Modern API documentation interface |
|
|
856
|
-
| **[next15-app-swagger](./examples/next15-app-swagger)** | Swagger UI | Classic Swagger documentation |
|
|
857
|
-
|
|
858
|
-
### 🚀 Running Examples
|
|
847
|
+
### 🚀 Run an Example
|
|
859
848
|
|
|
860
849
|
```bash
|
|
861
|
-
cd examples/next15-app-
|
|
850
|
+
cd examples/next15-app-zod
|
|
862
851
|
npm install
|
|
863
|
-
|
|
852
|
+
npx next-openapi-gen generate
|
|
864
853
|
npm run dev
|
|
865
854
|
```
|
|
866
855
|
|
|
867
|
-
|
|
856
|
+
Then open `http://localhost:3000/api-docs` to view the generated docs.
|
|
868
857
|
|
|
869
858
|
## Available UI providers
|
|
870
859
|
|
|
@@ -25,15 +25,39 @@ export class RouteProcessor {
|
|
|
25
25
|
// 1. Add success response
|
|
26
26
|
const successCode = dataTypes.successCode || this.getDefaultSuccessCode(method);
|
|
27
27
|
if (dataTypes.responseType) {
|
|
28
|
-
//
|
|
28
|
+
// Handle array notation (e.g., "Type[]", "Type[][]", "Generic<T>[]")
|
|
29
|
+
let schema;
|
|
30
|
+
let baseType = dataTypes.responseType;
|
|
31
|
+
let arrayDepth = 0;
|
|
32
|
+
// Count and remove array brackets
|
|
33
|
+
while (baseType.endsWith('[]')) {
|
|
34
|
+
arrayDepth++;
|
|
35
|
+
baseType = baseType.slice(0, -2);
|
|
36
|
+
}
|
|
37
|
+
// Ensure the base schema is defined in components/schemas
|
|
29
38
|
this.schemaProcessor.getSchemaContent({
|
|
30
|
-
responseType:
|
|
39
|
+
responseType: baseType,
|
|
31
40
|
});
|
|
41
|
+
// Build schema reference
|
|
42
|
+
if (arrayDepth === 0) {
|
|
43
|
+
// Not an array
|
|
44
|
+
schema = { $ref: `#/components/schemas/${baseType}` };
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
// Build nested array schema
|
|
48
|
+
schema = { $ref: `#/components/schemas/${baseType}` };
|
|
49
|
+
for (let i = 0; i < arrayDepth; i++) {
|
|
50
|
+
schema = {
|
|
51
|
+
type: "array",
|
|
52
|
+
items: schema,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
32
56
|
responses[successCode] = {
|
|
33
57
|
description: dataTypes.responseDescription || "Successful response",
|
|
34
58
|
content: {
|
|
35
59
|
"application/json": {
|
|
36
|
-
schema:
|
|
60
|
+
schema: schema,
|
|
37
61
|
},
|
|
38
62
|
},
|
|
39
63
|
};
|
|
@@ -825,12 +825,25 @@ export class SchemaProcessor {
|
|
|
825
825
|
};
|
|
826
826
|
}
|
|
827
827
|
getSchemaContent({ tag, paramsType, pathParamsType, bodyType, responseType, }) {
|
|
828
|
+
// Helper function to strip array notation from type names
|
|
829
|
+
const stripArrayNotation = (typeName) => {
|
|
830
|
+
if (!typeName)
|
|
831
|
+
return typeName;
|
|
832
|
+
let baseType = typeName;
|
|
833
|
+
while (baseType.endsWith('[]')) {
|
|
834
|
+
baseType = baseType.slice(0, -2);
|
|
835
|
+
}
|
|
836
|
+
return baseType;
|
|
837
|
+
};
|
|
838
|
+
// Strip array notation for schema lookups
|
|
839
|
+
const baseBodyType = stripArrayNotation(bodyType);
|
|
840
|
+
const baseResponseType = stripArrayNotation(responseType);
|
|
828
841
|
let params = paramsType ? this.openapiDefinitions[paramsType] : {};
|
|
829
842
|
let pathParams = pathParamsType
|
|
830
843
|
? this.openapiDefinitions[pathParamsType]
|
|
831
844
|
: {};
|
|
832
|
-
let body =
|
|
833
|
-
let responses =
|
|
845
|
+
let body = baseBodyType ? this.openapiDefinitions[baseBodyType] : {};
|
|
846
|
+
let responses = baseResponseType ? this.openapiDefinitions[baseResponseType] : {};
|
|
834
847
|
if (paramsType && !params) {
|
|
835
848
|
this.findSchemaDefinition(paramsType, "params");
|
|
836
849
|
params = this.openapiDefinitions[paramsType] || {};
|
|
@@ -839,20 +852,20 @@ export class SchemaProcessor {
|
|
|
839
852
|
this.findSchemaDefinition(pathParamsType, "pathParams");
|
|
840
853
|
pathParams = this.openapiDefinitions[pathParamsType] || {};
|
|
841
854
|
}
|
|
842
|
-
if (
|
|
843
|
-
this.findSchemaDefinition(
|
|
844
|
-
body = this.openapiDefinitions[
|
|
855
|
+
if (baseBodyType && !body) {
|
|
856
|
+
this.findSchemaDefinition(baseBodyType, "body");
|
|
857
|
+
body = this.openapiDefinitions[baseBodyType] || {};
|
|
845
858
|
}
|
|
846
|
-
if (
|
|
847
|
-
this.findSchemaDefinition(
|
|
848
|
-
responses = this.openapiDefinitions[
|
|
859
|
+
if (baseResponseType && !responses) {
|
|
860
|
+
this.findSchemaDefinition(baseResponseType, "response");
|
|
861
|
+
responses = this.openapiDefinitions[baseResponseType] || {};
|
|
849
862
|
}
|
|
850
863
|
if (this.schemaTypes.includes("zod")) {
|
|
851
864
|
const schemasToProcess = [
|
|
852
865
|
paramsType,
|
|
853
866
|
pathParamsType,
|
|
854
|
-
|
|
855
|
-
|
|
867
|
+
baseBodyType,
|
|
868
|
+
baseResponseType,
|
|
856
869
|
].filter(Boolean);
|
|
857
870
|
schemasToProcess.forEach((schemaName) => {
|
|
858
871
|
if (!this.openapiDefinitions[schemaName]) {
|
package/dist/lib/utils.js
CHANGED
|
@@ -159,9 +159,9 @@ export function extractJSDocComments(path) {
|
|
|
159
159
|
};
|
|
160
160
|
}
|
|
161
161
|
export function extractTypeFromComment(commentValue, tag) {
|
|
162
|
-
// Updated regex to support generic types with angle brackets
|
|
162
|
+
// Updated regex to support generic types with angle brackets and array brackets
|
|
163
163
|
return (commentValue
|
|
164
|
-
.match(new RegExp(`${tag}\\s*\\s*([\\w<>,\\s]+)`))?.[1]
|
|
164
|
+
.match(new RegExp(`${tag}\\s*\\s*([\\w<>,\\s\\[\\]]+)`))?.[1]
|
|
165
165
|
?.trim() || "");
|
|
166
166
|
}
|
|
167
167
|
export function cleanComment(commentValue) {
|
|
@@ -700,6 +700,16 @@ export class ZodSchemaConverter {
|
|
|
700
700
|
}
|
|
701
701
|
logger.debug(`[processZodNode] Could not expand factory function '${node.callee.name}' - missing context or not a factory`);
|
|
702
702
|
}
|
|
703
|
+
// Handle standalone identifier references (e.g., userSchema used directly)
|
|
704
|
+
if (t.isIdentifier(node)) {
|
|
705
|
+
const schemaName = node.name;
|
|
706
|
+
// Try to find and process the referenced schema
|
|
707
|
+
if (!this.zodSchemas[schemaName]) {
|
|
708
|
+
this.convertZodSchemaToOpenApi(schemaName);
|
|
709
|
+
}
|
|
710
|
+
// Return a reference to the schema
|
|
711
|
+
return { $ref: `#/components/schemas/${schemaName}` };
|
|
712
|
+
}
|
|
703
713
|
logger.debug("Unknown Zod schema node:", node);
|
|
704
714
|
return { type: "object" };
|
|
705
715
|
}
|
|
@@ -932,6 +942,7 @@ export class ZodSchemaConverter {
|
|
|
932
942
|
$ref: `#/components/schemas/${referencedSchemaName}`,
|
|
933
943
|
};
|
|
934
944
|
required.push(propName); // Assuming it's required unless marked optional
|
|
945
|
+
return; // Skip further processing for this property
|
|
935
946
|
}
|
|
936
947
|
// For array of schemas (like z.array(PaymentMethodSchema))
|
|
937
948
|
if (t.isCallExpression(prop.value) &&
|
|
@@ -957,6 +968,7 @@ export class ZodSchemaConverter {
|
|
|
957
968
|
if (!isOptional) {
|
|
958
969
|
required.push(propName);
|
|
959
970
|
}
|
|
971
|
+
return; // Skip further processing for this property
|
|
960
972
|
}
|
|
961
973
|
// Process property value (a Zod schema)
|
|
962
974
|
const propSchema = this.processZodNode(prop.value);
|
package/package.json
CHANGED