@tsoa-next/cli 8.0.1-dev.43.63e57e36 → 8.0.1-dev.44.a051324b
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.
|
@@ -51,6 +51,8 @@ const propertyTransformer_1 = require("./transformer/propertyTransformer");
|
|
|
51
51
|
const referenceTransformer_1 = require("./transformer/referenceTransformer");
|
|
52
52
|
const localReferenceTypeCache = {};
|
|
53
53
|
const inProgressTypes = {};
|
|
54
|
+
const escapedDoubleQuote = String.raw `\"`;
|
|
55
|
+
const backslash = '\\';
|
|
54
56
|
const hasInitializer = (declaration) => 'initializer' in declaration && declaration.initializer !== undefined;
|
|
55
57
|
const objectHasOwn = Object.hasOwn;
|
|
56
58
|
const getSyntheticOrigin = (symbol) => {
|
|
@@ -71,6 +73,36 @@ const isRefTypeTokenCharacter = (char) => {
|
|
|
71
73
|
const code = char.codePointAt(0) ?? -1;
|
|
72
74
|
return isAsciiLetter(char) || (code >= 48 && code <= 57) || char === '_';
|
|
73
75
|
};
|
|
76
|
+
const readRefTypeToken = (value, startIndex) => {
|
|
77
|
+
let nextIndex = startIndex;
|
|
78
|
+
while (nextIndex < value.length && isRefTypeTokenCharacter(value[nextIndex])) {
|
|
79
|
+
nextIndex += 1;
|
|
80
|
+
}
|
|
81
|
+
if (value[nextIndex] === '?') {
|
|
82
|
+
nextIndex += 1;
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
token: value.slice(startIndex, nextIndex),
|
|
86
|
+
nextIndex,
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
const readRefTypeLiteralTypeSegment = (value, colonIndex) => {
|
|
90
|
+
if (value[colonIndex] !== ':') {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
const typeStart = colonIndex + 1;
|
|
94
|
+
let typeEnd = typeStart;
|
|
95
|
+
while (typeEnd < value.length && isAsciiLetter(value[typeEnd])) {
|
|
96
|
+
typeEnd += 1;
|
|
97
|
+
}
|
|
98
|
+
if (typeEnd === typeStart) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
replacement: `-${value.slice(typeStart, typeEnd)}`,
|
|
103
|
+
nextIndex: typeEnd,
|
|
104
|
+
};
|
|
105
|
+
};
|
|
74
106
|
const replaceTypeLiteralPropertySeparators = (value) => {
|
|
75
107
|
let formatted = '';
|
|
76
108
|
let index = 0;
|
|
@@ -80,29 +112,15 @@ const replaceTypeLiteralPropertySeparators = (value) => {
|
|
|
80
112
|
index += 1;
|
|
81
113
|
continue;
|
|
82
114
|
}
|
|
83
|
-
const
|
|
84
|
-
while (index < value.length && isRefTypeTokenCharacter(value[index])) {
|
|
85
|
-
index += 1;
|
|
86
|
-
}
|
|
87
|
-
if (value[index] === '?') {
|
|
88
|
-
index += 1;
|
|
89
|
-
}
|
|
90
|
-
const token = value.slice(tokenStart, index);
|
|
91
|
-
if (value[index] === ':') {
|
|
92
|
-
const typeStart = index + 1;
|
|
93
|
-
let typeEnd = typeStart;
|
|
94
|
-
while (typeEnd < value.length && isAsciiLetter(value[typeEnd])) {
|
|
95
|
-
typeEnd += 1;
|
|
96
|
-
}
|
|
97
|
-
if (typeEnd > typeStart) {
|
|
98
|
-
formatted += token;
|
|
99
|
-
formatted += '-';
|
|
100
|
-
formatted += value.slice(typeStart, typeEnd);
|
|
101
|
-
index = typeEnd;
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
115
|
+
const { nextIndex, token } = readRefTypeToken(value, index);
|
|
105
116
|
formatted += token;
|
|
117
|
+
const literalTypeSegment = readRefTypeLiteralTypeSegment(value, nextIndex);
|
|
118
|
+
if (literalTypeSegment) {
|
|
119
|
+
formatted += literalTypeSegment.replacement;
|
|
120
|
+
index = literalTypeSegment.nextIndex;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
index = nextIndex;
|
|
106
124
|
}
|
|
107
125
|
return formatted;
|
|
108
126
|
};
|
|
@@ -157,362 +175,300 @@ class TypeResolver {
|
|
|
157
175
|
});
|
|
158
176
|
}
|
|
159
177
|
resolve() {
|
|
160
|
-
const
|
|
161
|
-
const primitiveType = new primitiveTransformer_1.PrimitiveTransformer().transform(this.current.defaultNumberType, this.typeNode,
|
|
178
|
+
const parentJsDocTagNames = this.parentNode ? (0, jsDocUtils_1.getJSDocTagNames)(this.parentNode) : undefined;
|
|
179
|
+
const primitiveType = new primitiveTransformer_1.PrimitiveTransformer().transform(this.current.defaultNumberType, this.typeNode, parentJsDocTagNames);
|
|
162
180
|
if (primitiveType) {
|
|
163
181
|
return primitiveType;
|
|
164
182
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
elementType: new TypeResolver(this.typeNode.elementType, this.current, this.parentNode, this.context).resolve(),
|
|
169
|
-
};
|
|
170
|
-
return arrayMetaType;
|
|
183
|
+
const nonReferenceType = this.resolveNonReferenceTypeNode();
|
|
184
|
+
if (nonReferenceType) {
|
|
185
|
+
return nonReferenceType;
|
|
171
186
|
}
|
|
172
|
-
|
|
173
|
-
|
|
187
|
+
(0, flowUtils_1.throwUnless)(ts.isTypeReferenceNode(this.typeNode), new exceptions_1.GenerateMetadataError(`Unknown type: ${ts.SyntaxKind[this.typeNode.kind]}`, this.typeNode));
|
|
188
|
+
return this.resolveTypeReferenceNode(this.typeNode, this.current, this.context, this.parentNode);
|
|
189
|
+
}
|
|
190
|
+
resolveNonReferenceTypeNode() {
|
|
191
|
+
return (this.resolveArrayTypeNode() ??
|
|
192
|
+
this.resolveRestTypeNode() ??
|
|
193
|
+
this.resolveUnionTypeNode() ??
|
|
194
|
+
this.resolveIntersectionTypeNode() ??
|
|
195
|
+
this.resolveTupleTypeNode() ??
|
|
196
|
+
this.resolveAnyOrUnknownTypeNode() ??
|
|
197
|
+
this.resolveLiteralTypeNode() ??
|
|
198
|
+
this.resolveTypeLiteralNode() ??
|
|
199
|
+
this.resolveObjectKeywordTypeNode() ??
|
|
200
|
+
this.resolveMappedTypeNode() ??
|
|
201
|
+
this.resolveConditionalTypeNode() ??
|
|
202
|
+
this.resolveTypeOperatorTypeNode() ??
|
|
203
|
+
this.resolveIndexedAccessTypeNodeWrapper() ??
|
|
204
|
+
this.resolveTemplateLiteralTypeNode() ??
|
|
205
|
+
this.resolveParenthesizedTypeNode());
|
|
206
|
+
}
|
|
207
|
+
resolveArrayTypeNode() {
|
|
208
|
+
if (!ts.isArrayTypeNode(this.typeNode)) {
|
|
209
|
+
return undefined;
|
|
174
210
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return unionMetaType;
|
|
211
|
+
return {
|
|
212
|
+
dataType: 'array',
|
|
213
|
+
elementType: new TypeResolver(this.typeNode.elementType, this.current, this.parentNode, this.context).resolve(),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
resolveRestTypeNode() {
|
|
217
|
+
if (!ts.isRestTypeNode(this.typeNode)) {
|
|
218
|
+
return undefined;
|
|
184
219
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (ts.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
220
|
+
return new TypeResolver(this.typeNode.type, this.current, this.parentNode, this.context).resolve();
|
|
221
|
+
}
|
|
222
|
+
resolveUnionTypeNode() {
|
|
223
|
+
if (!ts.isUnionTypeNode(this.typeNode)) {
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
dataType: 'union',
|
|
228
|
+
types: this.typeNode.types.map(type => new TypeResolver(type, this.current, this.parentNode, this.context).resolve()),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
resolveIntersectionTypeNode() {
|
|
232
|
+
if (!ts.isIntersectionTypeNode(this.typeNode)) {
|
|
233
|
+
return undefined;
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
dataType: 'intersection',
|
|
237
|
+
types: this.typeNode.types.filter(type => !this.isIoTsBrandMarker(type, this.current.typeChecker)).map(type => new TypeResolver(type, this.current, this.parentNode, this.context).resolve()),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
resolveTupleTypeNode() {
|
|
241
|
+
if (!ts.isTupleTypeNode(this.typeNode)) {
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
const elementTypes = [];
|
|
245
|
+
let restType;
|
|
246
|
+
for (const element of this.typeNode.elements) {
|
|
247
|
+
if (ts.isRestTypeNode(element)) {
|
|
248
|
+
restType = this.resolveTupleRestTypeNode(element);
|
|
249
|
+
continue;
|
|
215
250
|
}
|
|
251
|
+
const typeNode = ts.isNamedTupleMember(element) ? element.type : element;
|
|
252
|
+
elementTypes.push(new TypeResolver(typeNode, this.current, element, this.context).resolve());
|
|
253
|
+
}
|
|
254
|
+
return {
|
|
255
|
+
dataType: 'tuple',
|
|
256
|
+
types: elementTypes,
|
|
257
|
+
...(restType ? { restType } : {}),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
resolveTupleRestTypeNode(element) {
|
|
261
|
+
const resolvedRest = new TypeResolver(element.type, this.current, element, this.context).resolve();
|
|
262
|
+
return resolvedRest.dataType === 'array' ? resolvedRest.elementType : resolvedRest;
|
|
263
|
+
}
|
|
264
|
+
resolveAnyOrUnknownTypeNode() {
|
|
265
|
+
if (this.typeNode.kind !== ts.SyntaxKind.AnyKeyword && this.typeNode.kind !== ts.SyntaxKind.UnknownKeyword) {
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
return { dataType: 'any' };
|
|
269
|
+
}
|
|
270
|
+
resolveLiteralTypeNode() {
|
|
271
|
+
if (!ts.isLiteralTypeNode(this.typeNode)) {
|
|
272
|
+
return undefined;
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
dataType: 'enum',
|
|
276
|
+
enums: [this.getLiteralValue(this.typeNode)],
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
resolveTypeLiteralNode() {
|
|
280
|
+
if (!ts.isTypeLiteralNode(this.typeNode)) {
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
const properties = this.typeNode.members.filter(ts.isPropertySignature).reduce((result, propertySignature) => [this.resolveTypeLiteralProperty(propertySignature), ...result], []);
|
|
284
|
+
return {
|
|
285
|
+
additionalProperties: this.resolveTypeLiteralAdditionalProperties(this.typeNode),
|
|
286
|
+
dataType: 'nestedObjectLiteral',
|
|
287
|
+
properties,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
resolveTypeLiteralProperty(propertySignature) {
|
|
291
|
+
return {
|
|
292
|
+
example: this.getNodeExample(propertySignature),
|
|
293
|
+
default: TypeResolver.getDefault(propertySignature),
|
|
294
|
+
description: this.getNodeDescription(propertySignature),
|
|
295
|
+
format: this.getNodeFormat(propertySignature),
|
|
296
|
+
name: this.getPropertyName(propertySignature),
|
|
297
|
+
required: !propertySignature.questionToken,
|
|
298
|
+
type: new TypeResolver(propertySignature.type, this.current, propertySignature, this.context).resolve(),
|
|
299
|
+
validators: (0, validatorUtils_1.getPropertyValidators)(propertySignature) || {},
|
|
300
|
+
deprecated: (0, jsDocUtils_1.isExistJSDocTag)(propertySignature, tag => tag.tagName.text === 'deprecated'),
|
|
301
|
+
title: this.getNodeTitle(propertySignature),
|
|
302
|
+
extensions: this.getNodeExtension(propertySignature),
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
resolveTypeLiteralAdditionalProperties(typeLiteralNode) {
|
|
306
|
+
const indexMember = typeLiteralNode.members.find(member => ts.isIndexSignatureDeclaration(member));
|
|
307
|
+
if (!indexMember) {
|
|
308
|
+
return undefined;
|
|
309
|
+
}
|
|
310
|
+
const indexType = new TypeResolver(indexMember.parameters[0].type, this.current, this.parentNode, this.context).resolve();
|
|
311
|
+
(0, flowUtils_1.throwUnless)(indexType.dataType === 'string', new exceptions_1.GenerateMetadataError(`Only string indexers are supported.`, this.typeNode));
|
|
312
|
+
return new TypeResolver(indexMember.type, this.current, this.parentNode, this.context).resolve();
|
|
313
|
+
}
|
|
314
|
+
resolveObjectKeywordTypeNode() {
|
|
315
|
+
if (this.typeNode.kind !== ts.SyntaxKind.ObjectKeyword) {
|
|
316
|
+
return undefined;
|
|
317
|
+
}
|
|
318
|
+
return { dataType: 'object' };
|
|
319
|
+
}
|
|
320
|
+
resolveMappedTypeNode() {
|
|
321
|
+
if (!ts.isMappedTypeNode(this.typeNode)) {
|
|
322
|
+
return undefined;
|
|
323
|
+
}
|
|
324
|
+
return this.resolveMappedType(this.getReferencer(), this.typeNode);
|
|
325
|
+
}
|
|
326
|
+
getOriginalMappedDeclaration(prop) {
|
|
327
|
+
const declaration = prop.declarations?.[0];
|
|
328
|
+
if (declaration) {
|
|
329
|
+
return declaration;
|
|
330
|
+
}
|
|
331
|
+
const syntheticOrigin = getSyntheticOrigin(prop);
|
|
332
|
+
if (syntheticOrigin?.name === prop.name) {
|
|
333
|
+
// Otherwise loses jsDoc like in intellisense.
|
|
334
|
+
return syntheticOrigin.declarations?.[0];
|
|
335
|
+
}
|
|
336
|
+
return undefined;
|
|
337
|
+
}
|
|
338
|
+
isIgnoredMappedProperty(prop) {
|
|
339
|
+
const declaration = this.getOriginalMappedDeclaration(prop);
|
|
340
|
+
if (!declaration) {
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
const ignoredTargets = !ts.isPropertyDeclaration(declaration) && !ts.isPropertySignature(declaration) && !ts.isParameter(declaration);
|
|
344
|
+
return (0, jsDocUtils_1.getJSDocTagNames)(declaration).includes('ignore') || ignoredTargets;
|
|
345
|
+
}
|
|
346
|
+
resolveMappedType(type, mappedTypeNode) {
|
|
347
|
+
if (this.hasFlag(type, ts.TypeFlags.Union)) {
|
|
216
348
|
return {
|
|
217
|
-
dataType: '
|
|
218
|
-
types:
|
|
219
|
-
...(restType ? { restType } : {}),
|
|
349
|
+
dataType: 'union',
|
|
350
|
+
types: type.types.map(unionType => this.resolveMappedType(unionType, mappedTypeNode)),
|
|
220
351
|
};
|
|
221
352
|
}
|
|
222
|
-
if (this.
|
|
223
|
-
|
|
224
|
-
dataType: 'any',
|
|
225
|
-
};
|
|
226
|
-
return literallyAny;
|
|
353
|
+
if (this.hasFlag(type, ts.TypeFlags.Undefined)) {
|
|
354
|
+
return { dataType: 'undefined' };
|
|
227
355
|
}
|
|
228
|
-
if (
|
|
229
|
-
|
|
356
|
+
if (this.hasFlag(type, ts.TypeFlags.Null)) {
|
|
357
|
+
return {
|
|
230
358
|
dataType: 'enum',
|
|
231
|
-
enums: [
|
|
232
|
-
};
|
|
233
|
-
return enumType;
|
|
234
|
-
}
|
|
235
|
-
if (ts.isTypeLiteralNode(this.typeNode)) {
|
|
236
|
-
const properties = this.typeNode.members.filter(ts.isPropertySignature).reduce((res, propertySignature) => {
|
|
237
|
-
const type = new TypeResolver(propertySignature.type, this.current, propertySignature, this.context).resolve();
|
|
238
|
-
const def = TypeResolver.getDefault(propertySignature);
|
|
239
|
-
const property = {
|
|
240
|
-
example: this.getNodeExample(propertySignature),
|
|
241
|
-
default: def,
|
|
242
|
-
description: this.getNodeDescription(propertySignature),
|
|
243
|
-
format: this.getNodeFormat(propertySignature),
|
|
244
|
-
name: this.getPropertyName(propertySignature),
|
|
245
|
-
required: !propertySignature.questionToken,
|
|
246
|
-
type,
|
|
247
|
-
validators: (0, validatorUtils_1.getPropertyValidators)(propertySignature) || {},
|
|
248
|
-
deprecated: (0, jsDocUtils_1.isExistJSDocTag)(propertySignature, tag => tag.tagName.text === 'deprecated'),
|
|
249
|
-
title: this.getNodeTitle(propertySignature),
|
|
250
|
-
extensions: this.getNodeExtension(propertySignature),
|
|
251
|
-
};
|
|
252
|
-
return [property, ...res];
|
|
253
|
-
}, []);
|
|
254
|
-
const indexMember = this.typeNode.members.find(member => ts.isIndexSignatureDeclaration(member));
|
|
255
|
-
let additionalType;
|
|
256
|
-
if (indexMember) {
|
|
257
|
-
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
|
|
258
|
-
const indexSignatureDeclaration = indexMember;
|
|
259
|
-
const indexType = new TypeResolver(indexSignatureDeclaration.parameters[0].type, this.current, this.parentNode, this.context).resolve();
|
|
260
|
-
(0, flowUtils_1.throwUnless)(indexType.dataType === 'string', new exceptions_1.GenerateMetadataError(`Only string indexers are supported.`, this.typeNode));
|
|
261
|
-
additionalType = new TypeResolver(indexSignatureDeclaration.type, this.current, this.parentNode, this.context).resolve();
|
|
262
|
-
}
|
|
263
|
-
const objLiteral = {
|
|
264
|
-
additionalProperties: indexMember && additionalType,
|
|
265
|
-
dataType: 'nestedObjectLiteral',
|
|
266
|
-
properties,
|
|
359
|
+
enums: [null],
|
|
267
360
|
};
|
|
268
|
-
return objLiteral;
|
|
269
361
|
}
|
|
270
|
-
if (this.
|
|
271
|
-
return
|
|
362
|
+
if (this.hasFlag(type, ts.TypeFlags.Object)) {
|
|
363
|
+
return this.resolveMappedObjectType(type, mappedTypeNode);
|
|
272
364
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (this.hasFlag(type, ts.TypeFlags.Union)) {
|
|
294
|
-
//Intersections are not interesting somehow...
|
|
295
|
-
const types = type.types;
|
|
296
|
-
const resolvedTypes = types.map(calcMappedType);
|
|
297
|
-
return {
|
|
298
|
-
dataType: 'union',
|
|
299
|
-
types: resolvedTypes,
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
else if (this.hasFlag(type, ts.TypeFlags.Undefined)) {
|
|
303
|
-
return {
|
|
304
|
-
dataType: 'undefined',
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
else if (this.hasFlag(type, ts.TypeFlags.Null)) {
|
|
308
|
-
return {
|
|
309
|
-
dataType: 'enum',
|
|
310
|
-
enums: [null],
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
else if (this.hasFlag(type, ts.TypeFlags.Object)) {
|
|
314
|
-
const typeProperties = type.getProperties();
|
|
315
|
-
const properties = typeProperties
|
|
316
|
-
// Ignore methods, getter, setter and @ignored props
|
|
317
|
-
.filter(property => isIgnored(property) === false)
|
|
318
|
-
// Transform to property
|
|
319
|
-
.map(property => {
|
|
320
|
-
const propertyType = this.current.typeChecker.getTypeOfSymbolAtLocation(property, this.typeNode);
|
|
321
|
-
const typeNode = this.current.typeChecker.typeToTypeNode(propertyType, undefined, ts.NodeBuilderFlags.NoTruncation);
|
|
322
|
-
const parent = getOneOrigDeclaration(property); //If there are more declarations, we need to get one of them, from where we want to recognize jsDoc
|
|
323
|
-
const type = new TypeResolver(typeNode, this.current, parent, this.context, propertyType).resolve();
|
|
324
|
-
const required = !this.hasFlag(property, ts.SymbolFlags.Optional);
|
|
325
|
-
const comments = property.getDocumentationComment(this.current.typeChecker);
|
|
326
|
-
const description = comments.length ? ts.displayPartsToString(comments) : undefined;
|
|
327
|
-
const initializer = parent && hasInitializer(parent) ? parent.initializer : undefined;
|
|
328
|
-
let def;
|
|
329
|
-
if (initializer) {
|
|
330
|
-
def = (0, initializer_value_1.getInitializerValue)(initializer, this.current.typeChecker);
|
|
331
|
-
}
|
|
332
|
-
else if (parent) {
|
|
333
|
-
def = TypeResolver.getDefault(parent);
|
|
334
|
-
}
|
|
335
|
-
// Push property
|
|
336
|
-
return {
|
|
337
|
-
name: property.getName(),
|
|
338
|
-
required,
|
|
339
|
-
deprecated: parent
|
|
340
|
-
? (0, jsDocUtils_1.isExistJSDocTag)(parent, tag => tag.tagName.text === 'deprecated') || (0, decoratorUtils_1.isDecorator)(parent, (_identifier, canonicalName) => canonicalName === 'Deprecated', this.current.typeChecker)
|
|
341
|
-
: false,
|
|
342
|
-
type,
|
|
343
|
-
default: def,
|
|
344
|
-
// validators are disjunct via types, so it is now OK.
|
|
345
|
-
// if a type not changes while mapping, we need validators
|
|
346
|
-
// if a type changes, then the validators will be not relevant
|
|
347
|
-
validators: (parent ? (0, validatorUtils_1.getPropertyValidators)(parent) : {}) || {},
|
|
348
|
-
description,
|
|
349
|
-
format: parent ? this.getNodeFormat(parent) : undefined,
|
|
350
|
-
example: parent ? this.getNodeExample(parent) : undefined,
|
|
351
|
-
extensions: parent ? this.getNodeExtension(parent) : undefined,
|
|
352
|
-
};
|
|
353
|
-
});
|
|
354
|
-
const objectLiteral = {
|
|
355
|
-
dataType: 'nestedObjectLiteral',
|
|
356
|
-
properties,
|
|
357
|
-
};
|
|
358
|
-
const indexInfos = this.current.typeChecker.getIndexInfosOfType(type);
|
|
359
|
-
const indexTypes = indexInfos.flatMap(indexInfo => {
|
|
360
|
-
const typeNode = this.current.typeChecker.typeToTypeNode(indexInfo.type, undefined, ts.NodeBuilderFlags.NoTruncation);
|
|
361
|
-
if (typeNode.kind === ts.SyntaxKind.NeverKeyword) {
|
|
362
|
-
// { [k: string]: never; }
|
|
363
|
-
return [];
|
|
364
|
-
}
|
|
365
|
-
const type = new TypeResolver(typeNode, this.current, mappedTypeNode, this.context, indexInfo.type).resolve();
|
|
366
|
-
return [type];
|
|
367
|
-
});
|
|
368
|
-
if (indexTypes.length) {
|
|
369
|
-
if (indexTypes.length === 1) {
|
|
370
|
-
objectLiteral.additionalProperties = indexTypes[0];
|
|
371
|
-
}
|
|
372
|
-
else {
|
|
373
|
-
// { [k: string]: string; } & { [k: number]: number; }
|
|
374
|
-
// A | B is sometimes A type or B type, sometimes optionally accepts both A & B members.
|
|
375
|
-
// Most people & TSOA thinks that A | B can be only A or only B.
|
|
376
|
-
// So we can accept this merge
|
|
377
|
-
//Every additional property key assumed as string
|
|
378
|
-
objectLiteral.additionalProperties = {
|
|
379
|
-
dataType: 'union',
|
|
380
|
-
types: indexTypes,
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
return objectLiteral;
|
|
385
|
-
}
|
|
386
|
-
// Known issues & easy to implement: Partial<string>, Partial<never>, ... But I think a programmer not writes types like this
|
|
387
|
-
throw new exceptions_1.GenerateMetadataError(`Unhandled mapped type has found, flags: ${type.flags}`, this.typeNode);
|
|
388
|
-
};
|
|
389
|
-
const referencer = this.getReferencer();
|
|
390
|
-
const result = calcMappedType(referencer);
|
|
391
|
-
return result;
|
|
392
|
-
}
|
|
393
|
-
if (ts.isConditionalTypeNode(this.typeNode)) {
|
|
394
|
-
const referencer = this.getReferencer();
|
|
395
|
-
const resolvedNode = this.current.typeChecker.typeToTypeNode(referencer, undefined, ts.NodeBuilderFlags.NoTruncation);
|
|
396
|
-
return new TypeResolver(resolvedNode, this.current, this.typeNode, this.context, referencer).resolve();
|
|
397
|
-
}
|
|
398
|
-
// keyof & readonly arrays
|
|
399
|
-
if (ts.isTypeOperatorNode(this.typeNode)) {
|
|
400
|
-
return this.resolveTypeOperatorNode(this.typeNode, this.current.typeChecker, this.current, this.context, this.parentNode, this.referencer);
|
|
401
|
-
}
|
|
402
|
-
// Indexed type
|
|
403
|
-
if (ts.isIndexedAccessTypeNode(this.typeNode)) {
|
|
404
|
-
return this.resolveIndexedAccessTypeNode(this.typeNode, this.current.typeChecker, this.current, this.context);
|
|
405
|
-
}
|
|
406
|
-
if (ts.isTemplateLiteralTypeNode(this.typeNode)) {
|
|
407
|
-
const type = this.getReferencer();
|
|
408
|
-
(0, flowUtils_1.throwUnless)(type.isUnion() && type.types.every((unionElementType) => unionElementType.isStringLiteral()), new exceptions_1.GenerateMetadataError(`Could not the type of ${this.current.typeChecker.typeToString(this.current.typeChecker.getTypeFromTypeNode(this.typeNode), this.typeNode)}`, this.typeNode));
|
|
409
|
-
// `a${'c' | 'd'}b`
|
|
410
|
-
const stringLiteralEnum = {
|
|
411
|
-
dataType: 'enum',
|
|
412
|
-
enums: type.types.map((stringLiteralType) => stringLiteralType.value),
|
|
365
|
+
// Known issues & easy to implement: Partial<string>, Partial<never>, ...
|
|
366
|
+
throw new exceptions_1.GenerateMetadataError(`Unhandled mapped type has found, flags: ${type.flags}`, this.typeNode);
|
|
367
|
+
}
|
|
368
|
+
resolveMappedObjectType(type, mappedTypeNode) {
|
|
369
|
+
const properties = type
|
|
370
|
+
.getProperties()
|
|
371
|
+
.filter(property => !this.isIgnoredMappedProperty(property))
|
|
372
|
+
.map(property => this.resolveMappedProperty(property));
|
|
373
|
+
const objectLiteral = {
|
|
374
|
+
dataType: 'nestedObjectLiteral',
|
|
375
|
+
properties,
|
|
376
|
+
};
|
|
377
|
+
const indexTypes = this.resolveMappedIndexTypes(type, mappedTypeNode);
|
|
378
|
+
if (indexTypes.length === 1) {
|
|
379
|
+
objectLiteral.additionalProperties = indexTypes[0];
|
|
380
|
+
}
|
|
381
|
+
else if (indexTypes.length > 1) {
|
|
382
|
+
objectLiteral.additionalProperties = {
|
|
383
|
+
dataType: 'union',
|
|
384
|
+
types: indexTypes,
|
|
413
385
|
};
|
|
414
|
-
return stringLiteralEnum;
|
|
415
386
|
}
|
|
416
|
-
|
|
417
|
-
|
|
387
|
+
return objectLiteral;
|
|
388
|
+
}
|
|
389
|
+
resolveMappedProperty(property) {
|
|
390
|
+
const propertyType = this.current.typeChecker.getTypeOfSymbolAtLocation(property, this.typeNode);
|
|
391
|
+
const typeNode = this.current.typeChecker.typeToTypeNode(propertyType, undefined, ts.NodeBuilderFlags.NoTruncation);
|
|
392
|
+
const parent = this.getOriginalMappedDeclaration(property);
|
|
393
|
+
const comments = property.getDocumentationComment(this.current.typeChecker);
|
|
394
|
+
return {
|
|
395
|
+
name: property.getName(),
|
|
396
|
+
required: !this.hasFlag(property, ts.SymbolFlags.Optional),
|
|
397
|
+
deprecated: this.isDeprecatedMappedProperty(parent),
|
|
398
|
+
type: new TypeResolver(typeNode, this.current, parent, this.context, propertyType).resolve(),
|
|
399
|
+
default: this.getMappedPropertyDefault(parent),
|
|
400
|
+
validators: (parent ? (0, validatorUtils_1.getPropertyValidators)(parent) : {}) || {},
|
|
401
|
+
description: comments.length ? ts.displayPartsToString(comments) : undefined,
|
|
402
|
+
format: parent ? this.getNodeFormat(parent) : undefined,
|
|
403
|
+
example: parent ? this.getNodeExample(parent) : undefined,
|
|
404
|
+
extensions: parent ? this.getNodeExtension(parent) : undefined,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
isDeprecatedMappedProperty(parent) {
|
|
408
|
+
if (!parent) {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
return (0, jsDocUtils_1.isExistJSDocTag)(parent, tag => tag.tagName.text === 'deprecated') || (0, decoratorUtils_1.isDecorator)(parent, (_identifier, canonicalName) => canonicalName === 'Deprecated', this.current.typeChecker);
|
|
412
|
+
}
|
|
413
|
+
getMappedPropertyDefault(parent) {
|
|
414
|
+
if (!parent) {
|
|
415
|
+
return undefined;
|
|
416
|
+
}
|
|
417
|
+
if (hasInitializer(parent)) {
|
|
418
|
+
return (0, initializer_value_1.getInitializerValue)(parent.initializer, this.current.typeChecker);
|
|
418
419
|
}
|
|
419
|
-
|
|
420
|
-
|
|
420
|
+
return TypeResolver.getDefault(parent);
|
|
421
|
+
}
|
|
422
|
+
resolveMappedIndexTypes(type, mappedTypeNode) {
|
|
423
|
+
return this.current.typeChecker.getIndexInfosOfType(type).flatMap(indexInfo => {
|
|
424
|
+
const typeNode = this.current.typeChecker.typeToTypeNode(indexInfo.type, undefined, ts.NodeBuilderFlags.NoTruncation);
|
|
425
|
+
if (typeNode.kind === ts.SyntaxKind.NeverKeyword) {
|
|
426
|
+
return [];
|
|
427
|
+
}
|
|
428
|
+
return [new TypeResolver(typeNode, this.current, mappedTypeNode, this.context, indexInfo.type).resolve()];
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
resolveConditionalTypeNode() {
|
|
432
|
+
if (!ts.isConditionalTypeNode(this.typeNode)) {
|
|
433
|
+
return undefined;
|
|
434
|
+
}
|
|
435
|
+
const referencer = this.getReferencer();
|
|
436
|
+
const resolvedNode = this.current.typeChecker.typeToTypeNode(referencer, undefined, ts.NodeBuilderFlags.NoTruncation);
|
|
437
|
+
return new TypeResolver(resolvedNode, this.current, this.typeNode, this.context, referencer).resolve();
|
|
438
|
+
}
|
|
439
|
+
resolveTypeOperatorTypeNode() {
|
|
440
|
+
if (!ts.isTypeOperatorNode(this.typeNode)) {
|
|
441
|
+
return undefined;
|
|
442
|
+
}
|
|
443
|
+
return this.resolveTypeOperatorNode(this.typeNode, this.current.typeChecker, this.current, this.context, this.parentNode, this.referencer);
|
|
444
|
+
}
|
|
445
|
+
resolveIndexedAccessTypeNodeWrapper() {
|
|
446
|
+
if (!ts.isIndexedAccessTypeNode(this.typeNode)) {
|
|
447
|
+
return undefined;
|
|
448
|
+
}
|
|
449
|
+
return this.resolveIndexedAccessTypeNode(this.typeNode, this.current.typeChecker, this.current, this.context);
|
|
450
|
+
}
|
|
451
|
+
resolveTemplateLiteralTypeNode() {
|
|
452
|
+
if (!ts.isTemplateLiteralTypeNode(this.typeNode)) {
|
|
453
|
+
return undefined;
|
|
454
|
+
}
|
|
455
|
+
const type = this.getReferencer();
|
|
456
|
+
(0, flowUtils_1.throwUnless)(type.isUnion() && type.types.every((unionElementType) => unionElementType.isStringLiteral()), new exceptions_1.GenerateMetadataError(`Could not the type of ${this.current.typeChecker.typeToString(this.current.typeChecker.getTypeFromTypeNode(this.typeNode), this.typeNode)}`, this.typeNode));
|
|
457
|
+
return {
|
|
458
|
+
dataType: 'enum',
|
|
459
|
+
enums: type.types.map((stringLiteralType) => stringLiteralType.value),
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
resolveParenthesizedTypeNode() {
|
|
463
|
+
if (!ts.isParenthesizedTypeNode(this.typeNode)) {
|
|
464
|
+
return undefined;
|
|
465
|
+
}
|
|
466
|
+
return new TypeResolver(this.typeNode.type, this.current, this.typeNode, this.context, this.referencer).resolve();
|
|
421
467
|
}
|
|
422
468
|
resolveTypeOperatorNode(typeNode, typeChecker, current, context, parentNode, referencer) {
|
|
423
469
|
switch (typeNode.operator) {
|
|
424
470
|
case ts.SyntaxKind.KeyOfKeyword: {
|
|
425
|
-
|
|
426
|
-
const type = typeChecker.getTypeFromTypeNode(typeNode);
|
|
427
|
-
if (type.isIndexType()) {
|
|
428
|
-
// in case of generic: keyof T. Not handles all possible cases
|
|
429
|
-
const symbol = type.type.getSymbol();
|
|
430
|
-
if (symbol && symbol.getFlags() & ts.TypeFlags.TypeParameter) {
|
|
431
|
-
const typeName = symbol.getEscapedName();
|
|
432
|
-
(0, flowUtils_1.throwUnless)(typeof typeName === 'string', new exceptions_1.GenerateMetadataError(`typeName is not string, but ${typeof typeName}`, typeNode));
|
|
433
|
-
if (context[typeName]) {
|
|
434
|
-
const subResult = new TypeResolver(context[typeName].type, current, parentNode, context).resolve();
|
|
435
|
-
if (subResult.dataType === 'any') {
|
|
436
|
-
return {
|
|
437
|
-
dataType: 'union',
|
|
438
|
-
types: [{ dataType: 'string' }, { dataType: 'double' }],
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
const properties = subResult.properties?.map(v => v.name);
|
|
442
|
-
(0, flowUtils_1.throwUnless)(properties, new exceptions_1.GenerateMetadataError(`TypeOperator 'keyof' on node which have no properties`, context[typeName].type));
|
|
443
|
-
return {
|
|
444
|
-
dataType: 'enum',
|
|
445
|
-
enums: properties,
|
|
446
|
-
};
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
else if (type.isUnion()) {
|
|
451
|
-
const literals = type.types.filter((t) => t.isLiteral());
|
|
452
|
-
const literalValues = [];
|
|
453
|
-
for (const literal of literals) {
|
|
454
|
-
(0, flowUtils_1.throwUnless)(typeof literal.value == 'number' || typeof literal.value == 'string', new exceptions_1.GenerateMetadataError(`Not handled key Type, maybe ts.PseudoBigInt ${typeChecker.typeToString(literal)}`, typeNode));
|
|
455
|
-
literalValues.push(literal.value);
|
|
456
|
-
}
|
|
457
|
-
if (!literals.length) {
|
|
458
|
-
const length = type.types.length;
|
|
459
|
-
const someStringFlag = type.types.some(t => t.flags === ts.TypeFlags.String);
|
|
460
|
-
const someNumberFlag = type.types.some(t => t.flags === ts.TypeFlags.Number);
|
|
461
|
-
const someSymbolFlag = type.types.some(t => t.flags === ts.TypeFlags.ESSymbol);
|
|
462
|
-
if (someStringFlag && someNumberFlag) {
|
|
463
|
-
if (length === 2 || (length === 3 && someSymbolFlag)) {
|
|
464
|
-
return {
|
|
465
|
-
dataType: 'union',
|
|
466
|
-
types: [{ dataType: 'string' }, { dataType: 'double' }],
|
|
467
|
-
};
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
// Warn on nonsense (`number`, `typeof Symbol.iterator`)
|
|
472
|
-
if (type.types.some(t => !t.isLiteral())) {
|
|
473
|
-
const problems = type.types.filter(t => !t.isLiteral()).map(t => typeChecker.typeToString(t));
|
|
474
|
-
console.warn(new exceptions_1.GenerateMetaDataWarning(`Skipped non-literal type(s) ${problems.join(', ')}`, typeNode).toString());
|
|
475
|
-
}
|
|
476
|
-
const stringMembers = literalValues.filter(v => typeof v == 'string');
|
|
477
|
-
const numberMembers = literalValues.filter(v => typeof v == 'number');
|
|
478
|
-
if (stringMembers.length && numberMembers.length) {
|
|
479
|
-
return {
|
|
480
|
-
dataType: 'union',
|
|
481
|
-
types: [
|
|
482
|
-
{ dataType: 'enum', enums: stringMembers },
|
|
483
|
-
{ dataType: 'enum', enums: numberMembers },
|
|
484
|
-
],
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
return {
|
|
488
|
-
dataType: 'enum',
|
|
489
|
-
enums: literalValues,
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
else if (type.isLiteral()) {
|
|
493
|
-
(0, flowUtils_1.throwUnless)(typeof type.value == 'number' || typeof type.value == 'string', new exceptions_1.GenerateMetadataError(`Not handled indexType, maybe ts.PseudoBigInt ${typeChecker.typeToString(type)}`, typeNode));
|
|
494
|
-
return {
|
|
495
|
-
dataType: 'enum',
|
|
496
|
-
enums: [type.value],
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
|
-
else if (this.hasFlag(type, ts.TypeFlags.Never)) {
|
|
500
|
-
throw new exceptions_1.GenerateMetadataError(`TypeOperator 'keyof' on node produced a never type`, typeNode);
|
|
501
|
-
}
|
|
502
|
-
else if (this.hasFlag(type, ts.TypeFlags.TemplateLiteral)) {
|
|
503
|
-
//Now assumes template literals as string
|
|
504
|
-
console.warn(new exceptions_1.GenerateMetaDataWarning(`Template literals are assumed as strings`, typeNode).toString());
|
|
505
|
-
return {
|
|
506
|
-
dataType: 'string',
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
else if (this.hasFlag(type, ts.TypeFlags.Number)) {
|
|
510
|
-
return {
|
|
511
|
-
dataType: 'double',
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
const indexedTypeName = typeChecker.typeToString(typeChecker.getTypeFromTypeNode(typeNode.type));
|
|
515
|
-
throw new exceptions_1.GenerateMetadataError(`Could not determine the keys on ${indexedTypeName}`, typeNode);
|
|
471
|
+
return this.resolveKeyOfTypeOperator(typeNode, typeChecker, current, context, parentNode);
|
|
516
472
|
}
|
|
517
473
|
case ts.SyntaxKind.ReadonlyKeyword:
|
|
518
474
|
// Handle `readonly` arrays
|
|
@@ -521,46 +477,176 @@ class TypeResolver {
|
|
|
521
477
|
throw new exceptions_1.GenerateMetadataError(`Unknown type: ${ts.SyntaxKind[typeNode.kind]}`, typeNode);
|
|
522
478
|
}
|
|
523
479
|
}
|
|
480
|
+
resolveKeyOfTypeOperator(typeNode, typeChecker, current, context, parentNode) {
|
|
481
|
+
const type = typeChecker.getTypeFromTypeNode(typeNode);
|
|
482
|
+
const indexedType = this.resolveKeyOfIndexType(type, typeNode, current, context, parentNode);
|
|
483
|
+
if (indexedType) {
|
|
484
|
+
return indexedType;
|
|
485
|
+
}
|
|
486
|
+
if (type.isUnion()) {
|
|
487
|
+
return this.resolveKeyOfUnionType(type, typeNode, typeChecker);
|
|
488
|
+
}
|
|
489
|
+
if (type.isLiteral()) {
|
|
490
|
+
return this.resolveKeyOfLiteralType(type, typeNode, typeChecker);
|
|
491
|
+
}
|
|
492
|
+
return this.resolveFallbackKeyOfType(type, typeNode, typeChecker);
|
|
493
|
+
}
|
|
494
|
+
resolveKeyOfIndexType(type, typeNode, current, context, parentNode) {
|
|
495
|
+
if (!type.isIndexType()) {
|
|
496
|
+
return undefined;
|
|
497
|
+
}
|
|
498
|
+
const symbol = type.type.getSymbol();
|
|
499
|
+
if (!symbol || (symbol.getFlags() & ts.TypeFlags.TypeParameter) === 0) {
|
|
500
|
+
return undefined;
|
|
501
|
+
}
|
|
502
|
+
const typeName = symbol.getEscapedName();
|
|
503
|
+
(0, flowUtils_1.throwUnless)(typeof typeName === 'string', new exceptions_1.GenerateMetadataError(`typeName is not string, but ${typeof typeName}`, typeNode));
|
|
504
|
+
const contextualType = context[typeName];
|
|
505
|
+
if (!contextualType) {
|
|
506
|
+
return undefined;
|
|
507
|
+
}
|
|
508
|
+
const subResult = new TypeResolver(contextualType.type, current, parentNode, context).resolve();
|
|
509
|
+
if (subResult.dataType === 'any') {
|
|
510
|
+
return this.createStringAndNumberUnion();
|
|
511
|
+
}
|
|
512
|
+
const properties = subResult.properties?.map(property => property.name);
|
|
513
|
+
(0, flowUtils_1.throwUnless)(properties, new exceptions_1.GenerateMetadataError(`TypeOperator 'keyof' on node which have no properties`, contextualType.type));
|
|
514
|
+
return {
|
|
515
|
+
dataType: 'enum',
|
|
516
|
+
enums: properties,
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
resolveKeyOfUnionType(type, typeNode, typeChecker) {
|
|
520
|
+
const literals = type.types.filter((member) => member.isLiteral());
|
|
521
|
+
if (!literals.length) {
|
|
522
|
+
return this.resolveNonLiteralKeyOfUnionType(type, typeNode, typeChecker);
|
|
523
|
+
}
|
|
524
|
+
this.warnOnSkippedNonLiteralKeyTypes(type, typeNode, typeChecker);
|
|
525
|
+
return this.createLiteralKeyOfUnionType(literals, typeNode, typeChecker);
|
|
526
|
+
}
|
|
527
|
+
resolveNonLiteralKeyOfUnionType(type, typeNode, typeChecker) {
|
|
528
|
+
const typeFlags = new Set(type.types.map(member => member.flags));
|
|
529
|
+
const includesString = typeFlags.has(ts.TypeFlags.String);
|
|
530
|
+
const includesNumber = typeFlags.has(ts.TypeFlags.Number);
|
|
531
|
+
const includesSymbol = typeFlags.has(ts.TypeFlags.ESSymbol);
|
|
532
|
+
if (includesString && includesNumber && (type.types.length === 2 || (type.types.length === 3 && includesSymbol))) {
|
|
533
|
+
return this.createStringAndNumberUnion();
|
|
534
|
+
}
|
|
535
|
+
this.warnOnSkippedNonLiteralKeyTypes(type, typeNode, typeChecker);
|
|
536
|
+
return { dataType: 'enum', enums: [] };
|
|
537
|
+
}
|
|
538
|
+
warnOnSkippedNonLiteralKeyTypes(type, typeNode, typeChecker) {
|
|
539
|
+
const nonLiteralTypes = type.types.filter(member => !member.isLiteral());
|
|
540
|
+
if (!nonLiteralTypes.length) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const problems = nonLiteralTypes.map(member => typeChecker.typeToString(member));
|
|
544
|
+
console.warn(new exceptions_1.GenerateMetaDataWarning(`Skipped non-literal type(s) ${problems.join(', ')}`, typeNode).toString());
|
|
545
|
+
}
|
|
546
|
+
createLiteralKeyOfUnionType(literals, typeNode, typeChecker) {
|
|
547
|
+
const literalValues = literals.map(literal => this.getKeyLiteralValue(literal, typeNode, typeChecker));
|
|
548
|
+
const stringMembers = literalValues.filter((value) => typeof value === 'string');
|
|
549
|
+
const numberMembers = literalValues.filter((value) => typeof value === 'number');
|
|
550
|
+
if (stringMembers.length && numberMembers.length) {
|
|
551
|
+
return {
|
|
552
|
+
dataType: 'union',
|
|
553
|
+
types: [
|
|
554
|
+
{ dataType: 'enum', enums: stringMembers },
|
|
555
|
+
{ dataType: 'enum', enums: numberMembers },
|
|
556
|
+
],
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
return {
|
|
560
|
+
dataType: 'enum',
|
|
561
|
+
enums: literalValues,
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
getKeyLiteralValue(literal, typeNode, typeChecker) {
|
|
565
|
+
(0, flowUtils_1.throwUnless)(typeof literal.value === 'number' || typeof literal.value === 'string', new exceptions_1.GenerateMetadataError(`Not handled key Type, maybe ts.PseudoBigInt ${typeChecker.typeToString(literal)}`, typeNode));
|
|
566
|
+
return literal.value;
|
|
567
|
+
}
|
|
568
|
+
resolveKeyOfLiteralType(type, typeNode, typeChecker) {
|
|
569
|
+
(0, flowUtils_1.throwUnless)(typeof type.value === 'number' || typeof type.value === 'string', new exceptions_1.GenerateMetadataError(`Not handled indexType, maybe ts.PseudoBigInt ${typeChecker.typeToString(type)}`, typeNode));
|
|
570
|
+
return {
|
|
571
|
+
dataType: 'enum',
|
|
572
|
+
enums: [type.value],
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
resolveFallbackKeyOfType(type, typeNode, typeChecker) {
|
|
576
|
+
if (this.hasFlag(type, ts.TypeFlags.Never)) {
|
|
577
|
+
throw new exceptions_1.GenerateMetadataError(`TypeOperator 'keyof' on node produced a never type`, typeNode);
|
|
578
|
+
}
|
|
579
|
+
if (this.hasFlag(type, ts.TypeFlags.TemplateLiteral)) {
|
|
580
|
+
console.warn(new exceptions_1.GenerateMetaDataWarning(`Template literals are assumed as strings`, typeNode).toString());
|
|
581
|
+
return { dataType: 'string' };
|
|
582
|
+
}
|
|
583
|
+
if (this.hasFlag(type, ts.TypeFlags.Number)) {
|
|
584
|
+
return { dataType: 'double' };
|
|
585
|
+
}
|
|
586
|
+
const indexedTypeName = typeChecker.typeToString(typeChecker.getTypeFromTypeNode(typeNode.type));
|
|
587
|
+
throw new exceptions_1.GenerateMetadataError(`Could not determine the keys on ${indexedTypeName}`, typeNode);
|
|
588
|
+
}
|
|
589
|
+
createStringAndNumberUnion() {
|
|
590
|
+
return {
|
|
591
|
+
dataType: 'union',
|
|
592
|
+
types: [{ dataType: 'string' }, { dataType: 'double' }],
|
|
593
|
+
};
|
|
594
|
+
}
|
|
524
595
|
resolveIndexedAccessTypeNode(typeNode, typeChecker, current, context) {
|
|
525
596
|
const { indexType, objectType } = typeNode;
|
|
526
597
|
if ([ts.SyntaxKind.NumberKeyword, ts.SyntaxKind.StringKeyword].includes(indexType.kind)) {
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
(0, flowUtils_1.throwUnless)(type, new exceptions_1.GenerateMetadataError(`Could not determine ${isNumberIndexType ? 'number' : 'string'} index on ${typeChecker.typeToString(typeOfObjectType)}`, typeNode));
|
|
532
|
-
return new TypeResolver(typeChecker.typeToTypeNode(type, objectType, ts.NodeBuilderFlags.NoTruncation), current, typeNode, context).resolve();
|
|
533
|
-
}
|
|
534
|
-
else if (ts.isLiteralTypeNode(indexType) && (ts.isStringLiteral(indexType.literal) || ts.isNumericLiteral(indexType.literal))) {
|
|
535
|
-
// Indexed by literal
|
|
536
|
-
const hasType = (node) => node !== undefined && objectHasOwn(node, 'type');
|
|
537
|
-
const symbol = typeChecker.getPropertyOfType(typeChecker.getTypeFromTypeNode(objectType), indexType.literal.text);
|
|
538
|
-
(0, flowUtils_1.throwUnless)(symbol, new exceptions_1.GenerateMetadataError(`Could not determine the keys on ${typeChecker.typeToString(typeChecker.getTypeFromTypeNode(objectType))}`, typeNode));
|
|
539
|
-
if (hasType(symbol.valueDeclaration) && symbol.valueDeclaration.type) {
|
|
540
|
-
return new TypeResolver(symbol.valueDeclaration.type, current, typeNode, context).resolve();
|
|
541
|
-
}
|
|
542
|
-
const declaration = typeChecker.getTypeOfSymbolAtLocation(symbol, objectType);
|
|
543
|
-
try {
|
|
544
|
-
return new TypeResolver(typeChecker.typeToTypeNode(declaration, objectType, ts.NodeBuilderFlags.NoTruncation), current, typeNode, context).resolve();
|
|
545
|
-
}
|
|
546
|
-
catch {
|
|
547
|
-
throw new exceptions_1.GenerateMetadataError(`Could not determine the keys on ${typeChecker.typeToString(typeChecker.getTypeFromTypeNode(typeChecker.typeToTypeNode(declaration, undefined, ts.NodeBuilderFlags.NoTruncation)))}`, typeNode);
|
|
548
|
-
}
|
|
598
|
+
return this.resolveIndexedAccessKeywordType(typeNode, typeChecker, current, context, objectType, indexType);
|
|
599
|
+
}
|
|
600
|
+
if (ts.isLiteralTypeNode(indexType) && (ts.isStringLiteral(indexType.literal) || ts.isNumericLiteral(indexType.literal))) {
|
|
601
|
+
return this.resolveIndexedAccessLiteralType(typeNode, typeChecker, current, context, objectType, indexType);
|
|
549
602
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
const isSameTypeQuery = ts.isTypeQueryNode(typeOfObjectType) && ts.isTypeQueryNode(typeOfIndexType) && typeOfObjectType.exprName.getText() === typeOfIndexType.exprName.getText();
|
|
555
|
-
const isSameTypeReference = ts.isTypeReferenceNode(typeOfObjectType) && ts.isTypeReferenceNode(typeOfIndexType) && typeOfObjectType.typeName.getText() === typeOfIndexType.typeName.getText();
|
|
556
|
-
if (isSameTypeQuery || isSameTypeReference) {
|
|
557
|
-
const type = this.getReferencer();
|
|
558
|
-
const node = typeChecker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.InTypeAlias | ts.NodeBuilderFlags.NoTruncation);
|
|
559
|
-
return new TypeResolver(node, current, typeNode, context, this.referencer).resolve();
|
|
603
|
+
if (ts.isTypeOperatorNode(indexType) && indexType.operator === ts.SyntaxKind.KeyOfKeyword) {
|
|
604
|
+
const keyedIndexedAccessType = this.resolveKeyedIndexedAccessType(typeNode, typeChecker, current, context, objectType, indexType);
|
|
605
|
+
if (keyedIndexedAccessType) {
|
|
606
|
+
return keyedIndexedAccessType;
|
|
560
607
|
}
|
|
561
608
|
}
|
|
562
609
|
throw new exceptions_1.GenerateMetadataError(`Unknown type: ${ts.SyntaxKind[typeNode.kind]}`, typeNode);
|
|
563
610
|
}
|
|
611
|
+
resolveIndexedAccessKeywordType(typeNode, typeChecker, current, context, objectType, indexType) {
|
|
612
|
+
const isNumberIndexType = indexType.kind === ts.SyntaxKind.NumberKeyword;
|
|
613
|
+
const typeOfObjectType = typeChecker.getTypeFromTypeNode(objectType);
|
|
614
|
+
const indexedType = isNumberIndexType ? typeOfObjectType.getNumberIndexType() : typeOfObjectType.getStringIndexType();
|
|
615
|
+
(0, flowUtils_1.throwUnless)(indexedType, new exceptions_1.GenerateMetadataError(`Could not determine ${isNumberIndexType ? 'number' : 'string'} index on ${typeChecker.typeToString(typeOfObjectType)}`, typeNode));
|
|
616
|
+
return new TypeResolver(typeChecker.typeToTypeNode(indexedType, objectType, ts.NodeBuilderFlags.NoTruncation), current, typeNode, context).resolve();
|
|
617
|
+
}
|
|
618
|
+
resolveIndexedAccessLiteralType(typeNode, typeChecker, current, context, objectType, indexType) {
|
|
619
|
+
const propertyName = ts.isStringLiteral(indexType.literal) || ts.isNumericLiteral(indexType.literal) ? indexType.literal.text : indexType.literal.getText();
|
|
620
|
+
const symbol = typeChecker.getPropertyOfType(typeChecker.getTypeFromTypeNode(objectType), propertyName);
|
|
621
|
+
(0, flowUtils_1.throwUnless)(symbol, new exceptions_1.GenerateMetadataError(`Could not determine the keys on ${typeChecker.typeToString(typeChecker.getTypeFromTypeNode(objectType))}`, typeNode));
|
|
622
|
+
if (this.symbolHasTypeDeclaration(symbol.valueDeclaration)) {
|
|
623
|
+
return new TypeResolver(symbol.valueDeclaration.type, current, typeNode, context).resolve();
|
|
624
|
+
}
|
|
625
|
+
const declarationType = typeChecker.getTypeOfSymbolAtLocation(symbol, objectType);
|
|
626
|
+
try {
|
|
627
|
+
return new TypeResolver(typeChecker.typeToTypeNode(declarationType, objectType, ts.NodeBuilderFlags.NoTruncation), current, typeNode, context).resolve();
|
|
628
|
+
}
|
|
629
|
+
catch {
|
|
630
|
+
const typeNodeForError = typeChecker.typeToTypeNode(declarationType, undefined, ts.NodeBuilderFlags.NoTruncation);
|
|
631
|
+
const typeName = typeChecker.typeToString(typeChecker.getTypeFromTypeNode(typeNodeForError));
|
|
632
|
+
throw new exceptions_1.GenerateMetadataError(`Could not determine the keys on ${typeName}`, typeNode);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
symbolHasTypeDeclaration(node) {
|
|
636
|
+
return node !== undefined && objectHasOwn(node, 'type') && node.type !== undefined;
|
|
637
|
+
}
|
|
638
|
+
resolveKeyedIndexedAccessType(typeNode, typeChecker, current, context, objectType, indexType) {
|
|
639
|
+
const typeOfObjectType = ts.isParenthesizedTypeNode(objectType) ? objectType.type : objectType;
|
|
640
|
+
const typeOfIndexType = indexType.type;
|
|
641
|
+
const isSameTypeQuery = ts.isTypeQueryNode(typeOfObjectType) && ts.isTypeQueryNode(typeOfIndexType) && typeOfObjectType.exprName.getText() === typeOfIndexType.exprName.getText();
|
|
642
|
+
const isSameTypeReference = ts.isTypeReferenceNode(typeOfObjectType) && ts.isTypeReferenceNode(typeOfIndexType) && typeOfObjectType.typeName.getText() === typeOfIndexType.typeName.getText();
|
|
643
|
+
if (!isSameTypeQuery && !isSameTypeReference) {
|
|
644
|
+
return undefined;
|
|
645
|
+
}
|
|
646
|
+
const type = this.getReferencer();
|
|
647
|
+
const node = typeChecker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.InTypeAlias | ts.NodeBuilderFlags.NoTruncation);
|
|
648
|
+
return new TypeResolver(node, current, typeNode, context, this.referencer).resolve();
|
|
649
|
+
}
|
|
564
650
|
resolveTypeReferenceNode(typeNode, current, context, parentNode) {
|
|
565
651
|
const { typeName } = typeNode;
|
|
566
652
|
const resolvedTypeArguments = typeNode.typeArguments ? [...typeNode.typeArguments] : undefined;
|
|
@@ -772,92 +858,83 @@ class TypeResolver {
|
|
|
772
858
|
}
|
|
773
859
|
//Generates type name for type references
|
|
774
860
|
calcRefTypeName(type) {
|
|
775
|
-
const
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
861
|
+
const contextualName = this.context[this.getEntityNameText(type)]?.name;
|
|
862
|
+
if (contextualName) {
|
|
863
|
+
return contextualName;
|
|
864
|
+
}
|
|
865
|
+
const declarations = this.getModelTypeDeclarations(type);
|
|
866
|
+
if (!declarations.length) {
|
|
867
|
+
return this.getFallbackRefTypeName(type);
|
|
868
|
+
}
|
|
869
|
+
const name = this.getDeclarationBasedRefTypeName(type, declarations);
|
|
870
|
+
this.current.CheckModelUnicity(name, declarations.map(declaration => ({
|
|
871
|
+
fileName: declaration.getSourceFile().fileName,
|
|
872
|
+
pos: declaration.pos,
|
|
873
|
+
})));
|
|
874
|
+
return name;
|
|
875
|
+
}
|
|
876
|
+
getEntityNameText(type) {
|
|
877
|
+
if (ts.isIdentifier(type)) {
|
|
878
|
+
return type.text;
|
|
785
879
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
// - Add declaration positions into type names (In an order).
|
|
821
|
-
// - It accepts multiple types with same name, if the code compiles, there would be no conflicts in the type names
|
|
822
|
-
// - Clear namespaces from type names.
|
|
823
|
-
// - Horrible changes can be in the routes.ts in case of teamwork,
|
|
824
|
-
// because source files have paths in the computer where data generation runs.
|
|
825
|
-
// - Use fully namespaced names
|
|
826
|
-
// - Conflicts can be recognized because of the declarations
|
|
827
|
-
//
|
|
828
|
-
// The second was implemented, it not changes the usual type name formats.
|
|
829
|
-
const oneDeclaration = declarations[0]; //Every declarations should be in the same namespace hierarchy
|
|
830
|
-
if (oneDeclaration && ts.isEnumMember(oneDeclaration)) {
|
|
831
|
-
name = `${oneDeclaration.parent.name.getText()}.${oneDeclaration.name.getText()}`;
|
|
832
|
-
}
|
|
833
|
-
else {
|
|
834
|
-
name = oneDeclaration.name?.getText() || name;
|
|
880
|
+
return `${this.getEntityNameText(type.left)}.${type.right.text}`;
|
|
881
|
+
}
|
|
882
|
+
getFallbackRefTypeName(type) {
|
|
883
|
+
if (ts.isIdentifier(type)) {
|
|
884
|
+
return type.text;
|
|
885
|
+
}
|
|
886
|
+
return this.createInlineReferenceTypeName(type);
|
|
887
|
+
}
|
|
888
|
+
createInlineReferenceTypeName(typeNode) {
|
|
889
|
+
const resolvedType = new TypeResolver(typeNode, this.current, this.parentNode, this.context).resolve();
|
|
890
|
+
const uniqueName = `Inline_${this.sanitizeInlineTypeName(this.calcTypeName(typeNode))}`;
|
|
891
|
+
this.current.AddReferenceType({
|
|
892
|
+
dataType: 'refAlias',
|
|
893
|
+
refName: uniqueName,
|
|
894
|
+
type: resolvedType,
|
|
895
|
+
validators: {},
|
|
896
|
+
deprecated: false,
|
|
897
|
+
});
|
|
898
|
+
return uniqueName;
|
|
899
|
+
}
|
|
900
|
+
sanitizeInlineTypeName(typeName) {
|
|
901
|
+
return typeName
|
|
902
|
+
.replaceAll(/[^A-Za-z0-9]/g, '_')
|
|
903
|
+
.replaceAll(/_+/g, '_')
|
|
904
|
+
.replaceAll(/^_+|_+$/g, '');
|
|
905
|
+
}
|
|
906
|
+
getDeclarationBasedRefTypeName(type, declarations) {
|
|
907
|
+
const declaration = declarations[0];
|
|
908
|
+
let name = this.getDeclarationRefTypeName(declaration, this.getEntityNameText(type));
|
|
909
|
+
let currentNode = declaration.parent;
|
|
910
|
+
let isFirst = true;
|
|
911
|
+
while (!ts.isSourceFile(currentNode)) {
|
|
912
|
+
if (ts.isBlock(currentNode)) {
|
|
913
|
+
break;
|
|
835
914
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
if (ts.isBlock(actNode)) {
|
|
841
|
-
break;
|
|
842
|
-
}
|
|
843
|
-
if (!(isFirst && ts.isEnumDeclaration(actNode)) && !ts.isModuleBlock(actNode)) {
|
|
844
|
-
(0, flowUtils_1.throwUnless)(ts.isModuleDeclaration(actNode), new exceptions_1.GenerateMetadataError(`This node kind is unknown: ${actNode.kind}`, type));
|
|
845
|
-
if (!isGlobalDeclaration(actNode)) {
|
|
846
|
-
const moduleName = actNode.name.text;
|
|
847
|
-
name = `${moduleName}.${name}`;
|
|
848
|
-
}
|
|
915
|
+
if (this.shouldPrefixDeclarationNamespace(currentNode, isFirst)) {
|
|
916
|
+
(0, flowUtils_1.throwUnless)(ts.isModuleDeclaration(currentNode), new exceptions_1.GenerateMetadataError(`This node kind is unknown: ${currentNode.kind}`, type));
|
|
917
|
+
if (!this.isGlobalDeclaration(currentNode)) {
|
|
918
|
+
name = `${currentNode.name.text}.${name}`;
|
|
849
919
|
}
|
|
850
|
-
isFirst = false;
|
|
851
|
-
actNode = actNode.parent;
|
|
852
920
|
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
pos: declaration.pos,
|
|
856
|
-
}));
|
|
857
|
-
this.current.CheckModelUnicity(name, declarationPositions);
|
|
921
|
+
isFirst = false;
|
|
922
|
+
currentNode = currentNode.parent;
|
|
858
923
|
}
|
|
859
924
|
return name;
|
|
860
925
|
}
|
|
926
|
+
getDeclarationRefTypeName(declaration, fallbackName) {
|
|
927
|
+
if (ts.isEnumMember(declaration)) {
|
|
928
|
+
return `${declaration.parent.name.getText()}.${declaration.name.getText()}`;
|
|
929
|
+
}
|
|
930
|
+
return declaration.name?.getText() ?? fallbackName;
|
|
931
|
+
}
|
|
932
|
+
shouldPrefixDeclarationNamespace(node, isFirst) {
|
|
933
|
+
return !(isFirst && ts.isEnumDeclaration(node)) && !ts.isModuleBlock(node);
|
|
934
|
+
}
|
|
935
|
+
isGlobalDeclaration(node) {
|
|
936
|
+
return node.name.kind === ts.SyntaxKind.Identifier && node.name.text === 'global';
|
|
937
|
+
}
|
|
861
938
|
calcMemberJsDocProperties(arg) {
|
|
862
939
|
const def = TypeResolver.getDefault(arg);
|
|
863
940
|
const isDeprecated = (0, jsDocUtils_1.isExistJSDocTag)(arg, tag => tag.tagName.text === 'deprecated') || (0, decoratorUtils_1.isDecorator)(arg, (_identifier, canonicalName) => canonicalName === 'Deprecated', this.current.typeChecker);
|
|
@@ -874,7 +951,7 @@ class TypeResolver {
|
|
|
874
951
|
description,
|
|
875
952
|
validators: validators && Object.keys(validators).length ? validators : undefined,
|
|
876
953
|
format,
|
|
877
|
-
example
|
|
954
|
+
example,
|
|
878
955
|
extensions: extensions.length ? extensions : undefined,
|
|
879
956
|
deprecated: isDeprecated ? true : undefined,
|
|
880
957
|
ignored: isIgnored ? true : undefined,
|
|
@@ -892,103 +969,131 @@ class TypeResolver {
|
|
|
892
969
|
}
|
|
893
970
|
//Generates type name for type references
|
|
894
971
|
calcTypeName(arg) {
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
return `'${literalValue}'`;
|
|
899
|
-
}
|
|
900
|
-
if (literalValue === null) {
|
|
901
|
-
return 'null';
|
|
902
|
-
}
|
|
903
|
-
if (typeof literalValue === 'boolean') {
|
|
904
|
-
return literalValue === true ? 'true' : 'false';
|
|
905
|
-
}
|
|
906
|
-
return `${literalValue}`;
|
|
972
|
+
const literalTypeName = this.getLiteralTypeName(arg);
|
|
973
|
+
if (literalTypeName) {
|
|
974
|
+
return literalTypeName;
|
|
907
975
|
}
|
|
908
976
|
const resolvedType = primitiveTransformer_1.PrimitiveTransformer.resolveKindToPrimitive(arg.kind);
|
|
909
977
|
if (resolvedType) {
|
|
910
978
|
return resolvedType;
|
|
911
979
|
}
|
|
912
|
-
|
|
913
|
-
|
|
980
|
+
const structuralTypeName = this.getStructuralTypeName(arg);
|
|
981
|
+
if (structuralTypeName) {
|
|
982
|
+
return structuralTypeName;
|
|
914
983
|
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
}
|
|
922
|
-
else if (ts.isIndexSignatureDeclaration(member)) {
|
|
923
|
-
(0, flowUtils_1.throwUnless)(member.parameters.length === 1, new exceptions_1.GenerateMetadataError(`Index signature parameters length != 1`, member));
|
|
924
|
-
const indexType = member.parameters[0];
|
|
925
|
-
(0, flowUtils_1.throwUnless)(
|
|
926
|
-
// now we can't reach this part of code
|
|
927
|
-
ts.isParameter(indexType), new exceptions_1.GenerateMetadataError(`indexSignature declaration parameter kind is not SyntaxKind.Parameter`, indexType));
|
|
928
|
-
(0, flowUtils_1.throwUnless)(!indexType.questionToken, new exceptions_1.GenerateMetadataError(`Question token has found for an indexSignature declaration`, indexType));
|
|
929
|
-
const typeText = this.calcTypeName(member.type);
|
|
930
|
-
const indexName = indexType.name.text;
|
|
931
|
-
const indexTypeText = this.calcTypeName(indexType.type);
|
|
932
|
-
return `["${indexName}": ${indexTypeText}]: ${typeText}`;
|
|
933
|
-
}
|
|
934
|
-
throw new exceptions_1.GenerateMetadataError(`Unhandled member kind has found: ${member.kind}`, member);
|
|
935
|
-
});
|
|
936
|
-
return `{${members.join('; ')}}`;
|
|
984
|
+
console.warn(new exceptions_1.GenerateMetaDataWarning(`This kind (${arg.kind}) is unhandled, so the type will be any, and no type conflict checks will made`, arg).toString());
|
|
985
|
+
return 'any';
|
|
986
|
+
}
|
|
987
|
+
getLiteralTypeName(arg) {
|
|
988
|
+
if (!ts.isLiteralTypeNode(arg)) {
|
|
989
|
+
return undefined;
|
|
937
990
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
return
|
|
991
|
+
const literalValue = this.getLiteralValue(arg);
|
|
992
|
+
if (typeof literalValue === 'string') {
|
|
993
|
+
return `'${literalValue}'`;
|
|
941
994
|
}
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
return memberTypeNames.join(' & ');
|
|
995
|
+
if (literalValue === null) {
|
|
996
|
+
return 'null';
|
|
945
997
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
return memberTypeNames.join(' | ');
|
|
998
|
+
if (typeof literalValue === 'boolean') {
|
|
999
|
+
return literalValue ? 'true' : 'false';
|
|
949
1000
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
1001
|
+
return `${literalValue}`;
|
|
1002
|
+
}
|
|
1003
|
+
getStructuralTypeName(arg) {
|
|
1004
|
+
return (this.getReferenceLikeTypeName(arg) ??
|
|
1005
|
+
this.getTypeLiteralName(arg) ??
|
|
1006
|
+
this.getArrayTypeName(arg) ??
|
|
1007
|
+
this.getIntersectionTypeName(arg) ??
|
|
1008
|
+
this.getUnionTypeName(arg) ??
|
|
1009
|
+
this.getTypeOperatorTypeName(arg) ??
|
|
1010
|
+
this.getTypeQueryName(arg) ??
|
|
1011
|
+
this.getIndexedAccessTypeName(arg) ??
|
|
1012
|
+
this.getKeywordTypeName(arg) ??
|
|
1013
|
+
this.getConditionalTypeName(arg) ??
|
|
1014
|
+
this.getParenthesizedTypeName(arg));
|
|
1015
|
+
}
|
|
1016
|
+
getReferenceLikeTypeName(arg) {
|
|
1017
|
+
if (!ts.isTypeReferenceNode(arg) && !ts.isExpressionWithTypeArguments(arg)) {
|
|
1018
|
+
return undefined;
|
|
959
1019
|
}
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
1020
|
+
return this.calcTypeReferenceTypeName(arg)[1];
|
|
1021
|
+
}
|
|
1022
|
+
getTypeLiteralName(arg) {
|
|
1023
|
+
if (!ts.isTypeLiteralNode(arg)) {
|
|
1024
|
+
return undefined;
|
|
963
1025
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1026
|
+
return `{${arg.members.map(member => this.getTypeLiteralMemberName(member)).join('; ')}}`;
|
|
1027
|
+
}
|
|
1028
|
+
getTypeLiteralMemberName(member) {
|
|
1029
|
+
if (ts.isPropertySignature(member)) {
|
|
1030
|
+
const name = member.name.text;
|
|
1031
|
+
const typeText = this.calcTypeName(member.type);
|
|
1032
|
+
return `"${name}"${member.questionToken ? '?' : ''}${this.calcMemberJsDocProperties(member)}: ${typeText}`;
|
|
968
1033
|
}
|
|
969
|
-
|
|
970
|
-
return
|
|
1034
|
+
if (ts.isIndexSignatureDeclaration(member)) {
|
|
1035
|
+
return this.getIndexSignatureTypeName(member);
|
|
971
1036
|
}
|
|
972
|
-
|
|
973
|
-
|
|
1037
|
+
throw new exceptions_1.GenerateMetadataError(`Unhandled member kind has found: ${member.kind}`, member);
|
|
1038
|
+
}
|
|
1039
|
+
getIndexSignatureTypeName(member) {
|
|
1040
|
+
(0, flowUtils_1.throwUnless)(member.parameters.length === 1, new exceptions_1.GenerateMetadataError(`Index signature parameters length != 1`, member));
|
|
1041
|
+
const indexType = member.parameters[0];
|
|
1042
|
+
(0, flowUtils_1.throwUnless)(ts.isParameter(indexType), new exceptions_1.GenerateMetadataError(`indexSignature declaration parameter kind is not SyntaxKind.Parameter`, indexType));
|
|
1043
|
+
(0, flowUtils_1.throwUnless)(!indexType.questionToken, new exceptions_1.GenerateMetadataError(`Question token has found for an indexSignature declaration`, indexType));
|
|
1044
|
+
const indexName = indexType.name.text;
|
|
1045
|
+
const indexTypeText = this.calcTypeName(indexType.type);
|
|
1046
|
+
return `["${indexName}": ${indexTypeText}]: ${this.calcTypeName(member.type)}`;
|
|
1047
|
+
}
|
|
1048
|
+
getArrayTypeName(arg) {
|
|
1049
|
+
return ts.isArrayTypeNode(arg) ? `${this.calcTypeName(arg.elementType)}[]` : undefined;
|
|
1050
|
+
}
|
|
1051
|
+
getIntersectionTypeName(arg) {
|
|
1052
|
+
return ts.isIntersectionTypeNode(arg) ? arg.types.map(type => this.calcTypeName(type)).join(' & ') : undefined;
|
|
1053
|
+
}
|
|
1054
|
+
getUnionTypeName(arg) {
|
|
1055
|
+
return ts.isUnionTypeNode(arg) ? arg.types.map(type => this.calcTypeName(type)).join(' | ') : undefined;
|
|
1056
|
+
}
|
|
1057
|
+
getTypeOperatorTypeName(arg) {
|
|
1058
|
+
if (!ts.isTypeOperatorNode(arg)) {
|
|
1059
|
+
return undefined;
|
|
974
1060
|
}
|
|
975
|
-
|
|
976
|
-
|
|
1061
|
+
const subTypeName = this.calcTypeName(arg.type);
|
|
1062
|
+
if (arg.operator === ts.SyntaxKind.KeyOfKeyword) {
|
|
1063
|
+
return `keyof ${subTypeName}`;
|
|
977
1064
|
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
const extendsTypeName = this.calcTypeName(arg.extendsType);
|
|
981
|
-
const trueTypeName = this.calcTypeName(arg.trueType);
|
|
982
|
-
const falseTypeName = this.calcTypeName(arg.falseType);
|
|
983
|
-
return `${checkTypeName} extends ${extendsTypeName} ? ${trueTypeName} : ${falseTypeName}`;
|
|
1065
|
+
if (arg.operator === ts.SyntaxKind.ReadonlyKeyword) {
|
|
1066
|
+
return `readonly ${subTypeName}`;
|
|
984
1067
|
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1068
|
+
throw new exceptions_1.GenerateMetadataError(`Unknown keyword has found: ${arg.operator}`, arg);
|
|
1069
|
+
}
|
|
1070
|
+
getTypeQueryName(arg) {
|
|
1071
|
+
return ts.isTypeQueryNode(arg) ? `typeof ${this.calcRefTypeName(arg.exprName)}` : undefined;
|
|
1072
|
+
}
|
|
1073
|
+
getIndexedAccessTypeName(arg) {
|
|
1074
|
+
return ts.isIndexedAccessTypeNode(arg) ? `${this.calcTypeName(arg.objectType)}[${this.calcTypeName(arg.indexType)}]` : undefined;
|
|
1075
|
+
}
|
|
1076
|
+
getKeywordTypeName(arg) {
|
|
1077
|
+
if (arg.kind === ts.SyntaxKind.UnknownKeyword) {
|
|
1078
|
+
return 'unknown';
|
|
988
1079
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1080
|
+
if (arg.kind === ts.SyntaxKind.AnyKeyword) {
|
|
1081
|
+
return 'any';
|
|
1082
|
+
}
|
|
1083
|
+
return arg.kind === ts.SyntaxKind.NeverKeyword ? 'never' : undefined;
|
|
1084
|
+
}
|
|
1085
|
+
getConditionalTypeName(arg) {
|
|
1086
|
+
if (!ts.isConditionalTypeNode(arg)) {
|
|
1087
|
+
return undefined;
|
|
1088
|
+
}
|
|
1089
|
+
const checkTypeName = this.calcTypeName(arg.checkType);
|
|
1090
|
+
const extendsTypeName = this.calcTypeName(arg.extendsType);
|
|
1091
|
+
const trueTypeName = this.calcTypeName(arg.trueType);
|
|
1092
|
+
const falseTypeName = this.calcTypeName(arg.falseType);
|
|
1093
|
+
return `${checkTypeName} extends ${extendsTypeName} ? ${trueTypeName} : ${falseTypeName}`;
|
|
1094
|
+
}
|
|
1095
|
+
getParenthesizedTypeName(arg) {
|
|
1096
|
+
return ts.isParenthesizedTypeNode(arg) ? `(${this.calcTypeName(arg.type)})` : undefined;
|
|
992
1097
|
}
|
|
993
1098
|
//Generates type name for type references
|
|
994
1099
|
calcTypeReferenceTypeName(node) {
|
|
@@ -1006,54 +1111,53 @@ class TypeResolver {
|
|
|
1006
1111
|
const refTypeName = this.getRefTypeName(name);
|
|
1007
1112
|
this.current.CheckExpressionUnicity(refTypeName, name);
|
|
1008
1113
|
this.context = this.typeArgumentsToContext(node, type);
|
|
1009
|
-
const
|
|
1010
|
-
try {
|
|
1011
|
-
const existingType = localReferenceTypeCache[name];
|
|
1012
|
-
if (existingType) {
|
|
1013
|
-
return existingType;
|
|
1014
|
-
}
|
|
1015
|
-
if (inProgressTypes[name]) {
|
|
1016
|
-
return this.createCircularDependencyResolver(name, refTypeName);
|
|
1017
|
-
}
|
|
1018
|
-
inProgressTypes[name] = [];
|
|
1019
|
-
const declarations = this.getModelTypeDeclarations(type);
|
|
1020
|
-
const referenceTypes = [];
|
|
1021
|
-
// If no declarations found, this might be a built-in type or a type that can't be resolved
|
|
1022
|
-
if (declarations.length === 0) {
|
|
1023
|
-
const fallbackReferenceType = this.getReferenceTypeFromTypeChecker(node, name, refTypeName);
|
|
1024
|
-
if (fallbackReferenceType) {
|
|
1025
|
-
this.addToLocalReferenceTypeCache(name, fallbackReferenceType);
|
|
1026
|
-
return fallbackReferenceType;
|
|
1027
|
-
}
|
|
1028
|
-
throw new exceptions_1.GenerateMetadataError(`Could not find declarations for type '${name}'. This might be a complex generic type that needs special handling.`);
|
|
1029
|
-
}
|
|
1030
|
-
for (const declaration of declarations) {
|
|
1031
|
-
if (ts.isTypeAliasDeclaration(declaration)) {
|
|
1032
|
-
const referencer = node.pos !== -1 ? this.current.typeChecker.getTypeFromTypeNode(node) : undefined;
|
|
1033
|
-
referenceTypes.push(new referenceTransformer_1.ReferenceTransformer().transform(declaration, refTypeName, this, referencer));
|
|
1034
|
-
}
|
|
1035
|
-
else if (enumTransformer_1.EnumTransformer.transformable(declaration)) {
|
|
1036
|
-
referenceTypes.push(new enumTransformer_1.EnumTransformer().transform(this, declaration, refTypeName));
|
|
1037
|
-
}
|
|
1038
|
-
else {
|
|
1039
|
-
referenceTypes.push(this.getModelReference(declaration, refTypeName));
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
const referenceType = referenceTransformer_1.ReferenceTransformer.merge(referenceTypes);
|
|
1043
|
-
this.addToLocalReferenceTypeCache(name, referenceType);
|
|
1044
|
-
return referenceType;
|
|
1045
|
-
}
|
|
1046
|
-
catch (err) {
|
|
1047
|
-
delete inProgressTypes[name];
|
|
1048
|
-
throw err;
|
|
1049
|
-
}
|
|
1050
|
-
};
|
|
1051
|
-
const result = calcReferenceType();
|
|
1114
|
+
const result = this.resolveReferenceType(node, type, name, refTypeName);
|
|
1052
1115
|
if (addToRefTypeMap) {
|
|
1053
1116
|
this.current.AddReferenceType(result);
|
|
1054
1117
|
}
|
|
1055
1118
|
return result;
|
|
1056
1119
|
}
|
|
1120
|
+
resolveReferenceType(node, type, name, refTypeName) {
|
|
1121
|
+
try {
|
|
1122
|
+
const existingType = localReferenceTypeCache[name];
|
|
1123
|
+
if (existingType) {
|
|
1124
|
+
return existingType;
|
|
1125
|
+
}
|
|
1126
|
+
if (inProgressTypes[name]) {
|
|
1127
|
+
return this.createCircularDependencyResolver(name, refTypeName);
|
|
1128
|
+
}
|
|
1129
|
+
inProgressTypes[name] = [];
|
|
1130
|
+
const declarations = this.getModelTypeDeclarations(type);
|
|
1131
|
+
if (!declarations.length) {
|
|
1132
|
+
return this.resolveReferenceTypeFallback(node, name, refTypeName);
|
|
1133
|
+
}
|
|
1134
|
+
const referenceType = referenceTransformer_1.ReferenceTransformer.merge(declarations.map(declaration => this.resolveDeclarationReferenceType(declaration, node, refTypeName)));
|
|
1135
|
+
this.addToLocalReferenceTypeCache(name, referenceType);
|
|
1136
|
+
return referenceType;
|
|
1137
|
+
}
|
|
1138
|
+
catch (error) {
|
|
1139
|
+
delete inProgressTypes[name];
|
|
1140
|
+
throw error;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
resolveReferenceTypeFallback(node, name, refTypeName) {
|
|
1144
|
+
const fallbackReferenceType = this.getReferenceTypeFromTypeChecker(node, name, refTypeName);
|
|
1145
|
+
if (fallbackReferenceType) {
|
|
1146
|
+
this.addToLocalReferenceTypeCache(name, fallbackReferenceType);
|
|
1147
|
+
return fallbackReferenceType;
|
|
1148
|
+
}
|
|
1149
|
+
throw new exceptions_1.GenerateMetadataError(`Could not find declarations for type '${name}'. This might be a complex generic type that needs special handling.`);
|
|
1150
|
+
}
|
|
1151
|
+
resolveDeclarationReferenceType(declaration, node, refTypeName) {
|
|
1152
|
+
if (ts.isTypeAliasDeclaration(declaration)) {
|
|
1153
|
+
const referencer = node.pos !== -1 ? this.current.typeChecker.getTypeFromTypeNode(node) : undefined;
|
|
1154
|
+
return new referenceTransformer_1.ReferenceTransformer().transform(declaration, refTypeName, this, referencer);
|
|
1155
|
+
}
|
|
1156
|
+
if (enumTransformer_1.EnumTransformer.transformable(declaration)) {
|
|
1157
|
+
return new enumTransformer_1.EnumTransformer().transform(this, declaration, refTypeName);
|
|
1158
|
+
}
|
|
1159
|
+
return this.getModelReference(declaration, refTypeName);
|
|
1160
|
+
}
|
|
1057
1161
|
addToLocalReferenceTypeCache(name, refType) {
|
|
1058
1162
|
if (inProgressTypes[name]) {
|
|
1059
1163
|
for (const fn of inProgressTypes[name]) {
|
|
@@ -1071,62 +1175,65 @@ class TypeResolver {
|
|
|
1071
1175
|
// Handle toJSON methods
|
|
1072
1176
|
(0, flowUtils_1.throwUnless)(modelType.name, new exceptions_1.GenerateMetadataError("Can't get Symbol from anonymous class", modelType));
|
|
1073
1177
|
const type = this.current.typeChecker.getTypeAtLocation(modelType.name);
|
|
1074
|
-
const
|
|
1075
|
-
if (
|
|
1076
|
-
let nodeType =
|
|
1178
|
+
const toJSONDeclaration = this.current.typeChecker.getPropertyOfType(type, 'toJSON')?.valueDeclaration;
|
|
1179
|
+
if (toJSONDeclaration && (ts.isMethodDeclaration(toJSONDeclaration) || ts.isMethodSignature(toJSONDeclaration))) {
|
|
1180
|
+
let nodeType = toJSONDeclaration.type;
|
|
1077
1181
|
if (!nodeType) {
|
|
1078
|
-
const signature = this.current.typeChecker.getSignatureFromDeclaration(
|
|
1182
|
+
const signature = this.current.typeChecker.getSignatureFromDeclaration(toJSONDeclaration);
|
|
1079
1183
|
const implicitType = this.current.typeChecker.getReturnTypeOfSignature(signature);
|
|
1080
1184
|
nodeType = this.current.typeChecker.typeToTypeNode(implicitType, undefined, ts.NodeBuilderFlags.NoTruncation);
|
|
1081
1185
|
}
|
|
1082
|
-
|
|
1083
|
-
const referenceType = {
|
|
1186
|
+
return this.withDefinedReferenceMetadata({
|
|
1084
1187
|
refName: refTypeName,
|
|
1085
1188
|
dataType: 'refAlias',
|
|
1086
1189
|
description,
|
|
1087
|
-
type,
|
|
1190
|
+
type: new TypeResolver(nodeType, this.current).resolve(),
|
|
1088
1191
|
validators: {},
|
|
1089
1192
|
deprecated,
|
|
1090
|
-
|
|
1091
|
-
...(title !== undefined ? { title } : {}),
|
|
1092
|
-
};
|
|
1093
|
-
return referenceType;
|
|
1193
|
+
}, { example, title });
|
|
1094
1194
|
}
|
|
1095
1195
|
const properties = new propertyTransformer_1.PropertyTransformer().transform(this, modelType);
|
|
1096
1196
|
const additionalProperties = this.getModelAdditionalProperties(modelType);
|
|
1097
1197
|
const inheritedProperties = this.getModelInheritedProperties(modelType) || [];
|
|
1098
|
-
const referenceType = {
|
|
1198
|
+
const referenceType = this.withDefinedReferenceMetadata({
|
|
1099
1199
|
additionalProperties,
|
|
1100
1200
|
dataType: 'refObject',
|
|
1101
1201
|
description,
|
|
1102
1202
|
properties: inheritedProperties,
|
|
1103
1203
|
refName: refTypeName,
|
|
1104
1204
|
deprecated,
|
|
1105
|
-
|
|
1106
|
-
...(title !== undefined ? { title } : {}),
|
|
1107
|
-
};
|
|
1205
|
+
}, { example, title });
|
|
1108
1206
|
referenceType.properties = referenceType.properties.concat(properties);
|
|
1109
1207
|
return referenceType;
|
|
1110
1208
|
}
|
|
1209
|
+
withDefinedReferenceMetadata(referenceType, metadata) {
|
|
1210
|
+
if (metadata.example !== undefined) {
|
|
1211
|
+
referenceType.example = metadata.example;
|
|
1212
|
+
}
|
|
1213
|
+
if (metadata.title !== undefined) {
|
|
1214
|
+
referenceType.title = metadata.title;
|
|
1215
|
+
}
|
|
1216
|
+
return referenceType;
|
|
1217
|
+
}
|
|
1111
1218
|
//Generates a name from the original type expression.
|
|
1112
1219
|
//This function is not invertable, so it's possible, that 2 type expressions have the same refTypeName.
|
|
1113
1220
|
getRefTypeName(name) {
|
|
1114
1221
|
let preformattedName = name //Preformatted name handles most cases
|
|
1115
1222
|
.replaceAll('<', '_')
|
|
1116
1223
|
.replaceAll('>', '_')
|
|
1117
|
-
.
|
|
1224
|
+
.replaceAll(/\s+/g, '')
|
|
1118
1225
|
.replaceAll(',', '.')
|
|
1119
|
-
.
|
|
1120
|
-
.
|
|
1226
|
+
.replaceAll(/'([^']*)'/g, '$1')
|
|
1227
|
+
.replaceAll(/"([^"]*)"/g, '$1')
|
|
1121
1228
|
.replaceAll('&', '-and-')
|
|
1122
1229
|
.replaceAll('|', '-or-')
|
|
1123
1230
|
.replaceAll('[]', '-Array')
|
|
1124
1231
|
.replaceAll(/[{}]/g, '_'); // SuccessResponse_{indexesCreated-number}_ -> SuccessResponse__indexesCreated-number__
|
|
1125
1232
|
preformattedName = replaceTypeLiteralPropertySeparators(preformattedName); // SuccessResponse_indexesCreated:number_ -> SuccessResponse_indexesCreated-number_
|
|
1126
|
-
preformattedName = preformattedName.
|
|
1233
|
+
preformattedName = preformattedName.replaceAll(';', '--');
|
|
1127
1234
|
preformattedName = replaceIndexedAccessSegments(preformattedName); // Partial_SerializedDatasourceWithVersion[format]_ -> Partial_SerializedDatasourceWithVersion~format~_,
|
|
1128
1235
|
//Safety fixes to replace all characters which are not accepted by swagger ui
|
|
1129
|
-
let formattedName = preformattedName.
|
|
1236
|
+
let formattedName = preformattedName.replaceAll(/[^A-Za-z0-9\-._]/g, match => {
|
|
1130
1237
|
return `_${match.codePointAt(0) ?? 0}_`;
|
|
1131
1238
|
});
|
|
1132
1239
|
formattedName = formattedName.replaceAll('92_r_92_n', '92_n'); //Windows uses \r\n, but linux uses \n.
|
|
@@ -1423,66 +1530,123 @@ class TypeResolver {
|
|
|
1423
1530
|
}
|
|
1424
1531
|
static getDefault(node) {
|
|
1425
1532
|
const defaultStr = (0, jsDocUtils_1.getJSDocComment)(node, 'default');
|
|
1426
|
-
if (typeof defaultStr
|
|
1427
|
-
|
|
1428
|
-
const inString = () => textStartCharacter !== undefined;
|
|
1429
|
-
let formattedStr = '';
|
|
1430
|
-
for (let i = 0; i < defaultStr.length; ++i) {
|
|
1431
|
-
const actCharacter = defaultStr[i];
|
|
1432
|
-
if (inString()) {
|
|
1433
|
-
if (actCharacter === textStartCharacter) {
|
|
1434
|
-
formattedStr += '"';
|
|
1435
|
-
textStartCharacter = undefined;
|
|
1436
|
-
}
|
|
1437
|
-
else if (actCharacter === '"') {
|
|
1438
|
-
formattedStr += '\\"';
|
|
1439
|
-
}
|
|
1440
|
-
else if (actCharacter === '\\') {
|
|
1441
|
-
++i;
|
|
1442
|
-
if (i < defaultStr.length) {
|
|
1443
|
-
const nextCharacter = defaultStr[i];
|
|
1444
|
-
if (['n', 't', 'r', 'b', 'f', '\\', '"'].includes(nextCharacter)) {
|
|
1445
|
-
formattedStr += '\\' + nextCharacter;
|
|
1446
|
-
}
|
|
1447
|
-
else if (!['v', '0'].includes(nextCharacter)) {
|
|
1448
|
-
//\v, \0 characters are not compatible with JSON
|
|
1449
|
-
formattedStr += nextCharacter;
|
|
1450
|
-
}
|
|
1451
|
-
}
|
|
1452
|
-
else {
|
|
1453
|
-
formattedStr += actCharacter; // this is a bug, but let the JSON parser decide how to handle it
|
|
1454
|
-
}
|
|
1455
|
-
}
|
|
1456
|
-
else {
|
|
1457
|
-
formattedStr += actCharacter;
|
|
1458
|
-
}
|
|
1459
|
-
}
|
|
1460
|
-
else {
|
|
1461
|
-
if ([`"`, "'", '`'].includes(actCharacter)) {
|
|
1462
|
-
textStartCharacter = actCharacter;
|
|
1463
|
-
formattedStr += '"';
|
|
1464
|
-
}
|
|
1465
|
-
else if (actCharacter === '/' && i + 1 < defaultStr.length && defaultStr[i + 1] === '/') {
|
|
1466
|
-
i += 2;
|
|
1467
|
-
while (i < defaultStr.length && defaultStr[i] !== '\n') {
|
|
1468
|
-
++i;
|
|
1469
|
-
}
|
|
1470
|
-
}
|
|
1471
|
-
else {
|
|
1472
|
-
formattedStr += actCharacter;
|
|
1473
|
-
}
|
|
1474
|
-
}
|
|
1475
|
-
}
|
|
1476
|
-
try {
|
|
1477
|
-
const parsed = JSON.parse(formattedStr);
|
|
1478
|
-
return parsed;
|
|
1479
|
-
}
|
|
1480
|
-
catch (err) {
|
|
1481
|
-
const message = err instanceof Error ? err.message : '-';
|
|
1482
|
-
throw new exceptions_1.GenerateMetadataError(`JSON could not parse default str: "${defaultStr}", preformatted: "${formattedStr}"\nmessage: "${message}"`);
|
|
1483
|
-
}
|
|
1533
|
+
if (typeof defaultStr !== 'string' || defaultStr === 'undefined') {
|
|
1534
|
+
return undefined;
|
|
1484
1535
|
}
|
|
1485
|
-
|
|
1536
|
+
const formattedStr = this.formatDefaultString(defaultStr);
|
|
1537
|
+
try {
|
|
1538
|
+
return JSON.parse(formattedStr);
|
|
1539
|
+
}
|
|
1540
|
+
catch (error) {
|
|
1541
|
+
const message = error instanceof Error ? error.message : '-';
|
|
1542
|
+
throw new exceptions_1.GenerateMetadataError(`JSON could not parse default str: "${defaultStr}", preformatted: "${formattedStr}"\nmessage: "${message}"`);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
static formatDefaultString(defaultStr) {
|
|
1546
|
+
const initialState = { formatted: '' };
|
|
1547
|
+
let state = initialState;
|
|
1548
|
+
let index = 0;
|
|
1549
|
+
while (index < defaultStr.length) {
|
|
1550
|
+
const formattedCharacter = this.formatDefaultCharacter(defaultStr, index, state);
|
|
1551
|
+
state = {
|
|
1552
|
+
formatted: formattedCharacter.formatted,
|
|
1553
|
+
textStartCharacter: formattedCharacter.textStartCharacter,
|
|
1554
|
+
};
|
|
1555
|
+
index = formattedCharacter.index + 1;
|
|
1556
|
+
}
|
|
1557
|
+
return state.formatted;
|
|
1558
|
+
}
|
|
1559
|
+
static formatDefaultCharacter(defaultStr, index, state) {
|
|
1560
|
+
const character = defaultStr[index];
|
|
1561
|
+
if (state.textStartCharacter !== undefined) {
|
|
1562
|
+
return this.formatDefaultStringCharacter(defaultStr, index, state, character);
|
|
1563
|
+
}
|
|
1564
|
+
return this.formatDefaultNonStringCharacter(defaultStr, index, state, character);
|
|
1565
|
+
}
|
|
1566
|
+
static formatDefaultStringCharacter(defaultStr, index, state, character) {
|
|
1567
|
+
if (character === state.textStartCharacter) {
|
|
1568
|
+
return {
|
|
1569
|
+
formatted: `${state.formatted}"`,
|
|
1570
|
+
index,
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
if (character === '"') {
|
|
1574
|
+
return {
|
|
1575
|
+
...state,
|
|
1576
|
+
formatted: `${state.formatted}${escapedDoubleQuote}`,
|
|
1577
|
+
index,
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
if (character !== backslash) {
|
|
1581
|
+
return {
|
|
1582
|
+
...state,
|
|
1583
|
+
formatted: `${state.formatted}${character}`,
|
|
1584
|
+
index,
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1587
|
+
return this.formatEscapedDefaultCharacter(defaultStr, index, state);
|
|
1588
|
+
}
|
|
1589
|
+
static formatEscapedDefaultCharacter(defaultStr, index, state) {
|
|
1590
|
+
const nextIndex = index + 1;
|
|
1591
|
+
if (nextIndex >= defaultStr.length) {
|
|
1592
|
+
return {
|
|
1593
|
+
...state,
|
|
1594
|
+
formatted: `${state.formatted}${backslash}`,
|
|
1595
|
+
index,
|
|
1596
|
+
};
|
|
1597
|
+
}
|
|
1598
|
+
const nextCharacter = defaultStr[nextIndex];
|
|
1599
|
+
if (['n', 't', 'r', 'b', 'f', backslash, '"'].includes(nextCharacter)) {
|
|
1600
|
+
return {
|
|
1601
|
+
...state,
|
|
1602
|
+
formatted: `${state.formatted}${backslash}${nextCharacter}`,
|
|
1603
|
+
index: nextIndex,
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
if (!['v', '0'].includes(nextCharacter)) {
|
|
1607
|
+
return {
|
|
1608
|
+
...state,
|
|
1609
|
+
formatted: `${state.formatted}${nextCharacter}`,
|
|
1610
|
+
index: nextIndex,
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
return {
|
|
1614
|
+
...state,
|
|
1615
|
+
index: nextIndex,
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
static formatDefaultNonStringCharacter(defaultStr, index, state, character) {
|
|
1619
|
+
if (this.isDefaultStringDelimiter(character)) {
|
|
1620
|
+
return {
|
|
1621
|
+
formatted: `${state.formatted}"`,
|
|
1622
|
+
textStartCharacter: character,
|
|
1623
|
+
index,
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
if (this.startsDefaultLineComment(defaultStr, index)) {
|
|
1627
|
+
return this.skipDefaultLineComment(defaultStr, index, state);
|
|
1628
|
+
}
|
|
1629
|
+
return {
|
|
1630
|
+
...state,
|
|
1631
|
+
formatted: `${state.formatted}${character}`,
|
|
1632
|
+
index,
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
static isDefaultStringDelimiter(character) {
|
|
1636
|
+
return character === '"' || character === "'" || character === '`';
|
|
1637
|
+
}
|
|
1638
|
+
static startsDefaultLineComment(defaultStr, index) {
|
|
1639
|
+
return defaultStr[index] === '/' && defaultStr[index + 1] === '/';
|
|
1640
|
+
}
|
|
1641
|
+
static skipDefaultLineComment(defaultStr, index, state) {
|
|
1642
|
+
let nextIndex = index + 2;
|
|
1643
|
+
while (nextIndex < defaultStr.length && defaultStr[nextIndex] !== '\n') {
|
|
1644
|
+
nextIndex += 1;
|
|
1645
|
+
}
|
|
1646
|
+
return {
|
|
1647
|
+
...state,
|
|
1648
|
+
index: nextIndex,
|
|
1649
|
+
};
|
|
1486
1650
|
}
|
|
1487
1651
|
}
|
|
1488
1652
|
exports.TypeResolver = TypeResolver;
|