@travetto/transformer 8.0.0-alpha.8 → 8.0.0
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/README.md +39 -34
- package/__index__.ts +16 -10
- package/package.json +13 -13
- package/src/importer.ts +40 -39
- package/src/manager.ts +9 -7
- package/src/register.ts +10 -4
- package/src/resolver/builder.ts +127 -66
- package/src/resolver/cache.ts +1 -0
- package/src/resolver/coerce.ts +12 -10
- package/src/resolver/service.ts +26 -19
- package/src/resolver/types.ts +17 -4
- package/src/state.ts +49 -36
- package/src/types/shared.ts +3 -3
- package/src/types/visitor.ts +13 -3
- package/src/util/core.ts +21 -16
- package/src/util/declaration.ts +69 -11
- package/src/util/decorator.ts +2 -2
- package/src/util/doc.ts +12 -11
- package/src/util/import.ts +2 -2
- package/src/util/literal.ts +11 -10
- package/src/util/log.ts +23 -5
- package/src/util/system.ts +2 -4
- package/src/visitor.ts +48 -48
package/src/resolver/builder.ts
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
|
-
/* eslint-disable no-bitwise */
|
|
2
1
|
import ts from 'typescript';
|
|
3
2
|
|
|
4
|
-
import {
|
|
3
|
+
import { ManifestModuleUtil, path } from '@travetto/manifest';
|
|
5
4
|
|
|
6
|
-
import {
|
|
5
|
+
import { type TemplateLiteralPart, transformCast } from '../types/shared.ts';
|
|
7
6
|
import { CoreUtil } from '../util/core.ts';
|
|
8
7
|
import { DeclarationUtil } from '../util/declaration.ts';
|
|
8
|
+
import { DocUtil } from '../util/doc.ts';
|
|
9
9
|
import { LiteralUtil } from '../util/literal.ts';
|
|
10
|
-
import { transformCast, type TemplateLiteralPart } from '../types/shared.ts';
|
|
11
|
-
|
|
12
|
-
import type { Type, AnyType, CompositionType, TransformResolver, TemplateType, MappedType, ShapeType, ResolverContext, ManagedType } from './types.ts';
|
|
13
10
|
import { CoerceUtil } from './coerce.ts';
|
|
11
|
+
import type {
|
|
12
|
+
AnyType,
|
|
13
|
+
CompositionType,
|
|
14
|
+
ManagedType,
|
|
15
|
+
MappedType,
|
|
16
|
+
ResolverContext,
|
|
17
|
+
ShapeType,
|
|
18
|
+
TemplateType,
|
|
19
|
+
TransformResolver,
|
|
20
|
+
Type
|
|
21
|
+
} from './types.ts';
|
|
14
22
|
|
|
15
23
|
const UNDEFINED = Symbol();
|
|
16
24
|
|
|
@@ -29,11 +37,15 @@ const getMappedFields = (type: ts.Type): string[] | undefined => {
|
|
|
29
37
|
* List of global types that can be parameterized
|
|
30
38
|
*/
|
|
31
39
|
const GLOBAL_COMPLEX: Record<string, Function> = {
|
|
32
|
-
Array,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
Array,
|
|
41
|
+
Promise,
|
|
42
|
+
Set,
|
|
43
|
+
Map,
|
|
44
|
+
ReadonlyArray: Array,
|
|
45
|
+
Iterator: function Iterator() {},
|
|
46
|
+
Iterable: function Iterable() {},
|
|
47
|
+
IterableIterator: function IterableIterator() {},
|
|
48
|
+
AsyncIterator: function AsyncIterator() {},
|
|
37
49
|
PropertyDescriptor: Object,
|
|
38
50
|
TypedPropertyDescriptor: Object
|
|
39
51
|
};
|
|
@@ -44,12 +56,29 @@ const GLOBAL_COMPLEX: Record<string, Function> = {
|
|
|
44
56
|
const UNDEFINED_GLOBAL = { undefined: 1, void: 1, null: 1 };
|
|
45
57
|
const SIMPLE_NAMES: Record<string, string> = { String: 'string', Number: 'number', Boolean: 'boolean', Object: 'object' };
|
|
46
58
|
const GLOBAL_SIMPLE: Record<string, Function> = {
|
|
47
|
-
RegExp,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
RegExp,
|
|
60
|
+
Date,
|
|
61
|
+
Number,
|
|
62
|
+
Boolean,
|
|
63
|
+
String,
|
|
64
|
+
Function,
|
|
65
|
+
Object,
|
|
66
|
+
Error,
|
|
67
|
+
BigInt,
|
|
68
|
+
ArrayBuffer,
|
|
69
|
+
SharedArrayBuffer,
|
|
70
|
+
Uint8Array,
|
|
71
|
+
Uint16Array,
|
|
72
|
+
Uint32Array,
|
|
73
|
+
Int8Array,
|
|
74
|
+
Int16Array,
|
|
75
|
+
Int32Array,
|
|
76
|
+
Uint8ClampedArray,
|
|
77
|
+
BigInt64Array,
|
|
78
|
+
BigUint64Array,
|
|
79
|
+
Float16Array,
|
|
80
|
+
Float32Array,
|
|
81
|
+
Float64Array,
|
|
53
82
|
PromiseConstructor: Promise.constructor
|
|
54
83
|
};
|
|
55
84
|
|
|
@@ -58,27 +87,30 @@ type Category = Exclude<AnyType['key'], 'pointer'> | 'concrete';
|
|
|
58
87
|
/**
|
|
59
88
|
* Type categorizer, input for builder
|
|
60
89
|
*/
|
|
61
|
-
export function TypeCategorize(resolver: TransformResolver, type: ts.Type): { category: Category
|
|
90
|
+
export function TypeCategorize(resolver: TransformResolver, type: ts.Type): { category: Category; type: ts.Type } {
|
|
62
91
|
const flags = type.getFlags();
|
|
63
92
|
const objectFlags = DeclarationUtil.getObjectFlags(type) ?? 0;
|
|
64
93
|
|
|
65
|
-
if (flags &
|
|
94
|
+
if (flags & ts.TypeFlags.TemplateLiteral) {
|
|
66
95
|
return { category: 'template', type };
|
|
67
|
-
} else if (
|
|
68
|
-
|
|
69
|
-
ts.TypeFlags.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
96
|
+
} else if (
|
|
97
|
+
flags &
|
|
98
|
+
(ts.TypeFlags.BigIntLike |
|
|
99
|
+
ts.TypeFlags.BooleanLike |
|
|
100
|
+
ts.TypeFlags.NumberLike |
|
|
101
|
+
ts.TypeFlags.StringLike |
|
|
102
|
+
ts.TypeFlags.Null |
|
|
103
|
+
ts.TypeFlags.Undefined |
|
|
104
|
+
ts.TypeFlags.Void)
|
|
105
|
+
) {
|
|
76
106
|
return { category: 'literal', type };
|
|
77
107
|
} else if (DocUtil.hasDocTag(type, 'concrete')) {
|
|
78
108
|
return { category: 'concrete', type };
|
|
79
|
-
} else if (flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.Never)) {
|
|
109
|
+
} else if (flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.Never)) {
|
|
110
|
+
// Any or unknown
|
|
80
111
|
return { category: 'unknown', type };
|
|
81
|
-
} else if (objectFlags & ts.ObjectFlags.Reference && !CoreUtil.getSymbol(type)) {
|
|
112
|
+
} else if (objectFlags & ts.ObjectFlags.Reference && !CoreUtil.getSymbol(type)) {
|
|
113
|
+
// Tuple type?
|
|
82
114
|
return { category: 'tuple', type };
|
|
83
115
|
} else if (type.isUnionOrIntersection()) {
|
|
84
116
|
return { category: 'composition', type };
|
|
@@ -89,12 +121,10 @@ export function TypeCategorize(resolver: TransformResolver, type: ts.Type): { ca
|
|
|
89
121
|
if (sourceFile && ManifestModuleUtil.TYPINGS_EXT_REGEX.test(sourceFile) && !resolver.isKnownFile(sourceFile)) {
|
|
90
122
|
return { category: 'foreign', type };
|
|
91
123
|
}
|
|
92
|
-
} catch {
|
|
124
|
+
} catch {}
|
|
93
125
|
|
|
94
126
|
const text = resolver.getTypeAsString(type);
|
|
95
|
-
if (flags & (ts.SymbolFlags.TypeAlias | ts.SymbolFlags.ExportValue)
|
|
96
|
-
&& text?.startsWith('typeof')
|
|
97
|
-
) {
|
|
127
|
+
if (flags & (ts.SymbolFlags.TypeAlias | ts.SymbolFlags.ExportValue) && text?.startsWith('typeof')) {
|
|
98
128
|
return { category: 'managed', type };
|
|
99
129
|
}
|
|
100
130
|
return { category: 'shape', type };
|
|
@@ -114,7 +144,8 @@ export function TypeCategorize(resolver: TransformResolver, type: ts.Type): { ca
|
|
|
114
144
|
return { category: 'literal', type };
|
|
115
145
|
} else if (sourceFile && ManifestModuleUtil.TYPINGS_EXT_REGEX.test(sourceFile) && !resolver.isKnownFile(sourceFile)) {
|
|
116
146
|
return { category: 'foreign', type: resolvedType };
|
|
117
|
-
} else if (!resolvedType.isClass()) {
|
|
147
|
+
} else if (!resolvedType.isClass()) {
|
|
148
|
+
// Not a real type
|
|
118
149
|
return { category: 'shape', type: resolvedType };
|
|
119
150
|
} else {
|
|
120
151
|
return { category: 'managed', type: resolvedType };
|
|
@@ -123,7 +154,8 @@ export function TypeCategorize(resolver: TransformResolver, type: ts.Type): { ca
|
|
|
123
154
|
return { category: 'tuple', type };
|
|
124
155
|
} else if (type.isLiteral()) {
|
|
125
156
|
return { category: 'shape', type };
|
|
126
|
-
} else if (objectFlags & ts.ObjectFlags.Mapped) {
|
|
157
|
+
} else if (objectFlags & ts.ObjectFlags.Mapped) {
|
|
158
|
+
// Mapped types
|
|
127
159
|
if (type.getProperties().some(property => property.declarations || property.valueDeclaration)) {
|
|
128
160
|
return { category: 'mapped', type };
|
|
129
161
|
}
|
|
@@ -138,7 +170,7 @@ export const TypeBuilder: {
|
|
|
138
170
|
[K in Category]: {
|
|
139
171
|
build(resolver: TransformResolver, type: ts.Type, context: ResolverContext): AnyType | undefined;
|
|
140
172
|
finalize?(type: Type<K>): AnyType;
|
|
141
|
-
}
|
|
173
|
+
};
|
|
142
174
|
} = {
|
|
143
175
|
unknown: {
|
|
144
176
|
build: (resolver, type) => {
|
|
@@ -154,8 +186,8 @@ export const TypeBuilder: {
|
|
|
154
186
|
// If we are have a template literal type, we need to make our own type node
|
|
155
187
|
if (type.flags & ts.TypeFlags.TemplateLiteral) {
|
|
156
188
|
const values: TemplateLiteralPart[] = [];
|
|
157
|
-
const texts = 'texts' in type &&
|
|
158
|
-
const types = 'types' in type &&
|
|
189
|
+
const texts = 'texts' in type && typeof type.texts === 'object' && Array.isArray(type.texts) ? type.texts : undefined;
|
|
190
|
+
const types = 'types' in type && typeof type.types === 'object' && Array.isArray(type.types) ? type.types : undefined;
|
|
159
191
|
if (texts?.length && types?.length) {
|
|
160
192
|
for (let i = 0; i < texts?.length; i += 1) {
|
|
161
193
|
if (texts[i] && texts[i] !== 'undefined') {
|
|
@@ -163,15 +195,23 @@ export const TypeBuilder: {
|
|
|
163
195
|
}
|
|
164
196
|
if (types[i]) {
|
|
165
197
|
switch (types[i].intrinsicName) {
|
|
166
|
-
case 'number':
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
case '
|
|
198
|
+
case 'number':
|
|
199
|
+
values.push(Number);
|
|
200
|
+
break;
|
|
201
|
+
case 'string':
|
|
202
|
+
values.push(String);
|
|
203
|
+
break;
|
|
204
|
+
case 'boolean':
|
|
205
|
+
values.push(Boolean);
|
|
206
|
+
break;
|
|
207
|
+
case 'undefined':
|
|
208
|
+
values.push('');
|
|
209
|
+
break;
|
|
170
210
|
}
|
|
171
211
|
}
|
|
172
212
|
}
|
|
173
213
|
if (values.length > 0) {
|
|
174
|
-
return
|
|
214
|
+
return { key: 'template', template: { operation: 'and', values }, ctor: String };
|
|
175
215
|
}
|
|
176
216
|
}
|
|
177
217
|
}
|
|
@@ -188,8 +228,7 @@ export const TypeBuilder: {
|
|
|
188
228
|
return { key: 'literal', ctor: undefined, name };
|
|
189
229
|
} else if (name in GLOBAL_SIMPLE) {
|
|
190
230
|
const cons = GLOBAL_SIMPLE[name];
|
|
191
|
-
const literal = LiteralUtil.isLiteralType(type) ? CoerceUtil.coerce(type.value, transformCast(cons), false) :
|
|
192
|
-
undefined;
|
|
231
|
+
const literal = LiteralUtil.isLiteralType(type) ? CoerceUtil.coerce(type.value, transformCast(cons), false) : undefined;
|
|
193
232
|
|
|
194
233
|
return {
|
|
195
234
|
key: 'literal',
|
|
@@ -233,8 +272,8 @@ export const TypeBuilder: {
|
|
|
233
272
|
|
|
234
273
|
// Detect managed, but shapeable
|
|
235
274
|
if (tsTypeArguments.length > 0) {
|
|
236
|
-
const hasEmptyConstructor =
|
|
237
|
-
|| type.getConstructSignatures().length === 0; // If no constructor, we can assume it's shapeable
|
|
275
|
+
const hasEmptyConstructor =
|
|
276
|
+
type.getConstructSignatures().some(signature => signature.parameters.length === 0) || type.getConstructSignatures().length === 0; // If no constructor, we can assume it's shapeable
|
|
238
277
|
const hasMethods = type.getProperties().some(property => property.getFlags() & ts.SymbolFlags.Method);
|
|
239
278
|
if (hasEmptyConstructor && !hasMethods && allowsVirtualTemplate(type)) {
|
|
240
279
|
return TypeBuilder.shape.build(resolver, type, {
|
|
@@ -246,26 +285,35 @@ export const TypeBuilder: {
|
|
|
246
285
|
}
|
|
247
286
|
|
|
248
287
|
return managedType;
|
|
249
|
-
}
|
|
288
|
+
}
|
|
250
289
|
},
|
|
251
290
|
composition: {
|
|
252
291
|
build: (resolver, uType: ts.UnionOrIntersectionType) => {
|
|
253
292
|
let undefinable = false;
|
|
254
293
|
let nullable = false;
|
|
255
294
|
const remainder = uType.types.filter(ut => {
|
|
256
|
-
const isUndefined = (ut.getFlags() &
|
|
257
|
-
const isNull = (ut.getFlags() &
|
|
295
|
+
const isUndefined = (ut.getFlags() & ts.TypeFlags.Undefined) > 0;
|
|
296
|
+
const isNull = (ut.getFlags() & ts.TypeFlags.Null) > 0;
|
|
258
297
|
undefinable ||= isUndefined;
|
|
259
298
|
nullable ||= isNull;
|
|
260
299
|
return !(isUndefined || isNull);
|
|
261
300
|
});
|
|
262
301
|
const name = CoreUtil.getSymbol(uType)?.getName();
|
|
263
|
-
return {
|
|
302
|
+
return {
|
|
303
|
+
key: 'composition',
|
|
304
|
+
name,
|
|
305
|
+
undefinable,
|
|
306
|
+
nullable,
|
|
307
|
+
tsSubTypes: remainder,
|
|
308
|
+
subTypes: [],
|
|
309
|
+
operation: uType.isUnion() ? 'or' : 'and'
|
|
310
|
+
};
|
|
264
311
|
},
|
|
265
312
|
finalize: (type: CompositionType) => {
|
|
266
313
|
const { undefinable, nullable, subTypes } = type;
|
|
267
314
|
|
|
268
|
-
if (subTypes.length === 0) {
|
|
315
|
+
if (subTypes.length === 0) {
|
|
316
|
+
// We have an unknown type?
|
|
269
317
|
return { key: 'unknown', nullable, undefinable };
|
|
270
318
|
}
|
|
271
319
|
|
|
@@ -281,14 +329,16 @@ export const TypeBuilder: {
|
|
|
281
329
|
};
|
|
282
330
|
} else if (subTypes.length === 1) {
|
|
283
331
|
return { undefinable, nullable, ...first };
|
|
284
|
-
} else if (first.key === 'literal' && subTypes.every(item => item.name === first.name)) {
|
|
332
|
+
} else if (first.key === 'literal' && subTypes.every(item => item.name === first.name)) {
|
|
333
|
+
// We have a common
|
|
285
334
|
type.commonType = first;
|
|
286
|
-
} else if (type.operation === 'and' && first.key === 'shape' && subTypes.every(item => item.key === 'shape')) {
|
|
335
|
+
} else if (type.operation === 'and' && first.key === 'shape' && subTypes.every(item => item.key === 'shape')) {
|
|
336
|
+
// All shapes
|
|
287
337
|
return {
|
|
288
338
|
importName: first.importName,
|
|
289
339
|
name: first.name,
|
|
290
340
|
key: 'shape',
|
|
291
|
-
fieldTypes: subTypes.reduce((map, subType) => (
|
|
341
|
+
fieldTypes: subTypes.reduce((map, subType) => Object.assign(map, { ...subType.fieldTypes }), {})
|
|
292
342
|
};
|
|
293
343
|
}
|
|
294
344
|
return type;
|
|
@@ -313,7 +363,7 @@ export const TypeBuilder: {
|
|
|
313
363
|
} else if (type.aliasTypeArguments && type.aliasSymbol) {
|
|
314
364
|
mainType = type.aliasTypeArguments[0];
|
|
315
365
|
operation = type.aliasSymbol.escapedName.toString();
|
|
316
|
-
fields =
|
|
366
|
+
fields = type.aliasTypeArguments.length > 1 ? getMappedFields(type.aliasTypeArguments[1]) : [];
|
|
317
367
|
name = `${resolver.getTypeAsString(mainType)!}_${operation}_${fields?.join('_')}`;
|
|
318
368
|
}
|
|
319
369
|
|
|
@@ -341,13 +391,14 @@ export const TypeBuilder: {
|
|
|
341
391
|
|
|
342
392
|
for (const member of properties) {
|
|
343
393
|
const declaration = DeclarationUtil.getPrimaryDeclarationNode(member);
|
|
344
|
-
if (DeclarationUtil.isPublic(declaration)) {
|
|
394
|
+
if (DeclarationUtil.isPublic(declaration)) {
|
|
395
|
+
// IF public
|
|
345
396
|
const memberType = resolver.getType(declaration);
|
|
346
397
|
if (
|
|
347
398
|
!member.getName().includes('@') && // if not a symbol
|
|
348
399
|
!memberType.getCallSignatures().length // if not a function
|
|
349
400
|
) {
|
|
350
|
-
if ((ts.isPropertySignature(declaration) || ts.isPropertyDeclaration(declaration)) &&
|
|
401
|
+
if ((ts.isPropertySignature(declaration) || ts.isPropertyDeclaration(declaration)) && declaration.questionToken) {
|
|
351
402
|
Object.defineProperty(memberType, UNDEFINED, { value: true });
|
|
352
403
|
}
|
|
353
404
|
tsFieldTypes[member.getName()] = memberType;
|
|
@@ -355,8 +406,14 @@ export const TypeBuilder: {
|
|
|
355
406
|
}
|
|
356
407
|
}
|
|
357
408
|
return {
|
|
358
|
-
key: 'shape',
|
|
359
|
-
|
|
409
|
+
key: 'shape',
|
|
410
|
+
name,
|
|
411
|
+
importName,
|
|
412
|
+
tsFieldTypes,
|
|
413
|
+
tsTypeArguments,
|
|
414
|
+
fieldTypes: {},
|
|
415
|
+
extendsFrom: context.extendsFrom,
|
|
416
|
+
canTemplate: allowsVirtualTemplate(type)
|
|
360
417
|
};
|
|
361
418
|
},
|
|
362
419
|
finalize: (type: ShapeType) => {
|
|
@@ -390,9 +447,12 @@ export const TypeBuilder: {
|
|
|
390
447
|
|
|
391
448
|
// Resolving relative to source file
|
|
392
449
|
if (!importName || importName.startsWith('.')) {
|
|
393
|
-
const rawSourceFile: string =
|
|
394
|
-
|
|
395
|
-
|
|
450
|
+
const rawSourceFile: string =
|
|
451
|
+
DeclarationUtil.getDeclarations(type)
|
|
452
|
+
?.find(
|
|
453
|
+
declaration => ts.getAllJSDocTags(declaration, (node): node is ts.JSDocTag => node.tagName.getText() === 'concrete').length
|
|
454
|
+
)
|
|
455
|
+
?.getSourceFile().fileName ?? '';
|
|
396
456
|
|
|
397
457
|
if (!importName || importName === '.') {
|
|
398
458
|
importName = resolver.getFileImportName(rawSourceFile);
|
|
@@ -404,8 +464,9 @@ export const TypeBuilder: {
|
|
|
404
464
|
|
|
405
465
|
// Convert name to $Concrete suffix if not provided
|
|
406
466
|
if (!name) {
|
|
407
|
-
const [primaryDeclaration] = DeclarationUtil.getDeclarations(type)
|
|
408
|
-
|
|
467
|
+
const [primaryDeclaration] = DeclarationUtil.getDeclarations(type).filter(
|
|
468
|
+
declaration => ts.isInterfaceDeclaration(declaration) || ts.isTypeAliasDeclaration(declaration)
|
|
469
|
+
);
|
|
409
470
|
name = `${primaryDeclaration.name.text}$Concrete`;
|
|
410
471
|
}
|
|
411
472
|
|
package/src/resolver/cache.ts
CHANGED
package/src/resolver/coerce.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
const REGEX_PATTERN = /[
|
|
1
|
+
const REGEX_PATTERN = /[/](.*)[/](i|g|m|s)?/;
|
|
2
2
|
|
|
3
3
|
export class CoerceUtil {
|
|
4
4
|
/**
|
|
5
5
|
* Is a value a plain JS object, created using {}
|
|
6
6
|
*/
|
|
7
7
|
static #isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
8
|
-
return
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
return (
|
|
9
|
+
typeof value === 'object' && // separate from primitives
|
|
10
|
+
value !== undefined &&
|
|
11
|
+
value !== null && // is obvious
|
|
12
|
+
value.constructor === Object && // separate instances (Array, DOM, ...)
|
|
13
|
+
Object.prototype.toString.call(value) === '[object Object]'
|
|
14
|
+
); // separate build-in like Math
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
/**
|
|
@@ -50,8 +52,7 @@ export class CoerceUtil {
|
|
|
50
52
|
|
|
51
53
|
switch (type) {
|
|
52
54
|
case Date: {
|
|
53
|
-
const value = typeof input === 'number' || /^[-]?\d+$/.test(`${input}`) ?
|
|
54
|
-
new Date(parseInt(`${input}`, 10)) : new Date(`${input}`);
|
|
55
|
+
const value = typeof input === 'number' || /^[-]?\d+$/.test(`${input}`) ? new Date(parseInt(`${input}`, 10)) : new Date(`${input}`);
|
|
55
56
|
if (strict && Number.isNaN(value.getTime())) {
|
|
56
57
|
throw new Error(`Invalid date value: ${input}`);
|
|
57
58
|
}
|
|
@@ -106,8 +107,9 @@ export class CoerceUtil {
|
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
109
|
case undefined:
|
|
109
|
-
case String:
|
|
110
|
+
case String:
|
|
111
|
+
return `${input}`;
|
|
110
112
|
}
|
|
111
113
|
throw new Error(`Unknown type ${type.name}`);
|
|
112
114
|
}
|
|
113
|
-
}
|
|
115
|
+
}
|
package/src/resolver/service.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { type IndexedFile, type ManifestIndex, ManifestModuleUtil, path } from '@travetto/manifest';
|
|
4
4
|
|
|
5
|
-
import type { AnyType, ResolverContext, TransformResolver } from './types.ts';
|
|
6
|
-
import { TypeCategorize, TypeBuilder } from './builder.ts';
|
|
7
|
-
import { VisitCache } from './cache.ts';
|
|
8
|
-
import { DocUtil } from '../util/doc.ts';
|
|
9
|
-
import { DeclarationUtil } from '../util/declaration.ts';
|
|
10
5
|
import { transformCast } from '../types/shared.ts';
|
|
6
|
+
import { DeclarationUtil } from '../util/declaration.ts';
|
|
7
|
+
import { DocUtil } from '../util/doc.ts';
|
|
8
|
+
import { TypeBuilder, TypeCategorize } from './builder.ts';
|
|
9
|
+
import { VisitCache } from './cache.ts';
|
|
10
|
+
import type { AnyType, ResolverContext, TransformResolver } from './types.ts';
|
|
11
11
|
|
|
12
12
|
const isFinalizeType = (key: string): key is keyof typeof TypeBuilder => key in TypeBuilder;
|
|
13
13
|
|
|
@@ -45,8 +45,10 @@ export class SimpleResolver implements TransformResolver {
|
|
|
45
45
|
|
|
46
46
|
const sourceType = ManifestModuleUtil.getFileType(sourceFile);
|
|
47
47
|
|
|
48
|
-
return
|
|
49
|
-
this.#manifestIndex.
|
|
48
|
+
return (
|
|
49
|
+
this.#manifestIndex.getEntry(sourceType === 'ts' || sourceType === 'js' ? sourceFile : undefined!) ??
|
|
50
|
+
this.#manifestIndex.getFromImport(ManifestModuleUtil.withoutSourceExtension(sourceFile).replace(/^.*node_modules\//, ''))
|
|
51
|
+
);
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
/**
|
|
@@ -69,8 +71,7 @@ export class SimpleResolver implements TransformResolver {
|
|
|
69
71
|
* Is the file/import known to the index, helpful for determine ownership
|
|
70
72
|
*/
|
|
71
73
|
isKnownFile(fileOrImport: string): boolean {
|
|
72
|
-
return
|
|
73
|
-
(this.#manifestIndex.getFromImport(fileOrImport) !== undefined);
|
|
74
|
+
return this.#manifestIndex.getFromSource(fileOrImport) !== undefined || this.#manifestIndex.getFromImport(fileOrImport) !== undefined;
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
/**
|
|
@@ -116,7 +117,8 @@ export class SimpleResolver implements TransformResolver {
|
|
|
116
117
|
* Get list of properties
|
|
117
118
|
*/
|
|
118
119
|
getPropertiesOfType(type: ts.Type): ts.Symbol[] {
|
|
119
|
-
return this.#tsChecker
|
|
120
|
+
return this.#tsChecker
|
|
121
|
+
.getPropertiesOfType(type)
|
|
120
122
|
.filter(property => property.getName() !== '__proto__' && property.getName() !== 'prototype');
|
|
121
123
|
}
|
|
122
124
|
|
|
@@ -128,7 +130,8 @@ export class SimpleResolver implements TransformResolver {
|
|
|
128
130
|
const resolve = (resType: ts.Type, context?: Omit<ResolverContext, 'node' | 'importName'>): AnyType => {
|
|
129
131
|
const { depth = 0 } = context ?? {};
|
|
130
132
|
|
|
131
|
-
if (depth > 20) {
|
|
133
|
+
if (depth > 20) {
|
|
134
|
+
// Max depth is 20
|
|
132
135
|
throw new Error(`Object structure too nested: ${'getText' in node ? node.getText() : ''}`);
|
|
133
136
|
}
|
|
134
137
|
|
|
@@ -137,7 +140,7 @@ export class SimpleResolver implements TransformResolver {
|
|
|
137
140
|
const typeArguments: ts.Type[] =
|
|
138
141
|
'resolvedTypeArguments' in resType && resType.resolvedTypeArguments ? transformCast(resType.resolvedTypeArguments) : [];
|
|
139
142
|
|
|
140
|
-
let result = TypeBuilder[category].build(this, type, { ...context, node:
|
|
143
|
+
let result = TypeBuilder[category].build(this, type, { ...context, node: node && 'kind' in node ? node : undefined, importName });
|
|
141
144
|
|
|
142
145
|
// Convert via cache if needed
|
|
143
146
|
result = visited.getOrSet(type, result);
|
|
@@ -148,15 +151,19 @@ export class SimpleResolver implements TransformResolver {
|
|
|
148
151
|
result.templateTypeName = context?.templateTypeName;
|
|
149
152
|
|
|
150
153
|
try {
|
|
151
|
-
result.
|
|
152
|
-
} catch {
|
|
154
|
+
result.description = DocUtil.describeDocs(type).description;
|
|
155
|
+
} catch {}
|
|
153
156
|
|
|
154
157
|
if ('tsTypeArguments' in result) {
|
|
155
158
|
const tsTypeArguments = result.tsTypeArguments!;
|
|
156
159
|
if (typeArguments.length) {
|
|
157
|
-
result.typeArguments = typeArguments.map((item, i) =>
|
|
160
|
+
result.typeArguments = typeArguments.map((item, i) =>
|
|
161
|
+
resolve(item, { alias: type.aliasSymbol, templateTypeName: tsTypeArguments[i][0], depth: depth + 1 })
|
|
162
|
+
);
|
|
158
163
|
} else {
|
|
159
|
-
result.typeArguments = tsTypeArguments.map(
|
|
164
|
+
result.typeArguments = tsTypeArguments.map(item =>
|
|
165
|
+
resolve(item[1], { alias: type.aliasSymbol, templateTypeName: item[0], depth: depth + 1 })
|
|
166
|
+
);
|
|
160
167
|
}
|
|
161
168
|
delete result.tsTypeArguments;
|
|
162
169
|
}
|
|
@@ -169,7 +176,7 @@ export class SimpleResolver implements TransformResolver {
|
|
|
169
176
|
delete result.tsFieldTypes;
|
|
170
177
|
}
|
|
171
178
|
if ('tsSubTypes' in result) {
|
|
172
|
-
result.subTypes = result.tsSubTypes!.map(
|
|
179
|
+
result.subTypes = result.tsSubTypes!.map(item => resolve(item, { alias: type.aliasSymbol, depth: depth + 1 }));
|
|
173
180
|
delete result.tsSubTypes;
|
|
174
181
|
}
|
|
175
182
|
if (isFinalizeType(result.key)) {
|
|
@@ -190,4 +197,4 @@ export class SimpleResolver implements TransformResolver {
|
|
|
190
197
|
return { key: 'literal', ctor: Object, name: 'object' };
|
|
191
198
|
}
|
|
192
199
|
}
|
|
193
|
-
}
|
|
200
|
+
}
|
package/src/resolver/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type ts from 'typescript';
|
|
2
|
+
|
|
2
3
|
import type { TemplateLiteral } from '../types/shared.ts';
|
|
3
4
|
|
|
4
5
|
type TemplateArgument = [templateName: string, type: ts.Type];
|
|
@@ -18,7 +19,11 @@ export interface Type<K extends string> {
|
|
|
18
19
|
/**
|
|
19
20
|
* JS Doc comment
|
|
20
21
|
*/
|
|
21
|
-
|
|
22
|
+
description?: string;
|
|
23
|
+
/**
|
|
24
|
+
* JS Doc examples
|
|
25
|
+
*/
|
|
26
|
+
examples?: string[];
|
|
22
27
|
/**
|
|
23
28
|
* Can be undefined
|
|
24
29
|
*/
|
|
@@ -218,11 +223,19 @@ export interface ForeignType extends Type<'foreign'> {
|
|
|
218
223
|
/**
|
|
219
224
|
* Unknown type, should default to object
|
|
220
225
|
*/
|
|
221
|
-
export interface UnknownType extends Type<'unknown'> {
|
|
226
|
+
export interface UnknownType extends Type<'unknown'> {}
|
|
222
227
|
|
|
223
228
|
export type AnyType =
|
|
224
|
-
|
|
225
|
-
|
|
229
|
+
| TupleType
|
|
230
|
+
| ShapeType
|
|
231
|
+
| CompositionType
|
|
232
|
+
| LiteralType
|
|
233
|
+
| MappedType
|
|
234
|
+
| ManagedType
|
|
235
|
+
| PointerType
|
|
236
|
+
| UnknownType
|
|
237
|
+
| ForeignType
|
|
238
|
+
| TemplateType;
|
|
226
239
|
|
|
227
240
|
/**
|
|
228
241
|
* Simple interface for checked methods
|