@travetto/transformer 3.0.0-rc.4 → 3.0.0-rc.7
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 +125 -35
- package/src/manager.ts +66 -0
- package/src/manifest-index.ts +29 -0
- package/src/register.ts +40 -11
- package/src/resolver/builder.ts +37 -37
- package/src/resolver/cache.ts +2 -2
- package/src/resolver/coerce.ts +103 -0
- package/src/resolver/service.ts +12 -5
- package/src/resolver/types.ts +6 -3
- package/src/state.ts +93 -61
- 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 +1 -1
- 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 +2 -4
- package/src/util/system.ts +31 -0
- package/src/visitor.ts +11 -30
- package/index.ts +0 -7
- package/src/util/index.ts +0 -6
- package/test-support/util.ts +0 -57
package/src/resolver/cache.ts
CHANGED
|
@@ -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,18 +1,21 @@
|
|
|
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
|
+
import { TransformerIndex } from '../manifest-index';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Type resolver
|
|
10
11
|
*/
|
|
11
12
|
export class TypeResolver implements Checker {
|
|
12
13
|
#tsChecker: ts.TypeChecker;
|
|
14
|
+
#index: TransformerIndex;
|
|
13
15
|
|
|
14
|
-
constructor(tsChecker: ts.TypeChecker) {
|
|
16
|
+
constructor(tsChecker: ts.TypeChecker, idx: TransformerIndex) {
|
|
15
17
|
this.#tsChecker = tsChecker;
|
|
18
|
+
this.#index = idx;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
/**
|
|
@@ -23,6 +26,10 @@ export class TypeResolver implements Checker {
|
|
|
23
26
|
return this.#tsChecker;
|
|
24
27
|
}
|
|
25
28
|
|
|
29
|
+
getIndex(): TransformerIndex {
|
|
30
|
+
return this.#index;
|
|
31
|
+
}
|
|
32
|
+
|
|
26
33
|
/**
|
|
27
34
|
* Get type from element
|
|
28
35
|
* @param el
|
|
@@ -73,7 +80,7 @@ export class TypeResolver implements Checker {
|
|
|
73
80
|
throw new Error('Object structure too nested');
|
|
74
81
|
}
|
|
75
82
|
|
|
76
|
-
const { category, type } = TypeCategorize(this.#tsChecker, resType);
|
|
83
|
+
const { category, type } = TypeCategorize(this.#tsChecker, resType, this.#index);
|
|
77
84
|
const { build, finalize } = TypeBuilder[category];
|
|
78
85
|
|
|
79
86
|
let result = build(this, type, alias);
|
package/src/resolver/types.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type ts from 'typescript';
|
|
2
|
+
|
|
3
|
+
import { TransformerIndex } from '../manifest-index';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Base type for a simplistic type structure
|
|
@@ -37,7 +39,7 @@ export interface ExternalType extends Type<'external'> {
|
|
|
37
39
|
/**
|
|
38
40
|
* Location the type came from, for class references
|
|
39
41
|
*/
|
|
40
|
-
|
|
42
|
+
importName: string;
|
|
41
43
|
/**
|
|
42
44
|
* Type arguments
|
|
43
45
|
*/
|
|
@@ -55,7 +57,7 @@ export interface ShapeType extends Type<'shape'> {
|
|
|
55
57
|
/**
|
|
56
58
|
* Location the type came from, for class references
|
|
57
59
|
*/
|
|
58
|
-
|
|
60
|
+
importName: string;
|
|
59
61
|
/**
|
|
60
62
|
* Does not include methods, used for shapes not concrete types
|
|
61
63
|
*/
|
|
@@ -156,4 +158,5 @@ export interface Checker {
|
|
|
156
158
|
getPropertiesOfType(type: ts.Type): ts.Symbol[];
|
|
157
159
|
getTypeAsString(type: ts.Type): string | undefined;
|
|
158
160
|
getType(node: ts.Node): ts.Type;
|
|
161
|
+
getIndex(): TransformerIndex;
|
|
159
162
|
}
|
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,32 @@ 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
|
+
#index: TransformerIndex;
|
|
35
38
|
#syntheticIdentifiers = new Map<string, ts.Identifier>();
|
|
36
39
|
#decorators = new Map<string, ts.PropertyAccessExpression>();
|
|
37
|
-
|
|
40
|
+
#options: ts.CompilerOptions;
|
|
38
41
|
added = new Map<number, ts.Statement[]>();
|
|
39
|
-
|
|
42
|
+
importName: string;
|
|
43
|
+
file: string;
|
|
44
|
+
|
|
45
|
+
constructor(public source: ts.SourceFile, public factory: ts.NodeFactory, checker: ts.TypeChecker, index: TransformerIndex, options: ts.CompilerOptions) {
|
|
46
|
+
this.#index = index;
|
|
47
|
+
this.#imports = new ImportManager(source, factory, index);
|
|
48
|
+
this.#resolver = new TypeResolver(checker, index);
|
|
49
|
+
this.file = path.toPosix(this.source.fileName);
|
|
50
|
+
this.importName = this.#index.getImportName(this.file, true);
|
|
51
|
+
this.#options = options;
|
|
52
|
+
}
|
|
40
53
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Are we building ESM Output?
|
|
56
|
+
*/
|
|
57
|
+
isEsmOutput(): boolean {
|
|
58
|
+
return this.#options.module !== ts.ModuleKind.CommonJS;
|
|
45
59
|
}
|
|
46
60
|
|
|
47
61
|
/**
|
|
@@ -62,8 +76,8 @@ export class TransformerState implements State {
|
|
|
62
76
|
/**
|
|
63
77
|
* Import a given file
|
|
64
78
|
*/
|
|
65
|
-
importFile(file: string): Import {
|
|
66
|
-
return this.#imports.importFile(file);
|
|
79
|
+
importFile(file: string, name?: string): Import {
|
|
80
|
+
return this.#imports.importFile(file, name);
|
|
67
81
|
}
|
|
68
82
|
|
|
69
83
|
/**
|
|
@@ -81,7 +95,9 @@ export class TransformerState implements State {
|
|
|
81
95
|
resolveExternalType(node: ts.Node): ExternalType {
|
|
82
96
|
const resolved = this.resolveType(node);
|
|
83
97
|
if (resolved.key !== 'external') {
|
|
84
|
-
|
|
98
|
+
const file = node.getSourceFile().fileName;
|
|
99
|
+
const src = this.#index.getImportName(file);
|
|
100
|
+
throw new Error(`Unable to import non-external type: ${node.getText()} ${resolved.key}: ${src}`);
|
|
85
101
|
}
|
|
86
102
|
return resolved;
|
|
87
103
|
}
|
|
@@ -141,23 +157,24 @@ export class TransformerState implements State {
|
|
|
141
157
|
/**
|
|
142
158
|
* Read a decorator's metadata
|
|
143
159
|
*/
|
|
144
|
-
getDecoratorMeta(dec: ts.Decorator): DecoratorMeta {
|
|
160
|
+
getDecoratorMeta(dec: ts.Decorator): DecoratorMeta | undefined {
|
|
145
161
|
const ident = DecoratorUtil.getDecoratorIdent(dec);
|
|
146
162
|
const decl = DeclarationUtil.getPrimaryDeclarationNode(
|
|
147
163
|
this.#resolver.getType(ident)
|
|
148
164
|
);
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
165
|
+
const src = decl?.getSourceFile().fileName;
|
|
166
|
+
const mod = src ? this.#index.getImportName(src, true) : undefined;
|
|
167
|
+
const file = this.#index.getFromImport(mod ?? '')?.output;
|
|
168
|
+
const targets = DocUtil.readAugments(this.#resolver.getType(ident));
|
|
169
|
+
const module = file ? mod : undefined;
|
|
170
|
+
const name = ident ?
|
|
171
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
172
|
+
ident.escapedText! as string :
|
|
173
|
+
undefined;
|
|
174
|
+
|
|
175
|
+
if (ident && name) {
|
|
176
|
+
return { dec, ident, file, module, targets, name };
|
|
177
|
+
}
|
|
161
178
|
}
|
|
162
179
|
|
|
163
180
|
/**
|
|
@@ -166,7 +183,7 @@ export class TransformerState implements State {
|
|
|
166
183
|
getDecoratorList(node: ts.Node): DecoratorMeta[] {
|
|
167
184
|
return ts.canHaveDecorators(node) ? (ts.getDecorators(node) ?? [])
|
|
168
185
|
.map(dec => this.getDecoratorMeta(dec))
|
|
169
|
-
.filter(x => !!x
|
|
186
|
+
.filter((x): x is DecoratorMeta => !!x) : [];
|
|
170
187
|
}
|
|
171
188
|
|
|
172
189
|
/**
|
|
@@ -181,44 +198,40 @@ export class TransformerState implements State {
|
|
|
181
198
|
* @param stmt
|
|
182
199
|
* @param before
|
|
183
200
|
*/
|
|
184
|
-
|
|
201
|
+
addStatements(added: ts.Statement[], before?: ts.Node | number): void {
|
|
185
202
|
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
|
-
|
|
203
|
+
let idx = stmts.length + 1000;
|
|
204
|
+
|
|
205
|
+
if (before && typeof before !== 'number') {
|
|
206
|
+
let n = before;
|
|
207
|
+
if (hasOriginal(n)) {
|
|
208
|
+
n = n.original;
|
|
209
|
+
}
|
|
210
|
+
while (n && !ts.isSourceFile(n.parent) && n !== n.parent) {
|
|
211
|
+
n = n.parent;
|
|
212
|
+
}
|
|
213
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
214
|
+
const nStmt: ts.Statement = n as ts.Statement;
|
|
215
|
+
if (n && ts.isSourceFile(n.parent) && stmts.indexOf(nStmt) >= 0) {
|
|
216
|
+
idx = stmts.indexOf(nStmt) - 1;
|
|
217
|
+
}
|
|
218
|
+
} else if (before !== undefined) {
|
|
219
|
+
idx = before;
|
|
198
220
|
}
|
|
199
221
|
if (!this.added.has(idx)) {
|
|
200
222
|
this.added.set(idx, []);
|
|
201
223
|
}
|
|
202
|
-
this.added.get(idx)!.push(
|
|
224
|
+
this.added.get(idx)!.push(...added);
|
|
203
225
|
}
|
|
204
226
|
|
|
205
227
|
/**
|
|
206
228
|
* Finalize the source file for emission
|
|
207
229
|
*/
|
|
208
230
|
finalize(ret: ts.SourceFile): ts.SourceFile {
|
|
209
|
-
ret = this.#imports.finalize(ret);
|
|
231
|
+
ret = this.#imports.finalize(ret, this.#resolver.getChecker());
|
|
210
232
|
return ret;
|
|
211
233
|
}
|
|
212
234
|
|
|
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
235
|
/**
|
|
223
236
|
* From literal
|
|
224
237
|
*/
|
|
@@ -243,7 +256,7 @@ export class TransformerState implements State {
|
|
|
243
256
|
/**
|
|
244
257
|
* Create property access
|
|
245
258
|
*/
|
|
246
|
-
createAccess(first: string | ts.Expression, second: string | ts.Identifier, ...items: (string | ts.Identifier)[]): ts.
|
|
259
|
+
createAccess(first: string | ts.Expression, second: string | ts.Identifier, ...items: (string | number | ts.Identifier)[]): ts.Expression {
|
|
247
260
|
return CoreUtil.createAccess(this.factory, first, second, ...items);
|
|
248
261
|
}
|
|
249
262
|
|
|
@@ -262,6 +275,24 @@ export class TransformerState implements State {
|
|
|
262
275
|
return this.factory.createIdentifier(typeof name === 'string' ? name : name.getText());
|
|
263
276
|
}
|
|
264
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Get filename identifier, regardless of module system
|
|
280
|
+
*/
|
|
281
|
+
getFilenameIdentifier(): ts.Expression {
|
|
282
|
+
return this.isEsmOutput() ?
|
|
283
|
+
this.createAccess('import', 'meta', 'url') :
|
|
284
|
+
this.createIdentifier('__filename');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Get the entry file identifier, supports both ESM and commonjs
|
|
289
|
+
*/
|
|
290
|
+
getEntryFileIdentifier(): ts.Expression {
|
|
291
|
+
return this.isEsmOutput() ?
|
|
292
|
+
this.createAccess('process', 'argv', 1) :
|
|
293
|
+
this.createAccess('require', 'main', 'filename');
|
|
294
|
+
}
|
|
295
|
+
|
|
265
296
|
/**
|
|
266
297
|
* Find decorator, relative to registered key
|
|
267
298
|
* @param state
|
|
@@ -269,10 +300,11 @@ export class TransformerState implements State {
|
|
|
269
300
|
* @param name
|
|
270
301
|
* @param module
|
|
271
302
|
*/
|
|
272
|
-
findDecorator(
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
303
|
+
findDecorator(mod: string | Transformer, node: ts.Node, name: string, module?: string): ts.Decorator | undefined {
|
|
304
|
+
mod = typeof mod === 'string' ? mod : mod[ModuleNameⲐ]!;
|
|
305
|
+
const target = `${mod}:${name}`;
|
|
306
|
+
const list = this.getDecoratorList(node);
|
|
307
|
+
return list.find(x => x.targets?.includes(target) && (!module || x.name === name && x.module === module))?.dec;
|
|
276
308
|
}
|
|
277
309
|
|
|
278
310
|
/**
|
|
@@ -291,7 +323,7 @@ export class TransformerState implements State {
|
|
|
291
323
|
}
|
|
292
324
|
} catch {
|
|
293
325
|
// Determine type unique ident
|
|
294
|
-
unique =
|
|
326
|
+
unique = SystemUtil.uuid(type.name ? 5 : 10);
|
|
295
327
|
}
|
|
296
328
|
// Otherwise read name with uuid
|
|
297
329
|
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
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
|
|
|
@@ -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 {
|