next-openapi-gen 0.6.4 → 0.6.6

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
@@ -61,7 +61,8 @@ During initialization (`npx next-openapi-gen init`), a configuration file `next.
61
61
  "schemaType": "typescript", // or "zod" for Zod schemas
62
62
  "outputFile": "openapi.json",
63
63
  "docsUrl": "/api-docs",
64
- "includeOpenApiRoutes": false
64
+ "includeOpenApiRoutes": false,
65
+ "debug": false
65
66
  }
66
67
  ```
67
68
 
@@ -78,6 +79,7 @@ During initialization (`npx next-openapi-gen init`), a configuration file `next.
78
79
  | `defaultResponseSet` | Default error response set for all endpoints |
79
80
  | `responseSets` | Named sets of error response codes |
80
81
  | `errorConfig` | Error schema configuration |
82
+ | `debug` | Enable detailed logging during generation |
81
83
 
82
84
  ## Documenting Your API
83
85
 
@@ -0,0 +1,39 @@
1
+ class Logger {
2
+ config = null;
3
+ init(config) {
4
+ this.config = config;
5
+ }
6
+ getCallerInfo() {
7
+ const stack = new Error().stack;
8
+ if (!stack)
9
+ return 'Unknown';
10
+ const lines = stack.split('\n');
11
+ // Skip: Error, getCallerInfo, log/warn/error
12
+ const callerLine = lines[3] || lines[2];
13
+ // Extract class/function name
14
+ const match = callerLine.match(/at (\w+)\.(\w+)|at (\w+)/);
15
+ if (match) {
16
+ return match[1] || match[3] || 'Unknown';
17
+ }
18
+ return 'Unknown';
19
+ }
20
+ log(message, ...args) {
21
+ const source = this.getCallerInfo();
22
+ console.log(`[${source}] ${message}`, ...args);
23
+ }
24
+ warn(message, ...args) {
25
+ const source = this.getCallerInfo();
26
+ console.warn(`[${source}] ${message}`, ...args);
27
+ }
28
+ error(message, ...args) {
29
+ const source = this.getCallerInfo();
30
+ console.error(`[${source}] ${message}`, ...args);
31
+ }
32
+ debug(message, ...args) {
33
+ if (this.config?.debug) {
34
+ const source = this.getCallerInfo();
35
+ console.log(`[${source}] ${message}`, ...args);
36
+ }
37
+ }
38
+ }
39
+ export const logger = new Logger();
@@ -2,6 +2,7 @@ import path from "path";
2
2
  import fs from "fs";
3
3
  import { RouteProcessor } from "./route-processor.js";
4
4
  import { cleanSpec } from "./utils.js";
5
+ import { logger } from "./logger.js";
5
6
  export class OpenApiGenerator {
6
7
  config;
7
8
  template;
@@ -11,10 +12,12 @@ export class OpenApiGenerator {
11
12
  this.template = JSON.parse(fs.readFileSync(templatePath, "utf-8"));
12
13
  this.config = this.getConfig();
13
14
  this.routeProcessor = new RouteProcessor(this.config);
15
+ // Initialize logger
16
+ logger.init(this.config);
14
17
  }
15
18
  getConfig() {
16
19
  // @ts-ignore
17
- const { apiDir, schemaDir, docsUrl, ui, outputFile, includeOpenApiRoutes, schemaType = "typescript", defaultResponseSet, responseSets, errorConfig } = this.template;
20
+ const { apiDir, schemaDir, docsUrl, ui, outputFile, includeOpenApiRoutes, schemaType = "typescript", defaultResponseSet, responseSets, errorConfig, debug } = this.template;
18
21
  return {
19
22
  apiDir,
20
23
  schemaDir,
@@ -26,15 +29,17 @@ export class OpenApiGenerator {
26
29
  defaultResponseSet,
27
30
  responseSets,
28
31
  errorConfig,
32
+ debug,
29
33
  };
30
34
  }
31
35
  generate() {
36
+ logger.log("Starting OpenAPI generation...");
32
37
  const { apiDir } = this.config;
33
38
  // Check if app router structure exists
34
39
  let appRouterApiDir = "";
35
40
  if (fs.existsSync(path.join(path.dirname(apiDir), "app", "api"))) {
36
41
  appRouterApiDir = path.join(path.dirname(apiDir), "app", "api");
37
- console.log(`Found app router API directory at ${appRouterApiDir}`);
42
+ logger.debug(`Found app router API directory at ${appRouterApiDir}`);
38
43
  }
39
44
  // Scan pages router routes
40
45
  this.routeProcessor.scanApiRoutes(apiDir);
@@ -86,6 +91,7 @@ export class OpenApiGenerator {
86
91
  };
87
92
  }
88
93
  const openapiSpec = cleanSpec(this.template);
94
+ logger.log("OpenAPI generation completed");
89
95
  return openapiSpec;
90
96
  }
91
97
  generateErrorResponsesFromConfig(errorConfig) {
@@ -5,6 +5,7 @@ import traverse from "@babel/traverse";
5
5
  import { parse } from "@babel/parser";
6
6
  import { SchemaProcessor } from "./schema-processor.js";
7
7
  import { capitalize, extractJSDocComments, extractPathParameters, getOperationId, } from "./utils.js";
8
+ import { logger } from "./logger.js";
8
9
  const HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
9
10
  const MUTATION_HTTP_METHODS = ["PATCH", "POST", "PUT"];
10
11
  export class RouteProcessor {
@@ -136,7 +137,7 @@ export class RouteProcessor {
136
137
  const pathParams = extractPathParameters(routePath);
137
138
  // If we have path parameters but no pathParamsType defined, we should log a warning
138
139
  if (pathParams.length > 0 && !dataTypes.pathParamsType) {
139
- console.warn(`Route ${routePath} contains path parameters ${pathParams.join(", ")} but no @pathParams type is defined.`);
140
+ logger.debug(`Route ${routePath} contains path parameters ${pathParams.join(", ")} but no @pathParams type is defined.`);
140
141
  }
141
142
  this.addRouteToPaths(declaration.id.name, filePath, dataTypes);
142
143
  }
@@ -153,7 +154,7 @@ export class RouteProcessor {
153
154
  const routePath = this.getRoutePath(filePath);
154
155
  const pathParams = extractPathParameters(routePath);
155
156
  if (pathParams.length > 0 && !dataTypes.pathParamsType) {
156
- console.warn(`Route ${routePath} contains path parameters ${pathParams.join(", ")} but no @pathParams type is defined.`);
157
+ logger.debug(`Route ${routePath} contains path parameters ${pathParams.join(", ")} but no @pathParams type is defined.`);
157
158
  }
158
159
  this.addRouteToPaths(decl.id.name, filePath, dataTypes);
159
160
  }
@@ -166,6 +167,7 @@ export class RouteProcessor {
166
167
  this.processFileTracker[filePath] = true;
167
168
  }
168
169
  scanApiRoutes(dir) {
170
+ logger.debug(`Scanning API routes in: ${dir}`);
169
171
  let files = this.directoryCache[dir];
170
172
  if (!files) {
171
173
  files = fs.readdirSync(dir);
@@ -4,6 +4,7 @@ import { parse } from "@babel/parser";
4
4
  import traverse from "@babel/traverse";
5
5
  import * as t from "@babel/types";
6
6
  import { ZodSchemaConverter } from "./zod-converter.js";
7
+ import { logger } from "./logger.js";
7
8
  export class SchemaProcessor {
8
9
  schemaDir;
9
10
  typeDefinitions = {};
@@ -42,20 +43,20 @@ export class SchemaProcessor {
42
43
  this.contentType = contentType;
43
44
  // Check if we should use Zod schemas
44
45
  if (this.schemaType === "zod") {
45
- console.log(`Looking for Zod schema: ${schemaName}`);
46
+ logger.debug(`Looking for Zod schema: ${schemaName}`);
46
47
  // Check type mapping first
47
48
  const mappedSchemaName = this.zodSchemaConverter.typeToSchemaMapping[schemaName];
48
49
  if (mappedSchemaName) {
49
- console.log(`Type '${schemaName}' is mapped to Zod schema '${mappedSchemaName}'`);
50
+ logger.debug(`Type '${schemaName}' is mapped to Zod schema '${mappedSchemaName}'`);
50
51
  }
51
52
  // Try to convert Zod schema
52
53
  const zodSchema = this.zodSchemaConverter.convertZodSchemaToOpenApi(schemaName);
53
54
  if (zodSchema) {
54
- console.log(`Found and processed Zod schema: ${schemaName}`);
55
+ logger.debug(`Found and processed Zod schema: ${schemaName}`);
55
56
  this.openapiDefinitions[schemaName] = zodSchema;
56
57
  return zodSchema;
57
58
  }
58
- console.log(`No Zod schema found for ${schemaName}, trying TypeScript fallback`);
59
+ logger.debug(`No Zod schema found for ${schemaName}, trying TypeScript fallback`);
59
60
  }
60
61
  // Fall back to TypeScript types
61
62
  this.scanSchemaDir(this.schemaDir, schemaName);
@@ -394,7 +395,7 @@ export class SchemaProcessor {
394
395
  if (t.isTSTypeReference(node) && t.isIdentifier(node.typeName)) {
395
396
  return { $ref: `#/components/schemas/${node.typeName.name}` };
