@tsoa-next/cli 8.0.0 → 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.
- package/dist/api.d.ts +2 -1
- package/dist/api.js +172 -144
- package/dist/api.js.map +1 -1
- package/dist/cli.d.ts +1 -2
- package/dist/cli.js +2 -1
- package/dist/cli.js.map +1 -1
- package/dist/metadataGeneration/controllerGenerator.js +10 -13
- package/dist/metadataGeneration/controllerGenerator.js.map +1 -1
- package/dist/metadataGeneration/exceptions.d.ts +3 -3
- package/dist/metadataGeneration/exceptions.js +5 -12
- package/dist/metadataGeneration/exceptions.js.map +1 -1
- package/dist/metadataGeneration/extension.js +2 -2
- package/dist/metadataGeneration/extension.js.map +1 -1
- package/dist/metadataGeneration/initializer-value.js +90 -80
- package/dist/metadataGeneration/initializer-value.js.map +1 -1
- package/dist/metadataGeneration/metadataGenerator.js +20 -22
- package/dist/metadataGeneration/metadataGenerator.js.map +1 -1
- package/dist/metadataGeneration/methodGenerator.d.ts +1 -0
- package/dist/metadataGeneration/methodGenerator.js +39 -27
- package/dist/metadataGeneration/methodGenerator.js.map +1 -1
- package/dist/metadataGeneration/parameterGenerator.d.ts +5 -0
- package/dist/metadataGeneration/parameterGenerator.js +79 -90
- package/dist/metadataGeneration/parameterGenerator.js.map +1 -1
- package/dist/metadataGeneration/transformer/dateTransformer.js +3 -5
- package/dist/metadataGeneration/transformer/dateTransformer.js.map +1 -1
- package/dist/metadataGeneration/transformer/enumTransformer.d.ts +2 -0
- package/dist/metadataGeneration/transformer/enumTransformer.js +34 -12
- package/dist/metadataGeneration/transformer/enumTransformer.js.map +1 -1
- package/dist/metadataGeneration/transformer/primitiveTransformer.js +2 -4
- package/dist/metadataGeneration/transformer/primitiveTransformer.js.map +1 -1
- package/dist/metadataGeneration/transformer/propertyTransformer.d.ts +1 -0
- package/dist/metadataGeneration/transformer/propertyTransformer.js +14 -19
- package/dist/metadataGeneration/transformer/propertyTransformer.js.map +1 -1
- package/dist/metadataGeneration/transformer/referenceTransformer.d.ts +2 -0
- package/dist/metadataGeneration/transformer/referenceTransformer.js +35 -18
- package/dist/metadataGeneration/transformer/referenceTransformer.js.map +1 -1
- package/dist/metadataGeneration/transformer/transformer.js +3 -4
- package/dist/metadataGeneration/transformer/transformer.js.map +1 -1
- package/dist/metadataGeneration/typeResolver.d.ts +79 -0
- package/dist/metadataGeneration/typeResolver.js +906 -735
- package/dist/metadataGeneration/typeResolver.js.map +1 -1
- package/dist/module/generate-routes.d.ts +1 -2
- package/dist/module/generate-routes.js +18 -17
- package/dist/module/generate-routes.js.map +1 -1
- package/dist/module/generate-spec.js +1 -3
- package/dist/module/generate-spec.js.map +1 -1
- package/dist/routeGeneration/defaultRouteGenerator.js +2 -2
- package/dist/routeGeneration/defaultRouteGenerator.js.map +1 -1
- package/dist/routeGeneration/routeGenerator.d.ts +2 -0
- package/dist/routeGeneration/routeGenerator.js +20 -30
- package/dist/routeGeneration/routeGenerator.js.map +1 -1
- package/dist/swagger/specGenerator.js +1 -1
- package/dist/swagger/specGenerator.js.map +1 -1
- package/dist/swagger/specGenerator2.d.ts +12 -0
- package/dist/swagger/specGenerator2.js +200 -170
- package/dist/swagger/specGenerator2.js.map +1 -1
- package/dist/swagger/specGenerator3.d.ts +17 -87
- package/dist/swagger/specGenerator3.js +178 -172
- package/dist/swagger/specGenerator3.js.map +1 -1
- package/dist/utils/decoratorUtils.js +25 -32
- package/dist/utils/decoratorUtils.js.map +1 -1
- package/dist/utils/fs.d.ts +1 -1
- package/dist/utils/fs.js +5 -5
- package/dist/utils/fs.js.map +1 -1
- package/dist/utils/headerTypeHelpers.js +2 -5
- package/dist/utils/headerTypeHelpers.js.map +1 -1
- package/dist/utils/importClassesFromDirectories.js +2 -2
- package/dist/utils/importClassesFromDirectories.js.map +1 -1
- package/dist/utils/jsDocUtils.js +4 -11
- package/dist/utils/jsDocUtils.js.map +1 -1
- package/dist/utils/pathUtils.js +2 -2
- package/dist/utils/pathUtils.js.map +1 -1
- package/dist/utils/swaggerUtils.js +7 -1
- package/dist/utils/swaggerUtils.js.map +1 -1
- package/dist/utils/validatorUtils.js +60 -97
- package/dist/utils/validatorUtils.js.map +1 -1
- package/package.json +2 -2
|
@@ -51,7 +51,10 @@ 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;
|
|
57
|
+
const objectHasOwn = Object.hasOwn;
|
|
55
58
|
const getSyntheticOrigin = (symbol) => {
|
|
56
59
|
const symbolWithLinks = symbol;
|
|
57
60
|
return symbolWithLinks.links?.syntheticOrigin;
|
|
@@ -60,16 +63,46 @@ const isAsciiLetter = (char) => {
|
|
|
60
63
|
if (!char) {
|
|
61
64
|
return false;
|
|
62
65
|
}
|
|
63
|
-
const code = char.
|
|
66
|
+
const code = char.codePointAt(0) ?? -1;
|
|
64
67
|
return (code >= 65 && code <= 90) || (code >= 97 && code <= 122);
|
|
65
68
|
};
|
|
66
69
|
const isRefTypeTokenCharacter = (char) => {
|
|
67
70
|
if (!char) {
|
|
68
71
|
return false;
|
|
69
72
|
}
|
|
70
|
-
const code = char.
|
|
73
|
+
const code = char.codePointAt(0) ?? -1;
|
|
71
74
|
return isAsciiLetter(char) || (code >= 48 && code <= 57) || char === '_';
|
|
72
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
|
+
};
|
|
73
106
|
const replaceTypeLiteralPropertySeparators = (value) => {
|
|
74
107
|
let formatted = '';
|
|
75
108
|
let index = 0;
|
|
@@ -79,29 +112,15 @@ const replaceTypeLiteralPropertySeparators = (value) => {
|
|
|
79
112
|
index += 1;
|
|
80
113
|
continue;
|
|
81
114
|
}
|
|
82
|
-
const
|
|
83
|
-
while (index < value.length && isRefTypeTokenCharacter(value[index])) {
|
|
84
|
-
index += 1;
|
|
85
|
-
}
|
|
86
|
-
if (value[index] === '?') {
|
|
87
|
-
index += 1;
|
|
88
|
-
}
|
|
89
|
-
const token = value.slice(tokenStart, index);
|
|
90
|
-
if (value[index] === ':') {
|
|
91
|
-
const typeStart = index + 1;
|
|
92
|
-
let typeEnd = typeStart;
|
|
93
|
-
while (typeEnd < value.length && isAsciiLetter(value[typeEnd])) {
|
|
94
|
-
typeEnd += 1;
|
|
95
|
-
}
|
|
96
|
-
if (typeEnd > typeStart) {
|
|
97
|
-
formatted += token;
|
|
98
|
-
formatted += '-';
|
|
99
|
-
formatted += value.slice(typeStart, typeEnd);
|
|
100
|
-
index = typeEnd;
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
115
|
+
const { nextIndex, token } = readRefTypeToken(value, index);
|
|
104
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;
|
|
105
124
|
}
|
|
106
125
|
return formatted;
|
|
107
126
|
};
|
|
@@ -114,7 +133,7 @@ const replaceIndexedAccessSegments = (value) => {
|
|
|
114
133
|
index += 1;
|
|
115
134
|
continue;
|
|
116
135
|
}
|
|
117
|
-
const previousCharacter = formatted
|
|
136
|
+
const previousCharacter = formatted.at(-1);
|
|
118
137
|
if (!(isAsciiLetter(previousCharacter) || previousCharacter === '}' || previousCharacter === ']' || previousCharacter === ')')) {
|
|
119
138
|
formatted += value[index];
|
|
120
139
|
index += 1;
|
|
@@ -156,356 +175,300 @@ class TypeResolver {
|
|
|
156
175
|
});
|
|
157
176
|
}
|
|
158
177
|
resolve() {
|
|
159
|
-
const
|
|
160
|
-
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);
|
|
161
180
|
if (primitiveType) {
|
|
162
181
|
return primitiveType;
|
|
163
182
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
elementType: new TypeResolver(this.typeNode.elementType, this.current, this.parentNode, this.context).resolve(),
|
|
168
|
-
};
|
|
169
|
-
return arrayMetaType;
|
|
183
|
+
const nonReferenceType = this.resolveNonReferenceTypeNode();
|
|
184
|
+
if (nonReferenceType) {
|
|
185
|
+
return nonReferenceType;
|
|
170
186
|
}
|
|
171
|
-
|
|
172
|
-
|
|
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;
|
|
173
210
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
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;
|
|
183
219
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (ts.
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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;
|
|
214
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)) {
|
|
215
348
|
return {
|
|
216
|
-
dataType: '
|
|
217
|
-
types:
|
|
218
|
-
...(restType ? { restType } : {}),
|
|
349
|
+
dataType: 'union',
|
|
350
|
+
types: type.types.map(unionType => this.resolveMappedType(unionType, mappedTypeNode)),
|
|
219
351
|
};
|
|
220
352
|
}
|
|
221
|
-
if (this.
|
|
222
|
-
|
|
223
|
-
dataType: 'any',
|
|
224
|
-
};
|
|
225
|
-
return literallyAny;
|
|
353
|
+
if (this.hasFlag(type, ts.TypeFlags.Undefined)) {
|
|
354
|
+
return { dataType: 'undefined' };
|
|
226
355
|
}
|
|
227
|
-
if (
|
|
228
|
-
|
|
356
|
+
if (this.hasFlag(type, ts.TypeFlags.Null)) {
|
|
357
|
+
return {
|
|
229
358
|
dataType: 'enum',
|
|
230
|
-
enums: [
|
|
231
|
-
};
|
|
232
|
-
return enumType;
|
|
233
|
-
}
|
|
234
|
-
if (ts.isTypeLiteralNode(this.typeNode)) {
|
|
235
|
-
const properties = this.typeNode.members.filter(ts.isPropertySignature).reduce((res, propertySignature) => {
|
|
236
|
-
const type = new TypeResolver(propertySignature.type, this.current, propertySignature, this.context).resolve();
|
|
237
|
-
const def = TypeResolver.getDefault(propertySignature);
|
|
238
|
-
const property = {
|
|
239
|
-
example: this.getNodeExample(propertySignature),
|
|
240
|
-
default: def,
|
|
241
|
-
description: this.getNodeDescription(propertySignature),
|
|
242
|
-
format: this.getNodeFormat(propertySignature),
|
|
243
|
-
name: this.getPropertyName(propertySignature),
|
|
244
|
-
required: !propertySignature.questionToken,
|
|
245
|
-
type,
|
|
246
|
-
validators: (0, validatorUtils_1.getPropertyValidators)(propertySignature) || {},
|
|
247
|
-
deprecated: (0, jsDocUtils_1.isExistJSDocTag)(propertySignature, tag => tag.tagName.text === 'deprecated'),
|
|
248
|
-
title: this.getNodeTitle(propertySignature),
|
|
249
|
-
extensions: this.getNodeExtension(propertySignature),
|
|
250
|
-
};
|
|
251
|
-
return [property, ...res];
|
|
252
|
-
}, []);
|
|
253
|
-
const indexMember = this.typeNode.members.find(member => ts.isIndexSignatureDeclaration(member));
|
|
254
|
-
let additionalType;
|
|
255
|
-
if (indexMember) {
|
|
256
|
-
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
|
|
257
|
-
const indexSignatureDeclaration = indexMember;
|
|
258
|
-
const indexType = new TypeResolver(indexSignatureDeclaration.parameters[0].type, this.current, this.parentNode, this.context).resolve();
|
|
259
|
-
(0, flowUtils_1.throwUnless)(indexType.dataType === 'string', new exceptions_1.GenerateMetadataError(`Only string indexers are supported.`, this.typeNode));
|
|
260
|
-
additionalType = new TypeResolver(indexSignatureDeclaration.type, this.current, this.parentNode, this.context).resolve();
|
|
261
|
-
}
|
|
262
|
-
const objLiteral = {
|
|
263
|
-
additionalProperties: indexMember && additionalType,
|
|
264
|
-
dataType: 'nestedObjectLiteral',
|
|
265
|
-
properties,
|
|
359
|
+
enums: [null],
|
|
266
360
|
};
|
|
267
|
-
return objLiteral;
|
|
268
361
|
}
|
|
269
|
-
if (this.
|
|
270
|
-
return
|
|
362
|
+
if (this.hasFlag(type, ts.TypeFlags.Object)) {
|
|
363
|
+
return this.resolveMappedObjectType(type, mappedTypeNode);
|
|
271
364
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
if (this.hasFlag(type, ts.TypeFlags.Union)) {
|
|
293
|
-
//Intersections are not interesting somehow...
|
|
294
|
-
const types = type.types;
|
|
295
|
-
const resolvedTypes = types.map(calcMappedType);
|
|
296
|
-
return {
|
|
297
|
-
dataType: 'union',
|
|
298
|
-
types: resolvedTypes,
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
else if (this.hasFlag(type, ts.TypeFlags.Undefined)) {
|
|
302
|
-
return {
|
|
303
|
-
dataType: 'undefined',
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
else if (this.hasFlag(type, ts.TypeFlags.Null)) {
|
|
307
|
-
return {
|
|
308
|
-
dataType: 'enum',
|
|
309
|
-
enums: [null],
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
else if (this.hasFlag(type, ts.TypeFlags.Object)) {
|
|
313
|
-
const typeProperties = type.getProperties();
|
|
314
|
-
const properties = typeProperties
|
|
315
|
-
// Ignore methods, getter, setter and @ignored props
|
|
316
|
-
.filter(property => isIgnored(property) === false)
|
|
317
|
-
// Transform to property
|
|
318
|
-
.map(property => {
|
|
319
|
-
const propertyType = this.current.typeChecker.getTypeOfSymbolAtLocation(property, this.typeNode);
|
|
320
|
-
const typeNode = this.current.typeChecker.typeToTypeNode(propertyType, undefined, ts.NodeBuilderFlags.NoTruncation);
|
|
321
|
-
const parent = getOneOrigDeclaration(property); //If there are more declarations, we need to get one of them, from where we want to recognize jsDoc
|
|
322
|
-
const type = new TypeResolver(typeNode, this.current, parent, this.context, propertyType).resolve();
|
|
323
|
-
const required = !this.hasFlag(property, ts.SymbolFlags.Optional);
|
|
324
|
-
const comments = property.getDocumentationComment(this.current.typeChecker);
|
|
325
|
-
const description = comments.length ? ts.displayPartsToString(comments) : undefined;
|
|
326
|
-
const initializer = parent && hasInitializer(parent) ? parent.initializer : undefined;
|
|
327
|
-
const def = initializer ? (0, initializer_value_1.getInitializerValue)(initializer, this.current.typeChecker) : parent ? TypeResolver.getDefault(parent) : undefined;
|
|
328
|
-
// Push property
|
|
329
|
-
return {
|
|
330
|
-
name: property.getName(),
|
|
331
|
-
required,
|
|
332
|
-
deprecated: parent
|
|
333
|
-
? (0, jsDocUtils_1.isExistJSDocTag)(parent, tag => tag.tagName.text === 'deprecated') || (0, decoratorUtils_1.isDecorator)(parent, (_identifier, canonicalName) => canonicalName === 'Deprecated', this.current.typeChecker)
|
|
334
|
-
: false,
|
|
335
|
-
type,
|
|
336
|
-
default: def,
|
|
337
|
-
// validators are disjunct via types, so it is now OK.
|
|
338
|
-
// if a type not changes while mapping, we need validators
|
|
339
|
-
// if a type changes, then the validators will be not relevant
|
|
340
|
-
validators: (parent ? (0, validatorUtils_1.getPropertyValidators)(parent) : {}) || {},
|
|
341
|
-
description,
|
|
342
|
-
format: parent ? this.getNodeFormat(parent) : undefined,
|
|
343
|
-
example: parent ? this.getNodeExample(parent) : undefined,
|
|
344
|
-
extensions: parent ? this.getNodeExtension(parent) : undefined,
|
|
345
|
-
};
|
|
346
|
-
});
|
|
347
|
-
const objectLiteral = {
|
|
348
|
-
dataType: 'nestedObjectLiteral',
|
|
349
|
-
properties,
|
|
350
|
-
};
|
|
351
|
-
const indexInfos = this.current.typeChecker.getIndexInfosOfType(type);
|
|
352
|
-
const indexTypes = indexInfos.flatMap(indexInfo => {
|
|
353
|
-
const typeNode = this.current.typeChecker.typeToTypeNode(indexInfo.type, undefined, ts.NodeBuilderFlags.NoTruncation);
|
|
354
|
-
if (typeNode.kind === ts.SyntaxKind.NeverKeyword) {
|
|
355
|
-
// { [k: string]: never; }
|
|
356
|
-
return [];
|
|
357
|
-
}
|
|
358
|
-
const type = new TypeResolver(typeNode, this.current, mappedTypeNode, this.context, indexInfo.type).resolve();
|
|
359
|
-
return [type];
|
|
360
|
-
});
|
|
361
|
-
if (indexTypes.length) {
|
|
362
|
-
if (indexTypes.length === 1) {
|
|
363
|
-
objectLiteral.additionalProperties = indexTypes[0];
|
|
364
|
-
}
|
|
365
|
-
else {
|
|
366
|
-
// { [k: string]: string; } & { [k: number]: number; }
|
|
367
|
-
// A | B is sometimes A type or B type, sometimes optionally accepts both A & B members.
|
|
368
|
-
// Most people & TSOA thinks that A | B can be only A or only B.
|
|
369
|
-
// So we can accept this merge
|
|
370
|
-
//Every additional property key assumed as string
|
|
371
|
-
objectLiteral.additionalProperties = {
|
|
372
|
-
dataType: 'union',
|
|
373
|
-
types: indexTypes,
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
return objectLiteral;
|
|
378
|
-
}
|
|
379
|
-
// Known issues & easy to implement: Partial<string>, Partial<never>, ... But I think a programmer not writes types like this
|
|
380
|
-
throw new exceptions_1.GenerateMetadataError(`Unhandled mapped type has found, flags: ${type.flags}`, this.typeNode);
|
|
381
|
-
};
|
|
382
|
-
const referencer = this.getReferencer();
|
|
383
|
-
const result = calcMappedType(referencer);
|
|
384
|
-
return result;
|
|
385
|
-
}
|
|
386
|
-
if (ts.isConditionalTypeNode(this.typeNode)) {
|
|
387
|
-
const referencer = this.getReferencer();
|
|
388
|
-
const resolvedNode = this.current.typeChecker.typeToTypeNode(referencer, undefined, ts.NodeBuilderFlags.NoTruncation);
|
|
389
|
-
return new TypeResolver(resolvedNode, this.current, this.typeNode, this.context, referencer).resolve();
|
|
390
|
-
}
|
|
391
|
-
// keyof & readonly arrays
|
|
392
|
-
if (ts.isTypeOperatorNode(this.typeNode)) {
|
|
393
|
-
return this.resolveTypeOperatorNode(this.typeNode, this.current.typeChecker, this.current, this.context, this.parentNode, this.referencer);
|
|
394
|
-
}
|
|
395
|
-
// Indexed type
|
|
396
|
-
if (ts.isIndexedAccessTypeNode(this.typeNode)) {
|
|
397
|
-
return this.resolveIndexedAccessTypeNode(this.typeNode, this.current.typeChecker, this.current, this.context);
|
|
398
|
-
}
|
|
399
|
-
if (ts.isTemplateLiteralTypeNode(this.typeNode)) {
|
|
400
|
-
const type = this.getReferencer();
|
|
401
|
-
(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));
|
|
402
|
-
// `a${'c' | 'd'}b`
|
|
403
|
-
const stringLiteralEnum = {
|
|
404
|
-
dataType: 'enum',
|
|
405
|
-
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,
|
|
406
385
|
};
|
|
407
|
-
return stringLiteralEnum;
|
|
408
386
|
}
|
|
409
|
-
|
|
410
|
-
|
|
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;
|
|
411
410
|
}
|
|
412
|
-
(0,
|
|
413
|
-
|
|
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);
|
|
419
|
+
}
|
|
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();
|
|
414
467
|
}
|
|
415
468
|
resolveTypeOperatorNode(typeNode, typeChecker, current, context, parentNode, referencer) {
|
|
416
469
|
switch (typeNode.operator) {
|
|
417
470
|
case ts.SyntaxKind.KeyOfKeyword: {
|
|
418
|
-
|
|
419
|
-
const type = typeChecker.getTypeFromTypeNode(typeNode);
|
|
420
|
-
if (type.isIndexType()) {
|
|
421
|
-
// in case of generic: keyof T. Not handles all possible cases
|
|
422
|
-
const symbol = type.type.getSymbol();
|
|
423
|
-
if (symbol && symbol.getFlags() & ts.TypeFlags.TypeParameter) {
|
|
424
|
-
const typeName = symbol.getEscapedName();
|
|
425
|
-
(0, flowUtils_1.throwUnless)(typeof typeName === 'string', new exceptions_1.GenerateMetadataError(`typeName is not string, but ${typeof typeName}`, typeNode));
|
|
426
|
-
if (context[typeName]) {
|
|
427
|
-
const subResult = new TypeResolver(context[typeName].type, current, parentNode, context).resolve();
|
|
428
|
-
if (subResult.dataType === 'any') {
|
|
429
|
-
return {
|
|
430
|
-
dataType: 'union',
|
|
431
|
-
types: [{ dataType: 'string' }, { dataType: 'double' }],
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
const properties = subResult.properties?.map(v => v.name);
|
|
435
|
-
(0, flowUtils_1.throwUnless)(properties, new exceptions_1.GenerateMetadataError(`TypeOperator 'keyof' on node which have no properties`, context[typeName].type));
|
|
436
|
-
return {
|
|
437
|
-
dataType: 'enum',
|
|
438
|
-
enums: properties,
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
else if (type.isUnion()) {
|
|
444
|
-
const literals = type.types.filter((t) => t.isLiteral());
|
|
445
|
-
const literalValues = [];
|
|
446
|
-
for (const literal of literals) {
|
|
447
|
-
(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));
|
|
448
|
-
literalValues.push(literal.value);
|
|
449
|
-
}
|
|
450
|
-
if (!literals.length) {
|
|
451
|
-
const length = type.types.length;
|
|
452
|
-
const someStringFlag = type.types.some(t => t.flags === ts.TypeFlags.String);
|
|
453
|
-
const someNumberFlag = type.types.some(t => t.flags === ts.TypeFlags.Number);
|
|
454
|
-
const someSymbolFlag = type.types.some(t => t.flags === ts.TypeFlags.ESSymbol);
|
|
455
|
-
if (someStringFlag && someNumberFlag) {
|
|
456
|
-
if (length === 2 || (length === 3 && someSymbolFlag)) {
|
|
457
|
-
return {
|
|
458
|
-
dataType: 'union',
|
|
459
|
-
types: [{ dataType: 'string' }, { dataType: 'double' }],
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
// Warn on nonsense (`number`, `typeof Symbol.iterator`)
|
|
465
|
-
if (type.types.find(t => !t.isLiteral()) !== undefined) {
|
|
466
|
-
const problems = type.types.filter(t => !t.isLiteral()).map(t => typeChecker.typeToString(t));
|
|
467
|
-
console.warn(new exceptions_1.GenerateMetaDataWarning(`Skipped non-literal type(s) ${problems.join(', ')}`, typeNode).toString());
|
|
468
|
-
}
|
|
469
|
-
const stringMembers = literalValues.filter(v => typeof v == 'string');
|
|
470
|
-
const numberMembers = literalValues.filter(v => typeof v == 'number');
|
|
471
|
-
if (stringMembers.length && numberMembers.length) {
|
|
472
|
-
return {
|
|
473
|
-
dataType: 'union',
|
|
474
|
-
types: [
|
|
475
|
-
{ dataType: 'enum', enums: stringMembers },
|
|
476
|
-
{ dataType: 'enum', enums: numberMembers },
|
|
477
|
-
],
|
|
478
|
-
};
|
|
479
|
-
}
|
|
480
|
-
return {
|
|
481
|
-
dataType: 'enum',
|
|
482
|
-
enums: literalValues,
|
|
483
|
-
};
|
|
484
|
-
}
|
|
485
|
-
else if (type.isLiteral()) {
|
|
486
|
-
(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));
|
|
487
|
-
return {
|
|
488
|
-
dataType: 'enum',
|
|
489
|
-
enums: [type.value],
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
else if (this.hasFlag(type, ts.TypeFlags.Never)) {
|
|
493
|
-
throw new exceptions_1.GenerateMetadataError(`TypeOperator 'keyof' on node produced a never type`, typeNode);
|
|
494
|
-
}
|
|
495
|
-
else if (this.hasFlag(type, ts.TypeFlags.TemplateLiteral)) {
|
|
496
|
-
//Now assumes template literals as string
|
|
497
|
-
console.warn(new exceptions_1.GenerateMetaDataWarning(`Template literals are assumed as strings`, typeNode).toString());
|
|
498
|
-
return {
|
|
499
|
-
dataType: 'string',
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
else if (this.hasFlag(type, ts.TypeFlags.Number)) {
|
|
503
|
-
return {
|
|
504
|
-
dataType: 'double',
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
const indexedTypeName = typeChecker.typeToString(typeChecker.getTypeFromTypeNode(typeNode.type));
|
|
508
|
-
throw new exceptions_1.GenerateMetadataError(`Could not determine the keys on ${indexedTypeName}`, typeNode);
|
|
471
|
+
return this.resolveKeyOfTypeOperator(typeNode, typeChecker, current, context, parentNode);
|
|
509
472
|
}
|
|
510
473
|
case ts.SyntaxKind.ReadonlyKeyword:
|
|
511
474
|
// Handle `readonly` arrays
|
|
@@ -514,46 +477,176 @@ class TypeResolver {
|
|
|
514
477
|
throw new exceptions_1.GenerateMetadataError(`Unknown type: ${ts.SyntaxKind[typeNode.kind]}`, typeNode);
|
|
515
478
|
}
|
|
516
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
|
+
}
|
|
517
595
|
resolveIndexedAccessTypeNode(typeNode, typeChecker, current, context) {
|
|
518
596
|
const { indexType, objectType } = typeNode;
|
|
519
597
|
if ([ts.SyntaxKind.NumberKeyword, ts.SyntaxKind.StringKeyword].includes(indexType.kind)) {
|
|
520
|
-
|
|
521
|
-
const isNumberIndexType = indexType.kind === ts.SyntaxKind.NumberKeyword;
|
|
522
|
-
const typeOfObjectType = typeChecker.getTypeFromTypeNode(objectType);
|
|
523
|
-
const type = isNumberIndexType ? typeOfObjectType.getNumberIndexType() : typeOfObjectType.getStringIndexType();
|
|
524
|
-
(0, flowUtils_1.throwUnless)(type, new exceptions_1.GenerateMetadataError(`Could not determine ${isNumberIndexType ? 'number' : 'string'} index on ${typeChecker.typeToString(typeOfObjectType)}`, typeNode));
|
|
525
|
-
return new TypeResolver(typeChecker.typeToTypeNode(type, objectType, ts.NodeBuilderFlags.NoTruncation), current, typeNode, context).resolve();
|
|
526
|
-
}
|
|
527
|
-
else if (ts.isLiteralTypeNode(indexType) && (ts.isStringLiteral(indexType.literal) || ts.isNumericLiteral(indexType.literal))) {
|
|
528
|
-
// Indexed by literal
|
|
529
|
-
const hasType = (node) => node !== undefined && Object.prototype.hasOwnProperty.call(node, 'type');
|
|
530
|
-
const symbol = typeChecker.getPropertyOfType(typeChecker.getTypeFromTypeNode(objectType), indexType.literal.text);
|
|
531
|
-
(0, flowUtils_1.throwUnless)(symbol, new exceptions_1.GenerateMetadataError(`Could not determine the keys on ${typeChecker.typeToString(typeChecker.getTypeFromTypeNode(objectType))}`, typeNode));
|
|
532
|
-
if (hasType(symbol.valueDeclaration) && symbol.valueDeclaration.type) {
|
|
533
|
-
return new TypeResolver(symbol.valueDeclaration.type, current, typeNode, context).resolve();
|
|
534
|
-
}
|
|
535
|
-
const declaration = typeChecker.getTypeOfSymbolAtLocation(symbol, objectType);
|
|
536
|
-
try {
|
|
537
|
-
return new TypeResolver(typeChecker.typeToTypeNode(declaration, objectType, ts.NodeBuilderFlags.NoTruncation), current, typeNode, context).resolve();
|
|
538
|
-
}
|
|
539
|
-
catch {
|
|
540
|
-
throw new exceptions_1.GenerateMetadataError(`Could not determine the keys on ${typeChecker.typeToString(typeChecker.getTypeFromTypeNode(typeChecker.typeToTypeNode(declaration, undefined, ts.NodeBuilderFlags.NoTruncation)))}`, typeNode);
|
|
541
|
-
}
|
|
598
|
+
return this.resolveIndexedAccessKeywordType(typeNode, typeChecker, current, context, objectType, indexType);
|
|
542
599
|
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
const type = this.getReferencer();
|
|
551
|
-
const node = typeChecker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.InTypeAlias | ts.NodeBuilderFlags.NoTruncation);
|
|
552
|
-
return new TypeResolver(node, current, typeNode, context, this.referencer).resolve();
|
|
600
|
+
if (ts.isLiteralTypeNode(indexType) && (ts.isStringLiteral(indexType.literal) || ts.isNumericLiteral(indexType.literal))) {
|
|
601
|
+
return this.resolveIndexedAccessLiteralType(typeNode, typeChecker, current, context, objectType, indexType);
|
|
602
|
+
}
|
|
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;
|
|
553
607
|
}
|
|
554
608
|
}
|
|
555
609
|
throw new exceptions_1.GenerateMetadataError(`Unknown type: ${ts.SyntaxKind[typeNode.kind]}`, typeNode);
|
|
556
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
|
+
}
|
|
557
650
|
resolveTypeReferenceNode(typeNode, current, context, parentNode) {
|
|
558
651
|
const { typeName } = typeNode;
|
|
559
652
|
const resolvedTypeArguments = typeNode.typeArguments ? [...typeNode.typeArguments] : undefined;
|
|
@@ -716,15 +809,15 @@ class TypeResolver {
|
|
|
716
809
|
case ts.SyntaxKind.StringLiteral:
|
|
717
810
|
return typeNode.literal.text;
|
|
718
811
|
case ts.SyntaxKind.NumericLiteral:
|
|
719
|
-
return parseFloat(typeNode.literal.text);
|
|
812
|
+
return Number.parseFloat(typeNode.literal.text);
|
|
720
813
|
case ts.SyntaxKind.PrefixUnaryExpression:
|
|
721
814
|
// make sure to only handle the MinusToken here
|
|
722
815
|
(0, flowUtils_1.throwUnless)(typeNode.literal.operator === ts.SyntaxKind.MinusToken, new exceptions_1.GenerateMetadataError(`Couldn't resolve literal node: ${typeNode.literal.getText()}`));
|
|
723
|
-
return parseFloat(typeNode.literal.getText());
|
|
816
|
+
return Number.parseFloat(typeNode.literal.getText());
|
|
724
817
|
case ts.SyntaxKind.NullKeyword:
|
|
725
818
|
return null;
|
|
726
819
|
default:
|
|
727
|
-
(0, flowUtils_1.throwUnless)(
|
|
820
|
+
(0, flowUtils_1.throwUnless)(objectHasOwn(typeNode.literal, 'text'), new exceptions_1.GenerateMetadataError(`Couldn't resolve literal node: ${typeNode.literal.getText()}`));
|
|
728
821
|
return typeNode.literal.text;
|
|
729
822
|
}
|
|
730
823
|
}
|
|
@@ -765,92 +858,83 @@ class TypeResolver {
|
|
|
765
858
|
}
|
|
766
859
|
//Generates type name for type references
|
|
767
860
|
calcRefTypeName(type) {
|
|
768
|
-
const
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
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;
|
|
778
879
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
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
|
-
// - Add declaration positions into type names (In an order).
|
|
814
|
-
// - It accepts multiple types with same name, if the code compiles, there would be no conflicts in the type names
|
|
815
|
-
// - Clear namespaces from type names.
|
|
816
|
-
// - Horrible changes can be in the routes.ts in case of teamwork,
|
|
817
|
-
// because source files have paths in the computer where data generation runs.
|
|
818
|
-
// - Use fully namespaced names
|
|
819
|
-
// - Conflicts can be recognized because of the declarations
|
|
820
|
-
//
|
|
821
|
-
// The second was implemented, it not changes the usual type name formats.
|
|
822
|
-
const oneDeclaration = declarations[0]; //Every declarations should be in the same namespace hierarchy
|
|
823
|
-
if (oneDeclaration && ts.isEnumMember(oneDeclaration)) {
|
|
824
|
-
name = `${oneDeclaration.parent.name.getText()}.${oneDeclaration.name.getText()}`;
|
|
825
|
-
}
|
|
826
|
-
else {
|
|
827
|
-
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;
|
|
828
914
|
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
if (ts.isBlock(actNode)) {
|
|
834
|
-
break;
|
|
835
|
-
}
|
|
836
|
-
if (!(isFirst && ts.isEnumDeclaration(actNode)) && !ts.isModuleBlock(actNode)) {
|
|
837
|
-
(0, flowUtils_1.throwUnless)(ts.isModuleDeclaration(actNode), new exceptions_1.GenerateMetadataError(`This node kind is unknown: ${actNode.kind}`, type));
|
|
838
|
-
if (!isGlobalDeclaration(actNode)) {
|
|
839
|
-
const moduleName = actNode.name.text;
|
|
840
|
-
name = `${moduleName}.${name}`;
|
|
841
|
-
}
|
|
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}`;
|
|
842
919
|
}
|
|
843
|
-
isFirst = false;
|
|
844
|
-
actNode = actNode.parent;
|
|
845
920
|
}
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
pos: declaration.pos,
|
|
849
|
-
}));
|
|
850
|
-
this.current.CheckModelUnicity(name, declarationPositions);
|
|
921
|
+
isFirst = false;
|
|
922
|
+
currentNode = currentNode.parent;
|
|
851
923
|
}
|
|
852
924
|
return name;
|
|
853
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
|
+
}
|
|
854
938
|
calcMemberJsDocProperties(arg) {
|
|
855
939
|
const def = TypeResolver.getDefault(arg);
|
|
856
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);
|
|
@@ -861,13 +945,13 @@ class TypeResolver {
|
|
|
861
945
|
const format = this.getNodeFormat(arg);
|
|
862
946
|
const example = this.getNodeExample(arg);
|
|
863
947
|
const extensions = this.getNodeExtension(arg);
|
|
864
|
-
const isIgnored = (0, jsDocUtils_1.getJSDocTagNames)(arg).
|
|
948
|
+
const isIgnored = (0, jsDocUtils_1.getJSDocTagNames)(arg).includes('ignore');
|
|
865
949
|
const jsonObj = {
|
|
866
950
|
default: def,
|
|
867
951
|
description,
|
|
868
952
|
validators: validators && Object.keys(validators).length ? validators : undefined,
|
|
869
953
|
format,
|
|
870
|
-
example
|
|
954
|
+
example,
|
|
871
955
|
extensions: extensions.length ? extensions : undefined,
|
|
872
956
|
deprecated: isDeprecated ? true : undefined,
|
|
873
957
|
ignored: isIgnored ? true : undefined,
|
|
@@ -885,103 +969,131 @@ class TypeResolver {
|
|
|
885
969
|
}
|
|
886
970
|
//Generates type name for type references
|
|
887
971
|
calcTypeName(arg) {
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
return `'${literalValue}'`;
|
|
892
|
-
}
|
|
893
|
-
if (literalValue === null) {
|
|
894
|
-
return 'null';
|
|
895
|
-
}
|
|
896
|
-
if (typeof literalValue === 'boolean') {
|
|
897
|
-
return literalValue === true ? 'true' : 'false';
|
|
898
|
-
}
|
|
899
|
-
return `${literalValue}`;
|
|
972
|
+
const literalTypeName = this.getLiteralTypeName(arg);
|
|
973
|
+
if (literalTypeName) {
|
|
974
|
+
return literalTypeName;
|
|
900
975
|
}
|
|
901
976
|
const resolvedType = primitiveTransformer_1.PrimitiveTransformer.resolveKindToPrimitive(arg.kind);
|
|
902
977
|
if (resolvedType) {
|
|
903
978
|
return resolvedType;
|
|
904
979
|
}
|
|
905
|
-
|
|
906
|
-
|
|
980
|
+
const structuralTypeName = this.getStructuralTypeName(arg);
|
|
981
|
+
if (structuralTypeName) {
|
|
982
|
+
return structuralTypeName;
|
|
907
983
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
}
|
|
915
|
-
else if (ts.isIndexSignatureDeclaration(member)) {
|
|
916
|
-
(0, flowUtils_1.throwUnless)(member.parameters.length === 1, new exceptions_1.GenerateMetadataError(`Index signature parameters length != 1`, member));
|
|
917
|
-
const indexType = member.parameters[0];
|
|
918
|
-
(0, flowUtils_1.throwUnless)(
|
|
919
|
-
// now we can't reach this part of code
|
|
920
|
-
ts.isParameter(indexType), new exceptions_1.GenerateMetadataError(`indexSignature declaration parameter kind is not SyntaxKind.Parameter`, indexType));
|
|
921
|
-
(0, flowUtils_1.throwUnless)(!indexType.questionToken, new exceptions_1.GenerateMetadataError(`Question token has found for an indexSignature declaration`, indexType));
|
|
922
|
-
const typeText = this.calcTypeName(member.type);
|
|
923
|
-
const indexName = indexType.name.text;
|
|
924
|
-
const indexTypeText = this.calcTypeName(indexType.type);
|
|
925
|
-
return `["${indexName}": ${indexTypeText}]: ${typeText}`;
|
|
926
|
-
}
|
|
927
|
-
throw new exceptions_1.GenerateMetadataError(`Unhandled member kind has found: ${member.kind}`, member);
|
|
928
|
-
});
|
|
929
|
-
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;
|
|
930
990
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
return
|
|
991
|
+
const literalValue = this.getLiteralValue(arg);
|
|
992
|
+
if (typeof literalValue === 'string') {
|
|
993
|
+
return `'${literalValue}'`;
|
|
934
994
|
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
return memberTypeNames.join(' & ');
|
|
995
|
+
if (literalValue === null) {
|
|
996
|
+
return 'null';
|
|
938
997
|
}
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
return memberTypeNames.join(' | ');
|
|
998
|
+
if (typeof literalValue === 'boolean') {
|
|
999
|
+
return literalValue ? 'true' : 'false';
|
|
942
1000
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
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;
|
|
952
1019
|
}
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1020
|
+
return this.calcTypeReferenceTypeName(arg)[1];
|
|
1021
|
+
}
|
|
1022
|
+
getTypeLiteralName(arg) {
|
|
1023
|
+
if (!ts.isTypeLiteralNode(arg)) {
|
|
1024
|
+
return undefined;
|
|
956
1025
|
}
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
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}`;
|
|
961
1033
|
}
|
|
962
|
-
|
|
963
|
-
return
|
|
1034
|
+
if (ts.isIndexSignatureDeclaration(member)) {
|
|
1035
|
+
return this.getIndexSignatureTypeName(member);
|
|
964
1036
|
}
|
|
965
|
-
|
|
966
|
-
|
|
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;
|
|
967
1060
|
}
|
|
968
|
-
|
|
969
|
-
|
|
1061
|
+
const subTypeName = this.calcTypeName(arg.type);
|
|
1062
|
+
if (arg.operator === ts.SyntaxKind.KeyOfKeyword) {
|
|
1063
|
+
return `keyof ${subTypeName}`;
|
|
970
1064
|
}
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1065
|
+
if (arg.operator === ts.SyntaxKind.ReadonlyKeyword) {
|
|
1066
|
+
return `readonly ${subTypeName}`;
|
|
1067
|
+
}
|
|
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';
|
|
977
1079
|
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
return `(${internalTypeName})`; //Parentheses are not really interesting. The type name generation adds parentheses for the clarity
|
|
1080
|
+
if (arg.kind === ts.SyntaxKind.AnyKeyword) {
|
|
1081
|
+
return 'any';
|
|
981
1082
|
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
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;
|
|
985
1097
|
}
|
|
986
1098
|
//Generates type name for type references
|
|
987
1099
|
calcTypeReferenceTypeName(node) {
|
|
@@ -999,54 +1111,53 @@ class TypeResolver {
|
|
|
999
1111
|
const refTypeName = this.getRefTypeName(name);
|
|
1000
1112
|
this.current.CheckExpressionUnicity(refTypeName, name);
|
|
1001
1113
|
this.context = this.typeArgumentsToContext(node, type);
|
|
1002
|
-
const
|
|
1003
|
-
try {
|
|
1004
|
-
const existingType = localReferenceTypeCache[name];
|
|
1005
|
-
if (existingType) {
|
|
1006
|
-
return existingType;
|
|
1007
|
-
}
|
|
1008
|
-
if (inProgressTypes[name]) {
|
|
1009
|
-
return this.createCircularDependencyResolver(name, refTypeName);
|
|
1010
|
-
}
|
|
1011
|
-
inProgressTypes[name] = [];
|
|
1012
|
-
const declarations = this.getModelTypeDeclarations(type);
|
|
1013
|
-
const referenceTypes = [];
|
|
1014
|
-
// If no declarations found, this might be a built-in type or a type that can't be resolved
|
|
1015
|
-
if (declarations.length === 0) {
|
|
1016
|
-
const fallbackReferenceType = this.getReferenceTypeFromTypeChecker(node, name, refTypeName);
|
|
1017
|
-
if (fallbackReferenceType) {
|
|
1018
|
-
this.addToLocalReferenceTypeCache(name, fallbackReferenceType);
|
|
1019
|
-
return fallbackReferenceType;
|
|
1020
|
-
}
|
|
1021
|
-
throw new exceptions_1.GenerateMetadataError(`Could not find declarations for type '${name}'. This might be a complex generic type that needs special handling.`);
|
|
1022
|
-
}
|
|
1023
|
-
for (const declaration of declarations) {
|
|
1024
|
-
if (ts.isTypeAliasDeclaration(declaration)) {
|
|
1025
|
-
const referencer = node.pos !== -1 ? this.current.typeChecker.getTypeFromTypeNode(node) : undefined;
|
|
1026
|
-
referenceTypes.push(new referenceTransformer_1.ReferenceTransformer().transform(declaration, refTypeName, this, referencer));
|
|
1027
|
-
}
|
|
1028
|
-
else if (enumTransformer_1.EnumTransformer.transformable(declaration)) {
|
|
1029
|
-
referenceTypes.push(new enumTransformer_1.EnumTransformer().transform(this, declaration, refTypeName));
|
|
1030
|
-
}
|
|
1031
|
-
else {
|
|
1032
|
-
referenceTypes.push(this.getModelReference(declaration, refTypeName));
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
const referenceType = referenceTransformer_1.ReferenceTransformer.merge(referenceTypes);
|
|
1036
|
-
this.addToLocalReferenceTypeCache(name, referenceType);
|
|
1037
|
-
return referenceType;
|
|
1038
|
-
}
|
|
1039
|
-
catch (err) {
|
|
1040
|
-
delete inProgressTypes[name];
|
|
1041
|
-
throw err;
|
|
1042
|
-
}
|
|
1043
|
-
};
|
|
1044
|
-
const result = calcReferenceType();
|
|
1114
|
+
const result = this.resolveReferenceType(node, type, name, refTypeName);
|
|
1045
1115
|
if (addToRefTypeMap) {
|
|
1046
1116
|
this.current.AddReferenceType(result);
|
|
1047
1117
|
}
|
|
1048
1118
|
return result;
|
|
1049
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
|
+
}
|
|
1050
1161
|
addToLocalReferenceTypeCache(name, refType) {
|
|
1051
1162
|
if (inProgressTypes[name]) {
|
|
1052
1163
|
for (const fn of inProgressTypes[name]) {
|
|
@@ -1064,64 +1175,68 @@ class TypeResolver {
|
|
|
1064
1175
|
// Handle toJSON methods
|
|
1065
1176
|
(0, flowUtils_1.throwUnless)(modelType.name, new exceptions_1.GenerateMetadataError("Can't get Symbol from anonymous class", modelType));
|
|
1066
1177
|
const type = this.current.typeChecker.getTypeAtLocation(modelType.name);
|
|
1067
|
-
const
|
|
1068
|
-
if (
|
|
1069
|
-
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;
|
|
1070
1181
|
if (!nodeType) {
|
|
1071
|
-
const signature = this.current.typeChecker.getSignatureFromDeclaration(
|
|
1182
|
+
const signature = this.current.typeChecker.getSignatureFromDeclaration(toJSONDeclaration);
|
|
1072
1183
|
const implicitType = this.current.typeChecker.getReturnTypeOfSignature(signature);
|
|
1073
1184
|
nodeType = this.current.typeChecker.typeToTypeNode(implicitType, undefined, ts.NodeBuilderFlags.NoTruncation);
|
|
1074
1185
|
}
|
|
1075
|
-
|
|
1076
|
-
const referenceType = {
|
|
1186
|
+
return this.withDefinedReferenceMetadata({
|
|
1077
1187
|
refName: refTypeName,
|
|
1078
1188
|
dataType: 'refAlias',
|
|
1079
1189
|
description,
|
|
1080
|
-
type,
|
|
1190
|
+
type: new TypeResolver(nodeType, this.current).resolve(),
|
|
1081
1191
|
validators: {},
|
|
1082
1192
|
deprecated,
|
|
1083
|
-
|
|
1084
|
-
...(title !== undefined ? { title } : {}),
|
|
1085
|
-
};
|
|
1086
|
-
return referenceType;
|
|
1193
|
+
}, { example, title });
|
|
1087
1194
|
}
|
|
1088
1195
|
const properties = new propertyTransformer_1.PropertyTransformer().transform(this, modelType);
|
|
1089
1196
|
const additionalProperties = this.getModelAdditionalProperties(modelType);
|
|
1090
1197
|
const inheritedProperties = this.getModelInheritedProperties(modelType) || [];
|
|
1091
|
-
const referenceType = {
|
|
1198
|
+
const referenceType = this.withDefinedReferenceMetadata({
|
|
1092
1199
|
additionalProperties,
|
|
1093
1200
|
dataType: 'refObject',
|
|
1094
1201
|
description,
|
|
1095
1202
|
properties: inheritedProperties,
|
|
1096
1203
|
refName: refTypeName,
|
|
1097
1204
|
deprecated,
|
|
1098
|
-
|
|
1099
|
-
...(title !== undefined ? { title } : {}),
|
|
1100
|
-
};
|
|
1205
|
+
}, { example, title });
|
|
1101
1206
|
referenceType.properties = referenceType.properties.concat(properties);
|
|
1102
1207
|
return referenceType;
|
|
1103
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
|
+
}
|
|
1104
1218
|
//Generates a name from the original type expression.
|
|
1105
1219
|
//This function is not invertable, so it's possible, that 2 type expressions have the same refTypeName.
|
|
1106
1220
|
getRefTypeName(name) {
|
|
1107
1221
|
let preformattedName = name //Preformatted name handles most cases
|
|
1108
|
-
.
|
|
1109
|
-
.
|
|
1110
|
-
.
|
|
1111
|
-
.
|
|
1112
|
-
.
|
|
1113
|
-
.
|
|
1114
|
-
.
|
|
1115
|
-
.
|
|
1116
|
-
.
|
|
1222
|
+
.replaceAll('<', '_')
|
|
1223
|
+
.replaceAll('>', '_')
|
|
1224
|
+
.replaceAll(/\s+/g, '')
|
|
1225
|
+
.replaceAll(',', '.')
|
|
1226
|
+
.replaceAll(/'([^']*)'/g, '$1')
|
|
1227
|
+
.replaceAll(/"([^"]*)"/g, '$1')
|
|
1228
|
+
.replaceAll('&', '-and-')
|
|
1229
|
+
.replaceAll('|', '-or-')
|
|
1230
|
+
.replaceAll('[]', '-Array')
|
|
1231
|
+
.replaceAll(/[{}]/g, '_'); // SuccessResponse_{indexesCreated-number}_ -> SuccessResponse__indexesCreated-number__
|
|
1117
1232
|
preformattedName = replaceTypeLiteralPropertySeparators(preformattedName); // SuccessResponse_indexesCreated:number_ -> SuccessResponse_indexesCreated-number_
|
|
1118
|
-
preformattedName = preformattedName.
|
|
1233
|
+
preformattedName = preformattedName.replaceAll(';', '--');
|
|
1119
1234
|
preformattedName = replaceIndexedAccessSegments(preformattedName); // Partial_SerializedDatasourceWithVersion[format]_ -> Partial_SerializedDatasourceWithVersion~format~_,
|
|
1120
1235
|
//Safety fixes to replace all characters which are not accepted by swagger ui
|
|
1121
|
-
let formattedName = preformattedName.
|
|
1122
|
-
return `_${match.
|
|
1236
|
+
let formattedName = preformattedName.replaceAll(/[^A-Za-z0-9\-._]/g, match => {
|
|
1237
|
+
return `_${match.codePointAt(0) ?? 0}_`;
|
|
1123
1238
|
});
|
|
1124
|
-
formattedName = formattedName.
|
|
1239
|
+
formattedName = formattedName.replaceAll('92_r_92_n', '92_n'); //Windows uses \r\n, but linux uses \n.
|
|
1125
1240
|
return formattedName;
|
|
1126
1241
|
}
|
|
1127
1242
|
createCircularDependencyResolver(refName, refTypeName) {
|
|
@@ -1154,7 +1269,7 @@ class TypeResolver {
|
|
|
1154
1269
|
symbol = fullEnumSymbol?.exports?.get(typeName);
|
|
1155
1270
|
}
|
|
1156
1271
|
// Handle built-in types that don't have declarations in user code
|
|
1157
|
-
if (!symbol
|
|
1272
|
+
if (!symbol?.getDeclarations) {
|
|
1158
1273
|
return [];
|
|
1159
1274
|
}
|
|
1160
1275
|
const declarations = symbol.getDeclarations();
|
|
@@ -1174,7 +1289,7 @@ class TypeResolver {
|
|
|
1174
1289
|
if (modelTypes.length > 1) {
|
|
1175
1290
|
// remove types that are from typescript e.g. 'Account'
|
|
1176
1291
|
modelTypes = modelTypes.filter(modelType => {
|
|
1177
|
-
return modelType.getSourceFile().fileName.
|
|
1292
|
+
return modelType.getSourceFile().fileName.replaceAll('\\', '/').toLowerCase().indexOf('node_modules/typescript') <= -1;
|
|
1178
1293
|
});
|
|
1179
1294
|
modelTypes = this.getDesignatedModels(modelTypes, typeName);
|
|
1180
1295
|
}
|
|
@@ -1203,7 +1318,7 @@ class TypeResolver {
|
|
|
1203
1318
|
const symbols = [resolvedType.aliasSymbol, resolvedType.symbol].filter((symbol) => !!symbol);
|
|
1204
1319
|
const declarations = symbols.flatMap(symbol => this.getUsableDeclarationsFromSymbol(symbol));
|
|
1205
1320
|
const uniqueDeclarations = declarations.filter((declaration, index, allDeclarations) => {
|
|
1206
|
-
return allDeclarations.
|
|
1321
|
+
return allDeclarations.indexOf(declaration) === index;
|
|
1207
1322
|
});
|
|
1208
1323
|
return uniqueDeclarations.map(declaration => {
|
|
1209
1324
|
if (ts.isTypeAliasDeclaration(declaration)) {
|
|
@@ -1263,23 +1378,20 @@ class TypeResolver {
|
|
|
1263
1378
|
}
|
|
1264
1379
|
typeArgumentsToContext(type, targetEntity) {
|
|
1265
1380
|
let newContext = {};
|
|
1266
|
-
//
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
try {
|
|
1270
|
-
declarations = this.getModelTypeDeclarations(targetEntity);
|
|
1381
|
+
// Inline object types don't contribute generic declarations, so they map to an empty context.
|
|
1382
|
+
if (!this.current.typeChecker) {
|
|
1383
|
+
return newContext;
|
|
1271
1384
|
}
|
|
1272
|
-
|
|
1273
|
-
// If we can't get declarations (e.g., inline object type),
|
|
1274
|
-
// we can't process type parameters, so return empty context
|
|
1385
|
+
if (!ts.isIdentifier(targetEntity) && !ts.isQualifiedName(targetEntity)) {
|
|
1275
1386
|
return newContext;
|
|
1276
1387
|
}
|
|
1388
|
+
const declarations = this.getModelTypeDeclarations(targetEntity);
|
|
1277
1389
|
const firstDeclaration = declarations[0];
|
|
1278
1390
|
const typeParameters = firstDeclaration?.typeParameters;
|
|
1279
1391
|
if (typeParameters) {
|
|
1280
1392
|
for (let index = 0; index < typeParameters.length; index++) {
|
|
1281
1393
|
const typeParameter = typeParameters[index];
|
|
1282
|
-
const typeArg = type.typeArguments
|
|
1394
|
+
const typeArg = type.typeArguments?.[index];
|
|
1283
1395
|
let resolvedType;
|
|
1284
1396
|
let name;
|
|
1285
1397
|
// Argument may be a forward reference from context
|
|
@@ -1307,6 +1419,47 @@ class TypeResolver {
|
|
|
1307
1419
|
}
|
|
1308
1420
|
return newContext;
|
|
1309
1421
|
}
|
|
1422
|
+
getReferenceAliasProperties(referenceType) {
|
|
1423
|
+
let type = referenceType;
|
|
1424
|
+
while (type.dataType === 'refAlias') {
|
|
1425
|
+
type = type.type;
|
|
1426
|
+
}
|
|
1427
|
+
if (type.dataType === 'refObject' || type.dataType === 'nestedObjectLiteral') {
|
|
1428
|
+
return type.properties;
|
|
1429
|
+
}
|
|
1430
|
+
return [];
|
|
1431
|
+
}
|
|
1432
|
+
appendInheritedProperties(properties, referenceType) {
|
|
1433
|
+
if (!referenceType || referenceType.dataType === 'refEnum') {
|
|
1434
|
+
return properties;
|
|
1435
|
+
}
|
|
1436
|
+
if (referenceType.dataType === 'refAlias') {
|
|
1437
|
+
return [...properties, ...this.getReferenceAliasProperties(referenceType)];
|
|
1438
|
+
}
|
|
1439
|
+
if (referenceType.dataType === 'refObject') {
|
|
1440
|
+
return [...properties, ...(referenceType.properties ?? [])];
|
|
1441
|
+
}
|
|
1442
|
+
return (0, runtime_1.assertNever)(referenceType);
|
|
1443
|
+
}
|
|
1444
|
+
getInheritedReferenceType(typeNode) {
|
|
1445
|
+
if (!ts.isIdentifier(typeNode.expression) && !ts.isQualifiedName(typeNode.expression)) {
|
|
1446
|
+
return undefined;
|
|
1447
|
+
}
|
|
1448
|
+
const resetContext = this.context;
|
|
1449
|
+
this.context = this.typeArgumentsToContext(typeNode, typeNode.expression);
|
|
1450
|
+
try {
|
|
1451
|
+
return this.getReferenceType(typeNode, false);
|
|
1452
|
+
}
|
|
1453
|
+
catch (error) {
|
|
1454
|
+
if (error instanceof exceptions_1.GenerateMetadataError) {
|
|
1455
|
+
return undefined;
|
|
1456
|
+
}
|
|
1457
|
+
throw error;
|
|
1458
|
+
}
|
|
1459
|
+
finally {
|
|
1460
|
+
this.context = resetContext;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1310
1463
|
getModelInheritedProperties(modelTypeDeclaration) {
|
|
1311
1464
|
let properties = [];
|
|
1312
1465
|
const heritageClauses = modelTypeDeclaration.heritageClauses;
|
|
@@ -1318,47 +1471,7 @@ class TypeResolver {
|
|
|
1318
1471
|
continue;
|
|
1319
1472
|
}
|
|
1320
1473
|
for (const t of clause.types) {
|
|
1321
|
-
|
|
1322
|
-
// create subContext
|
|
1323
|
-
const resetCtx = this.context;
|
|
1324
|
-
this.context = this.typeArgumentsToContext(t, baseEntityName);
|
|
1325
|
-
let referenceType;
|
|
1326
|
-
try {
|
|
1327
|
-
referenceType = this.getReferenceType(t, false);
|
|
1328
|
-
}
|
|
1329
|
-
catch (error) {
|
|
1330
|
-
if (error instanceof exceptions_1.GenerateMetadataError) {
|
|
1331
|
-
this.context = resetCtx;
|
|
1332
|
-
continue;
|
|
1333
|
-
}
|
|
1334
|
-
throw error;
|
|
1335
|
-
}
|
|
1336
|
-
if (referenceType) {
|
|
1337
|
-
if (referenceType.dataType === 'refEnum') {
|
|
1338
|
-
// since it doesn't have properties to iterate over, then we don't do anything with it
|
|
1339
|
-
}
|
|
1340
|
-
else if (referenceType.dataType === 'refAlias') {
|
|
1341
|
-
let type = referenceType;
|
|
1342
|
-
while (type.dataType === 'refAlias') {
|
|
1343
|
-
type = type.type;
|
|
1344
|
-
}
|
|
1345
|
-
if (type.dataType === 'refObject') {
|
|
1346
|
-
properties = [...properties, ...type.properties];
|
|
1347
|
-
}
|
|
1348
|
-
else if (type.dataType === 'nestedObjectLiteral') {
|
|
1349
|
-
properties = [...properties, ...type.properties];
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
else if (referenceType.dataType === 'refObject') {
|
|
1353
|
-
;
|
|
1354
|
-
(referenceType.properties || []).forEach(property => properties.push(property));
|
|
1355
|
-
}
|
|
1356
|
-
else {
|
|
1357
|
-
(0, runtime_1.assertNever)(referenceType);
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1360
|
-
// reset subContext
|
|
1361
|
-
this.context = resetCtx;
|
|
1474
|
+
properties = this.appendInheritedProperties(properties, this.getInheritedReferenceType(t));
|
|
1362
1475
|
}
|
|
1363
1476
|
}
|
|
1364
1477
|
return properties;
|
|
@@ -1369,8 +1482,8 @@ class TypeResolver {
|
|
|
1369
1482
|
return undefined;
|
|
1370
1483
|
}
|
|
1371
1484
|
/**
|
|
1372
|
-
*
|
|
1373
|
-
*
|
|
1485
|
+
* Workaround for a TypeScript compiler quirk tracked for follow-up investigation.
|
|
1486
|
+
* See https://github.com/tsoa-next/tsoa-next/issues for related metadata parsing context.
|
|
1374
1487
|
*/
|
|
1375
1488
|
if (node.kind === ts.SyntaxKind.Parameter) {
|
|
1376
1489
|
// TypeScript won't parse jsdoc if the flag is 4, i.e. 'Property'
|
|
@@ -1391,9 +1504,10 @@ class TypeResolver {
|
|
|
1391
1504
|
getPropertyName(prop) {
|
|
1392
1505
|
if (ts.isComputedPropertyName(prop.name) && ts.isPropertyAccessExpression(prop.name.expression)) {
|
|
1393
1506
|
const initializerValue = (0, initializer_value_1.getInitializerValue)(prop.name.expression, this.current.typeChecker);
|
|
1394
|
-
if (initializerValue) {
|
|
1395
|
-
return initializerValue
|
|
1507
|
+
if (typeof initializerValue === 'string' || typeof initializerValue === 'number' || typeof initializerValue === 'boolean') {
|
|
1508
|
+
return `${initializerValue}`;
|
|
1396
1509
|
}
|
|
1510
|
+
return prop.name.expression.getText();
|
|
1397
1511
|
}
|
|
1398
1512
|
return prop.name.text;
|
|
1399
1513
|
}
|
|
@@ -1416,66 +1530,123 @@ class TypeResolver {
|
|
|
1416
1530
|
}
|
|
1417
1531
|
static getDefault(node) {
|
|
1418
1532
|
const defaultStr = (0, jsDocUtils_1.getJSDocComment)(node, 'default');
|
|
1419
|
-
if (typeof defaultStr
|
|
1420
|
-
|
|
1421
|
-
const inString = () => textStartCharacter !== undefined;
|
|
1422
|
-
let formattedStr = '';
|
|
1423
|
-
for (let i = 0; i < defaultStr.length; ++i) {
|
|
1424
|
-
const actCharacter = defaultStr[i];
|
|
1425
|
-
if (inString()) {
|
|
1426
|
-
if (actCharacter === textStartCharacter) {
|
|
1427
|
-
formattedStr += '"';
|
|
1428
|
-
textStartCharacter = undefined;
|
|
1429
|
-
}
|
|
1430
|
-
else if (actCharacter === '"') {
|
|
1431
|
-
formattedStr += '\\"';
|
|
1432
|
-
}
|
|
1433
|
-
else if (actCharacter === '\\') {
|
|
1434
|
-
++i;
|
|
1435
|
-
if (i < defaultStr.length) {
|
|
1436
|
-
const nextCharacter = defaultStr[i];
|
|
1437
|
-
if (['n', 't', 'r', 'b', 'f', '\\', '"'].includes(nextCharacter)) {
|
|
1438
|
-
formattedStr += '\\' + nextCharacter;
|
|
1439
|
-
}
|
|
1440
|
-
else if (!['v', '0'].includes(nextCharacter)) {
|
|
1441
|
-
//\v, \0 characters are not compatible with JSON
|
|
1442
|
-
formattedStr += nextCharacter;
|
|
1443
|
-
}
|
|
1444
|
-
}
|
|
1445
|
-
else {
|
|
1446
|
-
formattedStr += actCharacter; // this is a bug, but let the JSON parser decide how to handle it
|
|
1447
|
-
}
|
|
1448
|
-
}
|
|
1449
|
-
else {
|
|
1450
|
-
formattedStr += actCharacter;
|
|
1451
|
-
}
|
|
1452
|
-
}
|
|
1453
|
-
else {
|
|
1454
|
-
if ([`"`, "'", '`'].includes(actCharacter)) {
|
|
1455
|
-
textStartCharacter = actCharacter;
|
|
1456
|
-
formattedStr += '"';
|
|
1457
|
-
}
|
|
1458
|
-
else if (actCharacter === '/' && i + 1 < defaultStr.length && defaultStr[i + 1] === '/') {
|
|
1459
|
-
i += 2;
|
|
1460
|
-
while (i < defaultStr.length && defaultStr[i] !== '\n') {
|
|
1461
|
-
++i;
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
else {
|
|
1465
|
-
formattedStr += actCharacter;
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
try {
|
|
1470
|
-
const parsed = JSON.parse(formattedStr);
|
|
1471
|
-
return parsed;
|
|
1472
|
-
}
|
|
1473
|
-
catch (err) {
|
|
1474
|
-
const message = err instanceof Error ? err.message : '-';
|
|
1475
|
-
throw new exceptions_1.GenerateMetadataError(`JSON could not parse default str: "${defaultStr}", preformatted: "${formattedStr}"\nmessage: "${message}"`);
|
|
1476
|
-
}
|
|
1533
|
+
if (typeof defaultStr !== 'string' || defaultStr === 'undefined') {
|
|
1534
|
+
return undefined;
|
|
1477
1535
|
}
|
|
1478
|
-
|
|
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
|
+
};
|
|
1479
1650
|
}
|
|
1480
1651
|
}
|
|
1481
1652
|
exports.TypeResolver = TypeResolver;
|