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.mjs
CHANGED
|
@@ -3,6 +3,53 @@ import path from "path";
|
|
|
3
3
|
//#region src/transformer.fixtures.ts
|
|
4
4
|
const constants = {
|
|
5
5
|
TS_CONFIG_DEFAULT_PATH: path.resolve(process.cwd(), "tsconfig.json"),
|
|
6
|
+
JS_KEYWORDS: new Set([
|
|
7
|
+
"constructor",
|
|
8
|
+
"static",
|
|
9
|
+
"get",
|
|
10
|
+
"set",
|
|
11
|
+
"async",
|
|
12
|
+
"return",
|
|
13
|
+
"if",
|
|
14
|
+
"else",
|
|
15
|
+
"for",
|
|
16
|
+
"while",
|
|
17
|
+
"switch",
|
|
18
|
+
"case",
|
|
19
|
+
"break",
|
|
20
|
+
"throw",
|
|
21
|
+
"new",
|
|
22
|
+
"delete",
|
|
23
|
+
"typeof",
|
|
24
|
+
"void",
|
|
25
|
+
"try",
|
|
26
|
+
"catch",
|
|
27
|
+
"finally",
|
|
28
|
+
"this",
|
|
29
|
+
"super",
|
|
30
|
+
"class",
|
|
31
|
+
"const",
|
|
32
|
+
"let",
|
|
33
|
+
"var",
|
|
34
|
+
"function",
|
|
35
|
+
"yield",
|
|
36
|
+
"await",
|
|
37
|
+
"import",
|
|
38
|
+
"export",
|
|
39
|
+
"default",
|
|
40
|
+
"extends",
|
|
41
|
+
"implements",
|
|
42
|
+
"in",
|
|
43
|
+
"of",
|
|
44
|
+
"do",
|
|
45
|
+
"with",
|
|
46
|
+
"continue",
|
|
47
|
+
"instanceof",
|
|
48
|
+
"true",
|
|
49
|
+
"false",
|
|
50
|
+
"null",
|
|
51
|
+
"undefined"
|
|
52
|
+
]),
|
|
6
53
|
jsPrimitives: {
|
|
7
54
|
String: {
|
|
8
55
|
type: "String",
|
|
@@ -165,6 +212,7 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
165
212
|
autoCleanup;
|
|
166
213
|
classFileIndex = /* @__PURE__ */ new Map();
|
|
167
214
|
transformCallIndex = /* @__PURE__ */ new Map();
|
|
215
|
+
nonGenericTransformCalls = /* @__PURE__ */ new Set();
|
|
168
216
|
constructor(tsConfigPath = constants.TS_CONFIG_DEFAULT_PATH, options = {}) {
|
|
169
217
|
this.maxCacheSize = options.maxCacheSize ?? 100;
|
|
170
218
|
this.autoCleanup = options.autoCleanup ?? true;
|
|
@@ -185,14 +233,19 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
185
233
|
else {
|
|
186
234
|
const heritageClause = classNode.heritageClauses[0];
|
|
187
235
|
if (heritageClause && heritageClause.token === ts.SyntaxKind.ExtendsKeyword) {
|
|
188
|
-
var _symbol$declarations;
|
|
189
236
|
const type = heritageClause.types[0];
|
|
190
237
|
let properties = [];
|
|
191
238
|
let baseProperties = [];
|
|
192
239
|
if (!type) return [];
|
|
193
240
|
const symbol = this.checker.getSymbolAtLocation(type.expression);
|
|
194
241
|
if (!symbol) return [];
|
|
195
|
-
const
|
|
242
|
+
const realSymbol = symbol && symbol.flags & ts.SymbolFlags.Alias ? this.checker.getAliasedSymbol(symbol) : symbol;
|
|
243
|
+
let declaration;
|
|
244
|
+
if (realSymbol && realSymbol.links && realSymbol.links.type && realSymbol.links.type.symbol && realSymbol.links.type.symbol.declarations) declaration = realSymbol.links.type.symbol.declarations[0];
|
|
245
|
+
else {
|
|
246
|
+
var _realSymbol$declarati;
|
|
247
|
+
declaration = realSymbol === null || realSymbol === void 0 || (_realSymbol$declarati = realSymbol.declarations) === null || _realSymbol$declarati === void 0 ? void 0 : _realSymbol$declarati.find(ts.isClassDeclaration);
|
|
248
|
+
}
|
|
196
249
|
if (declaration && ts.isClassDeclaration(declaration)) {
|
|
197
250
|
const newGenericTypeMap = /* @__PURE__ */ new Map();
|
|
198
251
|
if (declaration.typeParameters && type.typeArguments) declaration.typeParameters.forEach((param, index) => {
|
|
@@ -211,78 +264,77 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
211
264
|
}
|
|
212
265
|
getPropertiesByClassMembers(members, parentClassNode, genericTypeMap = /* @__PURE__ */ new Map()) {
|
|
213
266
|
const properties = [];
|
|
214
|
-
for (const member of members) {
|
|
215
|
-
var
|
|
216
|
-
if (
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (
|
|
233
|
-
const
|
|
234
|
-
if (
|
|
235
|
-
const matches = this.classFileIndex.get(baseTypeName);
|
|
236
|
-
if (matches && matches.length > 0 && matches[0]) genericClassReference = matches[0].node;
|
|
237
|
-
}
|
|
267
|
+
for (const member of members) if (ts.isPropertyDeclaration(member) && (ts.isIdentifier(member.name) || ts.isStringLiteral(member.name))) {
|
|
268
|
+
var _property$originalPro;
|
|
269
|
+
if (member.modifiers) {
|
|
270
|
+
if (member.modifiers.some((m) => m.kind === ts.SyntaxKind.PrivateKeyword || m.kind === ts.SyntaxKind.ProtectedKeyword)) continue;
|
|
271
|
+
}
|
|
272
|
+
const propertyName = member.name.text;
|
|
273
|
+
const type = this.getPropertyType(member, genericTypeMap);
|
|
274
|
+
const decorators = this.extractDecorators(member);
|
|
275
|
+
const isOptional = !!member.questionToken;
|
|
276
|
+
const isGeneric = this.isPropertyTypeGeneric(member);
|
|
277
|
+
const isEnum = this.isEnum(member);
|
|
278
|
+
const isPrimitive = this.isPrimitiveType(type) || isEnum;
|
|
279
|
+
const isClassType = this.isClassType(member);
|
|
280
|
+
const isArray = this.isArrayProperty(member);
|
|
281
|
+
const isTypeLiteral = this.isTypeLiteral(member);
|
|
282
|
+
let genericClassReference = void 0;
|
|
283
|
+
if (isGeneric && !isPrimitive) {
|
|
284
|
+
const baseTypeName = type.replace(/\[\]$/, "").trim();
|
|
285
|
+
if (!this.isPrimitiveType(baseTypeName)) {
|
|
286
|
+
const matches = this.classFileIndex.get(baseTypeName);
|
|
287
|
+
if (matches && matches.length > 0 && matches[0]) genericClassReference = matches[0].node;
|
|
238
288
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
289
|
+
}
|
|
290
|
+
const property = {
|
|
291
|
+
name: propertyName,
|
|
292
|
+
type,
|
|
293
|
+
decorators,
|
|
294
|
+
isOptional,
|
|
295
|
+
isGeneric,
|
|
296
|
+
originalProperty: member,
|
|
297
|
+
isPrimitive,
|
|
298
|
+
isClassType,
|
|
299
|
+
isArray,
|
|
300
|
+
isEnum,
|
|
301
|
+
isRef: false,
|
|
302
|
+
isTypeLiteral,
|
|
303
|
+
genericClassReference
|
|
304
|
+
};
|
|
305
|
+
if (property.isClassType) {
|
|
306
|
+
const declaration = this.getDeclarationProperty(property);
|
|
307
|
+
if (parentClassNode) {
|
|
308
|
+
if (declaration && declaration.name && this.checker.getSymbolAtLocation(declaration.name) === this.checker.getSymbolAtLocation(parentClassNode.name)) property.isRef = true;
|
|
259
309
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
310
|
+
}
|
|
311
|
+
if (property.isTypeLiteral && property.originalProperty.type !== void 0 && ts.isTypeReferenceNode(property.originalProperty.type) && ((_property$originalPro = property.originalProperty.type.typeArguments) === null || _property$originalPro === void 0 ? void 0 : _property$originalPro.length) === 1) {
|
|
312
|
+
const typeArguments = property.originalProperty.type.typeArguments;
|
|
313
|
+
if (typeArguments && typeArguments[0]) {
|
|
314
|
+
const firstTypeArg = typeArguments[0];
|
|
315
|
+
if (ts.isTypeReferenceNode(firstTypeArg)) {
|
|
316
|
+
const symbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
|
|
317
|
+
if (symbol && symbol.declarations) {
|
|
318
|
+
const classDeclaration = symbol.declarations.find((decl) => ts.isClassDeclaration(decl));
|
|
319
|
+
if (classDeclaration && ts.isClassDeclaration(classDeclaration)) property.typeLiteralClassReference = classDeclaration;
|
|
270
320
|
}
|
|
271
321
|
}
|
|
272
322
|
}
|
|
273
|
-
properties.push(property);
|
|
274
323
|
}
|
|
324
|
+
properties.push(property);
|
|
275
325
|
}
|
|
276
326
|
return properties;
|
|
277
327
|
}
|
|
278
328
|
getPropertyType(property, genericTypeMap = /* @__PURE__ */ new Map()) {
|
|
279
329
|
if (property.type) return this.getTypeNodeToString(property.type, genericTypeMap);
|
|
280
330
|
const type = this.checker.getTypeAtLocation(property);
|
|
281
|
-
return this.
|
|
331
|
+
return this.checker.typeToString(type);
|
|
282
332
|
}
|
|
283
333
|
getTypeNodeToString(typeNode, genericTypeMap = /* @__PURE__ */ new Map()) {
|
|
284
|
-
if (ts.isTypeReferenceNode(typeNode)
|
|
285
|
-
|
|
334
|
+
if (ts.isTypeReferenceNode(typeNode)) {
|
|
335
|
+
let typeName;
|
|
336
|
+
if (ts.isIdentifier(typeNode.typeName)) typeName = typeNode.typeName.text;
|
|
337
|
+
else typeName = typeNode.typeName.right.text;
|
|
286
338
|
if (genericTypeMap.has(typeName)) return genericTypeMap.get(typeName);
|
|
287
339
|
if (typeName.toLowerCase() === "uploadfile") return "UploadFile";
|
|
288
340
|
if (typeName.toLowerCase() === "uploadfiledto") return "UploadFileDto";
|
|
@@ -294,7 +346,7 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
294
346
|
}
|
|
295
347
|
return this.resolveGenericType(typeNode);
|
|
296
348
|
}
|
|
297
|
-
return
|
|
349
|
+
return typeName;
|
|
298
350
|
}
|
|
299
351
|
switch (typeNode.kind) {
|
|
300
352
|
case ts.SyntaxKind.StringKeyword: return constants.jsPrimitives.String.type;
|
|
@@ -309,31 +361,31 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
309
361
|
if (meaningfulTypes.length > 0 && meaningfulTypes[0]) return meaningfulTypes[0];
|
|
310
362
|
if (types.length > 0 && types[0]) return types[0];
|
|
311
363
|
return "object";
|
|
312
|
-
default:
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
if (
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
return typeText;
|
|
364
|
+
default: {
|
|
365
|
+
const resolvedType = this.checker.getTypeAtLocation(typeNode);
|
|
366
|
+
const resolved = this.checker.typeToString(resolvedType);
|
|
367
|
+
if (genericTypeMap && genericTypeMap.has(resolved)) return genericTypeMap.get(resolved);
|
|
368
|
+
if (this.isPrimitiveType(resolved)) return resolved;
|
|
369
|
+
if (resolved === "Date") return constants.jsPrimitives.Date.type;
|
|
370
|
+
if (resolved === "Buffer") return constants.jsPrimitives.Buffer.type;
|
|
371
|
+
if (resolved === "Uint8Array") return constants.jsPrimitives.Uint8Array.type;
|
|
372
|
+
return resolved;
|
|
373
|
+
}
|
|
323
374
|
}
|
|
324
375
|
}
|
|
325
376
|
resolveGenericType(typeNode) {
|
|
326
|
-
|
|
377
|
+
let typeName;
|
|
378
|
+
if (ts.isIdentifier(typeNode.typeName)) typeName = typeNode.typeName.text;
|
|
379
|
+
else typeName = typeNode.typeName.right.text;
|
|
327
380
|
const typeArguments = typeNode.typeArguments;
|
|
328
381
|
if (!typeArguments || typeArguments.length === 0) return typeName;
|
|
329
382
|
const type = this.checker.getTypeAtLocation(typeNode);
|
|
330
|
-
|
|
331
|
-
|
|
383
|
+
if (!(type.flags & ts.TypeFlags.Any)) {
|
|
384
|
+
const resolvedType = this.checker.typeToString(type);
|
|
385
|
+
if (resolvedType && resolvedType !== typeName) return resolvedType;
|
|
386
|
+
}
|
|
332
387
|
return typeName;
|
|
333
388
|
}
|
|
334
|
-
getStringFromType(type) {
|
|
335
|
-
return this.checker.typeToString(type);
|
|
336
|
-
}
|
|
337
389
|
extractDecorators(member) {
|
|
338
390
|
const decorators = [];
|
|
339
391
|
if (member.modifiers) {
|
|
@@ -353,6 +405,7 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
353
405
|
}
|
|
354
406
|
getDecoratorName(callExpression) {
|
|
355
407
|
if (ts.isIdentifier(callExpression.expression)) return callExpression.expression.text;
|
|
408
|
+
if (ts.isPropertyAccessExpression(callExpression.expression)) return callExpression.expression.name.text;
|
|
356
409
|
return "unknown";
|
|
357
410
|
}
|
|
358
411
|
getDecoratorArguments(callExpression) {
|
|
@@ -365,7 +418,13 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
365
418
|
});
|
|
366
419
|
}
|
|
367
420
|
getSafeDecoratorArgument(arg) {
|
|
368
|
-
if (arg && typeof arg === "object" && "kind" in arg)
|
|
421
|
+
if (arg && typeof arg === "object" && "kind" in arg) {
|
|
422
|
+
const node = arg;
|
|
423
|
+
const type = this.checker.getTypeAtLocation(node);
|
|
424
|
+
if (type.isNumberLiteral()) return type.value;
|
|
425
|
+
if (type.isStringLiteral()) return type.value;
|
|
426
|
+
return this.checker.typeToString(type);
|
|
427
|
+
}
|
|
369
428
|
return arg;
|
|
370
429
|
}
|
|
371
430
|
isPropertyTypeGeneric(property) {
|
|
@@ -389,15 +448,18 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
389
448
|
isGenericTypeFromSymbol(type) {
|
|
390
449
|
if (this.isSimpleArrayType(type)) return false;
|
|
391
450
|
if (type.aliasTypeArguments && type.aliasTypeArguments.length > 0) return true;
|
|
392
|
-
if (type.
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
451
|
+
if (type.flags & ts.TypeFlags.Object && type.objectFlags & ts.ObjectFlags.Reference) {
|
|
452
|
+
var _typeArgs$;
|
|
453
|
+
const typeArgs = this.checker.getTypeArguments(type);
|
|
454
|
+
if (typeArgs.length > 0 && ((_typeArgs$ = typeArgs[0]) === null || _typeArgs$ === void 0 || (_typeArgs$ = _typeArgs$.getSymbol()) === null || _typeArgs$ === void 0 ? void 0 : _typeArgs$.getName()) === "Array") {
|
|
455
|
+
const symbol = type.getSymbol();
|
|
456
|
+
if (symbol && symbol.getName() === "Array") {
|
|
457
|
+
const elementType = typeArgs[0];
|
|
458
|
+
return elementType ? this.isUtilityTypeFromType(elementType) : false;
|
|
459
|
+
}
|
|
460
|
+
const elementType = typeArgs[0];
|
|
461
|
+
return elementType ? this.isUtilityTypeFromType(elementType) : false;
|
|
398
462
|
}
|
|
399
|
-
const elementType = type.typeArguments[0];
|
|
400
|
-
return this.isUtilityTypeFromType(elementType);
|
|
401
463
|
}
|
|
402
464
|
if (type.flags & ts.TypeFlags.TypeParameter) return true;
|
|
403
465
|
if (type.flags & ts.TypeFlags.Conditional) return true;
|
|
@@ -429,13 +491,18 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
429
491
|
isSimpleArrayType(type) {
|
|
430
492
|
const symbol = type.getSymbol();
|
|
431
493
|
if (!symbol || symbol.getName() !== "Array") return false;
|
|
432
|
-
if (type.
|
|
433
|
-
const
|
|
434
|
-
if (
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
494
|
+
if (type.flags & ts.TypeFlags.Object && type.objectFlags & ts.ObjectFlags.Reference) {
|
|
495
|
+
const typeArgs = this.checker.getTypeArguments(type);
|
|
496
|
+
if (typeArgs.length === 1) {
|
|
497
|
+
const elementType = typeArgs[0];
|
|
498
|
+
if (this.isUtilityTypeFromType(elementType)) return false;
|
|
499
|
+
if (elementType.flags & ts.TypeFlags.Object && elementType.objectFlags & ts.ObjectFlags.Reference) {
|
|
500
|
+
if (this.checker.getTypeArguments(elementType).length > 0) return false;
|
|
501
|
+
}
|
|
502
|
+
const elementSymbol = elementType.getSymbol();
|
|
503
|
+
if (elementSymbol && elementSymbol.getName() !== "Array") return false;
|
|
504
|
+
return true;
|
|
505
|
+
}
|
|
439
506
|
}
|
|
440
507
|
return false;
|
|
441
508
|
}
|
|
@@ -493,23 +560,40 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
493
560
|
return matches[0];
|
|
494
561
|
}
|
|
495
562
|
findBestMatch(cls, matches) {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
563
|
+
const runtimeProps = this.extractRuntimePropertyNames(cls);
|
|
564
|
+
let bestMatch;
|
|
565
|
+
let bestScore = -1;
|
|
566
|
+
for (const match of matches) {
|
|
567
|
+
let score = 0;
|
|
568
|
+
for (const member of match.node.members) if (ts.isPropertyDeclaration(member) && member.name && (ts.isIdentifier(member.name) || ts.isStringLiteral(member.name))) {
|
|
569
|
+
if (runtimeProps.has(member.name.text)) score++;
|
|
570
|
+
}
|
|
571
|
+
if (score > bestScore) {
|
|
572
|
+
bestScore = score;
|
|
573
|
+
bestMatch = match;
|
|
574
|
+
}
|
|
501
575
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
576
|
+
return bestMatch;
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Safely extracts property names from a class constructor without instantiation.
|
|
580
|
+
* Parses the class source via Function.prototype.toString() and inspects
|
|
581
|
+
* the prototype for method names.
|
|
582
|
+
*/
|
|
583
|
+
extractRuntimePropertyNames(cls) {
|
|
584
|
+
const names = /* @__PURE__ */ new Set();
|
|
585
|
+
try {
|
|
586
|
+
const source = Function.prototype.toString.call(cls);
|
|
587
|
+
const thisAssign = /this\.(\w+)\s*=/g;
|
|
588
|
+
let m;
|
|
589
|
+
while ((m = thisAssign.exec(source)) !== null) if (m[1]) names.add(m[1]);
|
|
590
|
+
const classField = /[;}](\w+)(?=[=;}\s])/g;
|
|
591
|
+
while ((m = classField.exec(source)) !== null) if (m[1] && !constants.JS_KEYWORDS.has(m[1])) names.add(m[1]);
|
|
592
|
+
} catch {}
|
|
593
|
+
try {
|
|
594
|
+
for (const name of Object.getOwnPropertyNames(cls.prototype)) if (name !== "constructor") names.add(name);
|
|
595
|
+
} catch {}
|
|
596
|
+
return names;
|
|
513
597
|
}
|
|
514
598
|
getFilteredSourceFiles(sourceOptions) {
|
|
515
599
|
if (sourceOptions === null || sourceOptions === void 0 ? void 0 : sourceOptions.isExternal) return this.program.getSourceFiles().filter((sf) => {
|
|
@@ -524,77 +608,54 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
524
608
|
if (!propertyDeclaration.type) return false;
|
|
525
609
|
let typeNode = propertyDeclaration.type;
|
|
526
610
|
if (ts.isArrayTypeNode(typeNode)) typeNode = typeNode.elementType;
|
|
527
|
-
if (ts.
|
|
528
|
-
const
|
|
529
|
-
|
|
611
|
+
if (ts.isUnionTypeNode(typeNode)) {
|
|
612
|
+
const nonNullTypes = typeNode.types.filter((t) => t.kind !== ts.SyntaxKind.NullKeyword && t.kind !== ts.SyntaxKind.UndefinedKeyword);
|
|
613
|
+
if (nonNullTypes.length === 1 && nonNullTypes[0]) typeNode = nonNullTypes[0];
|
|
530
614
|
}
|
|
615
|
+
if (ts.isTypeReferenceNode(typeNode)) return !!(this.checker.getTypeAtLocation(typeNode).flags & ts.TypeFlags.EnumLike);
|
|
531
616
|
return false;
|
|
532
617
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
const firstTypeArg = elementType.typeArguments[0];
|
|
543
|
-
if (firstTypeArg) {
|
|
544
|
-
const argSymbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
|
|
545
|
-
if (argSymbol && argSymbol.declarations) {
|
|
546
|
-
if (argSymbol.declarations.some((decl) => ts.isClassDeclaration(decl))) return true;
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
const symbol = this.checker.getTypeAtLocation(elementType).getSymbol();
|
|
551
|
-
if (symbol && symbol.declarations) return symbol.declarations.some((decl) => ts.isClassDeclaration(decl));
|
|
552
|
-
return false;
|
|
553
|
-
} else {
|
|
554
|
-
if (ts.isTypeReferenceNode(propertyDeclaration.type) && propertyDeclaration.type.typeArguments && propertyDeclaration.type.typeArguments.length > 0) {
|
|
555
|
-
const firstTypeArg = propertyDeclaration.type.typeArguments[0];
|
|
556
|
-
if (firstTypeArg) {
|
|
557
|
-
const argSymbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
|
|
558
|
-
if (argSymbol && argSymbol.declarations) {
|
|
559
|
-
if (argSymbol.declarations.some((decl) => ts.isClassDeclaration(decl))) return true;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
const symbol = this.checker.getTypeAtLocation(propertyDeclaration.type).getSymbol();
|
|
564
|
-
if (symbol && symbol.declarations) return symbol.declarations.some((decl) => ts.isClassDeclaration(decl));
|
|
565
|
-
return false;
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
getDeclarationProperty(property) {
|
|
569
|
-
if (!property.originalProperty.type) return;
|
|
570
|
-
if (ts.isArrayTypeNode(property.originalProperty.type)) {
|
|
571
|
-
const elementType = property.originalProperty.type.elementType;
|
|
572
|
-
if (ts.isTypeReferenceNode(elementType) && elementType.typeArguments && elementType.typeArguments.length > 0) {
|
|
573
|
-
const firstTypeArg = elementType.typeArguments[0];
|
|
574
|
-
if (firstTypeArg) {
|
|
575
|
-
const argSymbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
|
|
576
|
-
if (argSymbol && argSymbol.declarations) {
|
|
577
|
-
const classDecl = argSymbol.declarations.find((decl) => ts.isClassDeclaration(decl));
|
|
578
|
-
if (classDecl) return classDecl;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
const symbol = this.checker.getTypeAtLocation(elementType).getSymbol();
|
|
583
|
-
if (symbol && symbol.declarations) return symbol.declarations.find((decl) => ts.isClassDeclaration(decl)) || symbol.declarations[0];
|
|
584
|
-
return;
|
|
585
|
-
}
|
|
586
|
-
if (ts.isTypeReferenceNode(property.originalProperty.type) && property.originalProperty.type.typeArguments && property.originalProperty.type.typeArguments.length > 0) {
|
|
587
|
-
const firstTypeArg = property.originalProperty.type.typeArguments[0];
|
|
618
|
+
/**
|
|
619
|
+
* Resolves a type node to its underlying symbol via the type-checker.
|
|
620
|
+
* For type references with type arguments (e.g., PayloadEntity<Person>),
|
|
621
|
+
* it checks the first type argument for a class declaration first.
|
|
622
|
+
* Returns the ts.Symbol or undefined.
|
|
623
|
+
*/
|
|
624
|
+
resolveClassSymbolFromTypeNode(typeNode) {
|
|
625
|
+
if (ts.isTypeReferenceNode(typeNode) && typeNode.typeArguments && typeNode.typeArguments.length > 0) {
|
|
626
|
+
const firstTypeArg = typeNode.typeArguments[0];
|
|
588
627
|
if (firstTypeArg) {
|
|
589
628
|
const argSymbol = this.checker.getTypeAtLocation(firstTypeArg).getSymbol();
|
|
590
629
|
if (argSymbol && argSymbol.declarations) {
|
|
591
|
-
|
|
592
|
-
if (classDecl) return classDecl;
|
|
630
|
+
if (argSymbol.declarations.some((decl) => ts.isClassDeclaration(decl))) return argSymbol;
|
|
593
631
|
}
|
|
594
632
|
}
|
|
595
633
|
}
|
|
596
|
-
|
|
597
|
-
|
|
634
|
+
return this.checker.getTypeAtLocation(typeNode).getSymbol() ?? void 0;
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Resolves the element type node from an array property declaration.
|
|
638
|
+
* Handles both `T[]` and `Array<T>` syntax.
|
|
639
|
+
*/
|
|
640
|
+
resolveArrayElementTypeNode(propertyDeclaration) {
|
|
641
|
+
var _propertyDeclaration$;
|
|
642
|
+
if (!propertyDeclaration.type) return void 0;
|
|
643
|
+
if (ts.isArrayTypeNode(propertyDeclaration.type)) return propertyDeclaration.type.elementType;
|
|
644
|
+
if (ts.isTypeReferenceNode(propertyDeclaration.type) && ((_propertyDeclaration$ = propertyDeclaration.type.typeArguments) === null || _propertyDeclaration$ === void 0 ? void 0 : _propertyDeclaration$[0])) return propertyDeclaration.type.typeArguments[0];
|
|
645
|
+
}
|
|
646
|
+
isClassType(propertyDeclaration) {
|
|
647
|
+
if (!propertyDeclaration.type) return false;
|
|
648
|
+
const typeNode = this.isArrayProperty(propertyDeclaration) ? this.resolveArrayElementTypeNode(propertyDeclaration) : propertyDeclaration.type;
|
|
649
|
+
if (!typeNode) return false;
|
|
650
|
+
const symbol = this.resolveClassSymbolFromTypeNode(typeNode);
|
|
651
|
+
if (symbol && symbol.declarations) return symbol.declarations.some((decl) => ts.isClassDeclaration(decl));
|
|
652
|
+
return false;
|
|
653
|
+
}
|
|
654
|
+
getDeclarationProperty(property) {
|
|
655
|
+
if (!property.originalProperty.type) return;
|
|
656
|
+
const typeNode = ts.isArrayTypeNode(property.originalProperty.type) ? property.originalProperty.type.elementType : property.originalProperty.type;
|
|
657
|
+
const symbol = this.resolveClassSymbolFromTypeNode(typeNode);
|
|
658
|
+
if (symbol && symbol.declarations) return symbol.declarations.find((decl) => ts.isClassDeclaration(decl)) ?? symbol.declarations[0];
|
|
598
659
|
}
|
|
599
660
|
isArrayProperty(propertyDeclaration) {
|
|
600
661
|
if (!propertyDeclaration.type) return false;
|
|
@@ -641,16 +702,28 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
641
702
|
transformedSchema,
|
|
642
703
|
declaration: property.genericClassReference
|
|
643
704
|
});
|
|
644
|
-
else
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
705
|
+
else {
|
|
706
|
+
const inner = {
|
|
707
|
+
type: "object",
|
|
708
|
+
properties: {},
|
|
709
|
+
additionalProperties: true
|
|
710
|
+
};
|
|
711
|
+
schema = property.isArray ? {
|
|
712
|
+
type: "array",
|
|
713
|
+
items: inner
|
|
714
|
+
} : inner;
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
const inner = {
|
|
718
|
+
type: "object",
|
|
719
|
+
properties: {},
|
|
720
|
+
additionalProperties: true
|
|
721
|
+
};
|
|
722
|
+
schema = property.isArray ? {
|
|
723
|
+
type: "array",
|
|
724
|
+
items: inner
|
|
725
|
+
} : inner;
|
|
726
|
+
}
|
|
654
727
|
this.applyDecorators(property, schema);
|
|
655
728
|
return schema;
|
|
656
729
|
}
|
|
@@ -746,7 +819,12 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
746
819
|
if (enumSchema) return enumSchema;
|
|
747
820
|
}
|
|
748
821
|
const propertySchema = { type: "object" };
|
|
749
|
-
|
|
822
|
+
let baseTypeNode = property.originalProperty.type;
|
|
823
|
+
if (property.isArray && baseTypeNode) baseTypeNode = this.resolveArrayElementTypeNode(property.originalProperty) ?? baseTypeNode;
|
|
824
|
+
const resolvedType = this.checker.getTypeAtLocation(baseTypeNode ?? property.originalProperty);
|
|
825
|
+
let propertyType;
|
|
826
|
+
if (resolvedType.flags & ts.TypeFlags.TypeParameter) propertyType = property.type.toLowerCase().replace(/\[\]$/, "").trim();
|
|
827
|
+
else propertyType = this.checker.typeToString(resolvedType).toLowerCase();
|
|
750
828
|
let isFile = false;
|
|
751
829
|
switch (propertyType) {
|
|
752
830
|
case constants.jsPrimitives.String.value:
|
|
@@ -783,17 +861,26 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
783
861
|
propertySchema.type = constants.jsPrimitives.Symbol.value;
|
|
784
862
|
break;
|
|
785
863
|
case constants.jsPrimitives.Object.value:
|
|
864
|
+
case "unknown":
|
|
865
|
+
case "any":
|
|
786
866
|
propertySchema.type = constants.jsPrimitives.Object.value;
|
|
867
|
+
propertySchema.additionalProperties = true;
|
|
787
868
|
break;
|
|
788
869
|
default: propertySchema.type = constants.jsPrimitives.String.value;
|
|
789
870
|
}
|
|
790
871
|
if (property.isArray) {
|
|
791
872
|
delete propertySchema.format;
|
|
792
|
-
propertySchema.type
|
|
793
|
-
|
|
794
|
-
type: isFile ? constants.jsPrimitives.UploadFile.value :
|
|
873
|
+
const resolvedItemType = propertySchema.type;
|
|
874
|
+
const itemSchema = {
|
|
875
|
+
type: isFile ? constants.jsPrimitives.UploadFile.value : resolvedItemType,
|
|
795
876
|
format: isFile ? constants.jsPrimitives.UploadFile.format : propertySchema.format
|
|
796
877
|
};
|
|
878
|
+
if (propertySchema.additionalProperties) {
|
|
879
|
+
itemSchema.additionalProperties = true;
|
|
880
|
+
delete propertySchema.additionalProperties;
|
|
881
|
+
}
|
|
882
|
+
propertySchema.type = `array`;
|
|
883
|
+
propertySchema.items = itemSchema;
|
|
797
884
|
}
|
|
798
885
|
return propertySchema;
|
|
799
886
|
}
|
|
@@ -991,21 +1078,24 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
991
1078
|
const callee = node.expression;
|
|
992
1079
|
if (ts.isIdentifier(callee) && callee.text === "transform" || ts.isPropertyAccessExpression(callee) && callee.name.text === "transform") {
|
|
993
1080
|
const firstArg = node.arguments[0];
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
const
|
|
997
|
-
if (
|
|
1081
|
+
if (ts.isExpressionWithTypeArguments(firstArg) && firstArg.typeArguments && firstArg.typeArguments.length > 0) {
|
|
1082
|
+
const baseExpr = firstArg.expression;
|
|
1083
|
+
const className = ts.isIdentifier(baseExpr) ? baseExpr.text : ts.isPropertyAccessExpression(baseExpr) ? baseExpr.name.text : void 0;
|
|
1084
|
+
if (className) {
|
|
998
1085
|
var _this$classFileIndex$;
|
|
999
|
-
const classNode = (_this$classFileIndex$ = this.classFileIndex.get(
|
|
1086
|
+
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;
|
|
1000
1087
|
if (classNode === null || classNode === void 0 ? void 0 : classNode.typeParameters) {
|
|
1001
1088
|
const typeMap = /* @__PURE__ */ new Map();
|
|
1002
1089
|
classNode.typeParameters.forEach((param, i) => {
|
|
1003
|
-
const typeArg =
|
|
1090
|
+
const typeArg = firstArg.typeArguments[i];
|
|
1004
1091
|
if (typeArg) typeMap.set(param.name.text, this.getTypeNodeToString(typeArg, /* @__PURE__ */ new Map()));
|
|
1005
1092
|
});
|
|
1006
|
-
if (typeMap.size > 0) this.transformCallIndex.set(
|
|
1093
|
+
if (typeMap.size > 0) this.transformCallIndex.set(className, typeMap);
|
|
1007
1094
|
}
|
|
1008
1095
|
}
|
|
1096
|
+
} else {
|
|
1097
|
+
const className = ts.isIdentifier(firstArg) ? firstArg.text : ts.isPropertyAccessExpression(firstArg) ? firstArg.name.text : void 0;
|
|
1098
|
+
if (className) this.nonGenericTransformCalls.add(className);
|
|
1009
1099
|
}
|
|
1010
1100
|
}
|
|
1011
1101
|
}
|
|
@@ -1028,7 +1118,14 @@ var SchemaTransformer = class SchemaTransformer {
|
|
|
1028
1118
|
}
|
|
1029
1119
|
};
|
|
1030
1120
|
}
|
|
1031
|
-
const genericTypeMap =
|
|
1121
|
+
const genericTypeMap = /* @__PURE__ */ new Map();
|
|
1122
|
+
if (result.node.typeParameters) {
|
|
1123
|
+
const indexedTypeMap = this.transformCallIndex.get(cls.name);
|
|
1124
|
+
const hasNonGenericCall = this.nonGenericTransformCalls.has(cls.name);
|
|
1125
|
+
const allHaveDefaults = result.node.typeParameters.every((p) => !!p.default);
|
|
1126
|
+
if (indexedTypeMap && !(hasNonGenericCall && allHaveDefaults)) for (const [key, value] of indexedTypeMap) genericTypeMap.set(key, value);
|
|
1127
|
+
else for (const param of result.node.typeParameters) if (param.default) genericTypeMap.set(param.name.text, this.getTypeNodeToString(param.default, /* @__PURE__ */ new Map()));
|
|
1128
|
+
}
|
|
1032
1129
|
const hasGenericArgs = genericTypeMap.size > 0;
|
|
1033
1130
|
if (!hasGenericArgs && this.classCache.has(cls)) return this.classCache.get(cls);
|
|
1034
1131
|
let schema = {
|