@travetto/transformer 3.0.0-rc.0 → 3.0.0-rc.10
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 +122 -34
- package/src/manager.ts +62 -0
- package/src/manifest-index.ts +36 -0
- package/src/register.ts +40 -11
- package/src/resolver/builder.ts +42 -37
- package/src/resolver/cache.ts +2 -2
- package/src/resolver/coerce.ts +103 -0
- package/src/resolver/service.ts +3 -3
- package/src/resolver/types.ts +3 -3
- package/src/state.ts +79 -59
- 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 +7 -4
- 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 +2 -4
- package/src/util/system.ts +31 -0
- package/src/visitor.ts +13 -28
- package/index.ts +0 -7
- package/src/util/index.ts +0 -6
- package/test-support/util.ts +0 -57
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const REGEX_PAT = /[\/](.*)[\/](i|g|m|s)?/;
|
|
2
|
+
|
|
3
|
+
export class CoerceUtil {
|
|
4
|
+
/**
|
|
5
|
+
* Is a value a plain JS object, created using {}
|
|
6
|
+
*/
|
|
7
|
+
static #isPlainObject(obj: unknown): obj is Record<string, unknown> {
|
|
8
|
+
return typeof obj === 'object' // separate from primitives
|
|
9
|
+
&& obj !== undefined
|
|
10
|
+
&& obj !== null // is obvious
|
|
11
|
+
&& obj.constructor === Object // separate instances (Array, DOM, ...)
|
|
12
|
+
&& Object.prototype.toString.call(obj) === '[object Object]'; // separate build-in like Math
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create regex from string, including flags
|
|
17
|
+
*/
|
|
18
|
+
static #toRegex(input: string | RegExp): RegExp {
|
|
19
|
+
if (input instanceof RegExp) {
|
|
20
|
+
return input;
|
|
21
|
+
} else if (REGEX_PAT.test(input)) {
|
|
22
|
+
const [, pat, mod] = input.match(REGEX_PAT) ?? [];
|
|
23
|
+
return new RegExp(pat, mod);
|
|
24
|
+
} else {
|
|
25
|
+
return new RegExp(input);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Coerce an input of any type to the class provided
|
|
31
|
+
* @param input Input value
|
|
32
|
+
* @param type Class to coerce to (String, Boolean, Number, Date, RegEx, Object)
|
|
33
|
+
* @param strict Should a failure to coerce throw an error?
|
|
34
|
+
*/
|
|
35
|
+
static coerce(input: unknown, type: typeof String, strict?: boolean): string;
|
|
36
|
+
static coerce(input: unknown, type: typeof Number, strict?: boolean): number;
|
|
37
|
+
static coerce(input: unknown, type: typeof Boolean, strict?: boolean): boolean;
|
|
38
|
+
static coerce(input: unknown, type: typeof Date, strict?: boolean): Date;
|
|
39
|
+
static coerce(input: unknown, type: typeof RegExp, strict?: boolean): RegExp;
|
|
40
|
+
static coerce(input: unknown, type: Function, strict = true): unknown {
|
|
41
|
+
// Do nothing
|
|
42
|
+
if (input === null || input === undefined) {
|
|
43
|
+
return input;
|
|
44
|
+
} else if (!strict && type !== String && input === '') {
|
|
45
|
+
return undefined; // treat empty string as undefined for non-strings in non-strict mode
|
|
46
|
+
} else if (type && input instanceof type) {
|
|
47
|
+
return input;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
switch (type) {
|
|
51
|
+
case Date: {
|
|
52
|
+
const res = typeof input === 'number' || /^[-]?\d+$/.test(`${input}`) ?
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
54
|
+
new Date(parseInt(input as string, 10)) : new Date(input as Date);
|
|
55
|
+
if (strict && Number.isNaN(res.getTime())) {
|
|
56
|
+
throw new Error(`Invalid date value: ${input}`);
|
|
57
|
+
}
|
|
58
|
+
return res;
|
|
59
|
+
}
|
|
60
|
+
case Number: {
|
|
61
|
+
const res = `${input}`.includes('.') ? parseFloat(`${input}`) : parseInt(`${input}`, 10);
|
|
62
|
+
if (strict && Number.isNaN(res)) {
|
|
63
|
+
throw new Error(`Invalid numeric value: ${input}`);
|
|
64
|
+
}
|
|
65
|
+
return res;
|
|
66
|
+
}
|
|
67
|
+
case Boolean: {
|
|
68
|
+
const match = `${input}`.match(/^((?<TRUE>true|yes|1|on)|false|no|off|0)$/i);
|
|
69
|
+
if (strict && !match) {
|
|
70
|
+
throw new Error(`Invalid boolean value: ${input}`);
|
|
71
|
+
}
|
|
72
|
+
return !!match?.groups?.TRUE;
|
|
73
|
+
}
|
|
74
|
+
case RegExp: {
|
|
75
|
+
if (typeof input === 'string') {
|
|
76
|
+
try {
|
|
77
|
+
return this.#toRegex(input);
|
|
78
|
+
} catch {
|
|
79
|
+
if (strict) {
|
|
80
|
+
throw new Error(`Invalid regex: ${input}`);
|
|
81
|
+
} else {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} else if (strict) {
|
|
86
|
+
throw new Error('Invalid regex type');
|
|
87
|
+
} else {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
case Object: {
|
|
92
|
+
if (!strict || this.#isPlainObject(input)) {
|
|
93
|
+
return input;
|
|
94
|
+
} else {
|
|
95
|
+
throw new Error('Invalid object type');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
case undefined:
|
|
99
|
+
case String: return `${input}`;
|
|
100
|
+
}
|
|
101
|
+
throw new Error(`Unknown type ${type.name}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
package/src/resolver/service.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ts from 'typescript';
|
|
2
2
|
|
|
3
|
-
import { AnyType, Checker } from './types';
|
|
3
|
+
import type { AnyType, Checker } from './types';
|
|
4
4
|
import { TypeCategorize, TypeBuilder } from './builder';
|
|
5
5
|
import { VisitCache } from './cache';
|
|
6
|
-
import { DocUtil } from '../util';
|
|
6
|
+
import { DocUtil } from '../util/doc';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Type resolver
|
package/src/resolver/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type ts from 'typescript';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Base type for a simplistic type structure
|
|
@@ -37,7 +37,7 @@ export interface ExternalType extends Type<'external'> {
|
|
|
37
37
|
/**
|
|
38
38
|
* Location the type came from, for class references
|
|
39
39
|
*/
|
|
40
|
-
|
|
40
|
+
importName: string;
|
|
41
41
|
/**
|
|
42
42
|
* Type arguments
|
|
43
43
|
*/
|
|
@@ -55,7 +55,7 @@ export interface ShapeType extends Type<'shape'> {
|
|
|
55
55
|
/**
|
|
56
56
|
* Location the type came from, for class references
|
|
57
57
|
*/
|
|
58
|
-
|
|
58
|
+
importName: string;
|
|
59
59
|
/**
|
|
60
60
|
* Does not include methods, used for shapes not concrete types
|
|
61
61
|
*/
|
package/src/state.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
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 { path } from '@travetto/manifest';
|
|
6
4
|
|
|
7
5
|
import { ExternalType, AnyType } from './resolver/types';
|
|
8
|
-
import { State, DecoratorMeta, Transformer,
|
|
6
|
+
import { State, DecoratorMeta, Transformer, ModuleNameⲐ } from './types/visitor';
|
|
9
7
|
import { TypeResolver } 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';
|
|
17
|
+
import { TransformerIndex } from './manifest-index';
|
|
16
18
|
|
|
17
19
|
function hasOriginal(n: unknown): n is { original: ts.Node } {
|
|
18
20
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
@@ -28,20 +30,22 @@ function hasEscapedName(n: unknown): n is { name: { escapedText: string } } {
|
|
|
28
30
|
* Transformer runtime state
|
|
29
31
|
*/
|
|
30
32
|
export class TransformerState implements State {
|
|
31
|
-
static SYNTHETIC_EXT = '
|
|
33
|
+
static SYNTHETIC_EXT = 'Ⲑsyn';
|
|
32
34
|
|
|
33
35
|
#resolver: TypeResolver;
|
|
34
36
|
#imports: ImportManager;
|
|
37
|
+
#fileIdent: ts.Identifier;
|
|
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
44
|
constructor(public source: ts.SourceFile, public factory: ts.NodeFactory, checker: ts.TypeChecker) {
|
|
42
45
|
this.#imports = new ImportManager(source, factory);
|
|
43
46
|
this.#resolver = new TypeResolver(checker);
|
|
44
|
-
this.
|
|
47
|
+
this.file = path.toPosix(this.source.fileName);
|
|
48
|
+
this.importName = TransformerIndex.getImportName(this.file, true);
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
/**
|
|
@@ -62,8 +66,8 @@ export class TransformerState implements State {
|
|
|
62
66
|
/**
|
|
63
67
|
* Import a given file
|
|
64
68
|
*/
|
|
65
|
-
importFile(file: string): Import {
|
|
66
|
-
return this.#imports.importFile(file);
|
|
69
|
+
importFile(file: string, name?: string): Import {
|
|
70
|
+
return this.#imports.importFile(file, name);
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
/**
|
|
@@ -81,7 +85,9 @@ export class TransformerState implements State {
|
|
|
81
85
|
resolveExternalType(node: ts.Node): ExternalType {
|
|
82
86
|
const resolved = this.resolveType(node);
|
|
83
87
|
if (resolved.key !== 'external') {
|
|
84
|
-
|
|
88
|
+
const file = node.getSourceFile().fileName;
|
|
89
|
+
const src = TransformerIndex.getImportName(file);
|
|
90
|
+
throw new Error(`Unable to import non-external type: ${node.getText()} ${resolved.key}: ${src}`);
|
|
85
91
|
}
|
|
86
92
|
return resolved;
|
|
87
93
|
}
|
|
@@ -141,32 +147,33 @@ export class TransformerState implements State {
|
|
|
141
147
|
/**
|
|
142
148
|
* Read a decorator's metadata
|
|
143
149
|
*/
|
|
144
|
-
getDecoratorMeta(dec: ts.Decorator): DecoratorMeta {
|
|
150
|
+
getDecoratorMeta(dec: ts.Decorator): DecoratorMeta | undefined {
|
|
145
151
|
const ident = DecoratorUtil.getDecoratorIdent(dec);
|
|
146
152
|
const decl = DeclarationUtil.getPrimaryDeclarationNode(
|
|
147
153
|
this.#resolver.getType(ident)
|
|
148
154
|
);
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
155
|
+
const src = decl?.getSourceFile().fileName;
|
|
156
|
+
const mod = src ? TransformerIndex.getImportName(src, true) : undefined;
|
|
157
|
+
const file = TransformerIndex.getFromImport(mod ?? '')?.outputFile;
|
|
158
|
+
const targets = DocUtil.readAugments(this.#resolver.getType(ident));
|
|
159
|
+
const module = file ? mod : undefined;
|
|
160
|
+
const name = ident ?
|
|
161
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
162
|
+
ident.escapedText! as string :
|
|
163
|
+
undefined;
|
|
164
|
+
|
|
165
|
+
if (ident && name) {
|
|
166
|
+
return { dec, ident, file, module, targets, name };
|
|
167
|
+
}
|
|
161
168
|
}
|
|
162
169
|
|
|
163
170
|
/**
|
|
164
171
|
* Get list of all #decorators for a node
|
|
165
172
|
*/
|
|
166
173
|
getDecoratorList(node: ts.Node): DecoratorMeta[] {
|
|
167
|
-
return (node.
|
|
174
|
+
return ts.canHaveDecorators(node) ? (ts.getDecorators(node) ?? [])
|
|
168
175
|
.map(dec => this.getDecoratorMeta(dec))
|
|
169
|
-
.filter(x => !!x
|
|
176
|
+
.filter((x): x is DecoratorMeta => !!x) : [];
|
|
170
177
|
}
|
|
171
178
|
|
|
172
179
|
/**
|
|
@@ -181,44 +188,40 @@ export class TransformerState implements State {
|
|
|
181
188
|
* @param stmt
|
|
182
189
|
* @param before
|
|
183
190
|
*/
|
|
184
|
-
|
|
191
|
+
addStatements(added: ts.Statement[], before?: ts.Node | number): void {
|
|
185
192
|
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
|
-
|
|
193
|
+
let idx = stmts.length + 1000;
|
|
194
|
+
|
|
195
|
+
if (before && typeof before !== 'number') {
|
|
196
|
+
let n = before;
|
|
197
|
+
if (hasOriginal(n)) {
|
|
198
|
+
n = n.original;
|
|
199
|
+
}
|
|
200
|
+
while (n && !ts.isSourceFile(n.parent) && n !== n.parent) {
|
|
201
|
+
n = n.parent;
|
|
202
|
+
}
|
|
203
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
204
|
+
const nStmt: ts.Statement = n as ts.Statement;
|
|
205
|
+
if (n && ts.isSourceFile(n.parent) && stmts.indexOf(nStmt) >= 0) {
|
|
206
|
+
idx = stmts.indexOf(nStmt) - 1;
|
|
207
|
+
}
|
|
208
|
+
} else if (before !== undefined) {
|
|
209
|
+
idx = before;
|
|
198
210
|
}
|
|
199
211
|
if (!this.added.has(idx)) {
|
|
200
212
|
this.added.set(idx, []);
|
|
201
213
|
}
|
|
202
|
-
this.added.get(idx)!.push(
|
|
214
|
+
this.added.get(idx)!.push(...added);
|
|
203
215
|
}
|
|
204
216
|
|
|
205
217
|
/**
|
|
206
218
|
* Finalize the source file for emission
|
|
207
219
|
*/
|
|
208
220
|
finalize(ret: ts.SourceFile): ts.SourceFile {
|
|
209
|
-
ret = this.#imports.finalize(ret);
|
|
221
|
+
ret = this.#imports.finalize(ret, this.#resolver.getChecker());
|
|
210
222
|
return ret;
|
|
211
223
|
}
|
|
212
224
|
|
|
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
225
|
/**
|
|
223
226
|
* From literal
|
|
224
227
|
*/
|
|
@@ -243,7 +246,7 @@ export class TransformerState implements State {
|
|
|
243
246
|
/**
|
|
244
247
|
* Create property access
|
|
245
248
|
*/
|
|
246
|
-
createAccess(first: string | ts.Expression, second: string | ts.Identifier, ...items: (string | ts.Identifier)[]): ts.
|
|
249
|
+
createAccess(first: string | ts.Expression, second: string | ts.Identifier, ...items: (string | number | ts.Identifier)[]): ts.Expression {
|
|
247
250
|
return CoreUtil.createAccess(this.factory, first, second, ...items);
|
|
248
251
|
}
|
|
249
252
|
|
|
@@ -262,6 +265,22 @@ export class TransformerState implements State {
|
|
|
262
265
|
return this.factory.createIdentifier(typeof name === 'string' ? name : name.getText());
|
|
263
266
|
}
|
|
264
267
|
|
|
268
|
+
/**
|
|
269
|
+
* Get filename identifier, regardless of module system
|
|
270
|
+
*/
|
|
271
|
+
getFilenameIdentifier(): ts.Expression {
|
|
272
|
+
if (this.#fileIdent === undefined) {
|
|
273
|
+
this.#fileIdent = this.createIdentifier('ᚕf');
|
|
274
|
+
const decl = this.factory.createVariableDeclaration(this.#fileIdent, undefined, undefined,
|
|
275
|
+
this.fromLiteral(TransformerIndex.getImportName(this.source.fileName) ?? this.source.fileName)
|
|
276
|
+
);
|
|
277
|
+
this.addStatements([
|
|
278
|
+
this.factory.createVariableStatement([], this.factory.createVariableDeclarationList([decl]))
|
|
279
|
+
], -1);
|
|
280
|
+
}
|
|
281
|
+
return this.#fileIdent;
|
|
282
|
+
}
|
|
283
|
+
|
|
265
284
|
/**
|
|
266
285
|
* Find decorator, relative to registered key
|
|
267
286
|
* @param state
|
|
@@ -269,10 +288,11 @@ export class TransformerState implements State {
|
|
|
269
288
|
* @param name
|
|
270
289
|
* @param module
|
|
271
290
|
*/
|
|
272
|
-
findDecorator(
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
291
|
+
findDecorator(mod: string | Transformer, node: ts.Node, name: string, module?: string): ts.Decorator | undefined {
|
|
292
|
+
mod = typeof mod === 'string' ? mod : mod[ModuleNameⲐ]!;
|
|
293
|
+
const target = `${mod}:${name}`;
|
|
294
|
+
const list = this.getDecoratorList(node);
|
|
295
|
+
return list.find(x => x.targets?.includes(target) && (!module || x.name === name && x.module === module))?.dec;
|
|
276
296
|
}
|
|
277
297
|
|
|
278
298
|
/**
|
|
@@ -291,7 +311,7 @@ export class TransformerState implements State {
|
|
|
291
311
|
}
|
|
292
312
|
} catch {
|
|
293
313
|
// Determine type unique ident
|
|
294
|
-
unique =
|
|
314
|
+
unique = SystemUtil.uuid(type.name ? 5 : 10);
|
|
295
315
|
}
|
|
296
316
|
// Otherwise read name with uuid
|
|
297
317
|
let name = type.name && !type.name.startsWith('_') ? type.name : '';
|
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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ts from 'typescript';
|
|
2
2
|
import { CoreUtil } from './core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -24,11 +24,14 @@ export class DecoratorUtil {
|
|
|
24
24
|
/**
|
|
25
25
|
* Replace or add a decorator to a list of decorators
|
|
26
26
|
*/
|
|
27
|
-
static spliceDecorators(node: ts.Node, target: ts.Decorator | undefined, replacements: ts.Decorator[], idx = -1): ts.
|
|
27
|
+
static spliceDecorators(node: ts.Node, target: ts.Decorator | undefined, replacements: ts.Decorator[], idx = -1): ts.ModifierLike[] {
|
|
28
|
+
if (!ts.canHaveDecorators(node)) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
28
31
|
if (idx < 0 && target) {
|
|
29
|
-
idx = node.
|
|
32
|
+
idx = node.modifiers?.indexOf(target) ?? -1;
|
|
30
33
|
}
|
|
31
|
-
const out = (node.
|
|
34
|
+
const out = (node.modifiers ?? []).filter(x => x !== target);
|
|
32
35
|
if (idx < 0) {
|
|
33
36
|
out.push(...replacements);
|
|
34
37
|
} else {
|
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
|
|
|
@@ -39,7 +37,7 @@ export class LogUtil {
|
|
|
39
37
|
const ox = x as object;
|
|
40
38
|
const out: Record<string, unknown> = {};
|
|
41
39
|
for (const key of Object.keys(ox)) {
|
|
42
|
-
if (
|
|
40
|
+
if (Object.getPrototypeOf(ox[key]) === Function.prototype || exclude.has(key) || ox[key] === undefined) {
|
|
43
41
|
continue;
|
|
44
42
|
}
|
|
45
43
|
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
|
+
}
|