next-openapi-gen 1.2.1 → 1.2.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.
@@ -756,6 +756,17 @@ function mergeJSDocData(target, source) {
756
756
  function cleanComment(commentValue) {
757
757
  return commentValue.replace(/\*\s*/g, "").trim();
758
758
  }
759
+ function extractSchemaIdFromComments(comments) {
760
+ if (!comments)
761
+ return null;
762
+ for (const comment of comments) {
763
+ const cleaned = cleanComment(comment.value);
764
+ const id = extractTokenValue(cleaned, "@id");
765
+ if (id)
766
+ return id;
767
+ }
768
+ return null;
769
+ }
759
770
  function extractLineValue(commentValue, tag) {
760
771
  return commentValue.match(new RegExp(`${escapeRegExp(tag)}\\s*(.*)`, "m"))?.[1]?.trim() || "";
761
772
  }
@@ -3172,7 +3183,8 @@ var DrizzleZodProcessor = class _DrizzleZodProcessor {
3172
3183
  if (firstArg && !t4.isSpreadElement(firstArg) && !t4.isArgumentPlaceholder(firstArg)) {
3173
3184
  const metadata = _DrizzleZodProcessor.extractStaticObject(firstArg);
3174
3185
  if (metadata) {
3175
- Object.assign(result, metadata);
3186
+ const { id: _id, ...rest } = metadata;
3187
+ Object.assign(result, rest);
3176
3188
  }
3177
3189
  }
3178
3190
  break;
@@ -11456,8 +11468,11 @@ var ZodRuntimeExporter = class {
11456
11468
  case "deprecated":
11457
11469
  return schema.meta({ deprecated: true });
11458
11470
  case "meta": {
11459
- const metadata = node.arguments[0] ? this.buildMetadataObject(node.arguments[0]) : null;
11460
- return metadata ? schema.meta(metadata) : schema;
11471
+ const rawMetadata = node.arguments[0] ? this.buildMetadataObject(node.arguments[0]) : null;
11472
+ if (!rawMetadata)
11473
+ return schema;
11474
+ const { id: _id, ...metadata } = rawMetadata;
11475
+ return Object.keys(metadata).length > 0 ? schema.meta(metadata) : schema;
11461
11476
  }
11462
11477
  case "default":
11463
11478
  case "prefault":
@@ -12247,6 +12262,8 @@ var ZodSchemaConverter = class {
12247
12262
  try {
12248
12263
  const content = this.fileAccess.readFileSync(filePath, "utf-8");
12249
12264
  const ast = parseTypeScriptFile(content);
12265
+ this.currentFilePath = filePath;
12266
+ this.currentAST = ast;
12250
12267
  resolvedTraverse(ast, {
12251
12268
  ExportNamedDeclaration: (path17) => {
12252
12269
  if (t10.isVariableDeclaration(path17.node.declaration)) {
@@ -12261,6 +12278,17 @@ var ZodSchemaConverter = class {
12261
12278
  }
12262
12279
  this.processingSchemas.delete(schemaName);
12263
12280
  }
12281
+ } else if (t10.isIdentifier(declaration.id) && declaration.init) {
12282
+ const schemaName = declaration.id.name;
12283
+ const overrideId = this.extractMetaIdFromNode(declaration.init);
12284
+ if (overrideId && !this.getStoredSchema(schemaName) && !this.processingSchemas.has(schemaName)) {
12285
+ this.processingSchemas.add(schemaName);
12286
+ const schema = this.processZodNode(declaration.init);
12287
+ this.processingSchemas.delete(schemaName);
12288
+ if (schema) {
12289
+ this.applyMetaIdOverride(schemaName, schema, overrideId, filePath);
12290
+ }
12291
+ }
12264
12292
  }
12265
12293
  });
12266
12294
  }
@@ -12814,6 +12842,24 @@ var ZodSchemaConverter = class {
12814
12842
  }
12815
12843
  return void 0;
12816
12844
  }
12845
+ extractMetaIdFromNode(node) {
12846
+ if (!t10.isCallExpression(node))
12847
+ return null;
12848
+ if (t10.isMemberExpression(node.callee) && t10.isIdentifier(node.callee.property)) {
12849
+ if (node.callee.property.name === "meta" && node.arguments.length > 0) {
12850
+ const metadata = this.extractStaticJsonValue(node.arguments[0]);
12851
+ if (metadata && typeof metadata === "object" && !Array.isArray(metadata)) {
12852
+ const id = metadata.id;
12853
+ if (typeof id === "string" && id.length > 0)
12854
+ return id;
12855
+ }
12856
+ }
12857
+ if (t10.isCallExpression(node.callee.object)) {
12858
+ return this.extractMetaIdFromNode(node.callee.object);
12859
+ }
12860
+ }
12861
+ return null;
12862
+ }
12817
12863
  shouldUseRuntimeExport(node) {
12818
12864
  if (!t10.isCallExpression(node)) {
12819
12865
  return false;
@@ -13054,7 +13100,8 @@ var ZodSchemaConverter = class {
13054
13100
  if (node.arguments.length > 0) {
13055
13101
  const metadata = this.extractStaticJsonValue(node.arguments[0]);
13056
13102
  if (metadata && typeof metadata === "object" && !Array.isArray(metadata)) {
13057
- Object.assign(schema, metadata);
13103
+ const { id: _id, ...rest } = metadata;
13104
+ Object.assign(schema, rest);
13058
13105
  }
13059
13106
  }
13060
13107
  break;
@@ -13340,6 +13387,7 @@ var ZodSchemaConverter = class {
13340
13387
  this.currentFilePath = filePath;
13341
13388
  this.currentAST = ast;
13342
13389
  this.currentImports = importedModules;
13390
+ this.preprocessedFiles.add(filePath);
13343
13391
  resolvedTraverse(ast, {
13344
13392
  ExportNamedDeclaration: (path17) => {
13345
13393
  if (t10.isVariableDeclaration(path17.node.declaration)) {
@@ -13347,15 +13395,19 @@ var ZodSchemaConverter = class {
13347
13395
  if (t10.isIdentifier(declaration.id) && declaration.init) {
13348
13396
  const schemaName = declaration.id.name;
13349
13397
  if (this.isZodSchema(declaration.init)) {
13350
- this.indexSchemaName(schemaName, filePath);
13351
13398
  if (!this.getStoredSchema(schemaName)) {
13352
13399
  logger.debug(`Pre-processing Zod schema: ${schemaName}`);
13353
13400
  this.processingSchemas.add(schemaName);
13354
13401
  const schema = this.processZodNode(declaration.init);
13402
+ this.processingSchemas.delete(schemaName);
13355
13403
  if (schema) {
13356
- this.storeResolvedSchema(schemaName, schema);
13404
+ const overrideId = this.extractMetaIdFromNode(declaration.init);
13405
+ this.applyMetaIdOverride(schemaName, schema, overrideId, filePath);
13406
+ } else {
13407
+ this.indexSchemaName(schemaName, filePath);
13357
13408
  }
13358
- this.processingSchemas.delete(schemaName);
13409
+ } else {
13410
+ this.indexSchemaName(schemaName, filePath);
13359
13411
  }
13360
13412
  }
13361
13413
  }
@@ -13368,22 +13420,25 @@ var ZodSchemaConverter = class {
13368
13420
  if (t10.isIdentifier(declaration.id) && declaration.init) {
13369
13421
  const schemaName = declaration.id.name;
13370
13422
  if (this.isZodSchema(declaration.init)) {
13371
- this.indexSchemaName(schemaName, filePath);
13372
13423
  if (!this.getStoredSchema(schemaName) && !this.processingSchemas.has(schemaName)) {
13373
13424
  logger.debug(`Pre-processing Zod schema: ${schemaName}`);
13374
13425
  this.processingSchemas.add(schemaName);
13375
13426
  const schema = this.processZodNode(declaration.init);
13427
+ this.processingSchemas.delete(schemaName);
13376
13428
  if (schema) {
13377
- this.storeResolvedSchema(schemaName, schema);
13429
+ const overrideId = this.extractMetaIdFromNode(declaration.init);
13430
+ this.applyMetaIdOverride(schemaName, schema, overrideId, filePath);
13431
+ } else {
13432
+ this.indexSchemaName(schemaName, filePath);
13378
13433
  }
13379
- this.processingSchemas.delete(schemaName);
13434
+ } else {
13435
+ this.indexSchemaName(schemaName, filePath);
13380
13436
  }
13381
13437
  }
13382
13438
  }
13383
13439
  });
13384
13440
  }
13385
13441
  });
13386
- this.preprocessedFiles.add(filePath);
13387
13442
  } catch (error2) {
13388
13443
  logger.error(`Error pre-processing file ${filePath}: ${error2}`);
13389
13444
  }
@@ -13401,6 +13456,21 @@ var ZodSchemaConverter = class {
13401
13456
  }
13402
13457
  bucket.add(filePath);
13403
13458
  }
13459
+ applyMetaIdOverride(schemaName, schema, overrideId, filePath) {
13460
+ const finalName = overrideId && overrideId !== schemaName ? overrideId : schemaName;
13461
+ this.indexSchemaName(schemaName, filePath);
13462
+ if (finalName !== schemaName) {
13463
+ this.indexSchemaName(finalName, filePath);
13464
+ }
13465
+ if (!this.getStoredSchema(finalName)) {
13466
+ if (overrideId && overrideId !== schemaName) {
13467
+ this.typeToSchemaMapping[schemaName] = overrideId;
13468
+ }
13469
+ this.storeResolvedSchema(finalName, schema);
13470
+ } else {
13471
+ logger.warn(`Schema component name '${overrideId ?? finalName}' conflicts with an existing schema, ignoring .meta({ id }) on '${schemaName}'`);
13472
+ }
13473
+ }
13404
13474
  /**
13405
13475
  * Check if node is Zod schema
13406
13476
  */
@@ -13632,7 +13702,7 @@ function parsePropertyComment(commentValue) {
13632
13702
  function getPropertyOptions(node, contentType) {
13633
13703
  const isOptional = !!node.optional;
13634
13704
  const options = {};
13635
- const leadingComment = node.leadingComments?.[node.leadingComments.length - 1];
13705
+ const leadingComment = node.leadingComments?.findLast((c) => c.type === "CommentBlock" || !node.trailingComments?.length);
13636
13706
  const trailingComment = node.trailingComments?.[0];
13637
13707
  const sourceComment = leadingComment ?? trailingComment;
13638
13708
  if (sourceComment) {
@@ -14035,31 +14105,60 @@ function resolveImportPath(importPath, fromFilePath, fileAccess) {
14035
14105
  }
14036
14106
  return null;
14037
14107
  }
14038
- function collectAllExportedDefinitions(ast, typeDefinitions, currentFile) {
14108
+ function collectFirstMemberLeadingComments(interfaceDecl) {
14109
+ const body = interfaceDecl?.body;
14110
+ if (!body)
14111
+ return [];
14112
+ const firstMember = body.body?.[0];
14113
+ return firstMember?.leadingComments ?? [];
14114
+ }
14115
+ function collectAllExportedDefinitions(ast, typeDefinitions, currentFile, schemaIdAliases) {
14116
+ function registerDefinition(name, entry, allComments) {
14117
+ if (!typeDefinitions[name]) {
14118
+ typeDefinitions[name] = entry;
14119
+ }
14120
+ const overrideId = extractSchemaIdFromComments(allComments);
14121
+ if (overrideId && schemaIdAliases) {
14122
+ schemaIdAliases[name] = overrideId;
14123
+ if (!typeDefinitions[overrideId]) {
14124
+ typeDefinitions[overrideId] = entry;
14125
+ }
14126
+ }
14127
+ }
14039
14128
  resolvedTraverse(ast, {
14040
14129
  TSTypeAliasDeclaration: (path17) => {
14041
14130
  if (path17.node.id && t12.isIdentifier(path17.node.id)) {
14042
14131
  const name = path17.node.id.name;
14043
- if (!typeDefinitions[name]) {
14044
- const node = path17.node.typeParameters && path17.node.typeParameters.params.length > 0 ? path17.node : path17.node.typeAnnotation;
14045
- typeDefinitions[name] = { node, filePath: currentFile };
14046
- }
14132
+ const node = path17.node.typeParameters && path17.node.typeParameters.params.length > 0 ? path17.node : path17.node.typeAnnotation;
14133
+ const allComments = [
14134
+ ...path17.parentPath?.node?.leadingComments ?? [],
14135
+ ...path17.node.leadingComments ?? [],
14136
+ ...path17.node.trailingComments ?? []
14137
+ ];
14138
+ registerDefinition(name, { node, filePath: currentFile }, allComments);
14047
14139
  }
14048
14140
  },
14049
14141
  TSInterfaceDeclaration: (path17) => {
14050
14142
  if (path17.node.id && t12.isIdentifier(path17.node.id)) {
14051
14143
  const name = path17.node.id.name;
14052
- if (!typeDefinitions[name]) {
14053
- typeDefinitions[name] = { node: path17.node, filePath: currentFile };
14054
- }
14144
+ const allComments = [
14145
+ ...path17.parentPath?.node?.leadingComments ?? [],
14146
+ ...path17.node.leadingComments ?? [],
14147
+ ...path17.node.trailingComments ?? [],
14148
+ ...collectFirstMemberLeadingComments(path17.node)
14149
+ ];
14150
+ registerDefinition(name, { node: path17.node, filePath: currentFile }, allComments);
14055
14151
  }
14056
14152
  },
14057
14153
  TSEnumDeclaration: (path17) => {
14058
14154
  if (path17.node.id && t12.isIdentifier(path17.node.id)) {
14059
14155
  const name = path17.node.id.name;
14060
- if (!typeDefinitions[name]) {
14061
- typeDefinitions[name] = { node: path17.node, filePath: currentFile };
14062
- }
14156
+ const allComments = [
14157
+ ...path17.parentPath?.node?.leadingComments ?? [],
14158
+ ...path17.node.leadingComments ?? [],
14159
+ ...path17.node.trailingComments ?? []
14160
+ ];
14161
+ registerDefinition(name, { node: path17.node, filePath: currentFile }, allComments);
14063
14162
  }
14064
14163
  },
14065
14164
  ExportNamedDeclaration: (path17) => {
@@ -14067,19 +14166,26 @@ function collectAllExportedDefinitions(ast, typeDefinitions, currentFile) {
14067
14166
  const interfaceDecl = path17.node.declaration;
14068
14167
  if (interfaceDecl.id && t12.isIdentifier(interfaceDecl.id)) {
14069
14168
  const name = interfaceDecl.id.name;
14070
- if (!typeDefinitions[name]) {
14071
- typeDefinitions[name] = { node: interfaceDecl, filePath: currentFile };
14072
- }
14169
+ const allComments = [
14170
+ ...path17.node.leadingComments ?? [],
14171
+ ...interfaceDecl.leadingComments ?? [],
14172
+ ...interfaceDecl.trailingComments ?? [],
14173
+ ...collectFirstMemberLeadingComments(interfaceDecl)
14174
+ ];
14175
+ registerDefinition(name, { node: interfaceDecl, filePath: currentFile }, allComments);
14073
14176
  }
14074
14177
  }
14075
14178
  if (t12.isTSTypeAliasDeclaration(path17.node.declaration)) {
14076
14179
  const typeDecl = path17.node.declaration;
14077
14180
  if (typeDecl.id && t12.isIdentifier(typeDecl.id)) {
14078
14181
  const name = typeDecl.id.name;
14079
- if (!typeDefinitions[name]) {
14080
- const node = typeDecl.typeParameters && typeDecl.typeParameters.params.length > 0 ? typeDecl : typeDecl.typeAnnotation;
14081
- typeDefinitions[name] = { node, filePath: currentFile };
14082
- }
14182
+ const node = typeDecl.typeParameters && typeDecl.typeParameters.params.length > 0 ? typeDecl : typeDecl.typeAnnotation;
14183
+ const allComments = [
14184
+ ...path17.node.leadingComments ?? [],
14185
+ ...typeDecl.leadingComments ?? [],
14186
+ ...typeDecl.trailingComments ?? []
14187
+ ];
14188
+ registerDefinition(name, { node, filePath: currentFile }, allComments);
14083
14189
  }
14084
14190
  }
14085
14191
  }
@@ -14496,6 +14602,7 @@ var SchemaProcessor = class {
14496
14602
  zodSchemaProcessor = null;
14497
14603
  schemaTypes;
14498
14604
  isResolvingPickOmitBase = false;
14605
+ schemaIdAliases = {};
14499
14606
  fileAccess;
14500
14607
  symbolResolver;
14501
14608
  // Track imports per file for resolving ReturnType<typeof func>
@@ -14536,7 +14643,7 @@ var SchemaProcessor = class {
14536
14643
  getDefinedSchemas() {
14537
14644
  const filteredSchemas = {};
14538
14645
  Object.entries(this.openapiDefinitions).forEach(([key, value]) => {
14539
- if (!this.isGenericTypeParameter(key) && !this.isInvalidSchemaName(key) && !this.isBuiltInUtilityType(key) && !this.isFunctionSchema(key)) {
14646
+ if (!this.schemaIdAliases[key] && !this.isGenericTypeParameter(key) && !this.isInvalidSchemaName(key) && !this.isBuiltInUtilityType(key) && !this.isFunctionSchema(key)) {
14540
14647
  filteredSchemas[key] = value;
14541
14648
  }
14542
14649
  });
@@ -14551,6 +14658,10 @@ var SchemaProcessor = class {
14551
14658
  if (schemaName.includes("<") && schemaName.includes(">")) {
14552
14659
  return this.resolveGenericTypeFromString(schemaName);
14553
14660
  }
14661
+ const overrideId = this.schemaIdAliases[schemaName];
14662
+ if (overrideId) {
14663
+ return this.findSchemaDefinition(overrideId, contentType);
14664
+ }
14554
14665
  if (this.openapiDefinitions[schemaName]) {
14555
14666
  return this.openapiDefinitions[schemaName];
14556
14667
  }
@@ -14632,6 +14743,7 @@ var SchemaProcessor = class {
14632
14743
  return;
14633
14744
  }
14634
14745
  this.collectImports(ast, filePath);
14746
+ const aliasesBeforeFile = new Set(Object.keys(this.schemaIdAliases));
14635
14747
  this.collectAllExportedDefinitions(ast, filePath);
14636
14748
  collectTopLevelDefinitionNames(ast).forEach((name) => {
14637
14749
  const indexedFiles = this.schemaDefinitionIndex[name];
@@ -14643,6 +14755,16 @@ var SchemaProcessor = class {
14643
14755
  }
14644
14756
  this.schemaDefinitionIndex[name] = [filePath];
14645
14757
  });
14758
+ Object.entries(this.schemaIdAliases).forEach(([originalName, aliasName]) => {
14759
+ if (aliasesBeforeFile.has(originalName))
14760
+ return;
14761
+ if (!this.schemaDefinitionIndex[aliasName]) {
14762
+ this.schemaDefinitionIndex[aliasName] = [];
14763
+ }
14764
+ if (!this.schemaDefinitionIndex[aliasName].includes(filePath)) {
14765
+ this.schemaDefinitionIndex[aliasName].push(filePath);
14766
+ }
14767
+ });
14646
14768
  }
14647
14769
  getParsedSchemaFile(filePath) {
14648
14770
  const cachedAst = this.fileASTCache.get(filePath);
@@ -14681,7 +14803,7 @@ var SchemaProcessor = class {
14681
14803
  * Used when processing imported files to ensure all referenced types are available
14682
14804
  */
14683
14805
  collectAllExportedDefinitions(ast, filePath) {
14684
- collectAllExportedDefinitions(ast, this.typeDefinitions, filePath || this.currentFilePath);
14806
+ collectAllExportedDefinitions(ast, this.typeDefinitions, filePath || this.currentFilePath, this.schemaIdAliases);
14685
14807
  }
14686
14808
  collectTypeDefinitions(ast, schemaName, filePath) {
14687
14809
  collectTypeDefinitions(ast, schemaName, this.typeDefinitions, filePath || this.currentFilePath);
@@ -15278,6 +15400,9 @@ var SchemaProcessor = class {
15278
15400
  logger.debug(`Record<...> used with ${node.typeParameters?.params.length ?? 0} type parameters; expected 2`);
15279
15401
  return { type: "object", additionalProperties: true };
15280
15402
  }
15403
+ if ((!node.typeParameters || node.typeParameters.params.length === 0) && this.schemaIdAliases[typeName] && this.openapiDefinitions[this.schemaIdAliases[typeName]]) {
15404
+ return { $ref: `#/components/schemas/${this.schemaIdAliases[typeName]}` };
15405
+ }
15281
15406
  const utilityType = resolveUtilityTypeReference(node, {
15282
15407
  currentFilePath: this.currentFilePath,
15283
15408
  contentType: this.contentType,
@@ -15453,7 +15578,9 @@ var SchemaProcessor = class {
15453
15578
  };
15454
15579
  }
15455
15580
  if (t15.isTSTypeReference(node) && t15.isIdentifier(node.typeName)) {
15456
- return { $ref: `#/components/schemas/${node.typeName.name}` };
15581
+ const refName = node.typeName.name;
15582
+ const aliasedName = this.schemaIdAliases[refName] ?? refName;
15583
+ return { $ref: `#/components/schemas/${aliasedName}` };
15457
15584
  }
15458
15585
  logger.debug("Unrecognized TypeScript type node:", node);
15459
15586
  return { type: "object" };
@@ -15594,7 +15721,8 @@ var SchemaProcessor = class {
15594
15721
  if (this.schemaTypes.includes("zod") && this.zodSchemaConverter) {
15595
15722
  return this.zodSchemaConverter.getSchemaReferenceName(baseTypeName, contentType);
15596
15723
  }
15597
- return baseTypeName;
15724
+ const aliasedName = this.schemaIdAliases[baseTypeName] ?? baseTypeName;
15725
+ return aliasedName;
15598
15726
  }
15599
15727
  /**
15600
15728
  * Parse and resolve a generic type from a string like "MyApiSuccessResponseBody<LLMSResponse>"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-openapi-gen",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "Automatically generate OpenAPI 3.0, 3.1, and 3.2 documentation from Next.js projects, with support for Zod schemas, TypeScript types, and reusable OpenAPI fragments.",
5
5
  "keywords": [
6
6
  "api",