next-openapi-gen 0.6.6 → 0.6.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.
@@ -16,6 +16,7 @@ export class SchemaProcessor {
16
16
  processingTypes = new Set();
17
17
  zodSchemaConverter;
18
18
  schemaType;
19
+ isResolvingPickOmitBase = false;
19
20
  constructor(schemaDir, schemaType = "typescript") {
20
21
  this.schemaDir = path.resolve(schemaDir);
21
22
  this.schemaType = schemaType;
@@ -166,10 +167,21 @@ export class SchemaProcessor {
166
167
  const enumValues = this.processEnum(typeNode);
167
168
  return enumValues;
168
169
  }
169
- if (t.isTSTypeLiteral(typeNode) || t.isTSInterfaceBody(typeNode)) {
170
+ if (t.isTSTypeLiteral(typeNode) || t.isTSInterfaceBody(typeNode) || t.isTSInterfaceDeclaration(typeNode)) {
170
171
  const properties = {};
171
- if ("members" in typeNode) {
172
- (typeNode.members || []).forEach((member) => {
172
+ // Handle interface extends clause
173
+ if (t.isTSInterfaceDeclaration(typeNode) && typeNode.extends && typeNode.extends.length > 0) {
174
+ typeNode.extends.forEach((extendedType) => {
175
+ const extendedSchema = this.resolveTSNodeType(extendedType);
176
+ if (extendedSchema.properties) {
177
+ Object.assign(properties, extendedSchema.properties);
178
+ }
179
+ });
180
+ }
181
+ // Get members from interface declaration body or direct members
182
+ const members = t.isTSInterfaceDeclaration(typeNode) ? typeNode.body.body : typeNode.members;
183
+ if (members) {
184
+ (members || []).forEach((member) => {
173
185
  if (t.isTSPropertySignature(member) && t.isIdentifier(member.key)) {
174
186
  const propName = member.key.name;
175
187
  const options = this.getPropertyOptions(member);
@@ -192,6 +204,9 @@ export class SchemaProcessor {
192
204
  if (t.isTSUnionType(typeNode)) {
193
205
  return this.resolveTSNodeType(typeNode);
194
206
  }
207
+ if (t.isTSTypeReference(typeNode)) {
208
+ return this.resolveTSNodeType(typeNode);
209
+ }
195
210
  return {};
196
211
  }
197
212
  finally {
@@ -250,6 +265,18 @@ export class SchemaProcessor {
250
265
  };
251
266
  }
252
267
  }
268
+ // Handle TSExpressionWithTypeArguments (used in interface extends)
269
+ if (t.isTSExpressionWithTypeArguments(node)) {
270
+ if (t.isIdentifier(node.expression)) {
271
+ // Convert to TSTypeReference-like structure for processing
272
+ const syntheticNode = {
273
+ type: 'TSTypeReference',
274
+ typeName: node.expression,
275
+ typeParameters: node.typeParameters
276
+ };
277
+ return this.resolveTSNodeType(syntheticNode);
278
+ }
279
+ }
253
280
  if (t.isTSTypeReference(node) && t.isIdentifier(node.typeName)) {
254
281
  const typeName = node.typeName.name;
255
282
  // Special handling for built-in types
@@ -284,6 +311,34 @@ export class SchemaProcessor {
284
311
  }
285
312
  }
286
313
  if (typeName === "Pick" || typeName === "Omit") {
314
+ if (node.typeParameters && node.typeParameters.params.length > 1) {
315
+ const baseTypeParam = node.typeParameters.params[0];
316
+ const keysParam = node.typeParameters.params[1];
317
+ // Resolve base type without adding it to schema definitions
318
+ this.isResolvingPickOmitBase = true;
319
+ const baseType = this.resolveTSNodeType(baseTypeParam);
320
+ this.isResolvingPickOmitBase = false;
321
+ if (baseType.properties) {
322
+ const properties = {};
323
+ const keyNames = this.extractKeysFromLiteralType(keysParam);
324
+ if (typeName === "Pick") {
325
+ keyNames.forEach(key => {
326
+ if (baseType.properties[key]) {
327
+ properties[key] = baseType.properties[key];
328
+ }
329
+ });
330
+ }
331
+ else { // Omit
332
+ Object.entries(baseType.properties).forEach(([key, value]) => {
333
+ if (!keyNames.includes(key)) {
334
+ properties[key] = value;
335
+ }
336
+ });
337
+ }
338
+ return { type: "object", properties };
339
+ }
340
+ }
341
+ // Fallback to just the base type if we can't process properly
287
342
  if (node.typeParameters && node.typeParameters.params.length > 0) {
288
343
  return this.resolveTSNodeType(node.typeParameters.params[0]);
289
344
  }
@@ -413,7 +468,9 @@ export class SchemaProcessor {
413
468
  // Reset the set of processed types before each schema processing
414
469
  this.processingTypes.clear();
415
470
  const definition = this.resolveType(schemaName);
416
- this.openapiDefinitions[schemaName] = definition;
471
+ if (!this.isResolvingPickOmitBase) {
472
+ this.openapiDefinitions[schemaName] = definition;
473
+ }
417
474
  this.processSchemaTracker[`${filePath}-${schemaName}`] = true;
418
475
  return definition;
419
476
  }
@@ -447,6 +504,21 @@ export class SchemaProcessor {
447
504
  });
448
505
  return enumSchema;
449
506
  }
507
+ extractKeysFromLiteralType(node) {
508
+ if (t.isTSLiteralType(node) && t.isStringLiteral(node.literal)) {
509
+ return [node.literal.value];
510
+ }
511
+ if (t.isTSUnionType(node)) {
512
+ const keys = [];
513
+ node.types.forEach((type) => {
514
+ if (t.isTSLiteralType(type) && t.isStringLiteral(type.literal)) {
515
+ keys.push(type.literal.value);
516
+ }
517
+ });
518
+ return keys;
519
+ }
520
+ return [];
521
+ }
450
522
  getPropertyOptions(node) {
451
523
  const isOptional = !!node.optional; // check if property is optional
452
524
  let description = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-openapi-gen",
3
- "version": "0.6.6",
3
+ "version": "0.6.7",
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",