ts-class-to-openapi 1.4.1 → 1.4.3
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/index.cjs +291 -194
- package/dist/index.mjs +291 -194
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -28,6 +28,53 @@ path = __toESM(path);
|
|
|
28
28
|
//#region src/transformer.fixtures.ts
|
|
29
29
|
const constants = {
|
|
30
30
|
TS_CONFIG_DEFAULT_PATH: path.default.resolve(process.cwd(), "tsconfig.json"),
|
|
31
|
+
JS_KEYWORDS: new Set([
|
|
32
|
+
"constructor",
|
|
33
|
+
"static",
|
|
34
|
+
"get",
|
|
35
|
+
"set",
|
|
36
|
+
"async",
|
|
37
|
+
"return",
|
|
38
|
+
"if",
|
|
39
|
+
"else",
|
|
40
|
+
"for",
|
|
41
|
+
"while",
|
|
42
|
+
"switch",
|
|
43
|
+
"case",
|
|
44
|
+
"break",
|
|
45
|
+
"throw",
|
|
46
|
+
"new",
|
|
47
|
+
"delete",
|
|
48
|
+
"typeof",
|
|
49
|
+
"void",
|
|
50
|
+
"try",
|
|
51
|
+
"catch",
|
|
52
|
+
"finally",
|
|
53
|
+
"this",
|
|
54
|
+
"super",
|
|
55
|
+
"class",
|
|
56
|
+
"const",
|
|
57
|
+
"let",
|
|
58
|
+
"var",
|
|
59
|
+
"function",
|
|
60
|
+
"yield",
|
|
61
|
+
"await",
|
|
62
|
+
"import",
|
|
63
|
+
"export",
|
|
64
|
+
"default",
|
|
65
|
+
"extends",
|
|
66
|
+
"implements",
|
|
67
|
+
"in",
|
|
68
|
+
"of",
|
|
69
|
+
"do",
|
|
70
|
+
"with",
|
|
71
|
+
"continue",
|
|
72
|
+
"instanceof",
|
|
73
|
+
"true",
|
|
74
|
+
"false",
|
|
75
|
+
"null",
|
|
76
|
+
"undefined"
|
|
77
|
+
]),
|
|
31
78
|
jsPrimitives: {
|
|
32
79
|
String: {
|
|
33
80
|
type: "String",
|
|
@@ -190,6 +237,7 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
190
237
|
autoCleanup;
|
|
191
238
|
classFileIndex = /* @__PURE__ */ new Map();
|
|
192
239
|
transformCallIndex = /* @__PURE__ */ new Map();
|
|
240
|
+
nonGenericTransformCalls = /* @__PURE__ */ new Set();
|
|
193
241
|
constructor(tsConfigPath = constants.TS_CONFIG_DEFAULT_PATH, options = {}) {
|
|
194
242
|
this.maxCacheSize = options.maxCacheSize ?? 100;
|
|
195
243
|
this.autoCleanup = options.autoCleanup ?? true;
|
|
@@ -210,14 +258,19 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
210
258
|
else {
|
|
211
259
|
const heritageClause = classNode.heritageClauses[0];
|
|
212
260
|
if (heritageClause && heritageClause.token === typescript.default.SyntaxKind.ExtendsKeyword) {
|
|
213
|
-
var _symbol$declarations;
|
|
214
261
|
const type = heritageClause.types[0];
|
|
215
262
|
let properties = [];
|
|
216
263
|
let baseProperties = [];
|
|
217
264
|
if (!type) return [];
|
|
218
265
|
const symbol = this.checker.getSymbolAtLocation(type.expression);
|
|
219
266
|
if (!symbol) return [];
|
|
220
|
-
const
|
|
267
|
+
const realSymbol = symbol && symbol.flags & typescript.default.SymbolFlags.Alias ? this.checker.getAliasedSymbol(symbol) : symbol;
|
|
268
|
+
let declaration;
|
|
269
|
+
if (realSymbol && realSymbol.links && realSymbol.links.type && realSymbol.links.type.symbol && realSymbol.links.type.symbol.declarations) declaration = realSymbol.links.type.symbol.declarations[0];
|
|
270
|
+
else {
|
|
271
|
+
var _realSymbol$declarati;
|
|
272
|
+
declaration = realSymbol === null || realSymbol === void 0 || (_realSymbol$declarati = realSymbol.declarations) === null || _realSymbol$declarati === void 0 ? void 0 : _realSymbol$declarati.find(typescript.default.isClassDeclaration);
|
|
273
|
+
}
|
|
221
274
|
if (declaration && typescript.default.isClassDeclaration(declaration)) {
|
|
222
275
|
const newGenericTypeMap = /* @__PURE__ */ new Map();
|
|
223
276
|
if (declaration.typeParameters && type.typeArguments) declaration.typeParameters.forEach((param, index) => {
|
|
@@ -236,78 +289,77 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
236
289
|
}
|
|
237
290
|
getPropertiesByClassMembers(members, parentClassNode, genericTypeMap = /* @__PURE__ */ new Map()) {
|
|
238
291
|
const properties = [];
|
|
239
|
-
for (const member of members) {
|
|
240
|
-
var
|
|
241
|
-
if (
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if (
|
|
258
|
-
const
|
|
259
|
-
if (
|
|
260
|
-
const matches = this.classFileIndex.get(baseTypeName);
|
|
261
|
-
if (matches && matches.length > 0 && matches[0]) genericClassReference = matches[0].node;
|
|
262
|
-
}
|
|
292
|
+
for (const member of members) if (typescript.default.isPropertyDeclaration(member) && (typescript.default.isIdentifier(member.name) || typescript.default.isStringLiteral(member.name))) {
|
|
293
|
+
var _property$originalPro;
|
|
294
|
+
if (member.modifiers) {
|
|
295
|
+
if (member.modifiers.some((m) => m.kind === typescript.default.SyntaxKind.PrivateKeyword || m.kind === typescript.default.SyntaxKind.ProtectedKeyword)) continue;
|
|
296
|
+
}
|
|
297
|
+
const propertyName = member.name.text;
|
|
298
|
+
const type = this.getPropertyType(member, genericTypeMap);
|
|
299
|
+
const decorators = this.extractDecorators(member);
|
|
300
|
+
const isOptional = !!member.questionToken;
|
|
301
|
+
const isGeneric = this.isPropertyTypeGeneric(member);
|
|
302
|
+
const isEnum = this.isEnum(member);
|
|
303
|
+
const isPrimitive = this.isPrimitiveType(type) || isEnum;
|
|
304
|
+
const isClassType = this.isClassType(member);
|
|
305
|
+
const isArray = this.isArrayProperty(member);
|
|
306
|
+
const isTypeLiteral = this.isTypeLiteral(member);
|
|
307
|
+
let genericClassReference = void 0;
|
|
308
|
+
if (isGeneric && !isPrimitive) {
|
|
309
|
+
const baseTypeName = type.replace(/\[\]$/, "").trim();
|
|
310
|
+
if (!this.isPrimitiveType(baseTypeName)) {
|
|
311
|
+
const matches = this.classFileIndex.get(baseTypeName);
|
|
312
|
+
if (matches && matches.length > 0 && matches[0]) genericClassReference = matches[0].node;
|
|
263
313
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
314
|
+
}
|
|
315
|
+
const property = {
|
|
316
|
+
name: propertyName,
|
|
317
|
+
type,
|
|
318
|
+
decorators,
|
|
319
|
+
isOptional,
|
|
320
|
+
isGeneric,
|
|
321
|
+
originalProperty: member,
|
|
322
|
+
isPrimitive,
|
|
323
|
+
isClassType,
|
|
324
|
+
isArray,
|
|
325
|
+
isEnum,
|
|
326
|
+
isRef: false,
|
|
327
|
+
isTypeLiteral,
|
|
328
|
+
genericClassReference
|
|
329
|
+
};
|
|
330
|
+
if (property.isClassType) {
|
|
331
|
+
const declaration = this.getDeclarationProperty(property);
|
|
332
|
+
if (parentClassNode) {
|
|
333
|
+
if (declaration && declaration.name && this.checker.getSymbolAtLocation(declaration.name) === this.checker.getSymbolAtLocation(parentClassNode.name)) property.isRef = true;
|
|
284
334
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
335
|
+
}
|
|
336
|
+
if (property.isTypeLiteral && property.originalProperty.type !== void 0 && typescript.default.isTypeReferenceNode(property.originalProperty.type) && ((_property$originalPro = property.originalProperty.type.typeArguments) === null || _property$originalPro === void 0 ? void 0 : _property$originalPro.length) === 1) {
|
|
337
|
+
const typeArguments = property.originalProperty.type.typeArguments;
|
|
338
|
+
if (typeArguments && typeArguments[0]) {
|
|
339
|
+
const firstTypeArg = typeArguments[0];
|
|
340
|
+
if (typescript.default.isTypeReferenceNode(firstTypeArg)) {
|
|
341
|
+
const symbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
|
|
342
|
+
if (symbol && symbol.declarations) {
|
|
343
|
+
const classDeclaration = symbol.declarations.find((decl) => typescript.default.isClassDeclaration(decl));
|
|
344
|
+
if (classDeclaration && typescript.default.isClassDeclaration(classDeclaration)) property.typeLiteralClassReference = classDeclaration;
|
|
295
345
|
}
|
|
296
346
|
}
|
|
297
347
|
}
|
|
298
|
-
properties.push(property);
|
|
299
348
|
}
|
|
349
|
+
properties.push(property);
|
|
300
350
|
}
|
|
301
351
|
return properties;
|
|
302
352
|
}
|
|
303
353
|
getPropertyType(property, genericTypeMap = /* @__PURE__ */ new Map()) {
|
|
304
354
|
if (property.type) return this.getTypeNodeToString(property.type, genericTypeMap);
|
|
305
355
|
const type = this.checker.getTypeAtLocation(property);
|
|
306
|
-
return this.
|
|
356
|
+
return this.checker.typeToString(type);
|
|
307
357
|
}
|
|
308
358
|
getTypeNodeToString(typeNode, genericTypeMap = /* @__PURE__ */ new Map()) {
|
|
309
|
-
if (typescript.default.isTypeReferenceNode(typeNode)
|
|
310
|
-
|
|
359
|
+
if (typescript.default.isTypeReferenceNode(typeNode)) {
|
|
360
|
+
let typeName;
|
|
361
|
+
if (typescript.default.isIdentifier(typeNode.typeName)) typeName = typeNode.typeName.text;
|
|
362
|
+
else typeName = typeNode.typeName.right.text;
|
|
311
363
|
if (genericTypeMap.has(typeName)) return genericTypeMap.get(typeName);
|
|
312
364
|
if (typeName.toLowerCase() === "uploadfile") return "UploadFile";
|
|
313
365
|
if (typeName.toLowerCase() === "uploadfiledto") return "UploadFileDto";
|
|
@@ -319,7 +371,7 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
319
371
|
}
|
|
320
372
|
return this.resolveGenericType(typeNode);
|
|
321
373
|
}
|
|
322
|
-
return
|
|
374
|
+
return typeName;
|
|
323
375
|
}
|
|
324
376
|
switch (typeNode.kind) {
|
|
325
377
|
case typescript.default.SyntaxKind.StringKeyword: return constants.jsPrimitives.String.type;
|
|
@@ -334,31 +386,31 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
334
386
|
if (meaningfulTypes.length > 0 && meaningfulTypes[0]) return meaningfulTypes[0];
|
|
335
387
|
if (types.length > 0 && types[0]) return types[0];
|
|
336
388
|
return "object";
|
|
337
|
-
default:
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
if (
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
return typeText;
|
|
389
|
+
default: {
|
|
390
|
+
const resolvedType = this.checker.getTypeAtLocation(typeNode);
|
|
391
|
+
const resolved = this.checker.typeToString(resolvedType);
|
|
392
|
+
if (genericTypeMap && genericTypeMap.has(resolved)) return genericTypeMap.get(resolved);
|
|
393
|
+
if (this.isPrimitiveType(resolved)) return resolved;
|
|
394
|
+
if (resolved === "Date") return constants.jsPrimitives.Date.type;
|
|
395
|
+
if (resolved === "Buffer") return constants.jsPrimitives.Buffer.type;
|
|
396
|
+
if (resolved === "Uint8Array") return constants.jsPrimitives.Uint8Array.type;
|
|
397
|
+
return resolved;
|
|
398
|
+
}
|
|
348
399
|
}
|
|
349
400
|
}
|
|
350
401
|
resolveGenericType(typeNode) {
|
|
351
|
-
|
|
402
|
+
let typeName;
|
|
403
|
+
if (typescript.default.isIdentifier(typeNode.typeName)) typeName = typeNode.typeName.text;
|
|
404
|
+
else typeName = typeNode.typeName.right.text;
|
|
352
405
|
const typeArguments = typeNode.typeArguments;
|
|
353
406
|
if (!typeArguments || typeArguments.length === 0) return typeName;
|
|
354
407
|
const type = this.checker.getTypeAtLocation(typeNode);
|
|
355
|
-
|
|
356
|
-
|
|
408
|
+
if (!(type.flags & typescript.default.TypeFlags.Any)) {
|
|
409
|
+
const resolvedType = this.checker.typeToString(type);
|
|
410
|
+
if (resolvedType && resolvedType !== typeName) return resolvedType;
|
|
411
|
+
}
|
|
357
412
|
return typeName;
|
|
358
413
|
}
|
|
359
|
-
getStringFromType(type) {
|
|
360
|
-
return this.checker.typeToString(type);
|
|
361
|
-
}
|
|
362
414
|
extractDecorators(member) {
|
|
363
415
|
const decorators = [];
|
|
364
416
|
if (member.modifiers) {
|
|
@@ -378,6 +430,7 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
378
430
|
}
|
|
379
431
|
getDecoratorName(callExpression) {
|
|
380
432
|
if (typescript.default.isIdentifier(callExpression.expression)) return callExpression.expression.text;
|
|
433
|
+
if (typescript.default.isPropertyAccessExpression(callExpression.expression)) return callExpression.expression.name.text;
|
|
381
434
|
return "unknown";
|
|
382
435
|
}
|
|
383
436
|
getDecoratorArguments(callExpression) {
|
|
@@ -390,7 +443,13 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
390
443
|
});
|
|
391
444
|
}
|
|
392
445
|
getSafeDecoratorArgument(arg) {
|
|
393
|
-
if (arg && typeof arg === "object" && "kind" in arg)
|
|
446
|
+
if (arg && typeof arg === "object" && "kind" in arg) {
|
|
447
|
+
const node = arg;
|
|
448
|
+
const type = this.checker.getTypeAtLocation(node);
|
|
449
|
+
if (type.isNumberLiteral()) return type.value;
|
|
450
|
+
if (type.isStringLiteral()) return type.value;
|
|
451
|
+
return this.checker.typeToString(type);
|
|
452
|
+
}
|
|
394
453
|
return arg;
|
|
395
454
|
}
|
|
396
455
|
isPropertyTypeGeneric(property) {
|
|
@@ -414,15 +473,18 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
414
473
|
isGenericTypeFromSymbol(type) {
|
|
415
474
|
if (this.isSimpleArrayType(type)) return false;
|
|
416
475
|
if (type.aliasTypeArguments && type.aliasTypeArguments.length > 0) return true;
|
|
417
|
-
if (type.
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
476
|
+
if (type.flags & typescript.default.TypeFlags.Object && type.objectFlags & typescript.default.ObjectFlags.Reference) {
|
|
477
|
+
var _typeArgs$;
|
|
478
|
+
const typeArgs = this.checker.getTypeArguments(type);
|
|
479
|
+
if (typeArgs.length > 0 && ((_typeArgs$ = typeArgs[0]) === null || _typeArgs$ === void 0 || (_typeArgs$ = _typeArgs$.getSymbol()) === null || _typeArgs$ === void 0 ? void 0 : _typeArgs$.getName()) === "Array") {
|
|
480
|
+
const symbol = type.getSymbol();
|
|
481
|
+
if (symbol && symbol.getName() === "Array") {
|
|
482
|
+
const elementType = typeArgs[0];
|
|
483
|
+
return elementType ? this.isUtilityTypeFromType(elementType) : false;
|
|
484
|
+
}
|
|
485
|
+
const elementType = typeArgs[0];
|
|
486
|
+
return elementType ? this.isUtilityTypeFromType(elementType) : false;
|
|
423
487
|
}
|
|
424
|
-
const elementType = type.typeArguments[0];
|
|
425
|
-
return this.isUtilityTypeFromType(elementType);
|
|
426
488
|
}
|
|
427
489
|
if (type.flags & typescript.default.TypeFlags.TypeParameter) return true;
|
|
428
490
|
if (type.flags & typescript.default.TypeFlags.Conditional) return true;
|
|
@@ -454,13 +516,18 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
454
516
|
isSimpleArrayType(type) {
|
|
455
517
|
const symbol = type.getSymbol();
|
|
456
518
|
if (!symbol || symbol.getName() !== "Array") return false;
|
|
457
|
-
if (type.
|
|
458
|
-
const
|
|
459
|
-
if (
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
519
|
+
if (type.flags & typescript.default.TypeFlags.Object && type.objectFlags & typescript.default.ObjectFlags.Reference) {
|
|
520
|
+
const typeArgs = this.checker.getTypeArguments(type);
|
|
521
|
+
if (typeArgs.length === 1) {
|
|
522
|
+
const elementType = typeArgs[0];
|
|
523
|
+
if (this.isUtilityTypeFromType(elementType)) return false;
|
|
524
|
+
if (elementType.flags & typescript.default.TypeFlags.Object && elementType.objectFlags & typescript.default.ObjectFlags.Reference) {
|
|
525
|
+
if (this.checker.getTypeArguments(elementType).length > 0) return false;
|
|
526
|
+
}
|
|
527
|
+
const elementSymbol = elementType.getSymbol();
|
|
528
|
+
if (elementSymbol && elementSymbol.getName() !== "Array") return false;
|
|
529
|
+
return true;
|
|
530
|
+
}
|
|
464
531
|
}
|
|
465
532
|
return false;
|
|
466
533
|
}
|
|
@@ -518,23 +585,40 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
518
585
|
return matches[0];
|
|
519
586
|
}
|
|
520
587
|
findBestMatch(cls, matches) {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
588
|
+
const runtimeProps = this.extractRuntimePropertyNames(cls);
|
|
589
|
+
let bestMatch;
|
|
590
|
+
let bestScore = -1;
|
|
591
|
+
for (const match of matches) {
|
|
592
|
+
let score = 0;
|
|
593
|
+
for (const member of match.node.members) if (typescript.default.isPropertyDeclaration(member) && member.name && (typescript.default.isIdentifier(member.name) || typescript.default.isStringLiteral(member.name))) {
|
|
594
|
+
if (runtimeProps.has(member.name.text)) score++;
|
|
595
|
+
}
|
|
596
|
+
if (score > bestScore) {
|
|
597
|
+
bestScore = score;
|
|
598
|
+
bestMatch = match;
|
|
599
|
+
}
|
|
526
600
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
601
|
+
return bestMatch;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Safely extracts property names from a class constructor without instantiation.
|
|
605
|
+
* Parses the class source via Function.prototype.toString() and inspects
|
|
606
|
+
* the prototype for method names.
|
|
607
|
+
*/
|
|
608
|
+
extractRuntimePropertyNames(cls) {
|
|
609
|
+
const names = /* @__PURE__ */ new Set();
|
|
610
|
+
try {
|
|
611
|
+
const source = Function.prototype.toString.call(cls);
|
|
612
|
+
const thisAssign = /this\.(\w+)\s*=/g;
|
|
613
|
+
let m;
|
|
614
|
+
while ((m = thisAssign.exec(source)) !== null) if (m[1]) names.add(m[1]);
|
|
615
|
+
const classField = /[;}](\w+)(?=[=;}\s])/g;
|
|
616
|
+
while ((m = classField.exec(source)) !== null) if (m[1] && !constants.JS_KEYWORDS.has(m[1])) names.add(m[1]);
|
|
617
|
+
} catch {}
|
|
618
|
+
try {
|
|
619
|
+
for (const name of Object.getOwnPropertyNames(cls.prototype)) if (name !== "constructor") names.add(name);
|
|
620
|
+
} catch {}
|
|
621
|
+
return names;
|
|
538
622
|
}
|
|
539
623
|
getFilteredSourceFiles(sourceOptions) {
|
|
540
624
|
if (sourceOptions === null || sourceOptions === void 0 ? void 0 : sourceOptions.isExternal) return this.program.getSourceFiles().filter((sf) => {
|
|
@@ -549,77 +633,54 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
549
633
|
if (!propertyDeclaration.type) return false;
|
|
550
634
|
let typeNode = propertyDeclaration.type;
|
|
551
635
|
if (typescript.default.isArrayTypeNode(typeNode)) typeNode = typeNode.elementType;
|
|
552
|
-
if (typescript.default.
|
|
553
|
-
const
|
|
554
|
-
|
|
636
|
+
if (typescript.default.isUnionTypeNode(typeNode)) {
|
|
637
|
+
const nonNullTypes = typeNode.types.filter((t) => t.kind !== typescript.default.SyntaxKind.NullKeyword && t.kind !== typescript.default.SyntaxKind.UndefinedKeyword);
|
|
638
|
+
if (nonNullTypes.length === 1 && nonNullTypes[0]) typeNode = nonNullTypes[0];
|
|
555
639
|
}
|
|
640
|
+
if (typescript.default.isTypeReferenceNode(typeNode)) return !!(this.checker.getTypeAtLocation(typeNode).flags & typescript.default.TypeFlags.EnumLike);
|
|
556
641
|
return false;
|
|
557
642
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
const firstTypeArg = elementType.typeArguments[0];
|
|
568
|
-
if (firstTypeArg) {
|
|
569
|
-
const argSymbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
|
|
570
|
-
if (argSymbol && argSymbol.declarations) {
|
|
571
|
-
if (argSymbol.declarations.some((decl) => typescript.default.isClassDeclaration(decl))) return true;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
const symbol = this.checker.getTypeAtLocation(elementType).getSymbol();
|
|
576
|
-
if (symbol && symbol.declarations) return symbol.declarations.some((decl) => typescript.default.isClassDeclaration(decl));
|
|
577
|
-
return false;
|
|
578
|
-
} else {
|
|
579
|
-
if (typescript.default.isTypeReferenceNode(propertyDeclaration.type) && propertyDeclaration.type.typeArguments && propertyDeclaration.type.typeArguments.length > 0) {
|
|
580
|
-
const firstTypeArg = propertyDeclaration.type.typeArguments[0];
|
|
581
|
-
if (firstTypeArg) {
|
|
582
|
-
const argSymbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
|
|
583
|
-
if (argSymbol && argSymbol.declarations) {
|
|
584
|
-
if (argSymbol.declarations.some((decl) => typescript.default.isClassDeclaration(decl))) return true;
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
const symbol = this.checker.getTypeAtLocation(propertyDeclaration.type).getSymbol();
|
|
589
|
-
if (symbol && symbol.declarations) return symbol.declarations.some((decl) => typescript.default.isClassDeclaration(decl));
|
|
590
|
-
return false;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
getDeclarationProperty(property) {
|
|
594
|
-
if (!property.originalProperty.type) return;
|
|
595
|
-
if (typescript.default.isArrayTypeNode(property.originalProperty.type)) {
|
|
596
|
-
const elementType = property.originalProperty.type.elementType;
|
|
597
|
-
if (typescript.default.isTypeReferenceNode(elementType) && elementType.typeArguments && elementType.typeArguments.length > 0) {
|
|
598
|
-
const firstTypeArg = elementType.typeArguments[0];
|
|
599
|
-
if (firstTypeArg) {
|
|
600
|
-
const argSymbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
|
|
601
|
-
if (argSymbol && argSymbol.declarations) {
|
|
602
|
-
const classDecl = argSymbol.declarations.find((decl) => typescript.default.isClassDeclaration(decl));
|
|
603
|
-
if (classDecl) return classDecl;
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
const symbol = this.checker.getTypeAtLocation(elementType).getSymbol();
|
|
608
|
-
if (symbol && symbol.declarations) return symbol.declarations.find((decl) => typescript.default.isClassDeclaration(decl)) || symbol.declarations[0];
|
|
609
|
-
return;
|
|
610
|
-
}
|
|
611
|
-
if (typescript.default.isTypeReferenceNode(property.originalProperty.type) && property.originalProperty.type.typeArguments && property.originalProperty.type.typeArguments.length > 0) {
|
|
612
|
-
const firstTypeArg = property.originalProperty.type.typeArguments[0];
|
|
643
|
+
/**
|
|
644
|
+
* Resolves a type node to its underlying symbol via the type-checker.
|
|
645
|
+
* For type references with type arguments (e.g., PayloadEntity<Person>),
|
|
646
|
+
* it checks the first type argument for a class declaration first.
|
|
647
|
+
* Returns the ts.Symbol or undefined.
|
|
648
|
+
*/
|
|
649
|
+
resolveClassSymbolFromTypeNode(typeNode) {
|
|
650
|
+
if (typescript.default.isTypeReferenceNode(typeNode) && typeNode.typeArguments && typeNode.typeArguments.length > 0) {
|
|
651
|
+
const firstTypeArg = typeNode.typeArguments[0];
|
|
613
652
|
if (firstTypeArg) {
|
|
614
653
|
const argSymbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
|
|
615
654
|
if (argSymbol && argSymbol.declarations) {
|
|
616
|
-
|
|
617
|
-
if (classDecl) return classDecl;
|
|
655
|
+
if (argSymbol.declarations.some((decl) => typescript.default.isClassDeclaration(decl))) return argSymbol;
|
|
618
656
|
}
|
|
619
657
|
}
|
|
620
658
|
}
|
|
621
|
-
|
|
622
|
-
|
|
659
|
+
return this.checker.getTypeAtLocation(typeNode).getSymbol() ?? void 0;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Resolves the element type node from an array property declaration.
|
|
663
|
+
* Handles both `T[]` and `Array<T>` syntax.
|
|
664
|
+
*/
|
|
665
|
+
resolveArrayElementTypeNode(propertyDeclaration) {
|
|
666
|
+
var _propertyDeclaration$;
|
|
667
|
+
if (!propertyDeclaration.type) return void 0;
|
|
668
|
+
if (typescript.default.isArrayTypeNode(propertyDeclaration.type)) return propertyDeclaration.type.elementType;
|
|
669
|
+
if (typescript.default.isTypeReferenceNode(propertyDeclaration.type) && ((_propertyDeclaration$ = propertyDeclaration.type.typeArguments) === null || _propertyDeclaration$ === void 0 ? void 0 : _propertyDeclaration$[0])) return propertyDeclaration.type.typeArguments[0];
|
|
670
|
+
}
|
|
671
|
+
isClassType(propertyDeclaration) {
|
|
672
|
+
if (!propertyDeclaration.type) return false;
|
|
673
|
+
const typeNode = this.isArrayProperty(propertyDeclaration) ? this.resolveArrayElementTypeNode(propertyDeclaration) : propertyDeclaration.type;
|
|
674
|
+
if (!typeNode) return false;
|
|
675
|
+
const symbol = this.resolveClassSymbolFromTypeNode(typeNode);
|
|
676
|
+
if (symbol && symbol.declarations) return symbol.declarations.some((decl) => typescript.default.isClassDeclaration(decl));
|
|
677
|
+
return false;
|
|
678
|
+
}
|
|
679
|
+
getDeclarationProperty(property) {
|
|
680
|
+
if (!property.originalProperty.type) return;
|
|
681
|
+
const typeNode = typescript.default.isArrayTypeNode(property.originalProperty.type) ? property.originalProperty.type.elementType : property.originalProperty.type;
|
|
682
|
+
const symbol = this.resolveClassSymbolFromTypeNode(typeNode);
|
|
683
|
+
if (symbol && symbol.declarations) return symbol.declarations.find((decl) => typescript.default.isClassDeclaration(decl)) ?? symbol.declarations[0];
|
|
623
684
|
}
|
|
624
685
|
isArrayProperty(propertyDeclaration) {
|
|
625
686
|
if (!propertyDeclaration.type) return false;
|
|
@@ -666,16 +727,28 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
666
727
|
transformedSchema,
|
|
667
728
|
declaration: property.genericClassReference
|
|
668
729
|
});
|
|
669
|
-
else
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
730
|
+
else {
|
|
731
|
+
const inner = {
|
|
732
|
+
type: "object",
|
|
733
|
+
properties: {},
|
|
734
|
+
additionalProperties: true
|
|
735
|
+
};
|
|
736
|
+
schema = property.isArray ? {
|
|
737
|
+
type: "array",
|
|
738
|
+
items: inner
|
|
739
|
+
} : inner;
|
|
740
|
+
}
|
|
741
|
+
else {
|
|
742
|
+
const inner = {
|
|
743
|
+
type: "object",
|
|
744
|
+
properties: {},
|
|
745
|
+
additionalProperties: true
|
|
746
|
+
};
|
|
747
|
+
schema = property.isArray ? {
|
|
748
|
+
type: "array",
|
|
749
|
+
items: inner
|
|
750
|
+
} : inner;
|
|
751
|
+
}
|
|
679
752
|
this.applyDecorators(property, schema);
|
|
680
753
|
return schema;
|
|
681
754
|
}
|
|
@@ -771,7 +844,12 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
771
844
|
if (enumSchema) return enumSchema;
|
|
772
845
|
}
|
|
773
846
|
const propertySchema = { type: "object" };
|
|
774
|
-
|
|
847
|
+
let baseTypeNode = property.originalProperty.type;
|
|
848
|
+
if (property.isArray && baseTypeNode) baseTypeNode = this.resolveArrayElementTypeNode(property.originalProperty) ?? baseTypeNode;
|
|
849
|
+
const resolvedType = this.checker.getTypeAtLocation(baseTypeNode ?? property.originalProperty);
|
|
850
|
+
let propertyType;
|
|
851
|
+
if (resolvedType.flags & typescript.default.TypeFlags.TypeParameter) propertyType = property.type.toLowerCase().replace(/\[\]$/, "").trim();
|
|
852
|
+
else propertyType = this.checker.typeToString(resolvedType).toLowerCase();
|
|
775
853
|
let isFile = false;
|
|
776
854
|
switch (propertyType) {
|
|
777
855
|
case constants.jsPrimitives.String.value:
|
|
@@ -808,17 +886,26 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
808
886
|
propertySchema.type = constants.jsPrimitives.Symbol.value;
|
|
809
887
|
break;
|
|
810
888
|
case constants.jsPrimitives.Object.value:
|
|
889
|
+
case "unknown":
|
|
890
|
+
case "any":
|
|
811
891
|
propertySchema.type = constants.jsPrimitives.Object.value;
|
|
892
|
+
propertySchema.additionalProperties = true;
|
|
812
893
|
break;
|
|
813
894
|
default: propertySchema.type = constants.jsPrimitives.String.value;
|
|
814
895
|
}
|
|
815
896
|
if (property.isArray) {
|
|
816
897
|
delete propertySchema.format;
|
|
817
|
-
propertySchema.type
|
|
818
|
-
|
|
819
|
-
type: isFile ? constants.jsPrimitives.UploadFile.value :
|
|
898
|
+
const resolvedItemType = propertySchema.type;
|
|
899
|
+
const itemSchema = {
|
|
900
|
+
type: isFile ? constants.jsPrimitives.UploadFile.value : resolvedItemType,
|
|
820
901
|
format: isFile ? constants.jsPrimitives.UploadFile.format : propertySchema.format
|
|
821
902
|
};
|
|
903
|
+
if (propertySchema.additionalProperties) {
|
|
904
|
+
itemSchema.additionalProperties = true;
|
|
905
|
+
delete propertySchema.additionalProperties;
|
|
906
|
+
}
|
|
907
|
+
propertySchema.type = `array`;
|
|
908
|
+
propertySchema.items = itemSchema;
|
|
822
909
|
}
|
|
823
910
|
return propertySchema;
|
|
824
911
|
}
|
|
@@ -1016,21 +1103,24 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
1016
1103
|
const callee = node.expression;
|
|
1017
1104
|
if (typescript.default.isIdentifier(callee) && callee.text === "transform" || typescript.default.isPropertyAccessExpression(callee) && callee.name.text === "transform") {
|
|
1018
1105
|
const firstArg = node.arguments[0];
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
const
|
|
1022
|
-
if (
|
|
1106
|
+
if (typescript.default.isExpressionWithTypeArguments(firstArg) && firstArg.typeArguments && firstArg.typeArguments.length > 0) {
|
|
1107
|
+
const baseExpr = firstArg.expression;
|
|
1108
|
+
const className = typescript.default.isIdentifier(baseExpr) ? baseExpr.text : typescript.default.isPropertyAccessExpression(baseExpr) ? baseExpr.name.text : void 0;
|
|
1109
|
+
if (className) {
|
|
1023
1110
|
var _this$classFileIndex$;
|
|
1024
|
-
const classNode = (_this$classFileIndex$ = this.classFileIndex.get(
|
|
1111
|
+
const classNode = (_this$classFileIndex$ = this.classFileIndex.get(className)) === null || _this$classFileIndex$ === void 0 || (_this$classFileIndex$ = _this$classFileIndex$[0]) === null || _this$classFileIndex$ === void 0 ? void 0 : _this$classFileIndex$.node;
|
|
1025
1112
|
if (classNode === null || classNode === void 0 ? void 0 : classNode.typeParameters) {
|
|
1026
1113
|
const typeMap = /* @__PURE__ */ new Map();
|
|
1027
1114
|
classNode.typeParameters.forEach((param, i) => {
|
|
1028
|
-
const typeArg =
|
|
1115
|
+
const typeArg = firstArg.typeArguments[i];
|
|
1029
1116
|
if (typeArg) typeMap.set(param.name.text, this.getTypeNodeToString(typeArg, /* @__PURE__ */ new Map()));
|
|
1030
1117
|
});
|
|
1031
|
-
if (typeMap.size > 0) this.transformCallIndex.set(
|
|
1118
|
+
if (typeMap.size > 0) this.transformCallIndex.set(className, typeMap);
|
|
1032
1119
|
}
|
|
1033
1120
|
}
|
|
1121
|
+
} else {
|
|
1122
|
+
const className = typescript.default.isIdentifier(firstArg) ? firstArg.text : typescript.default.isPropertyAccessExpression(firstArg) ? firstArg.name.text : void 0;
|
|
1123
|
+
if (className) this.nonGenericTransformCalls.add(className);
|
|
1034
1124
|
}
|
|
1035
1125
|
}
|
|
1036
1126
|
}
|
|
@@ -1053,7 +1143,14 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
1053
1143
|
}
|
|
1054
1144
|
};
|
|
1055
1145
|
}
|
|
1056
|
-
const genericTypeMap =
|
|
1146
|
+
const genericTypeMap = /* @__PURE__ */ new Map();
|
|
1147
|
+
if (result.node.typeParameters) {
|
|
1148
|
+
const indexedTypeMap = this.transformCallIndex.get(cls.name);
|
|
1149
|
+
const hasNonGenericCall = this.nonGenericTransformCalls.has(cls.name);
|
|
1150
|
+
const allHaveDefaults = result.node.typeParameters.every((p) => !!p.default);
|
|
1151
|
+
if (indexedTypeMap && !(hasNonGenericCall && allHaveDefaults)) for (const [key, value] of indexedTypeMap) genericTypeMap.set(key, value);
|
|
1152
|
+
else for (const param of result.node.typeParameters) if (param.default) genericTypeMap.set(param.name.text, this.getTypeNodeToString(param.default, /* @__PURE__ */ new Map()));
|
|
1153
|
+
}
|
|
1057
1154
|
const hasGenericArgs = genericTypeMap.size > 0;
|
|
1058
1155
|
if (!hasGenericArgs && this.classCache.has(cls)) return this.classCache.get(cls);
|
|
1059
1156
|
let schema = {
|