@travetto/transformer 2.1.3 → 2.2.1
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 +4 -4
- package/package.json +2 -2
- package/src/importer.ts +14 -11
- package/src/register.ts +20 -20
- package/src/resolver/builder.ts +5 -5
- package/src/resolver/service.ts +11 -6
- package/src/resolver/types.ts +6 -1
- package/src/state.ts +35 -28
- package/src/util/core.ts +18 -8
- package/src/util/declaration.ts +7 -5
- package/src/util/decorator.ts +6 -3
- package/src/util/doc.ts +4 -3
- package/src/util/import.ts +2 -2
- package/src/util/literal.ts +17 -4
- package/src/util/log.ts +4 -3
- package/src/visitor.ts +12 -7
- package/test-support/util.ts +3 -3
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Because working with the [Typescript](https://typescriptlang.org) API can be del
|
|
|
16
16
|
|
|
17
17
|
## Custom Transformer
|
|
18
18
|
|
|
19
|
-
Below is an example of a transformer that
|
|
19
|
+
Below is an example of a transformer that upper cases all `class`, `method` and `param` declarations. This will break any code that depends upon it as we are redefining all the identifiers at compile time.
|
|
20
20
|
|
|
21
21
|
**Code: Sample Transformer - Upper case all declarations**
|
|
22
22
|
```typescript
|
|
@@ -29,7 +29,7 @@ export class MakeUpper {
|
|
|
29
29
|
static [TransformerId] = '@trv:transformer-test';
|
|
30
30
|
|
|
31
31
|
@OnProperty()
|
|
32
|
-
static handleProperty(state: TransformerState, node: ts.PropertyDeclaration) {
|
|
32
|
+
static handleProperty(state: TransformerState, node: ts.PropertyDeclaration): ts.PropertyDeclaration {
|
|
33
33
|
if (!state.source.fileName.includes('doc/src')) {
|
|
34
34
|
return node;
|
|
35
35
|
}
|
|
@@ -45,7 +45,7 @@ export class MakeUpper {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
@OnClass()
|
|
48
|
-
static handleClass(state: TransformerState, node: ts.ClassDeclaration) {
|
|
48
|
+
static handleClass(state: TransformerState, node: ts.ClassDeclaration): ts.ClassDeclaration {
|
|
49
49
|
if (!state.source.fileName.includes('doc/src')) {
|
|
50
50
|
return node;
|
|
51
51
|
}
|
|
@@ -61,7 +61,7 @@ export class MakeUpper {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
@OnMethod()
|
|
64
|
-
static handleMethod(state: TransformerState, node: ts.MethodDeclaration) {
|
|
64
|
+
static handleMethod(state: TransformerState, node: ts.MethodDeclaration): ts.MethodDeclaration {
|
|
65
65
|
if (!state.source.fileName.includes('doc/src')) {
|
|
66
66
|
return node;
|
|
67
67
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/transformer",
|
|
3
3
|
"displayName": "Transformation",
|
|
4
|
-
"version": "2.1
|
|
4
|
+
"version": "2.2.1",
|
|
5
5
|
"description": "Functionality for AST transformations, with transformer registration, and general utils",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"typescript",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"directory": "module/transformer"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@travetto/base": "^2.1
|
|
28
|
+
"@travetto/base": "^2.2.1"
|
|
29
29
|
},
|
|
30
30
|
"publishConfig": {
|
|
31
31
|
"access": "public"
|
package/src/importer.ts
CHANGED
|
@@ -26,7 +26,7 @@ export class ImportManager {
|
|
|
26
26
|
/**
|
|
27
27
|
* Produces a unique ID for a given file, importing if needed
|
|
28
28
|
*/
|
|
29
|
-
getId(file: string) {
|
|
29
|
+
getId(file: string): string {
|
|
30
30
|
if (!this.#ids.has(file)) {
|
|
31
31
|
const key = basename(file).replace(/[.][^.]*$/, '').replace(/[^A-Za-z0-9]+/g, '_');
|
|
32
32
|
this.#ids.set(file, `ᚕ_${key}_${this.#idx[key] = (this.#idx[key] || 0) + 1}`);
|
|
@@ -37,7 +37,7 @@ export class ImportManager {
|
|
|
37
37
|
/**
|
|
38
38
|
* Import a file if needed, and record it's identifier
|
|
39
39
|
*/
|
|
40
|
-
importFile(file: string, base?: string) {
|
|
40
|
+
importFile(file: string, base?: string): Import {
|
|
41
41
|
file = ModuleUtil.normalizePath(file);
|
|
42
42
|
|
|
43
43
|
// Allow for node classes to be imported directly
|
|
@@ -65,9 +65,9 @@ export class ImportManager {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
const ident = this.factory.createIdentifier(id);
|
|
68
|
-
const
|
|
69
|
-
this.#imports.set(ident.escapedText.toString(),
|
|
70
|
-
this.#newImports.set(file,
|
|
68
|
+
const newImport = { path: file, ident };
|
|
69
|
+
this.#imports.set(ident.escapedText.toString(), newImport);
|
|
70
|
+
this.#newImports.set(file, newImport);
|
|
71
71
|
}
|
|
72
72
|
return this.#newImports.get(file)!;
|
|
73
73
|
}
|
|
@@ -75,7 +75,7 @@ export class ImportManager {
|
|
|
75
75
|
/**
|
|
76
76
|
* Import given an external type
|
|
77
77
|
*/
|
|
78
|
-
importFromResolved(...types: AnyType[]) {
|
|
78
|
+
importFromResolved(...types: AnyType[]): void {
|
|
79
79
|
for (const type of types) {
|
|
80
80
|
if (type.key === 'external' && type.source && type.source !== this.source.fileName) {
|
|
81
81
|
this.importFile(type.source, this.source.fileName);
|
|
@@ -93,19 +93,19 @@ export class ImportManager {
|
|
|
93
93
|
/**
|
|
94
94
|
* Add imports to a source file
|
|
95
95
|
*/
|
|
96
|
-
finalizeNewImports(file: ts.SourceFile) {
|
|
96
|
+
finalizeNewImports(file: ts.SourceFile): ts.SourceFile | undefined {
|
|
97
97
|
if (!this.#newImports.size) {
|
|
98
98
|
return;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
try {
|
|
102
102
|
const importStmts = [...this.#newImports.values()].map(({ path, ident }) => {
|
|
103
|
-
const
|
|
103
|
+
const importStmt = this.factory.createImportDeclaration(
|
|
104
104
|
undefined, undefined,
|
|
105
105
|
this.factory.createImportClause(false, undefined, this.factory.createNamespaceImport(ident)),
|
|
106
106
|
this.factory.createStringLiteral(path)
|
|
107
107
|
);
|
|
108
|
-
return
|
|
108
|
+
return importStmt;
|
|
109
109
|
});
|
|
110
110
|
|
|
111
111
|
return CoreUtil.updateSource(this.factory, file, [
|
|
@@ -113,6 +113,9 @@ export class ImportManager {
|
|
|
113
113
|
...file.statements.filter((x: ts.Statement & { remove?: boolean }) => !x.remove) // Exclude culled imports
|
|
114
114
|
]);
|
|
115
115
|
} catch (err) { // Missing import
|
|
116
|
+
if (!(err instanceof Error)) {
|
|
117
|
+
throw err;
|
|
118
|
+
}
|
|
116
119
|
const out = new Error(`${err.message} in ${file.fileName.replace(PathUtil.cwd, '.')}`);
|
|
117
120
|
out.stack = err.stack;
|
|
118
121
|
throw out;
|
|
@@ -122,14 +125,14 @@ export class ImportManager {
|
|
|
122
125
|
/**
|
|
123
126
|
* Reset the imports into the source file
|
|
124
127
|
*/
|
|
125
|
-
finalize(ret: ts.SourceFile) {
|
|
128
|
+
finalize(ret: ts.SourceFile): ts.SourceFile {
|
|
126
129
|
return this.finalizeNewImports(ret) ?? ret;
|
|
127
130
|
}
|
|
128
131
|
|
|
129
132
|
/**
|
|
130
133
|
* Get the identifier and import if needed
|
|
131
134
|
*/
|
|
132
|
-
getOrImport(factory: ts.NodeFactory, type: ExternalType) {
|
|
135
|
+
getOrImport(factory: ts.NodeFactory, type: ExternalType): ts.Identifier | ts.PropertyAccessExpression {
|
|
133
136
|
if (type.source === this.source.fileName) {
|
|
134
137
|
return factory.createIdentifier(type.name!);
|
|
135
138
|
} else {
|
package/src/register.ts
CHANGED
|
@@ -9,12 +9,12 @@ type TransformerWithHandlers = Transformer & { [HandlersProp]?: NodeTransformer[
|
|
|
9
9
|
* Get all transformers
|
|
10
10
|
* @param obj Object to search for transformers
|
|
11
11
|
*/
|
|
12
|
-
export function getAllTransformers(obj: Record<string, { [HandlersProp]?: NodeTransformer[] }>) {
|
|
12
|
+
export function getAllTransformers(obj: Record<string, { [HandlersProp]?: NodeTransformer[] }>): NodeTransformer[] {
|
|
13
13
|
return Object.values(obj).flatMap(x => x[HandlersProp] ?? []);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
// Store handlers in class
|
|
17
|
-
function storeHandler(cls: TransformerWithHandlers, fn: Function, phase: TransformPhase, type: TransformerType, target?: string[]) {
|
|
17
|
+
function storeHandler(cls: TransformerWithHandlers, fn: Function, phase: TransformPhase, type: TransformerType, target?: string[]): void {
|
|
18
18
|
if (target) {
|
|
19
19
|
const ns = cls[TransformerId].split('/')[0]; // Everything before the '/'
|
|
20
20
|
target = target.map(x => x.startsWith('@') ? x : `${ns}/${x}`);
|
|
@@ -29,7 +29,7 @@ function storeHandler(cls: TransformerWithHandlers, fn: Function, phase: Transfo
|
|
|
29
29
|
export function OnCall(...target: string[]) {
|
|
30
30
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
31
31
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.CallExpression) => R>
|
|
32
|
-
) => storeHandler(inst, d.value!, 'before', 'call', target);
|
|
32
|
+
): void => storeHandler(inst, d.value!, 'before', 'call', target);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
@@ -38,7 +38,7 @@ export function OnCall(...target: string[]) {
|
|
|
38
38
|
export function OnFunction(...target: string[]) {
|
|
39
39
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
40
40
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.FunctionDeclaration | ts.FunctionExpression) => R>
|
|
41
|
-
) => storeHandler(inst, d.value!, 'before', 'function', target);
|
|
41
|
+
): void => storeHandler(inst, d.value!, 'before', 'function', target);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
@@ -47,7 +47,7 @@ export function OnFunction(...target: string[]) {
|
|
|
47
47
|
export function OnParameter(...target: string[]) {
|
|
48
48
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
49
49
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.ParameterDeclaration, dm?: DecoratorMeta) => R>
|
|
50
|
-
) => storeHandler(inst, d.value!, 'before', 'parameter', target);
|
|
50
|
+
): void => storeHandler(inst, d.value!, 'before', 'parameter', target);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/**
|
|
@@ -56,7 +56,7 @@ export function OnParameter(...target: string[]) {
|
|
|
56
56
|
export function OnProperty(...target: string[]) {
|
|
57
57
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
58
58
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.PropertyDeclaration, dm?: DecoratorMeta) => R>
|
|
59
|
-
) => storeHandler(inst, d.value!, 'before', 'property', target);
|
|
59
|
+
): void => storeHandler(inst, d.value!, 'before', 'property', target);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/**
|
|
@@ -65,7 +65,7 @@ export function OnProperty(...target: string[]) {
|
|
|
65
65
|
export function OnGetter(...target: string[]) {
|
|
66
66
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
67
67
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.GetAccessorDeclaration, dm?: DecoratorMeta) => R>
|
|
68
|
-
) => storeHandler(inst, d.value!, 'before', 'getter', target);
|
|
68
|
+
): void => storeHandler(inst, d.value!, 'before', 'getter', target);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/**
|
|
@@ -74,7 +74,7 @@ export function OnGetter(...target: string[]) {
|
|
|
74
74
|
export function OnSetter(...target: string[]) {
|
|
75
75
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
76
76
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.SetAccessorDeclaration, dm?: DecoratorMeta) => R>
|
|
77
|
-
) => storeHandler(inst, d.value!, 'before', 'setter', target);
|
|
77
|
+
): void => storeHandler(inst, d.value!, 'before', 'setter', target);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
/**
|
|
@@ -83,7 +83,7 @@ export function OnSetter(...target: string[]) {
|
|
|
83
83
|
export function OnMethod(...target: string[]) {
|
|
84
84
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
85
85
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.MethodDeclaration, dm?: DecoratorMeta) => R>
|
|
86
|
-
) => storeHandler(inst, d.value!, 'before', 'method', target);
|
|
86
|
+
): void => storeHandler(inst, d.value!, 'before', 'method', target);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/**
|
|
@@ -92,7 +92,7 @@ export function OnMethod(...target: string[]) {
|
|
|
92
92
|
export function OnStaticMethod(...target: string[]) {
|
|
93
93
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
94
94
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.MethodDeclaration, dm?: DecoratorMeta) => R>
|
|
95
|
-
) => storeHandler(inst, d.value!, 'before', 'static-method', target);
|
|
95
|
+
): void => storeHandler(inst, d.value!, 'before', 'static-method', target);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/**
|
|
@@ -101,7 +101,7 @@ export function OnStaticMethod(...target: string[]) {
|
|
|
101
101
|
export function OnClass(...target: string[]) {
|
|
102
102
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
103
103
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.ClassDeclaration, dm?: DecoratorMeta) => R>
|
|
104
|
-
) => storeHandler(inst, d.value!, 'before', 'class', target);
|
|
104
|
+
): void => storeHandler(inst, d.value!, 'before', 'class', target);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
/**
|
|
@@ -110,7 +110,7 @@ export function OnClass(...target: string[]) {
|
|
|
110
110
|
export function AfterCall(...target: string[]) {
|
|
111
111
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
112
112
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.CallExpression, dm?: DecoratorMeta) => R>
|
|
113
|
-
) => storeHandler(inst, d.value!, 'after', 'call', target);
|
|
113
|
+
): void => storeHandler(inst, d.value!, 'after', 'call', target);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
/**
|
|
@@ -119,7 +119,7 @@ export function AfterCall(...target: string[]) {
|
|
|
119
119
|
export function AfterFunction(...target: string[]) {
|
|
120
120
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
121
121
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.FunctionDeclaration | ts.FunctionExpression) => R>
|
|
122
|
-
) => storeHandler(inst, d.value!, 'after', 'function', target);
|
|
122
|
+
): void => storeHandler(inst, d.value!, 'after', 'function', target);
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
/**
|
|
@@ -128,7 +128,7 @@ export function AfterFunction(...target: string[]) {
|
|
|
128
128
|
export function AfterParameter(...target: string[]) {
|
|
129
129
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
130
130
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.ParameterDeclaration, dm?: DecoratorMeta) => R>
|
|
131
|
-
) => storeHandler(inst, d.value!, 'after', 'parameter', target);
|
|
131
|
+
): void => storeHandler(inst, d.value!, 'after', 'parameter', target);
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
/**
|
|
@@ -137,7 +137,7 @@ export function AfterParameter(...target: string[]) {
|
|
|
137
137
|
export function AfterProperty(...target: string[]) {
|
|
138
138
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
139
139
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.PropertyDeclaration, dm?: DecoratorMeta) => R>
|
|
140
|
-
) => storeHandler(inst, d.value!, 'after', 'property', target);
|
|
140
|
+
): void => storeHandler(inst, d.value!, 'after', 'property', target);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
/**
|
|
@@ -146,7 +146,7 @@ export function AfterProperty(...target: string[]) {
|
|
|
146
146
|
export function AfterGetter(...target: string[]) {
|
|
147
147
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
148
148
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.GetAccessorDeclaration, dm?: DecoratorMeta) => R>
|
|
149
|
-
) => storeHandler(inst, d.value!, 'after', 'getter', target);
|
|
149
|
+
): void => storeHandler(inst, d.value!, 'after', 'getter', target);
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
/**
|
|
@@ -155,7 +155,7 @@ export function AfterGetter(...target: string[]) {
|
|
|
155
155
|
export function AfterSetter(...target: string[]) {
|
|
156
156
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
157
157
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.SetAccessorDeclaration, dm?: DecoratorMeta) => R>
|
|
158
|
-
) => storeHandler(inst, d.value!, 'after', 'setter', target);
|
|
158
|
+
): void => storeHandler(inst, d.value!, 'after', 'setter', target);
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
/**
|
|
@@ -164,7 +164,7 @@ export function AfterSetter(...target: string[]) {
|
|
|
164
164
|
export function AfterMethod(...target: string[]) {
|
|
165
165
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
166
166
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.MethodDeclaration, dm?: DecoratorMeta) => R>
|
|
167
|
-
) => storeHandler(inst, d.value!, 'after', 'method', target);
|
|
167
|
+
): void => storeHandler(inst, d.value!, 'after', 'method', target);
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
/**
|
|
@@ -173,7 +173,7 @@ export function AfterMethod(...target: string[]) {
|
|
|
173
173
|
export function AfterStaticMethod(...target: string[]) {
|
|
174
174
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
175
175
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.MethodDeclaration, dm?: DecoratorMeta) => R>
|
|
176
|
-
) => storeHandler(inst, d.value!, 'after', 'static-method', target);
|
|
176
|
+
): void => storeHandler(inst, d.value!, 'after', 'static-method', target);
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
/**
|
|
@@ -182,5 +182,5 @@ export function AfterStaticMethod(...target: string[]) {
|
|
|
182
182
|
export function AfterClass(...target: string[]) {
|
|
183
183
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
184
184
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.ClassDeclaration, dm?: DecoratorMeta) => R>
|
|
185
|
-
) => storeHandler(inst, d.value!, 'after', 'class', target);
|
|
185
|
+
): void => storeHandler(inst, d.value!, 'after', 'class', target);
|
|
186
186
|
}
|
package/src/resolver/builder.ts
CHANGED
|
@@ -117,6 +117,7 @@ export const TypeBuilder: {
|
|
|
117
117
|
|
|
118
118
|
if (name in GLOBAL_SIMPLE) {
|
|
119
119
|
const cons = GLOBAL_SIMPLE[name];
|
|
120
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
120
121
|
const ret = LiteralUtil.isLiteralType(type) ? Util.coerceType(type.value, cons as typeof String, false) :
|
|
121
122
|
undefined;
|
|
122
123
|
|
|
@@ -148,8 +149,7 @@ export const TypeBuilder: {
|
|
|
148
149
|
}
|
|
149
150
|
},
|
|
150
151
|
union: {
|
|
151
|
-
build: (checker,
|
|
152
|
-
const uType = type as ts.UnionType;
|
|
152
|
+
build: (checker, uType: ts.UnionType) => {
|
|
153
153
|
let undefinable = false;
|
|
154
154
|
let nullable = false;
|
|
155
155
|
const remainder = uType.types.filter(ut => {
|
|
@@ -159,7 +159,7 @@ export const TypeBuilder: {
|
|
|
159
159
|
nullable = nullable || n;
|
|
160
160
|
return !(u || n);
|
|
161
161
|
});
|
|
162
|
-
const name = CoreUtil.getSymbol(
|
|
162
|
+
const name = CoreUtil.getSymbol(uType)?.getName();
|
|
163
163
|
return { key: 'union', name, undefinable, nullable, tsSubTypes: remainder, subTypes: [] };
|
|
164
164
|
},
|
|
165
165
|
finalize: (type: UnionType) => {
|
|
@@ -208,9 +208,9 @@ export const TypeBuilder: {
|
|
|
208
208
|
source = '.';
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
const sourceFile = DeclarationUtil.getDeclarations(type)
|
|
211
|
+
const sourceFile: string = DeclarationUtil.getDeclarations(type)
|
|
212
212
|
?.find(x => ts.getAllJSDocTags(x, (t): t is ts.JSDocTag => t.tagName.getText() === 'concrete').length)
|
|
213
|
-
?.getSourceFile().fileName
|
|
213
|
+
?.getSourceFile().fileName ?? '';
|
|
214
214
|
|
|
215
215
|
if (source === '.') {
|
|
216
216
|
source = sourceFile;
|
package/src/resolver/service.ts
CHANGED
|
@@ -19,7 +19,7 @@ export class TypeResolver implements Checker {
|
|
|
19
19
|
* Get type checker
|
|
20
20
|
* @private
|
|
21
21
|
*/
|
|
22
|
-
getChecker() {
|
|
22
|
+
getChecker(): ts.TypeChecker {
|
|
23
23
|
return this.#tsChecker;
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -27,21 +27,22 @@ export class TypeResolver implements Checker {
|
|
|
27
27
|
* Get type from element
|
|
28
28
|
* @param el
|
|
29
29
|
*/
|
|
30
|
-
getType(el: ts.Type | ts.Node) {
|
|
31
|
-
return 'getSourceFile' in el ? this.#tsChecker.getTypeAtLocation(el
|
|
30
|
+
getType(el: ts.Type | ts.Node): ts.Type {
|
|
31
|
+
return 'getSourceFile' in el ? this.#tsChecker.getTypeAtLocation(el) : el;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Fetch all type arguments for a give type
|
|
36
36
|
*/
|
|
37
37
|
getAllTypeArguments(ref: ts.Type): ts.Type[] {
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
38
39
|
return this.#tsChecker.getTypeArguments(ref as ts.TypeReference) as ts.Type[];
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* Resolve the return type for a method
|
|
43
44
|
*/
|
|
44
|
-
getReturnType(node: ts.MethodDeclaration) {
|
|
45
|
+
getReturnType(node: ts.MethodDeclaration): ts.Type {
|
|
45
46
|
const type = this.getType(node);
|
|
46
47
|
const [sig] = type.getCallSignatures();
|
|
47
48
|
return this.#tsChecker.getReturnTypeOfSignature(sig);
|
|
@@ -50,14 +51,14 @@ export class TypeResolver implements Checker {
|
|
|
50
51
|
/**
|
|
51
52
|
* Get type as a string representation
|
|
52
53
|
*/
|
|
53
|
-
getTypeAsString(type: ts.Type) {
|
|
54
|
+
getTypeAsString(type: ts.Type): string | undefined {
|
|
54
55
|
return this.#tsChecker.typeToString(this.#tsChecker.getApparentType(type)) || undefined;
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
/**
|
|
58
59
|
* Get list of properties
|
|
59
60
|
*/
|
|
60
|
-
getPropertiesOfType(type: ts.Type) {
|
|
61
|
+
getPropertiesOfType(type: ts.Type): ts.Symbol[] {
|
|
61
62
|
return this.#tsChecker.getPropertiesOfType(type);
|
|
62
63
|
}
|
|
63
64
|
|
|
@@ -102,6 +103,7 @@ export class TypeResolver implements Checker {
|
|
|
102
103
|
delete result.tsSubTypes;
|
|
103
104
|
}
|
|
104
105
|
if (finalize) {
|
|
106
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
105
107
|
result = finalize(result as never);
|
|
106
108
|
}
|
|
107
109
|
}
|
|
@@ -112,6 +114,9 @@ export class TypeResolver implements Checker {
|
|
|
112
114
|
try {
|
|
113
115
|
return resolve(this.getType(node));
|
|
114
116
|
} catch (err) {
|
|
117
|
+
if (!(err instanceof Error)) {
|
|
118
|
+
throw err;
|
|
119
|
+
}
|
|
115
120
|
console.error('Unable to resolve type', err.stack);
|
|
116
121
|
return { key: 'literal', ctor: Object, name: 'object' };
|
|
117
122
|
}
|
package/src/resolver/types.ts
CHANGED
|
@@ -141,7 +141,12 @@ export interface PointerType extends Type<'pointer'> {
|
|
|
141
141
|
target: Exclude<AnyType, PointerType>;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Unknown type, should default to object
|
|
146
|
+
*/
|
|
147
|
+
export interface UnknownType extends Type<'unknown'> { }
|
|
148
|
+
|
|
149
|
+
export type AnyType = TupleType | ShapeType | UnionType | LiteralType | ExternalType | PointerType | UnknownType;
|
|
145
150
|
|
|
146
151
|
/**
|
|
147
152
|
* Simple interface for checked methods
|
package/src/state.ts
CHANGED
|
@@ -12,12 +12,15 @@ import { DocUtil } from './util/doc';
|
|
|
12
12
|
import { DecoratorUtil } from './util/decorator';
|
|
13
13
|
import { DeclarationUtil } from './util/declaration';
|
|
14
14
|
import { CoreUtil, LiteralUtil } from './util';
|
|
15
|
+
import { Import } from './types/shared';
|
|
15
16
|
|
|
16
17
|
function hasOriginal(n: unknown): n is { original: ts.Node } {
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
17
19
|
return !!n && !(n as { parent?: unknown }).parent && !!(n as { original: unknown }).original;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
function hasEscapedName(n: unknown): n is { name: { escapedText: string } } {
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
21
24
|
return !!n && !!(n as { name?: { escapedText?: string } }).name?.escapedText;
|
|
22
25
|
}
|
|
23
26
|
|
|
@@ -45,28 +48,28 @@ export class TransformerState implements State {
|
|
|
45
48
|
* Allow access to resolver
|
|
46
49
|
* @private
|
|
47
50
|
*/
|
|
48
|
-
getResolver() {
|
|
51
|
+
getResolver(): TypeResolver {
|
|
49
52
|
return this.#resolver;
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
/**
|
|
53
56
|
* Get or import the node or external type
|
|
54
57
|
*/
|
|
55
|
-
getOrImport(type: ExternalType) {
|
|
58
|
+
getOrImport(type: ExternalType): ts.Identifier | ts.PropertyAccessExpression {
|
|
56
59
|
return this.#imports.getOrImport(this.factory, type);
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
/**
|
|
60
63
|
* Import a given file
|
|
61
64
|
*/
|
|
62
|
-
importFile(file: string) {
|
|
65
|
+
importFile(file: string): Import {
|
|
63
66
|
return this.#imports.importFile(file);
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
/**
|
|
67
70
|
* Resolve an `AnyType` from a `ts.Type` or `ts.Node`
|
|
68
71
|
*/
|
|
69
|
-
resolveType(node: ts.Type | ts.Node) {
|
|
72
|
+
resolveType(node: ts.Type | ts.Node): AnyType {
|
|
70
73
|
const resolved = this.#resolver.resolveType(node);
|
|
71
74
|
this.#imports.importFromResolved(resolved);
|
|
72
75
|
return resolved;
|
|
@@ -75,7 +78,7 @@ export class TransformerState implements State {
|
|
|
75
78
|
/**
|
|
76
79
|
* Resolve external type
|
|
77
80
|
*/
|
|
78
|
-
resolveExternalType(node: ts.Node) {
|
|
81
|
+
resolveExternalType(node: ts.Node): ExternalType {
|
|
79
82
|
const resolved = this.resolveType(node);
|
|
80
83
|
if (resolved.key !== 'external') {
|
|
81
84
|
throw new Error(`Unable to import non-external type: ${node.getText()} ${resolved.key}: ${node.getSourceFile().fileName}`);
|
|
@@ -86,7 +89,7 @@ export class TransformerState implements State {
|
|
|
86
89
|
/**
|
|
87
90
|
* Convert a type to it's identifier, will return undefined if none match
|
|
88
91
|
*/
|
|
89
|
-
typeToIdentifier(node: ts.Type | AnyType) {
|
|
92
|
+
typeToIdentifier(node: ts.Type | AnyType): ts.Identifier | ts.PropertyAccessExpression | undefined {
|
|
90
93
|
const type = 'flags' in node ? this.resolveType(node) : node;
|
|
91
94
|
switch (type.key) {
|
|
92
95
|
case 'literal': return this.factory.createIdentifier(type.ctor!.name);
|
|
@@ -98,7 +101,7 @@ export class TransformerState implements State {
|
|
|
98
101
|
/**
|
|
99
102
|
* Resolve the return type
|
|
100
103
|
*/
|
|
101
|
-
resolveReturnType(node: ts.MethodDeclaration) {
|
|
104
|
+
resolveReturnType(node: ts.MethodDeclaration): AnyType {
|
|
102
105
|
const typeNode = ts.getJSDocReturnType(node);
|
|
103
106
|
if (typeNode) {
|
|
104
107
|
const resolved = this.#resolver.getChecker().getTypeFromTypeNode(typeNode);
|
|
@@ -111,14 +114,14 @@ export class TransformerState implements State {
|
|
|
111
114
|
/**
|
|
112
115
|
* Read all JSDoc tags
|
|
113
116
|
*/
|
|
114
|
-
readDocTag(node: ts.Declaration, name: string) {
|
|
117
|
+
readDocTag(node: ts.Declaration, name: string): string[] {
|
|
115
118
|
return DocUtil.readDocTag(this.#resolver.getType(node), name);
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
/**
|
|
119
122
|
* Import a decorator, generally to handle erasure
|
|
120
123
|
*/
|
|
121
|
-
importDecorator(pth: string, name: string) {
|
|
124
|
+
importDecorator(pth: string, name: string): ts.PropertyAccessExpression | undefined {
|
|
122
125
|
if (!this.#decorators.has(`${pth}:${name}`)) {
|
|
123
126
|
const ref = this.#imports.importFile(pth);
|
|
124
127
|
const ident = this.factory.createIdentifier(name);
|
|
@@ -130,7 +133,7 @@ export class TransformerState implements State {
|
|
|
130
133
|
/**
|
|
131
134
|
* Create a decorator to add functionality to a declaration
|
|
132
135
|
*/
|
|
133
|
-
createDecorator(pth: string, name: string, ...contents: (ts.Expression | undefined)[]) {
|
|
136
|
+
createDecorator(pth: string, name: string, ...contents: (ts.Expression | undefined)[]): ts.Decorator {
|
|
134
137
|
this.importDecorator(pth, name);
|
|
135
138
|
return CoreUtil.createDecorator(this.factory, this.#decorators.get(name)!, ...contents);
|
|
136
139
|
}
|
|
@@ -151,6 +154,7 @@ export class TransformerState implements State {
|
|
|
151
154
|
module: decl ? ModuleUtil.normalizePath(decl.getSourceFile().fileName) : undefined, // All #decorators will be absolute
|
|
152
155
|
targets: DocUtil.readAugments(this.#resolver.getType(ident)),
|
|
153
156
|
name: ident ?
|
|
157
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
154
158
|
ident.escapedText! as string :
|
|
155
159
|
undefined
|
|
156
160
|
};
|
|
@@ -160,7 +164,7 @@ export class TransformerState implements State {
|
|
|
160
164
|
* Get list of all #decorators for a node
|
|
161
165
|
*/
|
|
162
166
|
getDecoratorList(node: ts.Node): DecoratorMeta[] {
|
|
163
|
-
return (
|
|
167
|
+
return (node.decorators ?? [])
|
|
164
168
|
.map(dec => this.getDecoratorMeta(dec))
|
|
165
169
|
.filter(x => !!x.ident);
|
|
166
170
|
}
|
|
@@ -177,7 +181,7 @@ export class TransformerState implements State {
|
|
|
177
181
|
* @param stmt
|
|
178
182
|
* @param before
|
|
179
183
|
*/
|
|
180
|
-
addStatement(stmt: ts.Statement, before?: ts.Node) {
|
|
184
|
+
addStatement(stmt: ts.Statement, before?: ts.Node): void {
|
|
181
185
|
const stmts = this.source.statements.slice(0);
|
|
182
186
|
let idx = stmts.length;
|
|
183
187
|
let n = before;
|
|
@@ -187,8 +191,10 @@ export class TransformerState implements State {
|
|
|
187
191
|
while (n && !ts.isSourceFile(n.parent)) {
|
|
188
192
|
n = n.parent;
|
|
189
193
|
}
|
|
190
|
-
|
|
191
|
-
|
|
194
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
195
|
+
const nStmt: ts.Statement = n as ts.Statement;
|
|
196
|
+
if (n && ts.isSourceFile(n.parent) && stmts.indexOf(nStmt) >= 0) {
|
|
197
|
+
idx = stmts.indexOf(nStmt) - 1;
|
|
192
198
|
}
|
|
193
199
|
if (!this.added.has(idx)) {
|
|
194
200
|
this.added.set(idx, []);
|
|
@@ -199,7 +205,7 @@ export class TransformerState implements State {
|
|
|
199
205
|
/**
|
|
200
206
|
* Finalize the source file for emission
|
|
201
207
|
*/
|
|
202
|
-
finalize(ret: ts.SourceFile) {
|
|
208
|
+
finalize(ret: ts.SourceFile): ts.SourceFile {
|
|
203
209
|
ret = this.#imports.finalize(ret);
|
|
204
210
|
return ret;
|
|
205
211
|
}
|
|
@@ -207,9 +213,9 @@ export class TransformerState implements State {
|
|
|
207
213
|
/**
|
|
208
214
|
* Get Filename as ᚕsrc
|
|
209
215
|
*/
|
|
210
|
-
getFilenameAsSrc() {
|
|
216
|
+
getFilenameAsSrc(): ts.CallExpression {
|
|
211
217
|
const ident = this.factory.createIdentifier('ᚕsrc');
|
|
212
|
-
ident.getSourceFile = () => this.source;
|
|
218
|
+
ident.getSourceFile = (): ts.SourceFile => this.source;
|
|
213
219
|
return this.factory.createCallExpression(ident, [], [this.createIdentifier('__filename')]);
|
|
214
220
|
}
|
|
215
221
|
|
|
@@ -223,20 +229,21 @@ export class TransformerState implements State {
|
|
|
223
229
|
fromLiteral(val: unknown[]): ts.ArrayLiteralExpression;
|
|
224
230
|
fromLiteral(val: string | boolean | number): ts.LiteralExpression;
|
|
225
231
|
fromLiteral(val: unknown): ts.Node {
|
|
232
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
226
233
|
return LiteralUtil.fromLiteral(this.factory, val as object);
|
|
227
234
|
}
|
|
228
235
|
|
|
229
236
|
/**
|
|
230
237
|
* Extend
|
|
231
238
|
*/
|
|
232
|
-
extendObjectLiteral(src: object | ts.Expression, ...rest: (object | ts.Expression)[]) {
|
|
239
|
+
extendObjectLiteral(src: object | ts.Expression, ...rest: (object | ts.Expression)[]): ts.ObjectLiteralExpression {
|
|
233
240
|
return LiteralUtil.extendObjectLiteral(this.factory, src, ...rest);
|
|
234
241
|
}
|
|
235
242
|
|
|
236
243
|
/**
|
|
237
244
|
* Create property access
|
|
238
245
|
*/
|
|
239
|
-
createAccess(first: string | ts.Expression, second: string | ts.Identifier, ...items: (string | ts.Identifier)[]) {
|
|
246
|
+
createAccess(first: string | ts.Expression, second: string | ts.Identifier, ...items: (string | ts.Identifier)[]): ts.PropertyAccessExpression {
|
|
240
247
|
return CoreUtil.createAccess(this.factory, first, second, ...items);
|
|
241
248
|
}
|
|
242
249
|
|
|
@@ -248,10 +255,10 @@ export class TransformerState implements State {
|
|
|
248
255
|
}
|
|
249
256
|
|
|
250
257
|
/**
|
|
251
|
-
*
|
|
258
|
+
* Create identifier from node or text
|
|
252
259
|
* @param name
|
|
253
260
|
*/
|
|
254
|
-
createIdentifier(name: string | { getText(): string }) {
|
|
261
|
+
createIdentifier(name: string | { getText(): string }): ts.Identifier {
|
|
255
262
|
return this.factory.createIdentifier(typeof name === 'string' ? name : name.getText());
|
|
256
263
|
}
|
|
257
264
|
|
|
@@ -262,7 +269,7 @@ export class TransformerState implements State {
|
|
|
262
269
|
* @param name
|
|
263
270
|
* @param module
|
|
264
271
|
*/
|
|
265
|
-
findDecorator(cls: Transformer, node: ts.Node, name: string, module?: string) {
|
|
272
|
+
findDecorator(cls: Transformer, node: ts.Node, name: string, module?: string): ts.Decorator | undefined {
|
|
266
273
|
const target = `${cls[TransformerId]}/${name}`;
|
|
267
274
|
return this.getDecoratorList(node)
|
|
268
275
|
.find(x => x.targets?.includes(target) && (!module || x.name === name && x.module === module))?.dec;
|
|
@@ -273,7 +280,7 @@ export class TransformerState implements State {
|
|
|
273
280
|
* @param node
|
|
274
281
|
* @param type
|
|
275
282
|
*/
|
|
276
|
-
generateUniqueIdentifier(node: ts.Node, type: AnyType) {
|
|
283
|
+
generateUniqueIdentifier(node: ts.Node, type: AnyType): string {
|
|
277
284
|
let unique: string;
|
|
278
285
|
try {
|
|
279
286
|
// Tie to source location if possible
|
|
@@ -295,16 +302,16 @@ export class TransformerState implements State {
|
|
|
295
302
|
}
|
|
296
303
|
|
|
297
304
|
/**
|
|
298
|
-
* Register synthetic
|
|
305
|
+
* Register synthetic identifier
|
|
299
306
|
*/
|
|
300
|
-
createSyntheticIdentifier(id: string) {
|
|
307
|
+
createSyntheticIdentifier(id: string): [identifier: ts.Identifier, exists: boolean] {
|
|
301
308
|
id = `${id}${TransformerState.SYNTHETIC_EXT}`;
|
|
302
309
|
let exists = true;
|
|
303
310
|
if (!this.#syntheticIdentifiers.has(id)) {
|
|
304
311
|
this.#syntheticIdentifiers.set(id, this.factory.createIdentifier(id));
|
|
305
312
|
exists = false;
|
|
306
313
|
}
|
|
307
|
-
return [this.#syntheticIdentifiers.get(id)
|
|
314
|
+
return [this.#syntheticIdentifiers.get(id)!, exists];
|
|
308
315
|
}
|
|
309
316
|
|
|
310
317
|
/**
|
|
@@ -315,8 +322,8 @@ export class TransformerState implements State {
|
|
|
315
322
|
findMethodByName(cls: ts.ClassLikeDeclaration | ts.Type, method: string): ts.MethodDeclaration | undefined {
|
|
316
323
|
if ('getSourceFile' in cls) {
|
|
317
324
|
return cls.members.find(
|
|
318
|
-
m => ts.isMethodDeclaration(m) && ts.isIdentifier(m.name) && m.name.escapedText === method
|
|
319
|
-
)
|
|
325
|
+
(m): m is ts.MethodDeclaration => ts.isMethodDeclaration(m) && ts.isIdentifier(m.name) && m.name.escapedText === method
|
|
326
|
+
);
|
|
320
327
|
} else {
|
|
321
328
|
const props = this.getResolver().getPropertiesOfType(cls);
|
|
322
329
|
for (const prop of props) {
|
package/src/util/core.ts
CHANGED
|
@@ -8,6 +8,7 @@ export class CoreUtil {
|
|
|
8
8
|
* See if inbound node has an original property
|
|
9
9
|
*/
|
|
10
10
|
static hasOriginal(o: ts.Node): o is (ts.Node & { original: ts.Node }) {
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
11
12
|
return 'original' in o && !!(o as { original?: ts.Node }).original;
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -15,6 +16,7 @@ export class CoreUtil {
|
|
|
15
16
|
* See if type has target
|
|
16
17
|
*/
|
|
17
18
|
static hasTarget(o: ts.Type): o is (ts.Type & { target: ts.Type }) {
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
18
20
|
return 'target' in o && !!(o as { target?: ts.Type }).target;
|
|
19
21
|
}
|
|
20
22
|
|
|
@@ -22,7 +24,7 @@ export class CoreUtil {
|
|
|
22
24
|
* Get first line of method body
|
|
23
25
|
* @param m
|
|
24
26
|
*/
|
|
25
|
-
static getRangeOf<T extends ts.Node>(source: ts.SourceFile, o: T | undefined) {
|
|
27
|
+
static getRangeOf<T extends ts.Node>(source: ts.SourceFile, o: T | undefined): { start: number, end: number } | undefined {
|
|
26
28
|
if (o) {
|
|
27
29
|
const start = ts.getLineAndCharacterOfPosition(source, o.getStart(source));
|
|
28
30
|
const end = ts.getLineAndCharacterOfPosition(source, o.getEnd());
|
|
@@ -38,6 +40,7 @@ export class CoreUtil {
|
|
|
38
40
|
*/
|
|
39
41
|
static getArgument<T extends ts.Expression = ts.Expression>(node: ts.CallExpression | undefined, position = 0): T | undefined {
|
|
40
42
|
if (node && node!.arguments && node!.arguments.length >= position + 1) {
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
41
44
|
return node.arguments[position] as T;
|
|
42
45
|
}
|
|
43
46
|
return;
|
|
@@ -57,11 +60,13 @@ export class CoreUtil {
|
|
|
57
60
|
/**
|
|
58
61
|
* Get `ts.Symbol` from a `ts.Type`
|
|
59
62
|
*/
|
|
60
|
-
static getSymbol(type: ts.Type | ts.Symbol) {
|
|
63
|
+
static getSymbol(type: ts.Type | ts.Symbol | ts.TypeReference): ts.Symbol {
|
|
61
64
|
if ('valueDeclaration' in type || 'escapedName' in type) {
|
|
62
65
|
return type;
|
|
66
|
+
} else if ('aliasSymbol' in type && type.aliasSymbol) {
|
|
67
|
+
return type.aliasSymbol;
|
|
63
68
|
} else {
|
|
64
|
-
return
|
|
69
|
+
return type.symbol;
|
|
65
70
|
}
|
|
66
71
|
}
|
|
67
72
|
|
|
@@ -70,7 +75,7 @@ export class CoreUtil {
|
|
|
70
75
|
* @param src
|
|
71
76
|
* @param statements
|
|
72
77
|
*/
|
|
73
|
-
static updateSource(factory: ts.NodeFactory, src: ts.SourceFile, statements: ts.NodeArray<ts.Statement> | ts.Statement[]) {
|
|
78
|
+
static updateSource(factory: ts.NodeFactory, src: ts.SourceFile, statements: ts.NodeArray<ts.Statement> | ts.Statement[]): ts.SourceFile {
|
|
74
79
|
return factory.updateSourceFile(
|
|
75
80
|
src, statements, src.isDeclarationFile, src.referencedFiles, src.typeReferenceDirectives, src.hasNoDefaultLib
|
|
76
81
|
);
|
|
@@ -79,7 +84,12 @@ export class CoreUtil {
|
|
|
79
84
|
/**
|
|
80
85
|
* Create property access
|
|
81
86
|
*/
|
|
82
|
-
static createAccess(
|
|
87
|
+
static createAccess(
|
|
88
|
+
factory: ts.NodeFactory,
|
|
89
|
+
first: string | ts.Expression,
|
|
90
|
+
second: string | ts.Identifier,
|
|
91
|
+
...items: (string | ts.Identifier)[]
|
|
92
|
+
): ts.PropertyAccessExpression {
|
|
83
93
|
if (typeof first === 'string') {
|
|
84
94
|
first = factory.createIdentifier(first);
|
|
85
95
|
}
|
|
@@ -92,12 +102,12 @@ export class CoreUtil {
|
|
|
92
102
|
/**
|
|
93
103
|
* Create a decorator with a given name, and arguments
|
|
94
104
|
*/
|
|
95
|
-
static createDecorator(factory: ts.NodeFactory, name: ts.Expression, ...contents: (ts.Expression | undefined)[]) {
|
|
105
|
+
static createDecorator(factory: ts.NodeFactory, name: ts.Expression, ...contents: (ts.Expression | undefined)[]): ts.Decorator {
|
|
96
106
|
return factory.createDecorator(
|
|
97
107
|
factory.createCallExpression(
|
|
98
108
|
name,
|
|
99
109
|
undefined,
|
|
100
|
-
contents.filter(x
|
|
110
|
+
contents.filter((x?: ts.Expression): x is ts.Expression => !!x)
|
|
101
111
|
)
|
|
102
112
|
);
|
|
103
113
|
}
|
|
@@ -105,7 +115,7 @@ export class CoreUtil {
|
|
|
105
115
|
/**
|
|
106
116
|
* Is declaration abstract?
|
|
107
117
|
*/
|
|
108
|
-
static isAbstract(node: ts.Declaration) {
|
|
118
|
+
static isAbstract(node: ts.Declaration): boolean {
|
|
109
119
|
// eslint-disable-next-line no-bitwise
|
|
110
120
|
return !!(ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Abstract);
|
|
111
121
|
}
|
package/src/util/declaration.ts
CHANGED
|
@@ -10,7 +10,7 @@ export class DeclarationUtil {
|
|
|
10
10
|
* Searches upward from the node until it finds the variable declaration list,
|
|
11
11
|
* and then checks the toString for `const `
|
|
12
12
|
*/
|
|
13
|
-
static isConstantDeclaration(node: ts.Node) {
|
|
13
|
+
static isConstantDeclaration(node: ts.Node): boolean {
|
|
14
14
|
let s: ts.Node = node;
|
|
15
15
|
while (s && !ts.isVariableDeclarationList(s)) {
|
|
16
16
|
s = s.parent;
|
|
@@ -21,7 +21,7 @@ export class DeclarationUtil {
|
|
|
21
21
|
/**
|
|
22
22
|
* See if a declaration is public
|
|
23
23
|
*/
|
|
24
|
-
static isPublic(node: ts.Declaration) {
|
|
24
|
+
static isPublic(node: ts.Declaration): boolean {
|
|
25
25
|
// eslint-disable-next-line no-bitwise
|
|
26
26
|
return !(ts.getCombinedModifierFlags(node) & ts.ModifierFlags.NonPublicAccessibilityModifier);
|
|
27
27
|
}
|
|
@@ -57,8 +57,8 @@ export class DeclarationUtil {
|
|
|
57
57
|
* Resolve the `ts.ObjectFlags`
|
|
58
58
|
*/
|
|
59
59
|
static getObjectFlags(type: ts.Type): ts.ObjectFlags {
|
|
60
|
-
// @
|
|
61
|
-
return ts.getObjectFlags(type);
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
61
|
+
return (ts as unknown as { getObjectFlags(t: ts.Type): ts.ObjectFlags }).getObjectFlags(type);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
@@ -67,7 +67,9 @@ export class DeclarationUtil {
|
|
|
67
67
|
* @param node
|
|
68
68
|
* @returns
|
|
69
69
|
*/
|
|
70
|
-
static getAccessorPair(
|
|
70
|
+
static getAccessorPair(
|
|
71
|
+
node: ts.GetAccessorDeclaration | ts.SetAccessorDeclaration
|
|
72
|
+
): { getter?: ts.GetAccessorDeclaration, setter?: ts.SetAccessorDeclaration } {
|
|
71
73
|
const acc = { getter: ts.isGetAccessorDeclaration(node) ? node : undefined, setter: ts.isSetAccessorDeclaration(node) ? node : undefined };
|
|
72
74
|
if (ts.isClassDeclaration(node.parent)) {
|
|
73
75
|
for (const el of node.parent.members) {
|
package/src/util/decorator.ts
CHANGED
|
@@ -10,10 +10,12 @@ export class DecoratorUtil {
|
|
|
10
10
|
* Get identifier for a decorator
|
|
11
11
|
*/
|
|
12
12
|
static getDecoratorIdent(d: ts.Decorator): ts.Identifier {
|
|
13
|
-
if (ts.isCallExpression(d.expression)) {
|
|
14
|
-
return d.expression.expression
|
|
13
|
+
if (ts.isCallExpression(d.expression) && ts.isIdentifier(d.expression.expression)) {
|
|
14
|
+
return d.expression.expression;
|
|
15
15
|
} else if (ts.isIdentifier(d.expression)) {
|
|
16
16
|
return d.expression;
|
|
17
|
+
} else if (ts.isCallExpression(d.expression) && ts.isPropertyAccessExpression(d.expression.expression) && ts.isIdentifier(d.expression.expression.expression)) {
|
|
18
|
+
return d.expression.expression.expression;
|
|
17
19
|
} else {
|
|
18
20
|
throw new Error('No Identifier');
|
|
19
21
|
}
|
|
@@ -22,7 +24,7 @@ export class DecoratorUtil {
|
|
|
22
24
|
/**
|
|
23
25
|
* Replace or add a decorator to a list of decorators
|
|
24
26
|
*/
|
|
25
|
-
static spliceDecorators(node: ts.Node, target: ts.Decorator | undefined, replacements: ts.Decorator[], idx = -1) {
|
|
27
|
+
static spliceDecorators(node: ts.Node, target: ts.Decorator | undefined, replacements: ts.Decorator[], idx = -1): ts.Decorator[] {
|
|
26
28
|
if (idx < 0 && target) {
|
|
27
29
|
idx = node.decorators?.indexOf(target) ?? -1;
|
|
28
30
|
}
|
|
@@ -46,6 +48,7 @@ export class DecoratorUtil {
|
|
|
46
48
|
* Find the primary argument of a call expression, or decorator.
|
|
47
49
|
*/
|
|
48
50
|
static getArguments<T extends ts.Expression = ts.Expression>(node: ts.Decorator | undefined): T[] | undefined {
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
49
52
|
return node && ts.isCallExpression(node.expression) ? [...node.expression.arguments] as T[] : undefined;
|
|
50
53
|
}
|
|
51
54
|
}
|
package/src/util/doc.ts
CHANGED
|
@@ -18,14 +18,14 @@ export class DocUtil {
|
|
|
18
18
|
/**
|
|
19
19
|
* Read doc comment for node
|
|
20
20
|
*/
|
|
21
|
-
static getDocComment(o: ts.JSDoc | ts.JSDocTag, def?: string) {
|
|
21
|
+
static getDocComment(o: ts.JSDoc | ts.JSDocTag, def?: string): string | undefined {
|
|
22
22
|
return (typeof o.comment === 'string' ? o.comment : undefined) ?? def;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Read JS Docs from a `ts.Declaration`
|
|
27
27
|
*/
|
|
28
|
-
static describeDocs(node: ts.Declaration | ts.Type) {
|
|
28
|
+
static describeDocs(node: ts.Declaration | ts.Type): DeclDocumentation {
|
|
29
29
|
if (node && !('getSourceFile' in node)) {
|
|
30
30
|
node = DeclarationUtil.getPrimaryDeclarationNode(node);
|
|
31
31
|
}
|
|
@@ -38,6 +38,7 @@ export class DocUtil {
|
|
|
38
38
|
if (node) {
|
|
39
39
|
const tags = ts.getJSDocTags(node);
|
|
40
40
|
while (!this.hasJSDoc(node) && CoreUtil.hasOriginal(node)) {
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
41
42
|
node = node.original as ts.Declaration;
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -80,7 +81,7 @@ export class DocUtil {
|
|
|
80
81
|
* Read augments information
|
|
81
82
|
* @param type
|
|
82
83
|
*/
|
|
83
|
-
static readAugments(type: ts.Type | ts.Symbol) {
|
|
84
|
+
static readAugments(type: ts.Type | ts.Symbol): string[] {
|
|
84
85
|
return this.readDocTag(type, 'augments').map(x => x.replace(/^.*?([^` ]+).*?$/, (_, b) => b));
|
|
85
86
|
}
|
|
86
87
|
}
|
package/src/util/import.ts
CHANGED
|
@@ -12,7 +12,7 @@ export class ImportUtil {
|
|
|
12
12
|
/*
|
|
13
13
|
* useful for handling failed imports, but still transpiling
|
|
14
14
|
*/
|
|
15
|
-
static optionalResolve(file: string, base?: string) {
|
|
15
|
+
static optionalResolve(file: string, base?: string): string {
|
|
16
16
|
file = base ? pathResolve(base, file) : file;
|
|
17
17
|
try {
|
|
18
18
|
return require.resolve(file);
|
|
@@ -24,7 +24,7 @@ export class ImportUtil {
|
|
|
24
24
|
/**
|
|
25
25
|
* Collect all imports for a source file, as a hash map
|
|
26
26
|
*/
|
|
27
|
-
static collectImports(src: ts.SourceFile) {
|
|
27
|
+
static collectImports(src: ts.SourceFile): Map<string, Import> {
|
|
28
28
|
const pth = require.resolve(src.fileName);
|
|
29
29
|
const base = PathUtil.toUnix(pth);
|
|
30
30
|
|
package/src/util/literal.ts
CHANGED
|
@@ -25,7 +25,9 @@ export class LiteralUtil {
|
|
|
25
25
|
static fromLiteral(factory: ts.NodeFactory, val: unknown[]): ts.ArrayLiteralExpression;
|
|
26
26
|
static fromLiteral(factory: ts.NodeFactory, val: string | boolean | number): ts.LiteralExpression;
|
|
27
27
|
static fromLiteral(factory: ts.NodeFactory, val: unknown): ts.Node {
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
28
29
|
if (val && (val as ts.Expression).kind) { // If already a node
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
29
31
|
return val as ts.Node;
|
|
30
32
|
} else if (Array.isArray(val)) {
|
|
31
33
|
val = factory.createArrayLiteralExpression(val.map(v => this.fromLiteral(factory, v)));
|
|
@@ -40,11 +42,13 @@ export class LiteralUtil {
|
|
|
40
42
|
} else if (typeof val === 'boolean') {
|
|
41
43
|
val = val ? factory.createTrue() : factory.createFalse();
|
|
42
44
|
} else if (val === String || val === Number || val === Boolean || val === Date || val === RegExp) {
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
43
46
|
val = factory.createIdentifier((val as Function).name);
|
|
44
47
|
} else {
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
45
49
|
const ov = val as object;
|
|
46
50
|
const pairs: ts.PropertyAssignment[] = [];
|
|
47
|
-
for (const k of Object.keys(ov)
|
|
51
|
+
for (const k of Object.keys(ov)) {
|
|
48
52
|
if (ov[k] !== undefined) {
|
|
49
53
|
pairs.push(
|
|
50
54
|
factory.createPropertyAssignment(k, this.fromLiteral(factory, ov[k]))
|
|
@@ -53,13 +57,22 @@ export class LiteralUtil {
|
|
|
53
57
|
}
|
|
54
58
|
return factory.createObjectLiteralExpression(pairs);
|
|
55
59
|
}
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
56
61
|
return val as ts.Expression;
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
/**
|
|
60
65
|
* Convert a `ts.Node` to a JS literal
|
|
61
66
|
*/
|
|
62
|
-
static toLiteral(val: ts.
|
|
67
|
+
static toLiteral(val: ts.NullLiteral, strict?: boolean): null;
|
|
68
|
+
static toLiteral(val: ts.NumericLiteral, strict?: boolean): number;
|
|
69
|
+
static toLiteral(val: ts.StringLiteral, strict?: boolean): string;
|
|
70
|
+
static toLiteral(val: ts.BooleanLiteral, strict?: boolean): boolean;
|
|
71
|
+
static toLiteral(val: ts.ObjectLiteralExpression, strict?: boolean): object;
|
|
72
|
+
static toLiteral(val: ts.ArrayLiteralExpression, strict?: boolean): unknown[];
|
|
73
|
+
static toLiteral(val: undefined, strict?: boolean): undefined;
|
|
74
|
+
static toLiteral(val: ts.Node, strict?: boolean): unknown;
|
|
75
|
+
static toLiteral(val?: ts.Node, strict = true): unknown {
|
|
63
76
|
if (!val) {
|
|
64
77
|
throw new Error('Val is not defined');
|
|
65
78
|
} else if (ts.isArrayLiteralExpression(val)) {
|
|
@@ -102,7 +115,7 @@ export class LiteralUtil {
|
|
|
102
115
|
/**
|
|
103
116
|
* Extend object literal, whether JSON or ts.ObjectLiteralExpression
|
|
104
117
|
*/
|
|
105
|
-
static extendObjectLiteral(factory: ts.NodeFactory, src: object | ts.Expression, ...rest: (object | ts.Expression)[]) {
|
|
118
|
+
static extendObjectLiteral(factory: ts.NodeFactory, src: object | ts.Expression, ...rest: (object | ts.Expression)[]): ts.ObjectLiteralExpression {
|
|
106
119
|
let ret = this.fromLiteral(factory, src);
|
|
107
120
|
if (rest.find(x => !!x)) {
|
|
108
121
|
ret = factory.createObjectLiteralExpression([
|
|
@@ -116,7 +129,7 @@ export class LiteralUtil {
|
|
|
116
129
|
/**
|
|
117
130
|
* Get a value from the an object expression
|
|
118
131
|
*/
|
|
119
|
-
static getObjectValue(node: ts.Expression | undefined, key: string) {
|
|
132
|
+
static getObjectValue(node: ts.Expression | undefined, key: string): ts.Expression | undefined {
|
|
120
133
|
if (node && ts.isObjectLiteralExpression(node) && node.properties) {
|
|
121
134
|
for (const prop of node.properties) {
|
|
122
135
|
if (prop.name!.getText() === key) {
|
package/src/util/log.ts
CHANGED
|
@@ -14,7 +14,7 @@ export class LogUtil {
|
|
|
14
14
|
/**
|
|
15
15
|
* Clean up `ts.Node` contents for logging
|
|
16
16
|
*/
|
|
17
|
-
static collapseNodes(all: unknown[]) {
|
|
17
|
+
static collapseNodes(all: unknown[]): unknown[] {
|
|
18
18
|
return all.map(x => this.collapseNode(x));
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -35,15 +35,16 @@ export class LogUtil {
|
|
|
35
35
|
if (Array.isArray(x)) {
|
|
36
36
|
return x.map(v => this.collapseNode(v, cache));
|
|
37
37
|
} else {
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
38
39
|
const ox = x as object;
|
|
39
40
|
const out: Record<string, unknown> = {};
|
|
40
|
-
for (const key of Object.keys(ox)
|
|
41
|
+
for (const key of Object.keys(ox)) {
|
|
41
42
|
if (Util.isFunction(ox[key]) || exclude.has(key) || ox[key] === undefined) {
|
|
42
43
|
continue;
|
|
43
44
|
}
|
|
44
45
|
try {
|
|
45
46
|
out[key] = this.collapseNode(ox[key], cache);
|
|
46
|
-
} catch
|
|
47
|
+
} catch {
|
|
47
48
|
return undefined;
|
|
48
49
|
}
|
|
49
50
|
}
|
package/src/visitor.ts
CHANGED
|
@@ -54,7 +54,7 @@ export class VisitorFactory<S extends State = State> {
|
|
|
54
54
|
/**
|
|
55
55
|
* Initialize internal mapping given a list of transformers
|
|
56
56
|
*/
|
|
57
|
-
#init(transformers: NodeTransformer<S, TransformerType, ts.Node>[]) {
|
|
57
|
+
#init(transformers: NodeTransformer<S, TransformerType, ts.Node>[]): void {
|
|
58
58
|
for (const trn of transformers) {
|
|
59
59
|
if (!this.#transformers.has(trn.type)) {
|
|
60
60
|
this.#transformers.set(trn.type, {});
|
|
@@ -116,10 +116,13 @@ export class VisitorFactory<S extends State = State> {
|
|
|
116
116
|
ret = CoreUtil.updateSource(context.factory, ret, statements);
|
|
117
117
|
}
|
|
118
118
|
return state.finalize(ret);
|
|
119
|
-
} catch (
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
} catch (err) {
|
|
120
|
+
if (!(err instanceof Error)) {
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
console.error('Failed transforming', { error: err, file: file.fileName });
|
|
124
|
+
const out = new Error(`Failed transforming: ${file.fileName}: ${err.message}`);
|
|
125
|
+
out.stack = err.stack;
|
|
123
126
|
throw out;
|
|
124
127
|
} finally {
|
|
125
128
|
ConsoleManager.clear(); // Reset logging
|
|
@@ -130,12 +133,13 @@ export class VisitorFactory<S extends State = State> {
|
|
|
130
133
|
/**
|
|
131
134
|
* Handle transformer that target both ascent and descent
|
|
132
135
|
*/
|
|
133
|
-
executePhaseAlways<T extends ts.Node>(state: S, set: TransformerSet<S>, phase: TransformPhase, node: T) {
|
|
136
|
+
executePhaseAlways<T extends ts.Node>(state: S, set: TransformerSet<S>, phase: TransformPhase, node: T): T | undefined {
|
|
134
137
|
if (!set[phase]?.size) {
|
|
135
138
|
return;
|
|
136
139
|
}
|
|
137
140
|
|
|
138
141
|
for (const all of set[phase]!.get('ALL') ?? []) {
|
|
142
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
139
143
|
node = (all[phase]!(state, node) as T) ?? node;
|
|
140
144
|
}
|
|
141
145
|
return node;
|
|
@@ -144,7 +148,7 @@ export class VisitorFactory<S extends State = State> {
|
|
|
144
148
|
/**
|
|
145
149
|
* Handle a single phase of transformation
|
|
146
150
|
*/
|
|
147
|
-
executePhase<T extends ts.Node>(state: S, set: TransformerSet<S>, phase: TransformPhase, node: T) {
|
|
151
|
+
executePhase<T extends ts.Node>(state: S, set: TransformerSet<S>, phase: TransformPhase, node: T): T | undefined {
|
|
148
152
|
if (!set[phase]?.size) {
|
|
149
153
|
return;
|
|
150
154
|
}
|
|
@@ -169,6 +173,7 @@ export class VisitorFactory<S extends State = State> {
|
|
|
169
173
|
|
|
170
174
|
// For all matching handlers, execute
|
|
171
175
|
for (const item of values) {
|
|
176
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
172
177
|
node = (item[phase]!(state, node, dec) as T) ?? node;
|
|
173
178
|
}
|
|
174
179
|
}
|
package/test-support/util.ts
CHANGED
|
@@ -13,14 +13,14 @@ export class TransformerTestUtil {
|
|
|
13
13
|
/**
|
|
14
14
|
* Compile a single file from a folder
|
|
15
15
|
*/
|
|
16
|
-
static async compile(folder: string, file?: string) {
|
|
16
|
+
static async compile(folder: string, file?: string): Promise<string> {
|
|
17
17
|
|
|
18
18
|
const tsconfigObj = await import('@travetto/boot/tsconfig.trv.json');
|
|
19
19
|
|
|
20
20
|
const prog = ts.createProgram({
|
|
21
21
|
options: ts.convertCompilerOptionsFromJson(tsconfigObj, '').options,
|
|
22
22
|
rootNames: (await ScanFs.scanDir({ testFile: f => f.startsWith('src/') && f.endsWith('.ts') }, folder))
|
|
23
|
-
.filter(x => x.stats
|
|
23
|
+
.filter(x => x.stats?.isFile())
|
|
24
24
|
.filter(x => !file || x.file.endsWith(file))
|
|
25
25
|
.map(x => x.file),
|
|
26
26
|
});
|
|
@@ -30,7 +30,7 @@ export class TransformerTestUtil {
|
|
|
30
30
|
|
|
31
31
|
const transformers =
|
|
32
32
|
(await ScanFs.scanDir({ testFile: f => f.startsWith('support/transformer') }, folder))
|
|
33
|
-
.filter(x => x.stats
|
|
33
|
+
.filter(x => x.stats?.isFile())
|
|
34
34
|
.map(x => import(x.file).then(getAllTransformers));
|
|
35
35
|
|
|
36
36
|
const visitor = new VisitorFactory(
|