@travetto/transformer 6.0.0 → 7.0.0-rc.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/package.json +3 -3
- package/src/importer.ts +6 -4
- package/src/register.ts +18 -0
- package/src/resolver/builder.ts +45 -4
- package/src/resolver/service.ts +6 -4
- package/src/resolver/types.ts +23 -5
- package/src/state.ts +26 -10
- package/src/types/visitor.ts +4 -2
- package/src/util/declaration.ts +19 -5
- package/src/util/doc.ts +17 -9
- package/src/util/log.ts +1 -1
- package/src/visitor.ts +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/transformer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0-rc.0",
|
|
4
4
|
"description": "Functionality for AST transformations, with transformer registration, and general utils",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"directory": "module/transformer"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@travetto/manifest": "^
|
|
27
|
+
"@travetto/manifest": "^7.0.0-rc.0",
|
|
28
28
|
"tslib": "^2.8.1",
|
|
29
|
-
"typescript": "^5.
|
|
29
|
+
"typescript": "^5.9.3"
|
|
30
30
|
},
|
|
31
31
|
"travetto": {
|
|
32
32
|
"displayName": "Transformation",
|
package/src/importer.ts
CHANGED
|
@@ -2,7 +2,7 @@ import ts from 'typescript';
|
|
|
2
2
|
|
|
3
3
|
import { ManifestModuleUtil, PackageUtil, path } from '@travetto/manifest';
|
|
4
4
|
|
|
5
|
-
import { AnyType, TransformResolver, ManagedType } from './resolver/types.ts';
|
|
5
|
+
import { AnyType, TransformResolver, ManagedType, MappedType } from './resolver/types.ts';
|
|
6
6
|
import { ImportUtil } from './util/import.ts';
|
|
7
7
|
import { CoreUtil } from './util/core.ts';
|
|
8
8
|
import { Import } from './types/shared.ts';
|
|
@@ -246,12 +246,14 @@ export class ImportManager {
|
|
|
246
246
|
/**
|
|
247
247
|
* Get the identifier and import if needed
|
|
248
248
|
*/
|
|
249
|
-
getOrImport(factory: ts.NodeFactory, type: ManagedType): ts.Identifier | ts.PropertyAccessExpression {
|
|
249
|
+
getOrImport(factory: ts.NodeFactory, type: ManagedType | MappedType): ts.Identifier | ts.PropertyAccessExpression {
|
|
250
|
+
const targetName = type.key === 'managed' ? type.name! : type.mappedClassName!;
|
|
251
|
+
// In same file already
|
|
250
252
|
if (type.importName === this.#importName) {
|
|
251
|
-
return factory.createIdentifier(
|
|
253
|
+
return factory.createIdentifier(targetName);
|
|
252
254
|
} else {
|
|
253
255
|
const { ident } = this.#imports.get(type.importName) ?? this.importFile(type.importName);
|
|
254
|
-
return factory.createPropertyAccessExpression(ident,
|
|
256
|
+
return factory.createPropertyAccessExpression(ident, targetName);
|
|
255
257
|
}
|
|
256
258
|
}
|
|
257
259
|
}
|
package/src/register.ts
CHANGED
|
@@ -115,6 +115,15 @@ export function OnMethod(...target: string[]) {
|
|
|
115
115
|
): void => storeHandler(inst, d.value!, 'before', 'method', target);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Listens for a `ts.ConstructorDeclaration`, on descent
|
|
120
|
+
*/
|
|
121
|
+
export function OnConstructor(...target: string[]) {
|
|
122
|
+
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
123
|
+
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.ConstructorDeclaration, dm?: DecoratorMeta) => R>
|
|
124
|
+
): void => storeHandler(inst, d.value!, 'before', 'constructor', target);
|
|
125
|
+
}
|
|
126
|
+
|
|
118
127
|
/**
|
|
119
128
|
* Listens for a static `ts.MethodDeclaration`, on descent
|
|
120
129
|
*/
|
|
@@ -214,6 +223,15 @@ export function AfterMethod(...target: string[]) {
|
|
|
214
223
|
): void => storeHandler(inst, d.value!, 'after', 'method', target);
|
|
215
224
|
}
|
|
216
225
|
|
|
226
|
+
/**
|
|
227
|
+
* Listens for a `ts.ConstructorDeclaration`, on ascent
|
|
228
|
+
*/
|
|
229
|
+
export function AfterConstructor(...target: string[]) {
|
|
230
|
+
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
231
|
+
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.ConstructorDeclaration, dm?: DecoratorMeta) => R>
|
|
232
|
+
): void => storeHandler(inst, d.value!, 'after', 'constructor', target);
|
|
233
|
+
}
|
|
234
|
+
|
|
217
235
|
/**
|
|
218
236
|
* Listens for a static `ts.MethodDeclaration`, on ascent
|
|
219
237
|
*/
|
package/src/resolver/builder.ts
CHANGED
|
@@ -9,11 +9,13 @@ import { DeclarationUtil } from '../util/declaration.ts';
|
|
|
9
9
|
import { LiteralUtil } from '../util/literal.ts';
|
|
10
10
|
import { transformCast, TemplateLiteralPart } from '../types/shared.ts';
|
|
11
11
|
|
|
12
|
-
import { Type, AnyType, CompositionType, TransformResolver, TemplateType } from './types.ts';
|
|
12
|
+
import { Type, AnyType, CompositionType, TransformResolver, TemplateType, MappedType } from './types.ts';
|
|
13
13
|
import { CoerceUtil } from './coerce.ts';
|
|
14
14
|
|
|
15
15
|
const UNDEFINED = Symbol();
|
|
16
16
|
|
|
17
|
+
const MAPPED_TYPE_SET = new Set(['Omit', 'Pick', 'Required', 'Partial']);
|
|
18
|
+
const isMappedType = (type: string | undefined): type is MappedType['operation'] => MAPPED_TYPE_SET.has(type!);
|
|
17
19
|
/**
|
|
18
20
|
* List of global types that can be parameterized
|
|
19
21
|
*/
|
|
@@ -39,7 +41,7 @@ const GLOBAL_SIMPLE: Record<string, Function> = {
|
|
|
39
41
|
|
|
40
42
|
type Category =
|
|
41
43
|
'tuple' | 'shape' | 'literal' | 'template' | 'managed' |
|
|
42
|
-
'composition' | 'foreign' | 'concrete' | 'unknown';
|
|
44
|
+
'composition' | 'foreign' | 'concrete' | 'unknown' | 'mapped';
|
|
43
45
|
|
|
44
46
|
/**
|
|
45
47
|
* Type categorizer, input for builder
|
|
@@ -102,9 +104,9 @@ export function TypeCategorize(resolver: TransformResolver, type: ts.Type): { ca
|
|
|
102
104
|
return { category: 'tuple', type };
|
|
103
105
|
} else if (type.isLiteral()) {
|
|
104
106
|
return { category: 'shape', type };
|
|
105
|
-
} else if (
|
|
107
|
+
} else if (objectFlags & ts.ObjectFlags.Mapped) { // Mapped types
|
|
106
108
|
if (type.getProperties().some(x => x.declarations || x.valueDeclaration)) {
|
|
107
|
-
return { category: '
|
|
109
|
+
return { category: 'mapped', type };
|
|
108
110
|
}
|
|
109
111
|
}
|
|
110
112
|
return { category: 'literal', type };
|
|
@@ -249,6 +251,45 @@ export const TypeBuilder: {
|
|
|
249
251
|
return type;
|
|
250
252
|
}
|
|
251
253
|
},
|
|
254
|
+
mapped: {
|
|
255
|
+
build: (resolver, type, alias) => {
|
|
256
|
+
let mainType: ts.Type | undefined;
|
|
257
|
+
let fields: string[] | undefined;
|
|
258
|
+
let operation: string | undefined;
|
|
259
|
+
|
|
260
|
+
const decls = DeclarationUtil.getDeclarations(type).filter(x => ts.isTypeAliasDeclaration(x));
|
|
261
|
+
if (decls.length > 0) {
|
|
262
|
+
const ref = decls[0].type;
|
|
263
|
+
if (ts.isTypeReferenceNode(ref) && ref.typeArguments && ref.typeArguments.length > 0) {
|
|
264
|
+
const [first, second] = ref.typeArguments;
|
|
265
|
+
mainType = resolver.getType(first);
|
|
266
|
+
operation = ref.typeName.getText();
|
|
267
|
+
if (second) {
|
|
268
|
+
const resolved = resolver.getType(second);
|
|
269
|
+
if (resolved.isStringLiteral()) {
|
|
270
|
+
fields = [resolved.value];
|
|
271
|
+
} else if (resolved.isUnion() && resolved.types.every(t => t.isStringLiteral())) {
|
|
272
|
+
fields = resolved.types.map(t => t.value);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
mainType = type.aliasTypeArguments?.[0]!;
|
|
278
|
+
operation = type.aliasSymbol?.escapedName.toString();
|
|
279
|
+
fields = type.getApparentProperties().map(p => p.getName());
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (!isMappedType(operation) || fields === undefined || !mainType || !mainType.isClass()) {
|
|
283
|
+
return TypeBuilder.shape.build(resolver, type, alias);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const importName = resolver.getTypeImportName(mainType) ?? '<unknown>';
|
|
287
|
+
const mappedClassName = resolver.getTypeAsString(mainType)!;
|
|
288
|
+
const name = resolver.getTypeAsString(type)!;
|
|
289
|
+
|
|
290
|
+
return { key: 'mapped', name, original: mainType, operation, importName, mappedClassName, fields };
|
|
291
|
+
}
|
|
292
|
+
},
|
|
252
293
|
shape: {
|
|
253
294
|
build: (resolver, type, alias?) => {
|
|
254
295
|
const tsFieldTypes: Record<string, ts.Type> = {};
|
package/src/resolver/service.ts
CHANGED
|
@@ -59,7 +59,7 @@ export class SimpleResolver implements TransformResolver {
|
|
|
59
59
|
* Resolve an import name (e.g. @module/path/file) for a type
|
|
60
60
|
*/
|
|
61
61
|
getTypeImportName(type: ts.Type, removeExt?: boolean): string | undefined {
|
|
62
|
-
const ogSource = DeclarationUtil.
|
|
62
|
+
const ogSource = DeclarationUtil.getOptionalPrimaryDeclarationNode(type)?.getSourceFile()?.fileName;
|
|
63
63
|
return ogSource ? this.getFileImportName(ogSource, removeExt) : undefined;
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -106,7 +106,7 @@ export class SimpleResolver implements TransformResolver {
|
|
|
106
106
|
* Get list of properties
|
|
107
107
|
*/
|
|
108
108
|
getPropertiesOfType(type: ts.Type): ts.Symbol[] {
|
|
109
|
-
return this.#tsChecker.getPropertiesOfType(type);
|
|
109
|
+
return this.#tsChecker.getPropertiesOfType(type).filter(x => x.getName() !== '__proto__' && x.getName() !== 'prototype');
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
/**
|
|
@@ -117,7 +117,7 @@ export class SimpleResolver implements TransformResolver {
|
|
|
117
117
|
const resolve = (resType: ts.Type, alias?: ts.Symbol, depth = 0): AnyType => {
|
|
118
118
|
|
|
119
119
|
if (depth > 20) { // Max depth is 20
|
|
120
|
-
throw new Error(
|
|
120
|
+
throw new Error(`Object structure too nested: ${'getText' in node ? node.getText() : ''}`);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
const { category, type } = TypeCategorize(this, resType);
|
|
@@ -131,7 +131,9 @@ export class SimpleResolver implements TransformResolver {
|
|
|
131
131
|
// Recurse
|
|
132
132
|
if (result) {
|
|
133
133
|
result.original = resType;
|
|
134
|
-
|
|
134
|
+
try {
|
|
135
|
+
result.comment = DocUtil.describeDocs(type).description;
|
|
136
|
+
} catch { }
|
|
135
137
|
|
|
136
138
|
if ('tsTypeArguments' in result) {
|
|
137
139
|
result.typeArguments = result.tsTypeArguments!.map((elType) => resolve(elType, type.aliasSymbol, depth + 1));
|
package/src/resolver/types.ts
CHANGED
|
@@ -61,23 +61,42 @@ export interface ShapeType extends Type<'shape'> {
|
|
|
61
61
|
* Does not include methods, used for shapes not concrete types
|
|
62
62
|
*/
|
|
63
63
|
fieldTypes: Record<string, AnyType>;
|
|
64
|
-
|
|
65
64
|
/**
|
|
66
65
|
* Type Info
|
|
67
66
|
*/
|
|
68
67
|
tsFieldTypes?: Record<string, ts.Type>;
|
|
69
|
-
|
|
70
68
|
/**
|
|
71
69
|
* Type arguments
|
|
72
70
|
*/
|
|
73
71
|
typeArguments?: AnyType[];
|
|
74
|
-
|
|
75
72
|
/**
|
|
76
73
|
* Type Arguments
|
|
77
74
|
*/
|
|
78
75
|
tsTypeArguments?: ts.Type[];
|
|
79
76
|
}
|
|
80
77
|
|
|
78
|
+
/**
|
|
79
|
+
* A type that is mapped
|
|
80
|
+
*/
|
|
81
|
+
export interface MappedType extends Type<'mapped'> {
|
|
82
|
+
/**
|
|
83
|
+
* Location the type came from, for class references
|
|
84
|
+
*/
|
|
85
|
+
importName: string;
|
|
86
|
+
/**
|
|
87
|
+
* Mapped class name
|
|
88
|
+
*/
|
|
89
|
+
mappedClassName: string;
|
|
90
|
+
/**
|
|
91
|
+
* The type of mapping operation
|
|
92
|
+
*/
|
|
93
|
+
operation: 'Omit' | 'Pick' | 'Partial' | 'Required';
|
|
94
|
+
/**
|
|
95
|
+
* The fields being provided for the mapping
|
|
96
|
+
*/
|
|
97
|
+
fields: string[];
|
|
98
|
+
}
|
|
99
|
+
|
|
81
100
|
/**
|
|
82
101
|
* A literal type, with an optional real value
|
|
83
102
|
*/
|
|
@@ -168,7 +187,6 @@ export interface ForeignType extends Type<'foreign'> {
|
|
|
168
187
|
* Identifier for type
|
|
169
188
|
*/
|
|
170
189
|
name: string;
|
|
171
|
-
|
|
172
190
|
/**
|
|
173
191
|
* Primary source file
|
|
174
192
|
*/
|
|
@@ -181,7 +199,7 @@ export interface ForeignType extends Type<'foreign'> {
|
|
|
181
199
|
export interface UnknownType extends Type<'unknown'> { }
|
|
182
200
|
|
|
183
201
|
export type AnyType =
|
|
184
|
-
TupleType | ShapeType | CompositionType | LiteralType |
|
|
202
|
+
TupleType | ShapeType | CompositionType | LiteralType | MappedType |
|
|
185
203
|
ManagedType | PointerType | UnknownType | ForeignType | TemplateType;
|
|
186
204
|
|
|
187
205
|
/**
|
package/src/state.ts
CHANGED
|
@@ -2,7 +2,7 @@ import ts from 'typescript';
|
|
|
2
2
|
|
|
3
3
|
import { path, ManifestIndex } from '@travetto/manifest';
|
|
4
4
|
|
|
5
|
-
import { ManagedType, AnyType, ForeignType } from './resolver/types.ts';
|
|
5
|
+
import { ManagedType, AnyType, ForeignType, MappedType } from './resolver/types.ts';
|
|
6
6
|
import { State, DecoratorMeta, Transformer, ModuleNameSymbol } from './types/visitor.ts';
|
|
7
7
|
import { SimpleResolver } from './resolver/service.ts';
|
|
8
8
|
import { ImportManager } from './importer.ts';
|
|
@@ -64,7 +64,7 @@ export class TransformerState implements State {
|
|
|
64
64
|
/**
|
|
65
65
|
* Get or import the node or external type
|
|
66
66
|
*/
|
|
67
|
-
getOrImport(type: ManagedType): ts.Identifier | ts.PropertyAccessExpression {
|
|
67
|
+
getOrImport(type: ManagedType | MappedType): ts.Identifier | ts.PropertyAccessExpression {
|
|
68
68
|
return this.#imports.getOrImport(this.factory, type);
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -129,6 +129,16 @@ export class TransformerState implements State {
|
|
|
129
129
|
return DocUtil.readDocTag(this.#resolver.getType(node), name);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Read all JSDoc tags as a list, with split support
|
|
134
|
+
*/
|
|
135
|
+
readDocTagList(node: ts.Declaration, name: string): string[] {
|
|
136
|
+
return this.readDocTag(node, name)
|
|
137
|
+
.flatMap(x => x.split(/\s*,\s*/g))
|
|
138
|
+
.map(x => x.replace(/`/g, ''))
|
|
139
|
+
.filter(x => !!x);
|
|
140
|
+
}
|
|
141
|
+
|
|
132
142
|
/**
|
|
133
143
|
* Import a decorator, generally to handle erasure
|
|
134
144
|
*/
|
|
@@ -154,20 +164,20 @@ export class TransformerState implements State {
|
|
|
154
164
|
*/
|
|
155
165
|
getDecoratorMeta(dec: ts.Decorator): DecoratorMeta | undefined {
|
|
156
166
|
const ident = DecoratorUtil.getDecoratorIdent(dec);
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
);
|
|
167
|
+
const type = this.#resolver.getType(ident);
|
|
168
|
+
const decl = DeclarationUtil.getOptionalPrimaryDeclarationNode(type);
|
|
160
169
|
const src = decl?.getSourceFile().fileName;
|
|
161
170
|
const mod = src ? this.#resolver.getFileImportName(src, true) : undefined;
|
|
162
171
|
const file = this.#manifestIndex.getFromImport(mod ?? '')?.outputFile;
|
|
163
|
-
const targets = DocUtil.readAugments(
|
|
172
|
+
const targets = DocUtil.readAugments(type);
|
|
173
|
+
const example = DocUtil.readExample(type);
|
|
164
174
|
const module = file ? mod : undefined;
|
|
165
175
|
const name = ident ?
|
|
166
176
|
ident.escapedText?.toString()! :
|
|
167
177
|
undefined;
|
|
168
178
|
|
|
169
179
|
if (ident && name) {
|
|
170
|
-
return { dec, ident, file, module, targets, name };
|
|
180
|
+
return { dec, ident, file, module, targets, name, options: example };
|
|
171
181
|
}
|
|
172
182
|
}
|
|
173
183
|
|
|
@@ -392,9 +402,15 @@ export class TransformerState implements State {
|
|
|
392
402
|
* Produce a foreign target type
|
|
393
403
|
*/
|
|
394
404
|
getForeignTarget(ret: ForeignType): ts.Expression {
|
|
395
|
-
return this.
|
|
396
|
-
|
|
397
|
-
|
|
405
|
+
return this.factory.createClassExpression([], undefined, undefined, undefined, [
|
|
406
|
+
this.factory.createPropertyDeclaration(
|
|
407
|
+
[this.factory.createModifier(ts.SyntaxKind.StaticKeyword)],
|
|
408
|
+
'Ⲑid',
|
|
409
|
+
undefined,
|
|
410
|
+
undefined,
|
|
411
|
+
this.fromLiteral(`${ret.source.split('node_modules/')[1]}+${ret.name}`)
|
|
412
|
+
)
|
|
413
|
+
]);
|
|
398
414
|
}
|
|
399
415
|
|
|
400
416
|
/**
|
package/src/types/visitor.ts
CHANGED
|
@@ -10,6 +10,7 @@ export type DecoratorMeta = {
|
|
|
10
10
|
file?: string;
|
|
11
11
|
targets?: string[];
|
|
12
12
|
name?: string;
|
|
13
|
+
options?: string[];
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
export type State = {
|
|
@@ -23,8 +24,9 @@ export type State = {
|
|
|
23
24
|
export type TransformPhase = 'before' | 'after';
|
|
24
25
|
|
|
25
26
|
export type TransformerType =
|
|
26
|
-
'class' | 'method' | 'property' | 'getter' | 'setter' | 'parameter'
|
|
27
|
-
'static-method' | 'call' | 'function' | 'file' | 'type' | 'interface'
|
|
27
|
+
'class' | 'method' | 'property' | 'getter' | 'setter' | 'parameter'
|
|
28
|
+
| 'static-method' | 'call' | 'function' | 'file' | 'type' | 'interface'
|
|
29
|
+
| 'constructor';
|
|
28
30
|
|
|
29
31
|
export const ModuleNameSymbol = Symbol.for('@travetto/transformer:id');
|
|
30
32
|
|
package/src/util/declaration.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import { CoreUtil } from './core.ts';
|
|
3
3
|
|
|
4
|
+
const isNamed = (o: ts.Declaration): o is ts.Declaration & { name: ts.Node } => 'name' in o && !!o.name;
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Declaration utils
|
|
6
8
|
*/
|
|
@@ -23,7 +25,8 @@ export class DeclarationUtil {
|
|
|
23
25
|
*/
|
|
24
26
|
static isPublic(node: ts.Declaration): boolean {
|
|
25
27
|
// eslint-disable-next-line no-bitwise
|
|
26
|
-
return !(ts.getCombinedModifierFlags(node) & ts.ModifierFlags.NonPublicAccessibilityModifier)
|
|
28
|
+
return !(ts.getCombinedModifierFlags(node) & ts.ModifierFlags.NonPublicAccessibilityModifier) &&
|
|
29
|
+
(!isNamed(node) || !ts.isPrivateIdentifier(node.name));
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
/**
|
|
@@ -42,15 +45,19 @@ export class DeclarationUtil {
|
|
|
42
45
|
/**
|
|
43
46
|
* Find primary declaration out of a list of declarations
|
|
44
47
|
*/
|
|
45
|
-
static
|
|
46
|
-
|
|
48
|
+
static getPrimaryDeclarationNode(node: ts.Type | ts.Symbol): ts.Declaration {
|
|
49
|
+
const decls = this.getDeclarations(node);
|
|
50
|
+
if (!decls.length) {
|
|
51
|
+
throw new Error('No declarations found for type');
|
|
52
|
+
}
|
|
53
|
+
return decls[0];
|
|
47
54
|
}
|
|
48
55
|
|
|
49
56
|
/**
|
|
50
57
|
* Find primary declaration out of a list of declarations
|
|
51
58
|
*/
|
|
52
|
-
static
|
|
53
|
-
return this.
|
|
59
|
+
static getOptionalPrimaryDeclarationNode(node: ts.Type | ts.Symbol): ts.Declaration | undefined {
|
|
60
|
+
return this.getDeclarations(node)[0];
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
/**
|
|
@@ -88,4 +95,11 @@ export class DeclarationUtil {
|
|
|
88
95
|
}
|
|
89
96
|
return acc;
|
|
90
97
|
}
|
|
98
|
+
|
|
99
|
+
static isStatic(node: ts.Declaration): boolean {
|
|
100
|
+
if ('modifiers' in node && Array.isArray(node.modifiers)) {
|
|
101
|
+
return node.modifiers?.some(x => x.kind === ts.SyntaxKind.StaticKeyword) ?? false;
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
91
105
|
}
|
package/src/util/doc.ts
CHANGED
|
@@ -26,25 +26,25 @@ export class DocUtil {
|
|
|
26
26
|
* Read JS Docs from a `ts.Declaration`
|
|
27
27
|
*/
|
|
28
28
|
static describeDocs(node: ts.Declaration | ts.Type): DeclDocumentation {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
let toDescribe = (node && !('getSourceFile' in node)) ?
|
|
30
|
+
DeclarationUtil.getOptionalPrimaryDeclarationNode(node) : node;
|
|
31
|
+
|
|
32
32
|
const out: DeclDocumentation = {
|
|
33
33
|
description: undefined,
|
|
34
34
|
return: undefined,
|
|
35
35
|
params: []
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
if (
|
|
39
|
-
const tags = ts.getJSDocTags(
|
|
40
|
-
while (!this.hasJSDoc(
|
|
41
|
-
|
|
38
|
+
if (toDescribe) {
|
|
39
|
+
const tags = ts.getJSDocTags(toDescribe);
|
|
40
|
+
while (!this.hasJSDoc(toDescribe) && CoreUtil.hasOriginal(toDescribe)) {
|
|
41
|
+
toDescribe = transformCast<ts.Declaration>(toDescribe.original);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const docs = this.hasJSDoc(
|
|
44
|
+
const docs = this.hasJSDoc(toDescribe) ? toDescribe.jsDoc : undefined;
|
|
45
45
|
|
|
46
46
|
if (docs) {
|
|
47
|
-
const top = docs
|
|
47
|
+
const top = docs.at(-1)!;
|
|
48
48
|
if (ts.isJSDoc(top)) {
|
|
49
49
|
out.description = this.getDocComment(top, out.description);
|
|
50
50
|
}
|
|
@@ -92,4 +92,12 @@ export class DocUtil {
|
|
|
92
92
|
static readAugments(type: ts.Type | ts.Symbol): string[] {
|
|
93
93
|
return this.readDocTag(type, 'augments').map(x => x.replace(/^.*?([^` ]+).*?$/, (_, b) => b));
|
|
94
94
|
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Read example information
|
|
98
|
+
* @param type
|
|
99
|
+
*/
|
|
100
|
+
static readExample(type: ts.Type | ts.Symbol): string[] {
|
|
101
|
+
return this.readDocTag(type, 'example').map(x => x.replace(/^.*?([^` ]+).*?$/, (_, b) => b));
|
|
102
|
+
}
|
|
95
103
|
}
|
package/src/util/log.ts
CHANGED
|
@@ -38,7 +38,7 @@ export class LogUtil {
|
|
|
38
38
|
const ox = x;
|
|
39
39
|
const out: Record<string, unknown> = {};
|
|
40
40
|
for (const key of TypedObject.keys(ox)) {
|
|
41
|
-
if (Object.getPrototypeOf(ox[key]) === Function.prototype || exclude.has(key)
|
|
41
|
+
if (ox[key] === null || ox[key] === undefined || Object.getPrototypeOf(ox[key]) === Function.prototype || exclude.has(key)) {
|
|
42
42
|
continue;
|
|
43
43
|
}
|
|
44
44
|
try {
|
package/src/visitor.ts
CHANGED
|
@@ -16,7 +16,9 @@ export class VisitorFactory<S extends State = State> {
|
|
|
16
16
|
* Get the type of transformer from a given a ts.node
|
|
17
17
|
*/
|
|
18
18
|
static nodeToType(node: ts.Node): TransformerType | undefined {
|
|
19
|
-
if (ts.
|
|
19
|
+
if (ts.isConstructorDeclaration(node)) {
|
|
20
|
+
return 'constructor';
|
|
21
|
+
} else if (ts.isMethodDeclaration(node)) {
|
|
20
22
|
// eslint-disable-next-line no-bitwise
|
|
21
23
|
return (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Static) ? 'static-method' : 'method';
|
|
22
24
|
} else if (ts.isPropertyDeclaration(node)) {
|