next-openapi-gen 0.5.1 → 0.5.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.
@@ -43,6 +43,11 @@ export class SchemaProcessor {
43
43
  // Check if we should use Zod schemas
44
44
  if (this.schemaType === "zod") {
45
45
  console.log(`Looking for Zod schema: ${schemaName}`);
46
+ // Check type mapping first
47
+ const mappedSchemaName = this.zodSchemaConverter.typeToSchemaMapping[schemaName];
48
+ if (mappedSchemaName) {
49
+ console.log(`Type '${schemaName}' is mapped to Zod schema '${mappedSchemaName}'`);
50
+ }
46
51
  // Try to convert Zod schema
47
52
  const zodSchema = this.zodSchemaConverter.convertZodSchemaToOpenApi(schemaName);
48
53
  if (zodSchema) {
@@ -11,6 +11,7 @@ export class ZodSchemaConverter {
11
11
  zodSchemas = {};
12
12
  processingSchemas = new Set();
13
13
  processedModules = new Set();
14
+ typeToSchemaMapping = {};
14
15
  constructor(schemaDir) {
15
16
  this.schemaDir = path.resolve(schemaDir);
16
17
  }
@@ -18,7 +19,17 @@ export class ZodSchemaConverter {
18
19
  * Find a Zod schema by name and convert it to OpenAPI spec
19
20
  */
20
21
  convertZodSchemaToOpenApi(schemaName) {
22
+ // Run pre-scan only one time
23
+ if (Object.keys(this.typeToSchemaMapping).length === 0) {
24
+ this.preScanForTypeMappings();
25
+ }
21
26
  console.log(`Looking for Zod schema: ${schemaName}`);
27
+ // Check mapped types
28
+ const mappedSchemaName = this.typeToSchemaMapping[schemaName];
29
+ if (mappedSchemaName) {
30
+ console.log(`Type '${schemaName}' is mapped to schema '${mappedSchemaName}'`);
31
+ schemaName = mappedSchemaName;
32
+ }
22
33
  // Check for circular references
23
34
  if (this.processingSchemas.has(schemaName)) {
24
35
  return { $ref: `#/components/schemas/${schemaName}` };
@@ -240,22 +251,51 @@ export class ZodSchemaConverter {
240
251
  },
241
252
  // For type aliases that reference Zod schemas
242
253
  TSTypeAliasDeclaration: (path) => {
243
- if (t.isIdentifier(path.node.id) &&
244
- path.node.id.name === schemaName) {
245
- // Try to find if this is a z.infer<typeof SchemaName> pattern
254
+ if (t.isIdentifier(path.node.id)) {
255
+ const typeName = path.node.id.name;
246
256
  if (t.isTSTypeReference(path.node.typeAnnotation) &&
247
- t.isIdentifier(path.node.typeAnnotation.typeName) &&
248
- path.node.typeAnnotation.typeName.name === "infer" &&
249
- path.node.typeAnnotation.typeParameters &&
250
- path.node.typeAnnotation.typeParameters.params.length > 0) {
251
- const param = path.node.typeAnnotation.typeParameters.params[0];
252
- if (t.isTSTypeQuery(param) && t.isIdentifier(param.exprName)) {
253
- const referencedSchemaName = param.exprName.name;
254
- // Find the referenced schema
255
- this.processFileForZodSchema(filePath, referencedSchemaName);
256
- if (this.zodSchemas[referencedSchemaName]) {
257
- this.zodSchemas[schemaName] =
258
- this.zodSchemas[referencedSchemaName];
257
+ t.isTSQualifiedName(path.node.typeAnnotation.typeName) &&
258
+ t.isIdentifier(path.node.typeAnnotation.typeName.left) &&
259
+ path.node.typeAnnotation.typeName.left.name === "z" &&
260
+ t.isIdentifier(path.node.typeAnnotation.typeName.right) &&
261
+ path.node.typeAnnotation.typeName.right.name === "infer") {
262
+ // Extract schema name from z.infer<typeof SchemaName>
263
+ if (path.node.typeAnnotation.typeParameters &&
264
+ path.node.typeAnnotation.typeParameters.params.length > 0) {
265
+ const param = path.node.typeAnnotation.typeParameters.params[0];
266
+ if (t.isTSTypeQuery(param) && t.isIdentifier(param.exprName)) {
267
+ const referencedSchemaName = param.exprName.name;
268
+ // Save mapping: TypeName -> SchemaName
269
+ this.typeToSchemaMapping[typeName] = referencedSchemaName;
270
+ console.log(`Mapped type '${typeName}' to schema '${referencedSchemaName}'`);
271
+ // Process the referenced schema if not already processed
272
+ if (!this.zodSchemas[referencedSchemaName]) {
273
+ this.processFileForZodSchema(filePath, referencedSchemaName);
274
+ }
275
+ // Use the referenced schema for this type
276
+ if (this.zodSchemas[referencedSchemaName]) {
277
+ this.zodSchemas[typeName] =
278
+ this.zodSchemas[referencedSchemaName];
279
+ }
280
+ }
281
+ }
282
+ }
283
+ if (path.node.id.name === schemaName) {
284
+ // Try to find if this is a z.infer<typeof SchemaName> pattern
285
+ if (t.isTSTypeReference(path.node.typeAnnotation) &&
286
+ t.isIdentifier(path.node.typeAnnotation.typeName) &&
287
+ path.node.typeAnnotation.typeName.name === "infer" &&
288
+ path.node.typeAnnotation.typeParameters &&
289
+ path.node.typeAnnotation.typeParameters.params.length > 0) {
290
+ const param = path.node.typeAnnotation.typeParameters.params[0];
291
+ if (t.isTSTypeQuery(param) && t.isIdentifier(param.exprName)) {
292
+ const referencedSchemaName = param.exprName.name;
293
+ // Find the referenced schema
294
+ this.processFileForZodSchema(filePath, referencedSchemaName);
295
+ if (this.zodSchemas[referencedSchemaName]) {
296
+ this.zodSchemas[schemaName] =
297
+ this.zodSchemas[referencedSchemaName];
298
+ }
259
299
  }
260
300
  }
261
301
  }
@@ -1058,4 +1098,87 @@ export class ZodSchemaConverter {
1058
1098
  getProcessedSchemas() {
1059
1099
  return this.zodSchemas;
1060
1100
  }
1101
+ /**
1102
+ * Pre-scan all files to build type mappings
1103
+ */
1104
+ preScanForTypeMappings() {
1105
+ console.log("Pre-scanning for type mappings...");
1106
+ // Scan route files
1107
+ const routeFiles = this.findRouteFiles();
1108
+ for (const routeFile of routeFiles) {
1109
+ this.scanFileForTypeMappings(routeFile);
1110
+ }
1111
+ // Scan schema directory
1112
+ this.scanDirectoryForTypeMappings(this.schemaDir);
1113
+ }
1114
+ /**
1115
+ * Scan a single file for type mappings
1116
+ */
1117
+ scanFileForTypeMappings(filePath) {
1118
+ try {
1119
+ const content = fs.readFileSync(filePath, "utf-8");
1120
+ const ast = parse(content, {
1121
+ sourceType: "module",
1122
+ plugins: ["typescript", "decorators-legacy"],
1123
+ });
1124
+ traverse.default(ast, {
1125
+ TSTypeAliasDeclaration: (path) => {
1126
+ if (t.isIdentifier(path.node.id)) {
1127
+ const typeName = path.node.id.name;
1128
+ // Check for z.infer<typeof SchemaName> pattern
1129
+ if (t.isTSTypeReference(path.node.typeAnnotation)) {
1130
+ const typeRef = path.node.typeAnnotation;
1131
+ // Handle both z.infer and just infer (when z is imported)
1132
+ let isInferType = false;
1133
+ if (t.isTSQualifiedName(typeRef.typeName) &&
1134
+ t.isIdentifier(typeRef.typeName.left) &&
1135
+ typeRef.typeName.left.name === "z" &&
1136
+ t.isIdentifier(typeRef.typeName.right) &&
1137
+ typeRef.typeName.right.name === "infer") {
1138
+ isInferType = true;
1139
+ }
1140
+ else if (t.isIdentifier(typeRef.typeName) &&
1141
+ typeRef.typeName.name === "infer") {
1142
+ isInferType = true;
1143
+ }
1144
+ if (isInferType &&
1145
+ typeRef.typeParameters &&
1146
+ typeRef.typeParameters.params.length > 0) {
1147
+ const param = typeRef.typeParameters.params[0];
1148
+ if (t.isTSTypeQuery(param) && t.isIdentifier(param.exprName)) {
1149
+ const referencedSchemaName = param.exprName.name;
1150
+ this.typeToSchemaMapping[typeName] = referencedSchemaName;
1151
+ console.log(`Pre-scan: Mapped type '${typeName}' to schema '${referencedSchemaName}'`);
1152
+ }
1153
+ }
1154
+ }
1155
+ }
1156
+ },
1157
+ });
1158
+ }
1159
+ catch (error) {
1160
+ console.error(`Error scanning file ${filePath} for type mappings:`, error);
1161
+ }
1162
+ }
1163
+ /**
1164
+ * Recursively scan directory for type mappings
1165
+ */
1166
+ scanDirectoryForTypeMappings(dir) {
1167
+ try {
1168
+ const files = fs.readdirSync(dir);
1169
+ for (const file of files) {
1170
+ const filePath = path.join(dir, file);
1171
+ const stats = fs.statSync(filePath);
1172
+ if (stats.isDirectory()) {
1173
+ this.scanDirectoryForTypeMappings(filePath);
1174
+ }
1175
+ else if (file.endsWith(".ts") || file.endsWith(".tsx")) {
1176
+ this.scanFileForTypeMappings(filePath);
1177
+ }
1178
+ }
1179
+ }
1180
+ catch (error) {
1181
+ console.error(`Error scanning directory ${dir} for type mappings:`, error);
1182
+ }
1183
+ }
1061
1184
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-openapi-gen",
3
- "version": "0.5.1",
3
+ "version": "0.5.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",