396
397
  }
397
- console.warn("Unrecognized TypeScript type node:", node);
398
+ logger.debug("Unrecognized TypeScript type node:", node);
398
399
  return { type: "object" }; // By default we return an object
399
400
  }
400
401
  processSchemaFile(filePath, schemaName) {
@@ -417,7 +418,7 @@ export class SchemaProcessor {
417
418
  return definition;
418
419
  }
419
420
  catch (error) {
420
- console.error(`Error processing schema file ${filePath} for schema ${schemaName}:`, error);
421
+ logger.error(`Error processing schema file ${filePath} for schema ${schemaName}: ${error}`);
421
422
  return { type: "object" }; // By default we return an empty object on error
422
423
  }
423
424
  }
package/dist/lib/utils.js CHANGED
@@ -165,6 +165,7 @@ export function cleanSpec(spec) {
165
165
  "defaultResponseSet",
166
166
  "responseSets",
167
167
  "errorConfig",
168
+ "debug",
168
169
  ];
169
170
  const newSpec = { ...spec };
170
171
  propsToRemove.forEach((key) => delete newSpec[key]);
@@ -3,6 +3,7 @@ import path from "path";
3
3
  import { parse } from "@babel/parser";
4
4
  import traverse from "@babel/traverse";
5
5
  import * as t from "@babel/types";
