@travetto/transformer 7.1.3 → 8.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/importer.ts +4 -5
- package/src/resolver/builder.ts +8 -3
- package/src/state.ts +54 -33
- package/src/types/shared.ts +1 -1
- package/src/types/visitor.ts +1 -4
- package/src/util/declaration.ts +1 -1
- package/src/util/literal.ts +6 -0
- package/src/visitor.ts +7 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/transformer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0-alpha.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Functionality for AST transformations, with transformer registration, and general utils",
|
|
6
6
|
"keywords": [
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"directory": "module/transformer"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@travetto/manifest": "^
|
|
28
|
+
"@travetto/manifest": "^8.0.0-alpha.0",
|
|
29
29
|
"tslib": "^2.8.1",
|
|
30
|
-
"typescript": "^
|
|
30
|
+
"typescript": "^6.0.0"
|
|
31
31
|
},
|
|
32
32
|
"travetto": {
|
|
33
33
|
"displayName": "Transformation",
|
package/src/importer.ts
CHANGED
|
@@ -110,16 +110,15 @@ export class ImportManager {
|
|
|
110
110
|
* Produces a unique identifier for a given file
|
|
111
111
|
*/
|
|
112
112
|
getIdentifier(file: string, name?: string): ts.Identifier {
|
|
113
|
-
|
|
113
|
+
return this.#identifiers.getOrInsertComputed(file, () => {
|
|
114
114
|
if (name) {
|
|
115
|
-
|
|
115
|
+
return this.factory.createIdentifier(name);
|
|
116
116
|
} else {
|
|
117
117
|
const key = path.basename(file, path.extname(file)).replace(/\W+/g, '_');
|
|
118
118
|
const suffix = this.#idx[key] = (this.#idx[key] ?? -1) + 1;
|
|
119
|
-
|
|
119
|
+
return this.factory.createIdentifier(`Δ${key}${suffix ? suffix : ''}`);
|
|
120
120
|
}
|
|
121
|
-
}
|
|
122
|
-
return this.#identifiers.get(file)!;
|
|
121
|
+
});
|
|
123
122
|
}
|
|
124
123
|
|
|
125
124
|
/**
|
package/src/resolver/builder.ts
CHANGED
|
@@ -43,7 +43,12 @@ const GLOBAL_COMPLEX: Record<string, Function> = {
|
|
|
43
43
|
const UNDEFINED_GLOBAL = { undefined: 1, void: 1, null: 1 };
|
|
44
44
|
const SIMPLE_NAMES: Record<string, string> = { String: 'string', Number: 'number', Boolean: 'boolean', Object: 'object' };
|
|
45
45
|
const GLOBAL_SIMPLE: Record<string, Function> = {
|
|
46
|
-
RegExp, Date, Number, Boolean, String, Function, Object, Error,
|
|
46
|
+
RegExp, Date, Number, Boolean, String, Function, Object, Error, BigInt,
|
|
47
|
+
ArrayBuffer, SharedArrayBuffer,
|
|
48
|
+
Uint8Array, Uint16Array, Uint32Array,
|
|
49
|
+
Int8Array, Int16Array, Int32Array,
|
|
50
|
+
Uint8ClampedArray, BigInt64Array, BigUint64Array,
|
|
51
|
+
Float16Array, Float32Array, Float64Array,
|
|
47
52
|
PromiseConstructor: Promise.constructor
|
|
48
53
|
};
|
|
49
54
|
|
|
@@ -177,7 +182,7 @@ export const TypeBuilder: {
|
|
|
177
182
|
literal: {
|
|
178
183
|
build: (resolver, type) => {
|
|
179
184
|
// Handle void/undefined
|
|
180
|
-
const name = resolver.getTypeAsString(type) ?? '';
|
|
185
|
+
const name = (resolver.getTypeAsString(type) ?? '').split('<')[0]; // Ensure we strip off any type arguments for lookup
|
|
181
186
|
const complexName = CoreUtil.getSymbol(type)?.getName() ?? '';
|
|
182
187
|
|
|
183
188
|
if (name in UNDEFINED_GLOBAL) {
|
|
@@ -366,4 +371,4 @@ export const TypeBuilder: {
|
|
|
366
371
|
return { key: 'managed', name, importName };
|
|
367
372
|
}
|
|
368
373
|
}
|
|
369
|
-
};
|
|
374
|
+
};
|
package/src/state.ts
CHANGED
|
@@ -144,21 +144,20 @@ export class TransformerState implements State {
|
|
|
144
144
|
/**
|
|
145
145
|
* Import a decorator, generally to handle erasure
|
|
146
146
|
*/
|
|
147
|
-
importDecorator(
|
|
148
|
-
|
|
149
|
-
const ref = this.#imports.importFile(
|
|
147
|
+
importDecorator(location: string, name: string): ts.PropertyAccessExpression | undefined {
|
|
148
|
+
return this.#decorators.getOrInsertComputed(`${location}:${name}`, () => {
|
|
149
|
+
const ref = this.#imports.importFile(location);
|
|
150
150
|
const identifier = this.factory.createIdentifier(name);
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
return this.#decorators.get(name);
|
|
151
|
+
return this.factory.createPropertyAccessExpression(ref.identifier, identifier);
|
|
152
|
+
});
|
|
154
153
|
}
|
|
155
154
|
|
|
156
155
|
/**
|
|
157
156
|
* Create a decorator to add functionality to a declaration
|
|
158
157
|
*/
|
|
159
|
-
createDecorator(
|
|
160
|
-
this.importDecorator(
|
|
161
|
-
return CoreUtil.createDecorator(this.factory, this.#decorators.get(name)!, ...contents);
|
|
158
|
+
createDecorator(location: string, name: string, ...contents: (ts.Expression | undefined)[]): ts.Decorator {
|
|
159
|
+
this.importDecorator(location, name);
|
|
160
|
+
return CoreUtil.createDecorator(this.factory, this.#decorators.get(`${location}:${name}`)!, ...contents);
|
|
162
161
|
}
|
|
163
162
|
|
|
164
163
|
/**
|
|
@@ -189,7 +188,7 @@ export class TransformerState implements State {
|
|
|
189
188
|
getDecoratorList(node: ts.Node): DecoratorMeta[] {
|
|
190
189
|
return ts.canHaveDecorators(node) ? (ts.getDecorators(node) ?? [])
|
|
191
190
|
.map(decorator => this.getDecoratorMeta(decorator))
|
|
192
|
-
.filter(
|
|
191
|
+
.filter(metadata => !!metadata) : [];
|
|
193
192
|
}
|
|
194
193
|
|
|
195
194
|
/**
|
|
@@ -225,10 +224,7 @@ export class TransformerState implements State {
|
|
|
225
224
|
} else if (before !== undefined) {
|
|
226
225
|
idx = before;
|
|
227
226
|
}
|
|
228
|
-
|
|
229
|
-
this.added.set(idx, []);
|
|
230
|
-
}
|
|
231
|
-
this.added.get(idx)!.push(...added);
|
|
227
|
+
this.added.getOrInsert(idx, []).push(...added);
|
|
232
228
|
}
|
|
233
229
|
|
|
234
230
|
/**
|
|
@@ -280,15 +276,30 @@ export class TransformerState implements State {
|
|
|
280
276
|
return this.factory.createIdentifier(typeof name === 'string' ? name : name.getText());
|
|
281
277
|
}
|
|
282
278
|
|
|
279
|
+
/**
|
|
280
|
+
* Gets the module identifier target
|
|
281
|
+
*/
|
|
282
|
+
getModuleIdentifierTarget(): [string | undefined, string] {
|
|
283
|
+
const entry = this.#resolver.getFileImport(this.source.fileName);
|
|
284
|
+
return [entry?.module, entry?.relativeFile ?? ''];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Build a class identifier string
|
|
289
|
+
*/
|
|
290
|
+
buildClassId(text: string): string {
|
|
291
|
+
const [module, source] = this.getModuleIdentifierTarget();
|
|
292
|
+
return `${module}:${source}#${text}`;
|
|
293
|
+
}
|
|
294
|
+
|
|
283
295
|
/**
|
|
284
296
|
* Get filename identifier, regardless of module system
|
|
285
297
|
*/
|
|
286
298
|
getModuleIdentifier(): ts.Expression {
|
|
287
299
|
if (this.#moduleIdentifier === undefined) {
|
|
288
300
|
this.#moduleIdentifier = this.factory.createUniqueName('Δm');
|
|
289
|
-
const entry = this.#resolver.getFileImport(this.source.fileName);
|
|
290
301
|
const declaration = this.factory.createVariableDeclaration(this.#moduleIdentifier, undefined, undefined,
|
|
291
|
-
this.fromLiteral(
|
|
302
|
+
this.fromLiteral(this.getModuleIdentifierTarget())
|
|
292
303
|
);
|
|
293
304
|
this.addStatements([
|
|
294
305
|
this.factory.createVariableStatement([], this.factory.createVariableDeclarationList([declaration], ts.NodeFlags.Const))
|
|
@@ -309,7 +320,7 @@ export class TransformerState implements State {
|
|
|
309
320
|
const targetScope = typeof input === 'string' ? input : input[ModuleNameSymbol]!;
|
|
310
321
|
const target = `${targetScope}:${name}`;
|
|
311
322
|
const list = this.getDecoratorList(node);
|
|
312
|
-
return list.find(
|
|
323
|
+
return list.find(item => item.targets?.includes(target) && (!module || item.name === name && item.module === module))?.decorator;
|
|
313
324
|
}
|
|
314
325
|
|
|
315
326
|
/**
|
|
@@ -364,11 +375,11 @@ export class TransformerState implements State {
|
|
|
364
375
|
*/
|
|
365
376
|
registerIdentifier(id: string): [identifier: ts.Identifier, exists: boolean] {
|
|
366
377
|
let exists = true;
|
|
367
|
-
|
|
368
|
-
this.#syntheticIdentifiers.set(id, this.factory.createIdentifier(id));
|
|
378
|
+
const resolved = this.#syntheticIdentifiers.getOrInsertComputed(id, () => {
|
|
369
379
|
exists = false;
|
|
370
|
-
|
|
371
|
-
|
|
380
|
+
return this.factory.createIdentifier(id);
|
|
381
|
+
});
|
|
382
|
+
return [resolved, exists];
|
|
372
383
|
}
|
|
373
384
|
|
|
374
385
|
/**
|
|
@@ -403,30 +414,40 @@ export class TransformerState implements State {
|
|
|
403
414
|
/**
|
|
404
415
|
* Produce a foreign target type
|
|
405
416
|
*/
|
|
406
|
-
getForeignTarget(
|
|
417
|
+
getForeignTarget(typeOrClassId: ForeignType | string): ts.Expression {
|
|
407
418
|
const file = this.importFile(FOREIGN_TYPE_REGISTRY_FILE);
|
|
408
419
|
return this.factory.createCallExpression(this.createAccess(
|
|
409
420
|
file.identifier,
|
|
410
421
|
this.factory.createIdentifier('foreignType'),
|
|
411
422
|
), [], [
|
|
412
|
-
this.fromLiteral(
|
|
423
|
+
this.fromLiteral(typeof typeOrClassId === 'string' ? typeOrClassId : typeOrClassId.classId)
|
|
413
424
|
]);
|
|
414
425
|
}
|
|
415
426
|
|
|
416
427
|
/**
|
|
417
428
|
* Return a concrete type the given type of a node
|
|
418
429
|
*/
|
|
419
|
-
getConcreteType(node: ts.Node): ts.Expression {
|
|
430
|
+
getConcreteType(node: ts.Node, fallback?: ts.Expression): ts.Expression {
|
|
420
431
|
const type = this.resolveType(node);
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
432
|
+
try {
|
|
433
|
+
if (type.key === 'managed') {
|
|
434
|
+
return this.getOrImport(type);
|
|
435
|
+
} else if (type.key === 'foreign') {
|
|
436
|
+
return this.getForeignTarget(type);
|
|
437
|
+
} else {
|
|
438
|
+
const targetId = this.buildClassId(node.getText());
|
|
439
|
+
if (this.#resolver.isKnownFile(node.getSourceFile().fileName)) {
|
|
440
|
+
return this.getForeignTarget(targetId);
|
|
441
|
+
} else {
|
|
442
|
+
throw new Error(`Unable to import non - external type: ${node.getText()} ${type.key}: ${targetId} `);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
} catch (err) {
|
|
446
|
+
if (fallback) {
|
|
447
|
+
return fallback;
|
|
448
|
+
} else {
|
|
449
|
+
throw err;
|
|
450
|
+
}
|
|
430
451
|
}
|
|
431
452
|
}
|
|
432
453
|
|
package/src/types/shared.ts
CHANGED
|
@@ -27,7 +27,7 @@ export type Import = {
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
/** Template Literal Types */
|
|
30
|
-
export type TemplateLiteralPart = string | NumberConstructor | StringConstructor | BooleanConstructor;
|
|
30
|
+
export type TemplateLiteralPart = string | NumberConstructor | StringConstructor | BooleanConstructor | BigIntConstructor;
|
|
31
31
|
export type TemplateLiteral = { operation: 'and' | 'or', values: (TemplateLiteralPart | TemplateLiteral)[] };
|
|
32
32
|
|
|
33
33
|
export function transformCast<T>(input: unknown): T {
|
package/src/types/visitor.ts
CHANGED
|
@@ -35,10 +35,7 @@ export type Transformer = {
|
|
|
35
35
|
name: string;
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
export type TransformerSet<S extends State = State> =
|
|
39
|
-
before?: Map<string, NodeTransformer<S>[]>;
|
|
40
|
-
after?: Map<string, NodeTransformer<S>[]>;
|
|
41
|
-
};
|
|
38
|
+
export type TransformerSet<S extends State = State> = Map<'before' | 'after', Map<string, NodeTransformer<S>[]>>;
|
|
42
39
|
|
|
43
40
|
export interface NodeTransformer<S extends State = State, T extends TransformerType = TransformerType, N extends ts.Node = ts.Node> {
|
|
44
41
|
type: T;
|
package/src/util/declaration.ts
CHANGED
|
@@ -33,7 +33,7 @@ export class DeclarationUtil {
|
|
|
33
33
|
* Find declaration for a type, symbol or a declaration
|
|
34
34
|
*/
|
|
35
35
|
static getDeclarations(type: ts.Type | ts.Symbol | ts.Declaration[]): ts.Declaration[] {
|
|
36
|
-
let declarations: ts.Declaration[]
|
|
36
|
+
let declarations: ts.Declaration[];
|
|
37
37
|
if (Array.isArray(type)) {
|
|
38
38
|
declarations = type;
|
|
39
39
|
} else {
|
package/src/util/literal.ts
CHANGED
|
@@ -56,6 +56,8 @@ export class LiteralUtil {
|
|
|
56
56
|
} else if (typeof value === 'number') {
|
|
57
57
|
const number = factory.createNumericLiteral(Math.abs(value));
|
|
58
58
|
value = value < 0 ? factory.createPrefixMinus(number) : number;
|
|
59
|
+
} else if (typeof value === 'bigint') {
|
|
60
|
+
value = factory.createBigIntLiteral(value.toString());
|
|
59
61
|
} else if (typeof value === 'boolean') {
|
|
60
62
|
value = value ? factory.createTrue() : factory.createFalse();
|
|
61
63
|
} else if (value instanceof RegExp) {
|
|
@@ -103,6 +105,8 @@ export class LiteralUtil {
|
|
|
103
105
|
return null;
|
|
104
106
|
} else if (ts.isStringLiteral(value)) {
|
|
105
107
|
return value.text;
|
|
108
|
+
} else if (ts.isBigIntLiteral(value)) {
|
|
109
|
+
return BigInt(value.text.replace(/n$/i, ''));
|
|
106
110
|
} else if (ts.isNumericLiteral(value)) {
|
|
107
111
|
const txt = value.text;
|
|
108
112
|
if (txt.includes('.')) {
|
|
@@ -179,6 +183,8 @@ export class LiteralUtil {
|
|
|
179
183
|
out.push('(?:true|false)');
|
|
180
184
|
} else if (value === String) {
|
|
181
185
|
out.push('.+');
|
|
186
|
+
} else if (typeof value === 'bigint') {
|
|
187
|
+
out.push(`${value}n`);
|
|
182
188
|
} else if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
183
189
|
out.push(`${value}`);
|
|
184
190
|
} else {
|
package/src/visitor.ts
CHANGED
|
@@ -60,22 +60,14 @@ export class VisitorFactory<S extends State = State> {
|
|
|
60
60
|
*/
|
|
61
61
|
#init(transformers: NodeTransformer<S, TransformerType, ts.Node>[]): void {
|
|
62
62
|
for (const trn of transformers) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
const set = this.#transformers.get(trn.type)!;
|
|
63
|
+
const set = this.#transformers.getOrInsert(trn.type, new Map());
|
|
64
|
+
|
|
67
65
|
const targets = trn.target && trn.target.length ? trn.target : ['__all__'];
|
|
68
66
|
|
|
69
67
|
for (const target of targets) {
|
|
70
68
|
for (const phase of ['before', 'after'] as const) {
|
|
71
69
|
if (trn[phase]) {
|
|
72
|
-
|
|
73
|
-
set[phase] = new Map();
|
|
74
|
-
}
|
|
75
|
-
if (!set[phase]!.has(target)) {
|
|
76
|
-
set[phase]!.set(target, []);
|
|
77
|
-
}
|
|
78
|
-
set[phase]!.get(target)!.push(trn);
|
|
70
|
+
set.getOrInsert(phase, new Map()).getOrInsert(target, []).push(trn);
|
|
79
71
|
}
|
|
80
72
|
}
|
|
81
73
|
}
|
|
@@ -138,11 +130,11 @@ export class VisitorFactory<S extends State = State> {
|
|
|
138
130
|
* Handle transformer that target both ascent and descent
|
|
139
131
|
*/
|
|
140
132
|
executePhaseAlways<T extends ts.Node>(state: S, set: TransformerSet<S>, phase: TransformPhase, node: T): T | undefined {
|
|
141
|
-
if (!set
|
|
133
|
+
if (!set.get(phase)?.size) {
|
|
142
134
|
return;
|
|
143
135
|
}
|
|
144
136
|
|
|
145
|
-
for (const all of set
|
|
137
|
+
for (const all of set.get(phase)!.get('__all__') ?? []) {
|
|
146
138
|
node = all[phase]!<T>(state, node) ?? node;
|
|
147
139
|
}
|
|
148
140
|
return node;
|
|
@@ -152,7 +144,7 @@ export class VisitorFactory<S extends State = State> {
|
|
|
152
144
|
* Handle a single phase of transformation
|
|
153
145
|
*/
|
|
154
146
|
executePhase<T extends ts.Node>(state: S, set: TransformerSet<S>, phase: TransformPhase, node: T): T | undefined {
|
|
155
|
-
if (!set
|
|
147
|
+
if (!set.get(phase)?.size) {
|
|
156
148
|
return;
|
|
157
149
|
}
|
|
158
150
|
|
|
@@ -169,7 +161,7 @@ export class VisitorFactory<S extends State = State> {
|
|
|
169
161
|
}
|
|
170
162
|
|
|
171
163
|
for (const [key, decorator] of targets.entries()) {
|
|
172
|
-
const values = set
|
|
164
|
+
const values = set.get(phase)!.get(key);
|
|
173
165
|
if (!values || !values.length) {
|
|
174
166
|
continue;
|
|
175
167
|
}
|