@travetto/transformer 7.1.2 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/transformer",
3
- "version": "7.1.2",
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": "^7.1.2",
28
+ "@travetto/manifest": "^8.0.0-alpha.0",
29
29
  "tslib": "^2.8.1",
30
- "typescript": "^5.9.3"
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
- if (!this.#identifiers.has(file)) {
113
+ return this.#identifiers.getOrInsertComputed(file, () => {
114
114
  if (name) {
115
- this.#identifiers.set(file, this.factory.createIdentifier(name));
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
- this.#identifiers.set(file, this.factory.createIdentifier(`Δ${key}${suffix ? suffix : ''}`));
119
+ return this.factory.createIdentifier(`Δ${key}${suffix ? suffix : ''}`);
120
120
  }
121
- }
122
- return this.#identifiers.get(file)!;
121
+ });
123
122
  }
124
123
 
125
124
  /**
@@ -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(pth: string, name: string): ts.PropertyAccessExpression | undefined {
148
- if (!this.#decorators.has(`${pth}:${name}`)) {
149
- const ref = this.#imports.importFile(pth);
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
- this.#decorators.set(name, this.factory.createPropertyAccessExpression(ref.identifier, identifier));
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(pth: string, name: string, ...contents: (ts.Expression | undefined)[]): ts.Decorator {
160
- this.importDecorator(pth, name);
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(meta => !!meta) : [];
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
- if (!this.added.has(idx)) {
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([entry?.module, entry?.relativeFile ?? ''])
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(meta => meta.targets?.includes(target) && (!module || meta.name === name && meta.module === module))?.decorator;
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
- if (!this.#syntheticIdentifiers.has(id)) {
368
- this.#syntheticIdentifiers.set(id, this.factory.createIdentifier(id));
378
+ const resolved = this.#syntheticIdentifiers.getOrInsertComputed(id, () => {
369
379
  exists = false;
370
- }
371
- return [this.#syntheticIdentifiers.get(id)!, exists];
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(type: ForeignType): ts.Expression {
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(type.classId)
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
- if (type.key === 'managed') {
423
- return this.getOrImport(type);
424
- } else if (type.key === 'foreign') {
425
- return this.getForeignTarget(type);
426
- } else {
427
- const file = node.getSourceFile().fileName;
428
- const source = this.getFileImportName(file);
429
- throw new Error(`Unable to import non-external type: ${node.getText()} ${type.key}: ${source}`);
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
 
@@ -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 {
@@ -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;
@@ -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 {
@@ -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
- if (!this.#transformers.has(trn.type)) {
64
- this.#transformers.set(trn.type, {});
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
- if (!set[phase]) {
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[phase]?.size) {
133
+ if (!set.get(phase)?.size) {
142
134
  return;
143
135
  }
144
136
 
145
- for (const all of set[phase]!.get('__all__') ?? []) {
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[phase]?.size) {
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[phase]!.get(key);
164
+ const values = set.get(phase)!.get(key);
173
165
  if (!values || !values.length) {
174
166
  continue;
175
167
  }