6
+ import { logger } from "./logger.js";
6
7
  /**
7
8
  * Class for converting Zod schemas to OpenAPI specifications
8
9
  */
@@ -23,11 +24,11 @@ export class ZodSchemaConverter {
23
24
  if (Object.keys(this.typeToSchemaMapping).length === 0) {
24
25
  this.preScanForTypeMappings();
25
26
  }
26
- console.log(`Looking for Zod schema: ${schemaName}`);
27
+ logger.debug(`Looking for Zod schema: ${schemaName}`);
27
28
  // Check mapped types
28
29
  const mappedSchemaName = this.typeToSchemaMapping[schemaName];
29
30
  if (mappedSchemaName) {
30
- console.log(`Type '${schemaName}' is mapped to schema '${mappedSchemaName}'`);
31
+ logger.debug(`Type '${schemaName}' is mapped to schema '${mappedSchemaName}'`);
31
32
  schemaName = mappedSchemaName;
32
33
  }
33
34
  // Check for circular references
@@ -46,7 +47,7 @@ export class ZodSchemaConverter {
46
47
  for (const routeFile of routeFiles) {
47
48
  this.processFileForZodSchema(routeFile, schemaName);
48
49
  if (this.zodSchemas[schemaName]) {
49
- console.log(`Found Zod schema '${schemaName}' in route file: ${routeFile}`);
50
+ logger.debug(`Found Zod schema '${schemaName}' in route file: ${routeFile}`);
50
51
  return this.zodSchemas[schemaName];
51
52
  }
52
53
  }
@@ -54,10 +55,10 @@ export class ZodSchemaConverter {
54
55
  this.scanDirectoryForZodSchema(this.schemaDir, schemaName);
55
56
  // Return the schema if found, or null if not
56
57
  if (this.zodSchemas[schemaName]) {
57
- console.log(`Found and processed Zod schema: ${schemaName}`);
58
+ logger.debug(`Found and processed Zod schema: ${schemaName}`);
58
59
  return this.zodSchemas[schemaName];
59
60
  }
60
- console.log(`Could not find Zod schema: ${schemaName}`);
61
+ logger.debug(`Could not find Zod schema: ${schemaName}`);
61
62
  return null;
62
63
  }
63
64
  finally {
@@ -104,7 +105,7 @@ export class ZodSchemaConverter {
104
105
  }
105
106
  }
106
107
  catch (error) {
107
- console.error(`Error scanning directory ${dir} for route files:`, error);
108
+ logger.error(`Error scanning directory ${dir} for route files: ${error}`);
108
109
  }
109
110
  }
110
111
  /**
@@ -125,7 +126,7 @@ export class ZodSchemaConverter {
125
126
  }
126
127
  }
127
128
  catch (error) {
128
- console.error(`Error scanning directory ${dir}:`, error);
129
+ logger.error(`Error scanning directory ${dir}: ${error}`);
129
130
  }
130
131
  }
131
132
  /**
@@ -272,7 +273,7 @@ export class ZodSchemaConverter {
272
273
  ? prop.key.value
273
274
  : null;
274
275
  if (key && schema.properties) {
275
- console.log(`Removing property: ${key}`);
276
+ logger.debug(`Removing property: ${key}`);
276
277
  delete schema.properties[key];
277
278
  if (schema.required) {
278
279
  schema.required = schema.required.filter((r) => r !== key);
@@ -357,20 +358,20 @@ export class ZodSchemaConverter {
357
358
  if (t.isCallExpression(path.node.init)) {
358
359
  const baseSchemaName = findBaseSchema(path.node.init);
359
360
  if (baseSchemaName && baseSchemaName !== "z") {
360
- console.log(`Found chained call starting from: ${baseSchemaName}`);
361
+ logger.debug(`Found chained call starting from: ${baseSchemaName}`);
361
362
  // First make sure the underlying schema is processed
362
363
  if (!this.zodSchemas[baseSchemaName]) {
363
- console.log(`Base schema ${baseSchemaName} not found, processing it first`);
364
+ logger.debug(`Base schema ${baseSchemaName} not found, processing it first`);
364
365
  this.processFileForZodSchema(filePath, baseSchemaName);
365
366
  }
366
367
  if (this.zodSchemas[baseSchemaName]) {
367
- console.log(`Base schema found, applying transformations`);
368
+ logger.debug("Base schema found, applying transformations");
368
369
  // Copy base schema
369
370
  const baseSchema = JSON.parse(JSON.stringify(this.zodSchemas[baseSchemaName]));
370
371
  // Process the entire call chain
371
372
  const finalSchema = processChainedCall(path.node.init, baseSchema);
372
373
  this.zodSchemas[schemaName] = finalSchema;
373
- console.log(`Created ${schemaName} with properties:`, Object.keys(finalSchema.properties || {}));
374
+ logger.debug(`Created ${schemaName} with properties: ${Object.keys(finalSchema.properties || {})}`);
374
375
  return;
375
376
  }
376
377
  }
@@ -412,7 +413,7 @@ export class ZodSchemaConverter {
412
413
  const referencedSchemaName = param.exprName.name;
413
414
  // Save mapping: TypeName -> SchemaName
414
415
  this.typeToSchemaMapping[typeName] = referencedSchemaName;
415
- console.log(`Mapped type '${typeName}' to schema '${referencedSchemaName}'`);
416
+ logger.debug(`Mapped type '${typeName}' to schema '${referencedSchemaName}'`);
416
417
  // Process the referenced schema if not already processed
417
418
  if (!this.zodSchemas[referencedSchemaName]) {
418
419
  this.processFileForZodSchema(filePath, referencedSchemaName);
@@ -449,7 +450,7 @@ export class ZodSchemaConverter {
449
450
  });
450
451
  }
451
452
  catch (error) {
452
- console.error(`Error processing file ${filePath} for schema ${schemaName}: ${error}`);
453
+ logger.error(`Error processing file ${filePath} for schema ${schemaName}: ${error}`);
453
454
  }
454
455
  }
455
456
  /**
@@ -489,7 +490,7 @@ export class ZodSchemaConverter {
489
490
  });
490
491
  }
491
492
  catch (error) {
492
- console.error(`Error processing all schemas in file ${filePath}: ${error}`);
493
+ logger.error(`Error processing all schemas in file ${filePath}: ${error}`);
493
494
  }
494
495
  }
495
496
  /**
@@ -556,7 +557,7 @@ export class ZodSchemaConverter {
556
557
  node.arguments.length > 0) {
557
558
  return this.processZodLazy(node);
558
559
  }
559
- console.warn("Unknown Zod schema node:", node);
560
+ logger.debug("Unknown Zod schema node:", node);
560
561
  return { type: "object" };
561
562
  }
562
563
  /**
@@ -735,7 +736,7 @@ export class ZodSchemaConverter {
735
736
  propName = prop.key.value;
736
737
  }
737
738
  else {
738
- console.log(`Skipping property ${index} - unsupported key type`);
739
+ logger.debug(`Skipping property ${index} - unsupported key type`);
739
740
  return; // Skip if key is not identifier or string literal
740
741
  }
741
742
  if (t.isCallExpression(prop.value) &&
@@ -1222,7 +1223,7 @@ export class ZodSchemaConverter {
1222
1223
  schema.description = baseSchema.description;
1223
1224
  }
1224
1225
  else {
1225
- console.log(`Warning: Could not resolve base schema for extend`);
1226
+ logger.debug("Could not resolve base schema for extend");
1226
1227
  schema = extendedProps || { type: "object" };
1227
1228
  }
1228
1229
  }
@@ -1313,7 +1314,7 @@ export class ZodSchemaConverter {
1313
1314
  * Pre-scan all files to build type mappings
1314
1315
  */
1315
1316
  preScanForTypeMappings() {
1316
- console.log("Pre-scanning for type mappings...");
1317
+ logger.debug("Pre-scanning for type mappings...");
1317
1318
  // Scan route files
1318
1319
  const routeFiles = this.findRouteFiles();
1319
1320
  for (const routeFile of routeFiles) {
@@ -1359,7 +1360,7 @@ export class ZodSchemaConverter {
1359
1360
  if (t.isTSTypeQuery(param) && t.isIdentifier(param.exprName)) {
1360
1361
  const referencedSchemaName = param.exprName.name;
1361
1362
  this.typeToSchemaMapping[typeName] = referencedSchemaName;
1362
- console.log(`Pre-scan: Mapped type '${typeName}' to schema '${referencedSchemaName}'`);
1363
+ logger.debug(`Pre-scan: Mapped type '${typeName}' to schema '${referencedSchemaName}'`);
1363
1364
  }
1364
1365
  }
1365
1366
  }
@@ -1368,7 +1369,7 @@ export class ZodSchemaConverter {
1368
1369
  });
1369
1370
  }
