adorn-api 1.0.9 → 1.0.10

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/dist/cli.js CHANGED
@@ -137,6 +137,7 @@ function analyzeMethod(node, className, checker) {
137
137
  if (!signature) return null;
138
138
  let returnType = checker.getReturnTypeOfSignature(signature);
139
139
  returnType = unwrapPromise(returnType, checker);
140
+ const returnTypeNode = unwrapPromiseTypeNode(node.type);
140
141
  const parameters = [];
141
142
  for (let i = 0; i < node.parameters.length; i++) {
142
143
  const param = node.parameters[i];
@@ -161,6 +162,7 @@ function analyzeMethod(node, className, checker) {
161
162
  operationId: defaultOperationId(className, methodName),
162
163
  methodDeclaration: node,
163
164
  returnType,
165
+ returnTypeNode,
164
166
  parameters,
165
167
  pathParamIndices,
166
168
  bodyParamIndex,
@@ -291,6 +293,15 @@ function unwrapPromise(type, checker) {
291
293
  }
292
294
  return type;
293
295
  }
296
+ function unwrapPromiseTypeNode(typeNode) {
297
+ if (!typeNode) return void 0;
298
+ if (ts2.isTypeReferenceNode(typeNode)) {
299
+ if (ts2.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise") {
300
+ return typeNode.typeArguments?.[0] ?? typeNode;
301
+ }
302
+ }
303
+ return typeNode;
304
+ }
294
305
 
295
306
  // src/compiler/schema/openapi.ts
296
307
  import ts6 from "typescript";
@@ -337,10 +348,10 @@ function typeToJsonSchema(type, ctx, typeNode) {
337
348
  return { type: "boolean", enum: [intrinsic === "true"] };
338
349
  }
339
350
  if (type.isUnion()) {
340
- return handleUnion(type.types, ctx, typeNode);
351
+ return handleUnion(type, ctx, typeNode);
341
352
  }
342
353
  if (type.isIntersection()) {
343
- return handleIntersection(type.types, ctx, typeNode);
354
+ return handleIntersection(type, ctx, typeNode);
344
355
  }
345
356
  if (checker.isArrayType(type)) {
346
357
  const typeArgs = type.typeArguments;
@@ -377,56 +388,96 @@ function isSetType(type, checker) {
377
388
  if (name === "Set") return true;
378
389
  return false;
379
390
  }
380
- function handleUnion(types, ctx, typeNode) {
381
- const nullType = types.find((t) => t.flags & ts3.TypeFlags.Null);
382
- const otherTypes = types.filter((t) => !(t.flags & ts3.TypeFlags.Null) && !(t.flags & ts3.TypeFlags.Undefined));
383
- const allStringLiterals = otherTypes.every((t) => t.flags & ts3.TypeFlags.StringLiteral);
384
- if (allStringLiterals && otherTypes.length > 0) {
385
- const enumValues = otherTypes.map((t) => t.value);
386
- const schema = { type: "string", enum: enumValues };
387
- if (nullType) {
388
- schema.type = ["string", "null"];
389
- }
390
- return schema;
391
+ function getSchemaName(type, typeNode) {
392
+ const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
393
+ const aliasName = aliasSymbol?.getName();
394
+ if (aliasName && aliasName !== "__type") {
395
+ return aliasName;
391
396
  }
392
- if (otherTypes.length === 1 && nullType) {
393
- const innerSchema = typeToJsonSchema(otherTypes[0], ctx);
394
- if (typeof innerSchema.type === "string") {
395
- innerSchema.type = [innerSchema.type, "null"];
396
- }
397
- return innerSchema;
398
- }
399
- if (otherTypes.length > 1) {
400
- const branches = otherTypes.map((t) => typeToJsonSchema(t, ctx));
401
- const hasNull = !!nullType;
402
- const result = {};
403
- if (hasNull) {
404
- result.anyOf = [...branches, { type: "null" }];
405
- } else {
406
- result.anyOf = branches;
407
- }
408
- const discriminatorResult = detectDiscriminatedUnion(otherTypes, ctx, branches);
409
- if (discriminatorResult) {
410
- result.oneOf = branches;
411
- result.discriminator = discriminatorResult;
412
- }
413
- return result;
397
+ const symbol = type.getSymbol();
398
+ const symbolName = symbol?.getName?.();
399
+ if (symbolName && symbolName !== "__type") {
400
+ return symbolName;
414
401
  }
415
- if (otherTypes.length === 1) {
416
- return typeToJsonSchema(otherTypes[0], ctx);
402
+ const nodeName = getExplicitTypeNameFromNode(typeNode);
403
+ if (nodeName && nodeName !== "__type") {
404
+ return nodeName;
417
405
  }
418
- return {};
406
+ return null;
419
407
  }
420
- function handleIntersection(types, ctx, typeNode) {
421
- const brandCollapsed = tryCollapseBrandedIntersection(types, ctx, typeNode);
422
- if (brandCollapsed) {
423
- return brandCollapsed;
408
+ function buildNamedSchema(type, ctx, typeNode, build) {
409
+ const name = getSchemaName(type, typeNode);
410
+ if (!name) {
411
+ return build();
424
412
  }
425
- const allOf = [];
426
- for (const t of types) {
427
- allOf.push(typeToJsonSchema(t, ctx));
413
+ const { components, typeStack } = ctx;
414
+ if (components.has(name) || typeStack.has(type)) {
415
+ return { $ref: `#/components/schemas/${name}` };
428
416
  }
429
- return { allOf };
417
+ typeStack.add(type);
418
+ const schema = build();
419
+ typeStack.delete(type);
420
+ if (!components.has(name)) {
421
+ components.set(name, schema);
422
+ }
423
+ return { $ref: `#/components/schemas/${name}` };
424
+ }
425
+ function handleUnion(type, ctx, typeNode) {
426
+ return buildNamedSchema(type, ctx, typeNode, () => {
427
+ const types = type.types;
428
+ const nullType = types.find((t) => t.flags & ts3.TypeFlags.Null);
429
+ const otherTypes = types.filter((t) => !(t.flags & ts3.TypeFlags.Null) && !(t.flags & ts3.TypeFlags.Undefined));
430
+ const allStringLiterals = otherTypes.every((t) => t.flags & ts3.TypeFlags.StringLiteral);
431
+ if (allStringLiterals && otherTypes.length > 0) {
432
+ const enumValues = otherTypes.map((t) => t.value);
433
+ const schema = { type: "string", enum: enumValues };
434
+ if (nullType) {
435
+ schema.type = ["string", "null"];
436
+ }
437
+ return schema;
438
+ }
439
+ if (otherTypes.length === 1 && nullType) {
440
+ const innerSchema = typeToJsonSchema(otherTypes[0], ctx);
441
+ if (typeof innerSchema.type === "string") {
442
+ innerSchema.type = [innerSchema.type, "null"];
443
+ }
444
+ return innerSchema;
445
+ }
446
+ if (otherTypes.length > 1) {
447
+ const branches = otherTypes.map((t) => typeToJsonSchema(t, ctx));
448
+ const hasNull = !!nullType;
449
+ const result = {};
450
+ if (hasNull) {
451
+ result.anyOf = [...branches, { type: "null" }];
452
+ } else {
453
+ result.anyOf = branches;
454
+ }
455
+ const discriminatorResult = detectDiscriminatedUnion(otherTypes, ctx, branches);
456
+ if (discriminatorResult) {
457
+ result.oneOf = branches;
458
+ result.discriminator = discriminatorResult;
459
+ }
460
+ return result;
461
+ }
462
+ if (otherTypes.length === 1) {
463
+ return typeToJsonSchema(otherTypes[0], ctx);
464
+ }
465
+ return {};
466
+ });
467
+ }
468
+ function handleIntersection(type, ctx, typeNode) {
469
+ return buildNamedSchema(type, ctx, typeNode, () => {
470
+ const types = type.types;
471
+ const brandCollapsed = tryCollapseBrandedIntersection(types, ctx, typeNode);
472
+ if (brandCollapsed) {
473
+ return brandCollapsed;
474
+ }
475
+ const allOf = [];
476
+ for (const t of types) {
477
+ allOf.push(typeToJsonSchema(t, ctx));
478
+ }
479
+ return { allOf };
480
+ });
430
481
  }
431
482
  function tryCollapseBrandedIntersection(types, ctx, typeNode) {
432
483
  const { checker } = ctx;
@@ -557,8 +608,8 @@ function handleObjectType(type, ctx, typeNode) {
557
608
  typeStack.delete(type);
558
609
  return schema;
559
610
  }
560
- function getTypeNameFromNode(typeNode, ctx) {
561
- if (!typeNode) return `Anonymous_${ctx.typeNameStack.length}`;
611
+ function getExplicitTypeNameFromNode(typeNode) {
612
+ if (!typeNode) return null;
562
613
  if (ts3.isTypeReferenceNode(typeNode)) {
563
614
  if (ts3.isIdentifier(typeNode.typeName)) {
564
615
  return typeNode.typeName.text;
@@ -569,6 +620,11 @@ function getTypeNameFromNode(typeNode, ctx) {
569
620
  return typeNode.parent.name.text;
570
621
  }
571
622
  }
623
+ return null;
624
+ }
625
+ function getTypeNameFromNode(typeNode, ctx) {
626
+ const explicitName = getExplicitTypeNameFromNode(typeNode);
627
+ if (explicitName) return explicitName;
572
628
  return `Anonymous_${ctx.typeNameStack.length}`;
573
629
  }
574
630
  function buildObjectSchema(type, ctx, typeNode) {
@@ -867,7 +923,7 @@ function buildOperation(operation, ctx, controllerConsumes) {
867
923
  if (parameters.length > 0) {
868
924
  op.parameters = parameters;
869
925
  }
870
- const responseSchema = typeToJsonSchema(operation.returnType, ctx);
926
+ const responseSchema = typeToJsonSchema(operation.returnType, ctx, operation.returnTypeNode);
871
927
  const status = operation.httpMethod === "POST" ? 201 : 200;
872
928
  op.responses[status] = {
873
929
  description: status === 201 ? "Created" : "OK",
@@ -1104,7 +1160,7 @@ function buildOperationEntry(op, ctx) {
1104
1160
  };
1105
1161
  }
1106
1162
  }
1107
- const responseSchema = typeToJsonSchema(op.returnType, ctx);
1163
+ const responseSchema = typeToJsonSchema(op.returnType, ctx, op.returnTypeNode);
1108
1164
  const status = op.httpMethod === "POST" ? 201 : 200;
1109
1165
  let schemaRef = responseSchema.$ref;
1110
1166
  let isArray = false;