@travetto/transformer 5.0.11 → 5.0.12

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 CHANGED
@@ -104,10 +104,10 @@ export class Test {
104
104
  Object.defineProperty(exports, "__esModule", { value: true });
105
105
  exports.TEST = void 0;
106
106
  const tslib_1 = require("tslib");
107
- const Ⲑ_function_1 = tslib_1.__importStar(require("@travetto/runtime/src/function.js"));
108
- var ᚕm = ["@travetto/transformer", "doc/upper.ts"];
107
+ const Δfunction = tslib_1.__importStar(require("@travetto/runtime/src/function.js"));
108
+ var mod_1 = ["@travetto/transformer", "doc/upper.ts"];
109
109
  class TEST {
110
- static Ⲑinit = Ⲑ_function_1.registerFunction(TEST, ᚕm, { hash: 649563175, lines: [1, 9] }, { COMPUTEAGE: { hash: 1286718349, lines: [6, 8, 7] } }, false, false);
110
+ static { Δfunction.registerFunction(TEST, mod_1, { hash: 649563175, lines: [1, 9] }, { COMPUTEAGE: { hash: 1286718349, lines: [6, 8, 7] } }, false); }
111
111
  NAME;
112
112
  AGE;
113
113
  DOB;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/transformer",
3
- "version": "5.0.11",
3
+ "version": "5.0.12",
4
4
  "description": "Functionality for AST transformations, with transformer registration, and general utils",
5
5
  "keywords": [
6
6
  "typescript",
@@ -24,7 +24,7 @@
24
24
  "directory": "module/transformer"
25
25
  },
26
26
  "dependencies": {
27
- "@travetto/manifest": "^5.0.10",
27
+ "@travetto/manifest": "^5.0.11",
28
28
  "tslib": "^2.8.1",
29
29
  "typescript": "^5.7.2"
30
30
  },
package/src/importer.ts CHANGED
@@ -19,7 +19,7 @@ export class ImportManager {
19
19
  #newImports = new Map<string, Import>();
20
20
  #imports: Map<string, Import>;
21
21
  #idx: Record<string, number> = {};
22
- #ids = new Map<string, string>();
22
+ #ids = new Map<string, ts.Identifier>();
23
23
  #importName: string;
24
24
  #resolver: TransformResolver;
25
25
 
@@ -89,7 +89,6 @@ export class ImportManager {
89
89
  false;
90
90
  }
91
91
 
92
-
93
92
  /**
94
93
  * Is a file or import an untyped file access
95
94
  * @param fileOrImport
@@ -101,15 +100,17 @@ export class ImportManager {
101
100
  }
102
101
 
103
102
  /**
104
- * Produces a unique ID for a given file, importing if needed
103
+ * Produces a unique ID for a given file
105
104
  */
106
- getId(file: string, name?: string): string {
105
+ getId(file: string, name?: string): ts.Identifier {
107
106
  if (!this.#ids.has(file)) {
108
107
  if (name) {
109
- this.#ids.set(file, name);
108
+ this.#ids.set(file, this.factory.createIdentifier(name));
110
109
  } else {
111
- const key = path.basename(file, path.extname(file)).replace(/[^A-Za-z0-9]+/g, '_');
112
- this.#ids.set(file, `Ⲑ_${key}_${this.#idx[key] = (this.#idx[key] || 0) + 1}`);
110
+ const key = path.basename(file, path.extname(file)).replace(/\W+/g, '_');
111
+ const suffix = this.#idx[key] = (this.#idx[key] ?? -1) + 1;
112
+ // eslint-disable-next-line no-bitwise
113
+ this.#ids.set(file, this.factory.createIdentifier(`Δ${key}${suffix ? suffix : ''}`));
113
114
  }
114
115
  }
115
116
  return this.#ids.get(file)!;
@@ -131,15 +132,15 @@ export class ImportManager {
131
132
  }
132
133
 
133
134
  if (!D_OR_D_TS_EXT_RE.test(file) && !this.#newImports.has(file)) {
134
- const id = this.getId(file, name);
135
+ const ident = this.getId(file, name);
136
+ const uniqueName = ident.text;
135
137
 
136
- if (this.#imports.has(id)) { // Already imported, be cool
137
- return this.#imports.get(id)!;
138
+ if (this.#imports.has(uniqueName)) { // Already imported, be cool
139
+ return this.#imports.get(uniqueName)!;
138
140
  }
139
141
 
140
- const ident = this.factory.createIdentifier(id);
141
142
  const newImport = { path: file, ident };
142
- this.#imports.set(ident.escapedText.toString(), newImport);
143
+ this.#imports.set(uniqueName, newImport);
143
144
  this.#newImports.set(file, newImport);
144
145
  }
145
146
  return this.#newImports.get(file)!;
@@ -156,7 +157,7 @@ export class ImportManager {
156
157
  switch (type.key) {
157
158
  case 'managed':
158
159
  case 'literal': this.importFromResolved(...type.typeArguments || []); break;
159
- case 'union':
160
+ case 'composition':
160
161
  case 'tuple': this.importFromResolved(...type.subTypes || []); break;
161
162
  case 'shape': this.importFromResolved(...Object.values(type.fieldTypes)); break;
162
163
  }
package/src/register.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import ts from 'typescript';
2
2
 
3
- import { DecoratorMeta, NodeTransformer, State, TransformPhase, TransformerType, Transformer, ModuleNameⲐ } from './types/visitor';
3
+ import { DecoratorMeta, NodeTransformer, State, TransformPhase, TransformerType, Transformer, ModuleNameSymbol } from './types/visitor';
4
4
 
5
5
  const HandlersProp = Symbol.for('@travetto/transformer:handlers');
6
6
 
@@ -18,7 +18,7 @@ export function getAllTransformers(obj: Record<string, { [HandlersProp]?: NodeTr
18
18
  return Object.values(obj)
19
19
  .flatMap(x => {
20
20
  if (isTransformer(x)) {
21
- x[ModuleNameⲐ] = module;
21
+ x[ModuleNameSymbol] = module;
22
22
  }
23
23
  return (x[HandlersProp] ?? []);
24
24
  })
@@ -9,7 +9,7 @@ import { DeclarationUtil } from '../util/declaration';
9
9
  import { LiteralUtil } from '../util/literal';
10
10
  import { transformCast, TemplateLiteralPart } from '../types/shared';
11
11
 
12
- import { Type, AnyType, UnionType, TransformResolver, TemplateType } from './types';
12
+ import { Type, AnyType, CompositionType, TransformResolver, TemplateType } from './types';
13
13
  import { CoerceUtil } from './coerce';
14
14
 
15
15
  const TYPINGS_RE = /[.]d[.][cm]?ts$/;
@@ -39,7 +39,7 @@ const GLOBAL_SIMPLE: Record<string, Function> = {
39
39
  type Category =
40
40
  'void' | 'undefined' | 'concrete' | 'unknown' |
41
41
  'tuple' | 'shape' | 'literal' | 'template' | 'managed' |
42
- 'union' | 'foreign';
42
+ 'composition' | 'foreign';
43
43
 
44
44
  /**
45
45
  * Type categorizer, input for builder
@@ -98,12 +98,14 @@ export function TypeCategorize(resolver: TransformResolver, type: ts.Type): { ca
98
98
  ts.TypeFlags.Void | ts.TypeFlags.Undefined
99
99
  )) {
100
100
  return { category: 'literal', type };
101
- } else if (type.isUnion()) {
102
- return { category: 'union', type };
101
+ } else if (type.isUnionOrIntersection()) {
102
+ return { category: 'composition', type };
103
103
  } else if (objectFlags & ts.ObjectFlags.Tuple) {
104
104
  return { category: 'tuple', type };
105
105
  } else if (type.isLiteral()) {
106
106
  return { category: 'shape', type };
107
+ } else if ((objectFlags & ts.ObjectFlags.Mapped)) { // Mapped types: Pick, Omit, Exclude, Retain
108
+ return { category: 'shape', type };
107
109
  }
108
110
  return { category: 'literal', type };
109
111
  }
@@ -207,21 +209,21 @@ export const TypeBuilder: {
207
209
  return { key: 'managed', name, importName, tsTypeArguments };
208
210
  }
209
211
  },
210
- union: {
211
- build: (resolver, uType: ts.UnionType) => {
212
+ composition: {
213
+ build: (resolver, uType: ts.UnionOrIntersectionType) => {
212
214
  let undefinable = false;
213
215
  let nullable = false;
214
216
  const remainder = uType.types.filter(ut => {
215
217
  const u = (ut.getFlags() & (ts.TypeFlags.Undefined)) > 0;
216
218
  const n = (ut.getFlags() & (ts.TypeFlags.Null)) > 0;
217
- undefinable = undefinable || u;
218
- nullable = nullable || n;
219
+ undefinable ||= u;
220
+ nullable ||= n;
219
221
  return !(u || n);
220
222
  });
221
223
  const name = CoreUtil.getSymbol(uType)?.getName();
222
- return { key: 'union', name, undefinable, nullable, tsSubTypes: remainder, subTypes: [] };
224
+ return { key: 'composition', name, undefinable, nullable, tsSubTypes: remainder, subTypes: [], operation: uType.isUnion() ? 'or' : 'and' };
223
225
  },
224
- finalize: (type: UnionType) => {
226
+ finalize: (type: CompositionType) => {
225
227
  const { undefinable, nullable, subTypes } = type;
226
228
  const [first] = subTypes;
227
229
 
@@ -237,6 +239,8 @@ export const TypeBuilder: {
237
239
  return { undefinable, nullable, ...first };
238
240
  } else if (first.key === 'literal' && subTypes.every(el => el.name === first.name)) { // We have a common
239
241
  type.commonType = first;
242
+ } else if (type.operation === 'and' && first.key === 'shape' && subTypes.every(el => el.key === 'shape')) { // All shapes
243
+ return { importName: first.importName, name: first.name, key: 'shape', fieldTypes: subTypes.reduce((acc, x) => ({ ...acc, ...x.fieldTypes }), {}) };
240
244
  }
241
245
  return type;
242
246
  }
@@ -117,7 +117,7 @@ export interface TemplateType extends Type<'template'> {
117
117
  /**
118
118
  * Union type
119
119
  */
120
- export interface UnionType extends Type<'union'> {
120
+ export interface CompositionType extends Type<'composition'> {
121
121
  /**
122
122
  * A common type if derivable, e.g. 'a'|'b' will have a common type of string
123
123
  */
@@ -130,6 +130,10 @@ export interface UnionType extends Type<'union'> {
130
130
  * Type Info
131
131
  */
132
132
  tsSubTypes?: ts.Type[];
133
+ /**
134
+ * Operation to perform
135
+ */
136
+ operation?: 'and' | 'or';
133
137
  }
134
138
 
135
139
  /**
@@ -177,7 +181,7 @@ export interface ForeignType extends Type<'foreign'> {
177
181
  export interface UnknownType extends Type<'unknown'> { }
178
182
 
179
183
  export type AnyType =
180
- TupleType | ShapeType | UnionType | LiteralType |
184
+ TupleType | ShapeType | CompositionType | LiteralType |
181
185
  ManagedType | PointerType | UnknownType | ForeignType | TemplateType;
182
186
 
183
187
  /**
package/src/state.ts CHANGED
@@ -2,8 +2,8 @@ import ts from 'typescript';
2
2
 
3
3
  import { path, ManifestIndex } from '@travetto/manifest';
4
4
 
5
- import { ManagedType, AnyType, ForeignType } from './resolver/types';
6
- import { State, DecoratorMeta, Transformer, ModuleNameⲐ } from './types/visitor';
5
+ import { ManagedType, AnyType } from './resolver/types';
6
+ import { State, DecoratorMeta, Transformer, ModuleNameSymbol } from './types/visitor';
7
7
  import { SimpleResolver } from './resolver/service';
8
8
  import { ImportManager } from './importer';
9
9
  import { Import } from './types/shared';
@@ -23,12 +23,14 @@ function hasEscapedName(n: ts.Node): n is ts.Node & { name: { escapedText: strin
23
23
  return !!n && 'name' in n && typeof n.name === 'object' && !!n.name && 'escapedText' in n.name && !!n.name.escapedText;
24
24
  }
25
25
 
26
+ function isRedefinableDeclaration(x: ts.Node): x is ts.InterfaceDeclaration | ts.ClassDeclaration | ts.FunctionDeclaration {
27
+ return ts.isFunctionDeclaration(x) || ts.isClassDeclaration(x) || ts.isInterfaceDeclaration(x);
28
+ }
29
+
26
30
  /**
27
31
  * Transformer runtime state
28
32
  */
29
33
  export class TransformerState implements State {
30
- static SYNTHETIC_EXT = 'Ⲑsyn';
31
-
32
34
  #resolver: SimpleResolver;
33
35
  #imports: ImportManager;
34
36
  #modIdent: ts.Identifier;
@@ -268,7 +270,7 @@ export class TransformerState implements State {
268
270
  */
269
271
  getModuleIdentifier(): ts.Expression {
270
272
  if (this.#modIdent === undefined) {
271
- this.#modIdent = this.createIdentifier('ᚕm');
273
+ this.#modIdent = this.factory.createUniqueName('mod');
272
274
  const entry = this.#resolver.getFileImport(this.source.fileName);
273
275
  const decl = this.factory.createVariableDeclaration(this.#modIdent, undefined, undefined,
274
276
  this.fromLiteral([entry?.module, entry?.relativeFile ?? ''])
@@ -288,7 +290,7 @@ export class TransformerState implements State {
288
290
  * @param module
289
291
  */
290
292
  findDecorator(mod: string | Transformer, node: ts.Node, name: string, module?: string): ts.Decorator | undefined {
291
- mod = typeof mod === 'string' ? mod : mod[ModuleNameⲐ]!;
293
+ mod = typeof mod === 'string' ? mod : mod[ModuleNameSymbol]!;
292
294
  const target = `${mod}:${name}`;
293
295
  const list = this.getDecoratorList(node);
294
296
  return list.find(x => x.targets?.includes(target) && (!module || x.name === name && x.module === module))?.dec;
@@ -299,7 +301,7 @@ export class TransformerState implements State {
299
301
  * @param node
300
302
  * @param type
301
303
  */
302
- generateUniqueIdentifier(node: ts.Node, type: AnyType): string {
304
+ generateUniqueIdentifier(node: ts.Node, type: AnyType, suffix?: string): string {
303
305
  let unique: string[] = [];
304
306
  let name = type.name && !type.name.startsWith('_') ? type.name : '';
305
307
  if (!name && hasEscapedName(node)) {
@@ -315,14 +317,15 @@ export class TransformerState implements State {
315
317
  if (fileName === this.source.fileName) { // if in same file suffix with location
316
318
  let child: ts.Node = tgt;
317
319
  while (child && !ts.isSourceFile(child)) {
318
- if (ts.isFunctionDeclaration(child) || ts.isMethodDeclaration(child) || ts.isClassDeclaration(child) || ts.isInterfaceDeclaration(child)) {
320
+ if (isRedefinableDeclaration(child) || ts.isMethodDeclaration(child) || ts.isParameter(child)) {
319
321
  if (child.name) {
320
322
  unique.push(child.name.getText());
321
323
  }
322
324
  }
323
325
  child = child.parent;
324
326
  }
325
- if (!unique.length) { // a top level type
327
+
328
+ if (!unique.length) {
326
329
  unique.push(ts.getLineAndCharacterOfPosition(tgt.getSourceFile(), tgt.getStart()).line.toString());
327
330
  }
328
331
  } else {
@@ -334,7 +337,7 @@ export class TransformerState implements State {
334
337
 
335
338
  if (unique.length) { // Make unique to file
336
339
  unique.unshift(this.#resolver.getFileImportName(this.source.fileName));
337
- return `${name}__${SystemUtil.naiveHashString(unique.join(':'), 12)}`;
340
+ return `${name}__${SystemUtil.naiveHashString(unique.join(':'), 12)}${suffix || ''}`;
338
341
  } else {
339
342
  return name;
340
343
  }
@@ -343,8 +346,7 @@ export class TransformerState implements State {
343
346
  /**
344
347
  * Register synthetic identifier
345
348
  */
346
- createSyntheticIdentifier(id: string): [identifier: ts.Identifier, exists: boolean] {
347
- id = `${id}${TransformerState.SYNTHETIC_EXT}`;
349
+ registerIdentifier(id: string): [identifier: ts.Identifier, exists: boolean] {
348
350
  let exists = true;
349
351
  if (!this.#syntheticIdentifiers.has(id)) {
350
352
  this.#syntheticIdentifiers.set(id, this.factory.createIdentifier(id));
@@ -381,13 +383,4 @@ export class TransformerState implements State {
381
383
  getFileImportName(file: string): string {
382
384
  return this.#resolver.getFileImportName(file);
383
385
  }
384
-
385
- /**
386
- * Get foreign target
387
- */
388
- getForeignTarget(state: TransformerState, ret: ForeignType): ts.Expression {
389
- return state.fromLiteral({
390
- Ⲑid: `${ret.source.split('node_modules/')[1]}+${ret.name}`
391
- });
392
- }
393
386
  }
@@ -26,7 +26,6 @@ export type Import = {
26
26
  stmt?: ts.ImportDeclaration;
27
27
  };
28
28
 
29
-
30
29
  /** Template Literal Types */
31
30
  export type TemplateLiteralPart = string | NumberConstructor | StringConstructor | BooleanConstructor;
32
31
  export type TemplateLiteral = { op: 'and' | 'or', values: (TemplateLiteralPart | TemplateLiteral)[] };
@@ -26,10 +26,10 @@ export type TransformerType =
26
26
  'class' | 'method' | 'property' | 'getter' | 'setter' | 'parameter' |
27
27
  'static-method' | 'call' | 'function' | 'file';
28
28
 
29
- export const ModuleNameⲐ = Symbol.for('@travetto/transformer:id');
29
+ export const ModuleNameSymbol = Symbol.for('@travetto/transformer:id');
30
30
 
31
31
  export type Transformer = {
32
- [ModuleNameⲐ]?: string;
32
+ [ModuleNameSymbol]?: string;
33
33
  name: string;
34
34
  };
35
35