1370
1371
  catch (error) {
1371
- console.error(`Error scanning file ${filePath} for type mappings:`, error);
1372
+ logger.error(`Error scanning file ${filePath} for type mappings: ${error}`);
1372
1373
  }
1373
1374
  }
1374
1375
  /**
@@ -1389,7 +1390,7 @@ export class ZodSchemaConverter {
1389
1390
  }
1390
1391
  }
1391
1392
  catch (error) {
1392
- console.error(`Error scanning directory ${dir} for type mappings:`, error);
1393
+ logger.error(`Error scanning directory ${dir} for type mappings: ${error}`);
1393
1394
  }
1394
1395
  }
1395
1396
  /**
@@ -1412,7 +1413,7 @@ export class ZodSchemaConverter {
1412
1413
  // Check if is Zos schema
1413
1414
  if (this.isZodSchema(declaration.init) &&
1414
1415
  !this.zodSchemas[schemaName]) {
1415
- console.log(`Pre-processing Zod schema: ${schemaName}`);
1416
+ logger.debug(`Pre-processing Zod schema: ${schemaName}`);
1416
1417
  this.processingSchemas.add(schemaName);
1417
1418
  const schema = this.processZodNode(declaration.init);
1418
1419
  if (schema) {
@@ -1432,7 +1433,7 @@ export class ZodSchemaConverter {
1432
1433
  if (this.isZodSchema(declaration.init) &&
1433
1434
  !this.zodSchemas[schemaName] &&
1434
1435
  !this.processingSchemas.has(schemaName)) {
1435
- console.log(`Pre-processing Zod schema: ${schemaName}`);
1436
+ logger.debug(`Pre-processing Zod schema: ${schemaName}`);
1436
1437
  this.processingSchemas.add(schemaName);
1437
1438
  const schema = this.processZodNode(declaration.init);
1438
1439
  if (schema) {
@@ -1446,7 +1447,7 @@ export class ZodSchemaConverter {
1446
1447
  });
1447
1448
  }
1448
1449
  catch (error) {
1449
- console.error(`Error pre-processing file ${filePath}:`, error);
1450
+ logger.error(`Error pre-processing file ${filePath}: ${error}`);
1450
1451
  }
1451
1452
  }
1452
1453
  /**
@@ -70,4 +70,5 @@ export default {
70
70
  ui: "scalar",
71
71
  outputFile: "openapi.json",
72
72
  includeOpenApiRoutes: false,
73
+ debug: false,
73
74
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-openapi-gen",
3
- "version": "0.6.4",
3
+ "version": "0.6.6",
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",