@travetto/transformer 3.0.0-rc.2 → 3.0.0-rc.21
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 +7 -12
- package/__index__.ts +15 -0
- package/package.json +13 -6
- package/src/importer.ts +120 -35
- package/src/manager.ts +74 -0
- package/src/register.ts +40 -11
- package/src/resolver/builder.ts +61 -53
- package/src/resolver/cache.ts +2 -2
- package/src/resolver/coerce.ts +103 -0
- package/src/resolver/service.ts +48 -9
- package/src/resolver/types.ts +7 -4
- package/src/state.ts +85 -71
- package/src/types/shared.ts +1 -1
- package/src/types/visitor.ts +7 -5
- package/src/util/core.ts +8 -6
- package/src/util/declaration.ts +3 -3
- package/src/util/decorator.ts +1 -1
- package/src/util/doc.ts +1 -1
- package/src/util/import.ts +17 -10
- package/src/util/literal.ts +3 -2
- package/src/util/log.ts +4 -5
- package/src/util/system.ts +31 -0
- package/src/visitor.ts +13 -33
- package/index.ts +0 -7
- package/src/util/index.ts +0 -6
- package/test-support/util.ts +0 -57
package/src/state.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ts from 'typescript';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { ModuleUtil } from '@travetto/boot/src/internal/module-util';
|
|
5
|
-
import { Util } from '@travetto/base';
|
|
3
|
+
import { ManifestIndex, path } from '@travetto/manifest';
|
|
6
4
|
|
|
7
5
|
import { ExternalType, AnyType } from './resolver/types';
|
|
8
|
-
import { State, DecoratorMeta, Transformer,
|
|
9
|
-
import {
|
|
6
|
+
import { State, DecoratorMeta, Transformer, ModuleNameⲐ } from './types/visitor';
|
|
7
|
+
import { SimpleResolver } from './resolver/service';
|
|
10
8
|
import { ImportManager } from './importer';
|
|
9
|
+
import { Import } from './types/shared';
|
|
10
|
+
|
|
11
11
|
import { DocUtil } from './util/doc';
|
|
12
12
|
import { DecoratorUtil } from './util/decorator';
|
|
13
13
|
import { DeclarationUtil } from './util/declaration';
|
|
14
|
-
import { CoreUtil
|
|
15
|
-
import {
|
|
14
|
+
import { CoreUtil } from './util/core';
|
|
15
|
+
import { LiteralUtil } from './util/literal';
|
|
16
|
+
import { SystemUtil } from './util/system';
|
|
16
17
|
|
|
17
18
|
function hasOriginal(n: unknown): n is { original: ts.Node } {
|
|
18
19
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
@@ -28,28 +29,25 @@ function hasEscapedName(n: unknown): n is { name: { escapedText: string } } {
|
|
|
28
29
|
* Transformer runtime state
|
|
29
30
|
*/
|
|
30
31
|
export class TransformerState implements State {
|
|
31
|
-
static SYNTHETIC_EXT = '
|
|
32
|
+
static SYNTHETIC_EXT = 'Ⲑsyn';
|
|
32
33
|
|
|
33
|
-
#resolver:
|
|
34
|
+
#resolver: SimpleResolver;
|
|
34
35
|
#imports: ImportManager;
|
|
36
|
+
#fileIdent: ts.Identifier;
|
|
37
|
+
#manifestIndex: ManifestIndex;
|
|
35
38
|
#syntheticIdentifiers = new Map<string, ts.Identifier>();
|
|
36
39
|
#decorators = new Map<string, ts.PropertyAccessExpression>();
|
|
37
|
-
|
|
38
40
|
added = new Map<number, ts.Statement[]>();
|
|
39
|
-
|
|
41
|
+
importName: string;
|
|
42
|
+
file: string;
|
|
40
43
|
|
|
41
|
-
constructor(public source: ts.SourceFile, public factory: ts.NodeFactory, checker: ts.TypeChecker) {
|
|
42
|
-
this.#
|
|
43
|
-
this.#resolver = new
|
|
44
|
-
this
|
|
45
|
-
|
|
44
|
+
constructor(public source: ts.SourceFile, public factory: ts.NodeFactory, checker: ts.TypeChecker, manifestIndex: ManifestIndex) {
|
|
45
|
+
this.#manifestIndex = manifestIndex;
|
|
46
|
+
this.#resolver = new SimpleResolver(checker, manifestIndex);
|
|
47
|
+
this.#imports = new ImportManager(source, factory, this.#resolver);
|
|
48
|
+
this.file = path.toPosix(this.source.fileName);
|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
* Allow access to resolver
|
|
49
|
-
* @private
|
|
50
|
-
*/
|
|
51
|
-
getResolver(): TypeResolver {
|
|
52
|
-
return this.#resolver;
|
|
50
|
+
this.importName = this.#resolver.getFileImportName(this.file, true);
|
|
53
51
|
}
|
|
54
52
|
|
|
55
53
|
/**
|
|
@@ -62,15 +60,15 @@ export class TransformerState implements State {
|
|
|
62
60
|
/**
|
|
63
61
|
* Import a given file
|
|
64
62
|
*/
|
|
65
|
-
importFile(file: string): Import {
|
|
66
|
-
return this.#imports.importFile(file);
|
|
63
|
+
importFile(file: string, name?: string): Import {
|
|
64
|
+
return this.#imports.importFile(file, name);
|
|
67
65
|
}
|
|
68
66
|
|
|
69
67
|
/**
|
|
70
68
|
* Resolve an `AnyType` from a `ts.Type` or `ts.Node`
|
|
71
69
|
*/
|
|
72
70
|
resolveType(node: ts.Type | ts.Node): AnyType {
|
|
73
|
-
const resolved = this.#resolver.resolveType(node);
|
|
71
|
+
const resolved = this.#resolver.resolveType(node, this.importName);
|
|
74
72
|
this.#imports.importFromResolved(resolved);
|
|
75
73
|
return resolved;
|
|
76
74
|
}
|
|
@@ -81,7 +79,9 @@ export class TransformerState implements State {
|
|
|
81
79
|
resolveExternalType(node: ts.Node): ExternalType {
|
|
82
80
|
const resolved = this.resolveType(node);
|
|
83
81
|
if (resolved.key !== 'external') {
|
|
84
|
-
|
|
82
|
+
const file = node.getSourceFile().fileName;
|
|
83
|
+
const src = this.#resolver.getFileImportName(file);
|
|
84
|
+
throw new Error(`Unable to import non-external type: ${node.getText()} ${resolved.key}: ${src}`);
|
|
85
85
|
}
|
|
86
86
|
return resolved;
|
|
87
87
|
}
|
|
@@ -141,23 +141,24 @@ export class TransformerState implements State {
|
|
|
141
141
|
/**
|
|
142
142
|
* Read a decorator's metadata
|
|
143
143
|
*/
|
|
144
|
-
getDecoratorMeta(dec: ts.Decorator): DecoratorMeta {
|
|
144
|
+
getDecoratorMeta(dec: ts.Decorator): DecoratorMeta | undefined {
|
|
145
145
|
const ident = DecoratorUtil.getDecoratorIdent(dec);
|
|
146
146
|
const decl = DeclarationUtil.getPrimaryDeclarationNode(
|
|
147
147
|
this.#resolver.getType(ident)
|
|
148
148
|
);
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
149
|
+
const src = decl?.getSourceFile().fileName;
|
|
150
|
+
const mod = src ? this.#resolver.getFileImportName(src, true) : undefined;
|
|
151
|
+
const file = this.#manifestIndex.getFromImport(mod ?? '')?.outputFile;
|
|
152
|
+
const targets = DocUtil.readAugments(this.#resolver.getType(ident));
|
|
153
|
+
const module = file ? mod : undefined;
|
|
154
|
+
const name = ident ?
|
|
155
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
156
|
+
ident.escapedText! as string :
|
|
157
|
+
undefined;
|
|
158
|
+
|
|
159
|
+
if (ident && name) {
|
|
160
|
+
return { dec, ident, file, module, targets, name };
|
|
161
|
+
}
|
|
161
162
|
}
|
|
162
163
|
|
|
163
164
|
/**
|
|
@@ -166,7 +167,7 @@ export class TransformerState implements State {
|
|
|
166
167
|
getDecoratorList(node: ts.Node): DecoratorMeta[] {
|
|
167
168
|
return ts.canHaveDecorators(node) ? (ts.getDecorators(node) ?? [])
|
|
168
169
|
.map(dec => this.getDecoratorMeta(dec))
|
|
169
|
-
.filter(x => !!x
|
|
170
|
+
.filter((x): x is DecoratorMeta => !!x) : [];
|
|
170
171
|
}
|
|
171
172
|
|
|
172
173
|
/**
|
|
@@ -181,25 +182,30 @@ export class TransformerState implements State {
|
|
|
181
182
|
* @param stmt
|
|
182
183
|
* @param before
|
|
183
184
|
*/
|
|
184
|
-
|
|
185
|
+
addStatements(added: ts.Statement[], before?: ts.Node | number): void {
|
|
185
186
|
const stmts = this.source.statements.slice(0);
|
|
186
|
-
let idx = stmts.length;
|
|
187
|
-
|
|
188
|
-
if (
|
|
189
|
-
n =
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
187
|
+
let idx = stmts.length + 1000;
|
|
188
|
+
|
|
189
|
+
if (before && typeof before !== 'number') {
|
|
190
|
+
let n = before;
|
|
191
|
+
if (hasOriginal(n)) {
|
|
192
|
+
n = n.original;
|
|
193
|
+
}
|
|
194
|
+
while (n && !ts.isSourceFile(n.parent) && n !== n.parent) {
|
|
195
|
+
n = n.parent;
|
|
196
|
+
}
|
|
197
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
198
|
+
const nStmt: ts.Statement = n as ts.Statement;
|
|
199
|
+
if (n && ts.isSourceFile(n.parent) && stmts.indexOf(nStmt) >= 0) {
|
|
200
|
+
idx = stmts.indexOf(nStmt) - 1;
|
|
201
|
+
}
|
|
202
|
+
} else if (before !== undefined) {
|
|
203
|
+
idx = before;
|
|
198
204
|
}
|
|
199
205
|
if (!this.added.has(idx)) {
|
|
200
206
|
this.added.set(idx, []);
|
|
201
207
|
}
|
|
202
|
-
this.added.get(idx)!.push(
|
|
208
|
+
this.added.get(idx)!.push(...added);
|
|
203
209
|
}
|
|
204
210
|
|
|
205
211
|
/**
|
|
@@ -210,15 +216,6 @@ export class TransformerState implements State {
|
|
|
210
216
|
return ret;
|
|
211
217
|
}
|
|
212
218
|
|
|
213
|
-
/**
|
|
214
|
-
* Get Filename as ᚕsrc
|
|
215
|
-
*/
|
|
216
|
-
getFilenameAsSrc(): ts.CallExpression {
|
|
217
|
-
const ident = this.factory.createIdentifier('ᚕsrc');
|
|
218
|
-
ident.getSourceFile = (): ts.SourceFile => this.source;
|
|
219
|
-
return this.factory.createCallExpression(ident, [], [this.createIdentifier('__filename')]);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
219
|
/**
|
|
223
220
|
* From literal
|
|
224
221
|
*/
|
|
@@ -243,7 +240,7 @@ export class TransformerState implements State {
|
|
|
243
240
|
/**
|
|
244
241
|
* Create property access
|
|
245
242
|
*/
|
|
246
|
-
createAccess(first: string | ts.Expression, second: string | ts.Identifier, ...items: (string | ts.Identifier)[]): ts.
|
|
243
|
+
createAccess(first: string | ts.Expression, second: string | ts.Identifier, ...items: (string | number | ts.Identifier)[]): ts.Expression {
|
|
247
244
|
return CoreUtil.createAccess(this.factory, first, second, ...items);
|
|
248
245
|
}
|
|
249
246
|
|
|
@@ -262,6 +259,22 @@ export class TransformerState implements State {
|
|
|
262
259
|
return this.factory.createIdentifier(typeof name === 'string' ? name : name.getText());
|
|
263
260
|
}
|
|
264
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Get filename identifier, regardless of module system
|
|
264
|
+
*/
|
|
265
|
+
getFilenameIdentifier(): ts.Expression {
|
|
266
|
+
if (this.#fileIdent === undefined) {
|
|
267
|
+
this.#fileIdent = this.createIdentifier('ᚕf');
|
|
268
|
+
const decl = this.factory.createVariableDeclaration(this.#fileIdent, undefined, undefined,
|
|
269
|
+
this.fromLiteral(this.#resolver.getFileImportName(this.source.fileName) ?? this.source.fileName)
|
|
270
|
+
);
|
|
271
|
+
this.addStatements([
|
|
272
|
+
this.factory.createVariableStatement([], this.factory.createVariableDeclarationList([decl]))
|
|
273
|
+
], -1);
|
|
274
|
+
}
|
|
275
|
+
return this.#fileIdent;
|
|
276
|
+
}
|
|
277
|
+
|
|
265
278
|
/**
|
|
266
279
|
* Find decorator, relative to registered key
|
|
267
280
|
* @param state
|
|
@@ -269,10 +282,11 @@ export class TransformerState implements State {
|
|
|
269
282
|
* @param name
|
|
270
283
|
* @param module
|
|
271
284
|
*/
|
|
272
|
-
findDecorator(
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
285
|
+
findDecorator(mod: string | Transformer, node: ts.Node, name: string, module?: string): ts.Decorator | undefined {
|
|
286
|
+
mod = typeof mod === 'string' ? mod : mod[ModuleNameⲐ]!;
|
|
287
|
+
const target = `${mod}:${name}`;
|
|
288
|
+
const list = this.getDecoratorList(node);
|
|
289
|
+
return list.find(x => x.targets?.includes(target) && (!module || x.name === name && x.module === module))?.dec;
|
|
276
290
|
}
|
|
277
291
|
|
|
278
292
|
/**
|
|
@@ -291,7 +305,7 @@ export class TransformerState implements State {
|
|
|
291
305
|
}
|
|
292
306
|
} catch {
|
|
293
307
|
// Determine type unique ident
|
|
294
|
-
unique =
|
|
308
|
+
unique = SystemUtil.uuid(type.name ? 5 : 10);
|
|
295
309
|
}
|
|
296
310
|
// Otherwise read name with uuid
|
|
297
311
|
let name = type.name && !type.name.startsWith('_') ? type.name : '';
|
|
@@ -325,7 +339,7 @@ export class TransformerState implements State {
|
|
|
325
339
|
(m): m is ts.MethodDeclaration => ts.isMethodDeclaration(m) && ts.isIdentifier(m.name) && m.name.escapedText === method
|
|
326
340
|
);
|
|
327
341
|
} else {
|
|
328
|
-
const props = this.
|
|
342
|
+
const props = this.#resolver.getPropertiesOfType(cls);
|
|
329
343
|
for (const prop of props) {
|
|
330
344
|
const decl = prop.declarations?.[0];
|
|
331
345
|
if (decl && prop.escapedName === method && ts.isMethodDeclaration(decl)) {
|
package/src/types/shared.ts
CHANGED
package/src/types/visitor.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ts from 'typescript';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Decorator metadata
|
|
@@ -14,7 +14,7 @@ export type DecoratorMeta = {
|
|
|
14
14
|
|
|
15
15
|
export type State = {
|
|
16
16
|
source: ts.SourceFile;
|
|
17
|
-
|
|
17
|
+
importName: string;
|
|
18
18
|
added: Map<number, ts.Statement[]>;
|
|
19
19
|
getDecoratorList(node: ts.Node): DecoratorMeta[];
|
|
20
20
|
finalize(src: ts.SourceFile): ts.SourceFile;
|
|
@@ -22,12 +22,14 @@ export type State = {
|
|
|
22
22
|
|
|
23
23
|
export type TransformPhase = 'before' | 'after';
|
|
24
24
|
|
|
25
|
-
export type TransformerType =
|
|
25
|
+
export type TransformerType =
|
|
26
|
+
'class' | 'method' | 'property' | 'getter' | 'setter' | 'parameter' |
|
|
27
|
+
'static-method' | 'call' | 'function' | 'file';
|
|
26
28
|
|
|
27
|
-
export const
|
|
29
|
+
export const ModuleNameⲐ = Symbol.for('@travetto/transformer:id');
|
|
28
30
|
|
|
29
31
|
export type Transformer = {
|
|
30
|
-
[
|
|
32
|
+
[ModuleNameⲐ]?: string;
|
|
31
33
|
name: string;
|
|
32
34
|
};
|
|
33
35
|
|
package/src/util/core.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ts from 'typescript';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Core utilities util
|
|
5
5
|
*/
|
|
6
6
|
export class CoreUtil {
|
|
7
|
+
|
|
7
8
|
/**
|
|
8
9
|
* See if inbound node has an original property
|
|
9
10
|
*/
|
|
@@ -51,7 +52,6 @@ export class CoreUtil {
|
|
|
51
52
|
*/
|
|
52
53
|
static createStaticField(factory: ts.NodeFactory, name: string, val: ts.Expression): ts.PropertyDeclaration {
|
|
53
54
|
return factory.createPropertyDeclaration(
|
|
54
|
-
undefined,
|
|
55
55
|
[factory.createToken(ts.SyntaxKind.StaticKeyword)],
|
|
56
56
|
name, undefined, undefined, val
|
|
57
57
|
);
|
|
@@ -88,13 +88,15 @@ export class CoreUtil {
|
|
|
88
88
|
factory: ts.NodeFactory,
|
|
89
89
|
first: string | ts.Expression,
|
|
90
90
|
second: string | ts.Identifier,
|
|
91
|
-
...items: (string | ts.Identifier)[]
|
|
92
|
-
): ts.
|
|
91
|
+
...items: (string | number | ts.Identifier)[]
|
|
92
|
+
): ts.Expression {
|
|
93
93
|
if (typeof first === 'string') {
|
|
94
94
|
first = factory.createIdentifier(first);
|
|
95
95
|
}
|
|
96
|
-
return items.reduce(
|
|
97
|
-
(acc, p) =>
|
|
96
|
+
return items.reduce<ts.Expression>(
|
|
97
|
+
(acc, p) => typeof p === 'number' ?
|
|
98
|
+
factory.createElementAccessExpression(acc, p) :
|
|
99
|
+
factory.createPropertyAccessExpression(acc, p),
|
|
98
100
|
factory.createPropertyAccessExpression(first, second)
|
|
99
101
|
);
|
|
100
102
|
}
|
package/src/util/declaration.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ts from 'typescript';
|
|
2
2
|
import { CoreUtil } from './core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -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
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
61
|
-
return (ts as unknown as { getObjectFlags(t: ts.Type): ts.ObjectFlags }).getObjectFlags(type);
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, no-bitwise
|
|
61
|
+
return (ts as unknown as { getObjectFlags(t: ts.Type): ts.ObjectFlags }).getObjectFlags(type) & ~(ts.NodeFlags.ThisNodeOrAnySubNodesHasError);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
package/src/util/decorator.ts
CHANGED
package/src/util/doc.ts
CHANGED
package/src/util/import.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { resolve as pathResolve } from 'path';
|
|
1
|
+
import ts from 'typescript';
|
|
3
2
|
|
|
4
|
-
import {
|
|
3
|
+
import { PackageUtil, path } from '@travetto/manifest';
|
|
5
4
|
|
|
6
5
|
import { Import } from '../types/shared';
|
|
7
6
|
|
|
@@ -13,9 +12,17 @@ export class ImportUtil {
|
|
|
13
12
|
* useful for handling failed imports, but still transpiling
|
|
14
13
|
*/
|
|
15
14
|
static optionalResolve(file: string, base?: string): string {
|
|
16
|
-
|
|
15
|
+
if (base?.endsWith('.ts')) {
|
|
16
|
+
base = path.dirname(base);
|
|
17
|
+
}
|
|
18
|
+
if (base && file.startsWith('.')) {
|
|
19
|
+
return path.resolve(base, file);
|
|
20
|
+
// TODO: Replace with manifest reverse lookup
|
|
21
|
+
} else if (file.startsWith('@')) {
|
|
22
|
+
return path.resolve('node_modules', file);
|
|
23
|
+
}
|
|
17
24
|
try {
|
|
18
|
-
return
|
|
25
|
+
return PackageUtil.resolveImport(file);
|
|
19
26
|
} catch {
|
|
20
27
|
return file;
|
|
21
28
|
}
|
|
@@ -25,23 +32,23 @@ export class ImportUtil {
|
|
|
25
32
|
* Collect all imports for a source file, as a hash map
|
|
26
33
|
*/
|
|
27
34
|
static collectImports(src: ts.SourceFile): Map<string, Import> {
|
|
28
|
-
|
|
29
|
-
const base =
|
|
35
|
+
// TODO: Replace with manifest reverse lookup
|
|
36
|
+
const base = path.toPosix(src.fileName);
|
|
30
37
|
|
|
31
38
|
const imports = new Map<string, Import>();
|
|
32
39
|
|
|
33
40
|
for (const stmt of src.statements) {
|
|
34
41
|
if (ts.isImportDeclaration(stmt) && ts.isStringLiteral(stmt.moduleSpecifier)) {
|
|
35
|
-
const
|
|
42
|
+
const resolved = this.optionalResolve(stmt.moduleSpecifier.text, base);
|
|
36
43
|
|
|
37
44
|
if (stmt.importClause) {
|
|
38
45
|
if (stmt.importClause.namedBindings) {
|
|
39
46
|
const bindings = stmt.importClause.namedBindings;
|
|
40
47
|
if (ts.isNamespaceImport(bindings)) {
|
|
41
|
-
imports.set(bindings.name.text, { path, ident: bindings.name, stmt });
|
|
48
|
+
imports.set(bindings.name.text, { path: resolved, ident: bindings.name, stmt });
|
|
42
49
|
} else if (ts.isNamedImports(bindings)) {
|
|
43
50
|
for (const n of bindings.elements) {
|
|
44
|
-
imports.set(n.name.text, { path, ident: n.name, stmt });
|
|
51
|
+
imports.set(n.name.text, { path: resolved, ident: n.name, stmt });
|
|
45
52
|
}
|
|
46
53
|
}
|
|
47
54
|
}
|
package/src/util/literal.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ts from 'typescript';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Utilities for dealing with literals
|
|
@@ -48,7 +48,8 @@ export class LiteralUtil {
|
|
|
48
48
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
49
49
|
const ov = val as object;
|
|
50
50
|
const pairs: ts.PropertyAssignment[] = [];
|
|
51
|
-
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
52
|
+
for (const k of Object.keys(ov) as (keyof typeof ov)[]) {
|
|
52
53
|
if (ov[k] !== undefined) {
|
|
53
54
|
pairs.push(
|
|
54
55
|
factory.createPropertyAssignment(k, this.fromLiteral(factory, ov[k]))
|
package/src/util/log.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { Util } from '@travetto/base';
|
|
2
|
-
|
|
3
1
|
const exclude = new Set([
|
|
4
2
|
'parent', 'checker', 'end', 'pos', 'id', 'source', 'sourceFile', 'getSourceFile',
|
|
5
3
|
'statements', 'stringIndexInfo', 'numberIndexInfo', 'instantiations', 'thisType',
|
|
@@ -22,7 +20,7 @@ export class LogUtil {
|
|
|
22
20
|
* Clean up `ts.Node` contents for logging
|
|
23
21
|
*/
|
|
24
22
|
static collapseNode(x: unknown, cache: Set<unknown> = new Set()): unknown {
|
|
25
|
-
if (!x ||
|
|
23
|
+
if (!x || !(typeof x === 'object' || typeof x === 'function')) {
|
|
26
24
|
return x;
|
|
27
25
|
}
|
|
28
26
|
|
|
@@ -38,8 +36,9 @@ export class LogUtil {
|
|
|
38
36
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
39
37
|
const ox = x as object;
|
|
40
38
|
const out: Record<string, unknown> = {};
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
40
|
+
for (const key of Object.keys(ox) as (keyof typeof ox)[]) {
|
|
41
|
+
if (Object.getPrototypeOf(ox[key]) === Function.prototype || exclude.has(key) || ox[key] === undefined) {
|
|
43
42
|
continue;
|
|
44
43
|
}
|
|
45
44
|
try {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
|
|
3
|
+
export class SystemUtil {
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generate a random UUID
|
|
7
|
+
* @param len The length of the uuid to generate
|
|
8
|
+
*/
|
|
9
|
+
static uuid(len: number = 32): string {
|
|
10
|
+
const bytes = crypto.randomBytes(Math.ceil(len / 2));
|
|
11
|
+
// eslint-disable-next-line no-bitwise
|
|
12
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
13
|
+
// eslint-disable-next-line no-bitwise
|
|
14
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
15
|
+
return bytes.toString('hex').substring(0, len);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Naive hashing
|
|
20
|
+
*/
|
|
21
|
+
static naiveHash(text: string): number {
|
|
22
|
+
let hash = 5381;
|
|
23
|
+
|
|
24
|
+
for (let i = 0; i < text.length; i++) {
|
|
25
|
+
// eslint-disable-next-line no-bitwise
|
|
26
|
+
hash = (hash * 33) ^ text.charCodeAt(i);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return Math.abs(hash);
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/visitor.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { createWriteStream } from 'fs';
|
|
3
|
-
|
|
4
|
-
import { ConsoleManager } from '@travetto/base/src/console';
|
|
5
|
-
import { AppCache } from '@travetto/boot';
|
|
1
|
+
import ts from 'typescript';
|
|
6
2
|
|
|
7
3
|
import { DecoratorMeta, TransformerType, NodeTransformer, TransformerSet, State, TransformPhase } from './types/visitor';
|
|
8
|
-
import { LogUtil } from './util/log';
|
|
9
4
|
import { CoreUtil } from './util/core';
|
|
10
5
|
|
|
11
6
|
/**
|
|
@@ -28,26 +23,24 @@ export class VisitorFactory<S extends State = State> {
|
|
|
28
23
|
return 'class';
|
|
29
24
|
} else if (ts.isParameter(node)) {
|
|
30
25
|
return 'parameter';
|
|
31
|
-
} else if (ts.isFunctionDeclaration(node) || (ts.isFunctionExpression(node) && !ts.isArrowFunction(node))) {
|
|
26
|
+
} else if ((ts.isFunctionDeclaration(node) && node.body) || (ts.isFunctionExpression(node) && !ts.isArrowFunction(node))) {
|
|
32
27
|
return 'function';
|
|
33
28
|
} else if (ts.isGetAccessor(node)) {
|
|
34
29
|
return 'getter';
|
|
35
30
|
} else if (ts.isSetAccessor(node)) {
|
|
36
31
|
return 'setter';
|
|
32
|
+
} else if (ts.isSourceFile(node)) {
|
|
33
|
+
return 'file';
|
|
37
34
|
}
|
|
38
35
|
}
|
|
39
36
|
|
|
40
37
|
#transformers = new Map<TransformerType, TransformerSet<S>>();
|
|
41
|
-
#logTarget: string;
|
|
42
38
|
#getState: (context: ts.TransformationContext, src: ts.SourceFile) => S;
|
|
43
|
-
#logger: Console | undefined;
|
|
44
39
|
|
|
45
40
|
constructor(
|
|
46
41
|
getState: (context: ts.TransformationContext, src: ts.SourceFile) => S,
|
|
47
|
-
transformers: NodeTransformer<S, TransformerType, ts.Node>[]
|
|
48
|
-
logTarget = 'compiler.log'
|
|
42
|
+
transformers: NodeTransformer<S, TransformerType, ts.Node>[]
|
|
49
43
|
) {
|
|
50
|
-
this.#logTarget = logTarget;
|
|
51
44
|
this.#getState = getState;
|
|
52
45
|
this.#init(transformers);
|
|
53
46
|
}
|
|
@@ -79,26 +72,16 @@ export class VisitorFactory<S extends State = State> {
|
|
|
79
72
|
}
|
|
80
73
|
}
|
|
81
74
|
|
|
82
|
-
get logger(): Console {
|
|
83
|
-
this.#logger ??= new console.Console({
|
|
84
|
-
stdout: createWriteStream(AppCache.toEntryName(this.#logTarget), { flags: 'a' }),
|
|
85
|
-
inspectOptions: { depth: 4 },
|
|
86
|
-
});
|
|
87
|
-
return this.#logger;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
75
|
/**
|
|
91
76
|
* Produce a visitor for a given a file
|
|
92
77
|
*/
|
|
93
78
|
visitor(): ts.TransformerFactory<ts.SourceFile> {
|
|
94
79
|
return (context: ts.TransformationContext) => (file: ts.SourceFile): ts.SourceFile => {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
onLog: (level, ctx, args) => c[level](level, ctx, ...LogUtil.collapseNodes(args))
|
|
99
|
-
});
|
|
80
|
+
if (!file.fileName.endsWith('.ts')) { // Skip all non-ts files
|
|
81
|
+
return file;
|
|
82
|
+
}
|
|
100
83
|
|
|
101
|
-
|
|
84
|
+
try {
|
|
102
85
|
const state = this.#getState(context, file);
|
|
103
86
|
let ret = this.visit(state, context, file);
|
|
104
87
|
|
|
@@ -106,12 +89,11 @@ export class VisitorFactory<S extends State = State> {
|
|
|
106
89
|
const changed = state.added.size;
|
|
107
90
|
let statements: ts.NodeArray<ts.Statement> | ts.Statement[] = ret.statements;
|
|
108
91
|
while (state.added.size) {
|
|
109
|
-
for (const [
|
|
110
|
-
const idx = k === -1 ? state.added.size : k;
|
|
92
|
+
for (const [idx, all] of [...state.added].sort(([idxA], [idxB]) => idxB - idxA)) {
|
|
111
93
|
statements = [
|
|
112
|
-
...statements.slice(0, idx),
|
|
94
|
+
...statements.slice(0, Math.max(idx, 0)),
|
|
113
95
|
...all.map(v => this.visit(state, context, v)),
|
|
114
|
-
...statements.slice(idx)
|
|
96
|
+
...statements.slice(Math.max(idx, 0))
|
|
115
97
|
];
|
|
116
98
|
state.added.delete(idx);
|
|
117
99
|
}
|
|
@@ -125,12 +107,10 @@ export class VisitorFactory<S extends State = State> {
|
|
|
125
107
|
if (!(err instanceof Error)) {
|
|
126
108
|
throw err;
|
|
127
109
|
}
|
|
128
|
-
console
|
|
110
|
+
console!.error('Failed transforming', { error: `${err.message}\n${err.stack}`, file: file.fileName });
|
|
129
111
|
const out = new Error(`Failed transforming: ${file.fileName}: ${err.message}`);
|
|
130
112
|
out.stack = err.stack;
|
|
131
113
|
throw out;
|
|
132
|
-
} finally {
|
|
133
|
-
ConsoleManager.clear(); // Reset logging
|
|
134
114
|
}
|
|
135
115
|
};
|
|
136
116
|
}
|
package/index.ts
DELETED