@travetto/transformer 3.0.0-rc.3 → 3.0.0-rc.6

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
@@ -1,5 +1,5 @@
1
1
  <!-- This file was generated by @travetto/doc and should not be modified directly -->
2
- <!-- Please modify https://github.com/travetto/travetto/tree/main/module/transformer/doc.ts and execute "npx trv doc" to rebuild -->
2
+ <!-- Please modify https://github.com/travetto/travetto/tree/main/module/transformer/DOC.ts and execute "npx trv doc" to rebuild -->
3
3
  # Transformation
4
4
  ## Functionality for AST transformations, with transformer registration, and general utils
5
5
 
@@ -10,7 +10,7 @@ npm install @travetto/transformer
10
10
 
11
11
  This module provides support for enhanced AST transformations, and declarative transformer registration, with common patterns to support all the transformers used throughout the framework. Transformations are located by `support/transformer.<name>.ts` as the filename.
12
12
 
13
- The module is primarily aimed at extremely advanced usages for things that cannot be detected at runtime. The [Registry](https://github.com/travetto/travetto/tree/main/module/registry#readme "Patterns and utilities for handling registration of metadata and functionality for run-time use") module already has knowledge of all `class`es and `field`s, and is able to listen to changes there. Many of the modules build upon work by some of the foundational transformers defined in [Registry](https://github.com/travetto/travetto/tree/main/module/registry#readme "Patterns and utilities for handling registration of metadata and functionality for run-time use"), [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding. ") and [Dependency Injection](https://github.com/travetto/travetto/tree/main/module/di#readme "Dependency registration/management and injection support."). These all center around defining a registry of classes, and associated type information.
13
+ The module is primarily aimed at extremely advanced usages for things that cannot be detected at runtime. The [Registry](https://github.com/travetto/travetto/tree/main/module/registry#readme "Patterns and utilities for handling registration of metadata and functionality for run-time use") module already has knowledge of all `class`es and `field`s, and is able to listen to changes there. Many of the modules build upon work by some of the foundational transformers defined in [Registry](https://github.com/travetto/travetto/tree/main/module/registry#readme "Patterns and utilities for handling registration of metadata and functionality for run-time use"), [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding.") and [Dependency Injection](https://github.com/travetto/travetto/tree/main/module/di#readme "Dependency registration/management and injection support."). These all center around defining a registry of classes, and associated type information.
14
14
 
15
15
  Because working with the [Typescript](https://typescriptlang.org) API can be delicate (and open to breaking changes), creating new transformers should be done cautiously.
16
16
 
@@ -20,22 +20,19 @@ Below is an example of a transformer that upper cases all `class`, `method` and
20
20
 
21
21
  **Code: Sample Transformer - Upper case all declarations**
22
22
  ```typescript
23
- import * as ts from 'typescript';
23
+ import ts from 'typescript';
24
24
 
25
- import { OnProperty, TransformerState, OnMethod, OnClass, TransformerId } from '@travetto/transformer';
25
+ import { OnProperty, TransformerState, OnMethod, OnClass } from '@travetto/transformer';
26
26
 
27
27
  export class MakeUpper {
28
28
 
29
- static [TransformerId] = '@trv:transformer-test';
30
-
31
29
  @OnProperty()
32
30
  static handleProperty(state: TransformerState, node: ts.PropertyDeclaration): ts.PropertyDeclaration {
33
- if (!state.source.fileName.includes('doc/src')) {
31
+ if (!state.file.includes('doc/src')) {
34
32
  return node;
35
33
  }
36
34
  return state.factory.updatePropertyDeclaration(
37
35
  node,
38
- [],
39
36
  node.modifiers,
40
37
  node.name.getText().toUpperCase(),
41
38
  undefined,
@@ -46,12 +43,11 @@ export class MakeUpper {
46
43
 
47
44
  @OnClass()
48
45
  static handleClass(state: TransformerState, node: ts.ClassDeclaration): ts.ClassDeclaration {
49
- if (!state.source.fileName.includes('doc/src')) {
46
+ if (!state.file.includes('doc/src')) {
50
47
  return node;
51
48
  }
52
49
  return state.factory.updateClassDeclaration(
53
50
  node,
54
- [],
55
51
  node.modifiers,
56
52
  state.createIdentifier(node.name!.getText().toUpperCase()),
57
53
  node.typeParameters,
@@ -62,12 +58,11 @@ export class MakeUpper {
62
58
 
63
59
  @OnMethod()
64
60
  static handleMethod(state: TransformerState, node: ts.MethodDeclaration): ts.MethodDeclaration {
65
- if (!state.source.fileName.includes('doc/src')) {
61
+ if (!state.file.includes('doc/src')) {
66
62
  return node;
67
63
  }
68
64
  return state.factory.updateMethodDeclaration(
69
65
  node,
70
- [],
71
66
  node.modifiers,
72
67
  undefined,
73
68
  state.createIdentifier(node.name.getText().toUpperCase()),
package/__index__.ts ADDED
@@ -0,0 +1,15 @@
1
+ export * from './src/state';
2
+ export * from './src/visitor';
3
+ export * from './src/register';
4
+ export * from './src/types/visitor';
5
+ export * from './src/types/shared';
6
+ export type { AnyType } from './src/resolver/types';
7
+ export * from './src/manager';
8
+
9
+ export * from './src/util/core';
10
+ export * from './src/util/declaration';
11
+ export * from './src/util/decorator';
12
+ export * from './src/util/doc';
13
+ export * from './src/util/literal';
14
+ export * from './src/util/log';
15
+ export * from './src/util/system';
package/package.json CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/transformer",
3
- "displayName": "Transformation",
4
- "version": "3.0.0-rc.3",
3
+ "version": "3.0.0-rc.6",
5
4
  "description": "Functionality for AST transformations, with transformer registration, and general utils",
6
5
  "keywords": [
7
6
  "typescript",
@@ -15,17 +14,25 @@
15
14
  "name": "Travetto Framework"
16
15
  },
17
16
  "files": [
18
- "index.ts",
17
+ "__index__.ts",
19
18
  "src",
20
- "test-support"
19
+ "support"
21
20
  ],
22
- "main": "index.ts",
21
+ "main": "__index__.ts",
23
22
  "repository": {
24
23
  "url": "https://github.com/travetto/travetto.git",
25
24
  "directory": "module/transformer"
26
25
  },
27
26
  "dependencies": {
28
- "@travetto/base": "^3.0.0-rc.1"
27
+ "@travetto/manifest": "^3.0.0-rc.3",
28
+ "tslib": "^2.4.1",
29
+ "typescript": "^4.9.4"
30
+ },
31
+ "travetto": {
32
+ "displayName": "Transformation",
33
+ "profiles": [
34
+ "compile"
35
+ ]
29
36
  },
30
37
  "publishConfig": {
31
38
  "access": "public"
package/src/importer.ts CHANGED
@@ -1,13 +1,16 @@
1
- import * as ts from 'typescript';
2
- import { basename, dirname, relative } from 'path';
1
+ import ts from 'typescript';
3
2
 
4
- import { PathUtil } from '@travetto/boot';
5
- import { ModuleUtil } from '@travetto/boot/src/internal/module-util';
3
+ import { PackageUtil, path } from '@travetto/manifest';
6
4
 
7
5
  import { AnyType, ExternalType } from './resolver/types';
8
6
  import { ImportUtil } from './util/import';
9
7
  import { CoreUtil } from './util/core';
10
8
  import { Import } from './types/shared';
9
+ import { LiteralUtil } from './util/literal';
10
+ import { DeclarationUtil } from './util/declaration';
11
+ import { TransformerIndex } from './manifest-index';
12
+
13
+ const D_OR_D_TS_EXT_RE = /[.]d([.]ts)?$/;
11
14
 
12
15
  /**
13
16
  * Manages imports within a ts.SourceFile
@@ -18,18 +21,83 @@ export class ImportManager {
18
21
  #imports: Map<string, Import>;
19
22
  #idx: Record<string, number> = {};
20
23
  #ids = new Map<string, string>();
24
+ #importName: string;
25
+ #index: TransformerIndex;
21
26
 
22
- constructor(public source: ts.SourceFile, public factory: ts.NodeFactory) {
27
+ constructor(public source: ts.SourceFile, public factory: ts.NodeFactory, index: TransformerIndex) {
23
28
  this.#imports = ImportUtil.collectImports(source);
29
+ this.#index = index;
30
+ this.#importName = index.getImportName(source.fileName);
31
+ }
32
+
33
+ #getImportFile(spec?: ts.Expression): string | undefined {
34
+ if (spec && ts.isStringLiteral(spec)) {
35
+ return spec.text.replace(/^['"]|["']$/g, '');
36
+ }
37
+ }
38
+
39
+ #rewriteModuleSpecifier(spec: ts.Expression | undefined): ts.Expression | undefined {
40
+ const fileOrImport = this.#getImportFile(spec);
41
+ if (
42
+ fileOrImport &&
43
+ (fileOrImport.startsWith('.') || this.#index.getFromImport(fileOrImport)) &&
44
+ !/[.]([mc]?js|ts|json)$/.test(fileOrImport)
45
+ ) {
46
+ return LiteralUtil.fromLiteral(this.factory, `${fileOrImport}.js`);
47
+ }
48
+ return spec;
49
+ }
50
+
51
+ #rewriteImportClause(
52
+ spec: ts.Expression | undefined,
53
+ clause: ts.ImportClause | undefined,
54
+ checker: ts.TypeChecker
55
+ ): ts.ImportClause | undefined {
56
+ if (!(spec && clause?.namedBindings && ts.isNamedImports(clause.namedBindings))) {
57
+ return clause;
58
+ }
59
+
60
+ const fileOrImport = this.#getImportFile(spec);
61
+ if (!(fileOrImport && (fileOrImport.startsWith('.') || this.#index.getFromImport(fileOrImport)))) {
62
+ return clause;
63
+ }
64
+
65
+ const bindings = clause.namedBindings;
66
+ const newBindings: ts.ImportSpecifier[] = [];
67
+ // Remove all type only imports
68
+ for (const el of bindings.elements) {
69
+ if (!el.isTypeOnly) {
70
+ const type = checker.getTypeAtLocation(el.name);
71
+ const objFlags = DeclarationUtil.getObjectFlags(type);
72
+ const typeFlags = type.getFlags();
73
+ if (objFlags || typeFlags !== 1) {
74
+ newBindings.push(el);
75
+ }
76
+ }
77
+ }
78
+ if (newBindings.length !== bindings.elements.length) {
79
+ return this.factory.updateImportClause(
80
+ clause,
81
+ clause.isTypeOnly,
82
+ clause.name,
83
+ this.factory.createNamedImports(newBindings)
84
+ );
85
+ } else {
86
+ return clause;
87
+ }
24
88
  }
25
89
 
26
90
  /**
27
91
  * Produces a unique ID for a given file, importing if needed
28
92
  */
29
- getId(file: string): string {
93
+ getId(file: string, name?: string): string {
30
94
  if (!this.#ids.has(file)) {
31
- const key = basename(file).replace(/[.][^.]*$/, '').replace(/[^A-Za-z0-9]+/g, '_');
32
- this.#ids.set(file, `ᚕ_${key}_${this.#idx[key] = (this.#idx[key] || 0) + 1}`);
95
+ if (name) {
96
+ this.#ids.set(file, name);
97
+ } else {
98
+ const key = path.basename(file).replace(/[.][^.]*$/, '').replace(/[^A-Za-z0-9]+/g, '_');
99
+ this.#ids.set(file, `Ⲑ_${key}_${this.#idx[key] = (this.#idx[key] || 0) + 1}`);
100
+ }
33
101
  }
34
102
  return this.#ids.get(file)!;
35
103
  }
@@ -37,28 +105,16 @@ export class ImportManager {
37
105
  /**
38
106
  * Import a file if needed, and record it's identifier
39
107
  */
40
- importFile(file: string, base?: string): Import {
41
- file = ModuleUtil.normalizePath(file);
108
+ importFile(file: string, name?: string): Import {
109
+ file = this.#index.getImportName(file);
42
110
 
43
111
  // Allow for node classes to be imported directly
44
112
  if (/@types\/node/.test(file)) {
45
- file = require.resolve(file.replace(/.*@types\/node\//, '').replace(/[.]d([.]ts)?$/, ''));
46
- }
47
-
48
- // Handle relative imports
49
- if (file.startsWith('.') && base &&
50
- !base.startsWith('@travetto') && !base.includes('node_modules')
51
- ) { // Relative path
52
- const fileDir = dirname(PathUtil.resolveUnix(file));
53
- const baseDir = dirname(PathUtil.resolveUnix(base));
54
- file = `${relative(baseDir, fileDir) || '.'}/${basename(file)}`;
55
- if (/^[A-Za-z]/.test(file)) {
56
- file = `./${file}`;
57
- }
113
+ file = PackageUtil.resolveImport(file.replace(/.*@types\/node\//, '').replace(D_OR_D_TS_EXT_RE, ''));
58
114
  }
59
115
 
60
- if (!/[.]d([.]ts)?$/.test(file) && !this.#newImports.has(file)) {
61
- const id = this.getId(file);
116
+ if (!D_OR_D_TS_EXT_RE.test(file) && !this.#newImports.has(file)) {
117
+ const id = this.getId(file, name);
62
118
 
63
119
  if (this.#imports.has(id)) { // Already imported, be cool
64
120
  return this.#imports.get(id)!;
@@ -77,8 +133,8 @@ export class ImportManager {
77
133
  */
78
134
  importFromResolved(...types: AnyType[]): void {
79
135
  for (const type of types) {
80
- if (type.key === 'external' && type.source && type.source !== this.source.fileName) {
81
- this.importFile(type.source, this.source.fileName);
136
+ if (type.key === 'external' && type.importName && type.importName !== this.#importName) {
137
+ this.importFile(type.importName);
82
138
  }
83
139
  switch (type.key) {
84
140
  case 'external':
@@ -99,11 +155,11 @@ export class ImportManager {
99
155
  }
100
156
 
101
157
  try {
102
- const importStmts = [...this.#newImports.values()].map(({ path, ident }) => {
158
+ const importStmts = [...this.#newImports.values()].map(({ path: resolved, ident }) => {
103
159
  const importStmt = this.factory.createImportDeclaration(
104
- undefined, undefined,
160
+ undefined,
105
161
  this.factory.createImportClause(false, undefined, this.factory.createNamespaceImport(ident)),
106
- this.factory.createStringLiteral(path)
162
+ this.factory.createStringLiteral(resolved)
107
163
  );
108
164
  return importStmt;
109
165
  });
@@ -116,27 +172,61 @@ export class ImportManager {
116
172
  if (!(err instanceof Error)) {
117
173
  throw err;
118
174
  }
119
- const out = new Error(`${err.message} in ${file.fileName.replace(PathUtil.cwd, '.')}`);
175
+ const out = new Error(`${err.message} in ${file.fileName.replace(process.cwd(), '.')}`);
120
176
  out.stack = err.stack;
121
177
  throw out;
122
178
  }
123
179
  }
124
180
 
181
+ finalizeImportExportExtension(ret: ts.SourceFile, checker: ts.TypeChecker): ts.SourceFile {
182
+ const toAdd: ts.Statement[] = [];
183
+
184
+ for (const stmt of ret.statements) {
185
+ if (ts.isExportDeclaration(stmt)) {
186
+ if (!stmt.isTypeOnly) {
187
+ toAdd.push(this.factory.updateExportDeclaration(
188
+ stmt,
189
+ stmt.modifiers,
190
+ stmt.isTypeOnly,
191
+ stmt.exportClause,
192
+ this.#rewriteModuleSpecifier(stmt.moduleSpecifier),
193
+ stmt.assertClause
194
+ ));
195
+ }
196
+ } else if (ts.isImportDeclaration(stmt)) {
197
+ if (!stmt.importClause?.isTypeOnly) {
198
+ toAdd.push(this.factory.updateImportDeclaration(
199
+ stmt,
200
+ stmt.modifiers,
201
+ this.#rewriteImportClause(stmt.moduleSpecifier, stmt.importClause, checker)!,
202
+ this.#rewriteModuleSpecifier(stmt.moduleSpecifier)!,
203
+ stmt.assertClause
204
+ ));
205
+ }
206
+ } else {
207
+ toAdd.push(stmt);
208
+ }
209
+ }
210
+ return CoreUtil.updateSource(this.factory, ret, toAdd);
211
+ }
212
+
125
213
  /**
126
214
  * Reset the imports into the source file
127
215
  */
128
- finalize(ret: ts.SourceFile): ts.SourceFile {
129
- return this.finalizeNewImports(ret) ?? ret;
216
+ finalize(ret: ts.SourceFile, checker: ts.TypeChecker): ts.SourceFile {
217
+ ret = this.finalizeNewImports(ret) ?? ret;
218
+ ret = this.finalizeImportExportExtension(ret, checker) ?? ret;
219
+ return ret;
130
220
  }
131
221
 
132
222
  /**
133
223
  * Get the identifier and import if needed
134
224
  */
135
225
  getOrImport(factory: ts.NodeFactory, type: ExternalType): ts.Identifier | ts.PropertyAccessExpression {
136
- if (type.source === this.source.fileName) {
226
+ if (type.importName === this.#importName) {
137
227
  return factory.createIdentifier(type.name!);
138
228
  } else {
139
- const { ident } = this.#imports.get(type.source) ?? this.importFile(type.source, this.source.fileName);
229
+ const { ident } = this.#imports.get(type.importName) ?? this.importFile(type.importName);
140
230
  return factory.createPropertyAccessExpression(ident, type.name!);
141
231
  }
142
232
  }
package/src/manager.ts ADDED
@@ -0,0 +1,66 @@
1
+ import ts from 'typescript';
2
+
3
+ import { ManifestRoot } from '@travetto/manifest';
4
+
5
+ import { NodeTransformer } from './types/visitor';
6
+ import { VisitorFactory } from './visitor';
7
+ import { TransformerState } from './state';
8
+ import { getAllTransformers } from './register';
9
+ import { TransformerIndex } from './manifest-index';
10
+
11
+ /**
12
+ * Manages the typescript transformers
13
+ */
14
+ export class TransformerManager {
15
+
16
+ /**
17
+ * Create transformer manager
18
+ * @param transformerFiles
19
+ * @param manifest
20
+ * @returns
21
+ */
22
+ static async create(transformerFiles: string[], manifest: ManifestRoot): Promise<TransformerManager> {
23
+ const transformers: NodeTransformer<TransformerState>[] = [];
24
+ const idx = new TransformerIndex('.', manifest);
25
+
26
+ for (const file of transformerFiles) { // Exclude based on blacklist
27
+ const entry = idx.getEntry(file)!;
28
+ transformers.push(...getAllTransformers(await import(file), entry.module));
29
+ }
30
+
31
+ // Prepare a new visitor factory with a given type checker
32
+ return new TransformerManager(transformers, idx);
33
+ }
34
+
35
+ #cached: ts.CustomTransformers | undefined;
36
+ #transformers: NodeTransformer<TransformerState>[];
37
+ #index: TransformerIndex;
38
+
39
+ constructor(transformers: NodeTransformer<TransformerState>[], index: TransformerIndex) {
40
+ this.#transformers = transformers;
41
+ this.#index = index;
42
+ }
43
+
44
+ /**
45
+ * Initialize with type checker
46
+ * @param checker
47
+ */
48
+ init(checker: ts.TypeChecker): void {
49
+ const visitor = new VisitorFactory(
50
+ (ctx, src) => new TransformerState(src, ctx.factory, checker, this.#index, ctx.getCompilerOptions()),
51
+ this.#transformers
52
+ );
53
+
54
+ // Define transformers for the compiler
55
+ this.#cached = {
56
+ before: [visitor.visitor()]
57
+ };
58
+ }
59
+
60
+ /**
61
+ * Get typescript transformer object
62
+ */
63
+ get(): ts.CustomTransformers | undefined {
64
+ return this.#cached!;
65
+ }
66
+ }
@@ -0,0 +1,29 @@
1
+ import ts from 'typescript';
2
+
3
+ import { ManifestIndex, path } from '@travetto/manifest';
4
+ import { DeclarationUtil } from './util/declaration';
5
+
6
+ /**
7
+ * Specific logic for the transformer
8
+ */
9
+ export class TransformerIndex extends ManifestIndex {
10
+
11
+ /**
12
+ * Resolve import name for a given type
13
+ */
14
+ getImportName(type: ts.Type | string, removeExt = false): string {
15
+ const ogSource = typeof type === 'string' ? type : DeclarationUtil.getPrimaryDeclarationNode(type).getSourceFile().fileName;
16
+ let sourceFile = path.toPosix(ogSource);
17
+
18
+ if (!sourceFile.endsWith('.js') && !sourceFile.endsWith('.ts')) {
19
+ sourceFile = `${sourceFile}.ts`;
20
+ }
21
+
22
+ const imp =
23
+ this.getEntry(/[.]ts$/.test(sourceFile) ? sourceFile : `${sourceFile}.js`)?.import ??
24
+ this.getFromImport(sourceFile.replace(/^.*node_modules\//, '').replace(/[.]ts$/, ''))?.import ??
25
+ ogSource;
26
+
27
+ return removeExt ? imp.replace(/[.]js$/, '') : imp;
28
+ }
29
+ }
package/src/register.ts CHANGED
@@ -1,26 +1,55 @@
1
- import * as ts from 'typescript';
2
- import { DecoratorMeta, NodeTransformer, State, TransformPhase, TransformerType, Transformer, TransformerId } from './types/visitor';
1
+ import ts from 'typescript';
3
2
 
4
- const HandlersProp = Symbol.for('@trv:transformer/handlers');
3
+ import { DecoratorMeta, NodeTransformer, State, TransformPhase, TransformerType, Transformer, ModuleNameⲐ } from './types/visitor';
4
+
5
+ const HandlersProp = Symbol.for('@travetto/transformer:handlers');
5
6
 
6
7
  type TransformerWithHandlers = Transformer & { [HandlersProp]?: NodeTransformer[] };
7
8
 
9
+ function isTransformer(x: unknown): x is Transformer {
10
+ return x !== null && x !== undefined && typeof x === 'function';
11
+ }
12
+
8
13
  /**
9
14
  * Get all transformers
10
15
  * @param obj Object to search for transformers
11
16
  */
12
- export function getAllTransformers(obj: Record<string, { [HandlersProp]?: NodeTransformer[] }>): NodeTransformer[] {
13
- return Object.values(obj).flatMap(x => x[HandlersProp] ?? []);
17
+ export function getAllTransformers(obj: Record<string, { [HandlersProp]?: NodeTransformer[] }>, module: string): NodeTransformer[] {
18
+ return Object.values(obj)
19
+ .flatMap(x => {
20
+ if (isTransformer(x)) {
21
+ x[ModuleNameⲐ] = module;
22
+ }
23
+ return (x[HandlersProp] ?? []);
24
+ })
25
+ .map(handler => ({
26
+ ...handler,
27
+ key: `${module}:${handler.key}`,
28
+ target: handler.target?.map(t => `${module}:${t}`)
29
+ }));
14
30
  }
15
31
 
16
32
  // Store handlers in class
17
33
  function storeHandler(cls: TransformerWithHandlers, fn: Function, phase: TransformPhase, type: TransformerType, target?: string[]): void {
18
- if (target) {
19
- const ns = cls[TransformerId].split('/')[0]; // Everything before the '/'
20
- target = target.map(x => x.startsWith('@') ? x : `${ns}/${x}`);
21
- }
22
- cls[HandlersProp] = cls[HandlersProp] ?? [];
23
- cls[HandlersProp]!.push({ key: `${cls[TransformerId]}/${fn.name}`, [phase]: fn.bind(cls), type, target });
34
+ (cls[HandlersProp] ??= []).push({ key: fn.name, [phase]: fn.bind(cls), type, target });
35
+ }
36
+
37
+ /**
38
+ * Wraps entire file before transforming
39
+ */
40
+ export function OnFile(...target: string[]) {
41
+ return <S extends State = State, R extends ts.Node = ts.Node>(
42
+ inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.SourceFile) => R>
43
+ ): void => storeHandler(inst, d.value!, 'before', 'file', target);
44
+ }
45
+
46
+ /**
47
+ * Wraps entire file after transforming
48
+ */
49
+ export function AfterFile(...target: string[]) {
50
+ return <S extends State = State, R extends ts.Node = ts.Node>(
51
+ inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.SourceFile) => R>
52
+ ): void => storeHandler(inst, d.value!, 'before', 'file', target);
24
53
  }
25
54
 
26
55
  /**
@@ -1,12 +1,15 @@
1
1
  /* eslint-disable no-bitwise */
2
- import * as ts from 'typescript';
3
- import { dirname } from 'path';
2
+ import ts from 'typescript';
4
3
 
5
- import { PathUtil } from '@travetto/boot';
6
- import { Util } from '@travetto/base';
4
+ import { ManifestIndex, path } from '@travetto/manifest';
5
+
6
+ import { DocUtil } from '../util/doc';
7
+ import { CoreUtil } from '../util/core';
8
+ import { DeclarationUtil } from '../util/declaration';
9
+ import { LiteralUtil } from '../util/literal';
7
10
 
8
11
  import { Type, AnyType, UnionType, Checker } from './types';
9
- import { DocUtil, CoreUtil, DeclarationUtil, LiteralUtil } from '../util';
12
+ import { CoerceUtil } from './coerce';
10
13
 
11
14
  /**
12
15
  * List of global types that can be parameterized
@@ -35,7 +38,7 @@ type Category = 'void' | 'undefined' | 'concrete' | 'unknown' | 'tuple' | 'shape
35
38
  /**
36
39
  * Type categorizer, input for builder
37
40
  */
38
- export function TypeCategorize(checker: ts.TypeChecker, type: ts.Type): { category: Category, type: ts.Type } {
41
+ export function TypeCategorize(checker: ts.TypeChecker, type: ts.Type, index: ManifestIndex): { category: Category, type: ts.Type } {
39
42
  const flags = type.getFlags();
40
43
  const objectFlags = DeclarationUtil.getObjectFlags(type) ?? 0;
41
44
 
@@ -62,9 +65,10 @@ export function TypeCategorize(checker: ts.TypeChecker, type: ts.Type): { catego
62
65
  }
63
66
 
64
67
  const source = DeclarationUtil.getPrimaryDeclarationNode(resolvedType).getSourceFile();
65
- if (source?.fileName.includes('@types/node/globals') || source?.fileName.includes('typescript/lib')) {
68
+ const sourceFile = source.fileName;
69
+ if (sourceFile?.includes('@types/node/globals') || sourceFile?.includes('typescript/lib')) {
66
70
  return { category: 'literal', type };
67
- } else if (!source?.fileName.includes('@travetto') && source?.fileName.endsWith('.d.ts')) {
71
+ } else if (sourceFile?.endsWith('.d.ts') && !index.getFromSource(sourceFile)) {
68
72
  return { category: 'unknown', type };
69
73
  } else if (!resolvedType.isClass()) { // Not a real type
70
74
  return { category: 'shape', type: resolvedType };
@@ -118,7 +122,7 @@ export const TypeBuilder: {
118
122
  if (name in GLOBAL_SIMPLE) {
119
123
  const cons = GLOBAL_SIMPLE[name];
120
124
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
121
- const ret = LiteralUtil.isLiteralType(type) ? Util.coerceType(type.value, cons as typeof String, false) :
125
+ const ret = LiteralUtil.isLiteralType(type) ? CoerceUtil.coerce(type.value, cons as typeof String, false) :
122
126
  undefined;
123
127
 
124
128
  return {
@@ -140,12 +144,10 @@ export const TypeBuilder: {
140
144
  },
141
145
  external: {
142
146
  build: (checker, type) => {
143
- const source = DeclarationUtil.getPrimaryDeclarationNode(type).getSourceFile();
144
147
  const name = CoreUtil.getSymbol(type)?.getName();
145
- return {
146
- key: 'external', name, source: source.fileName,
147
- tsTypeArguments: checker.getAllTypeArguments(type)
148
- };
148
+ const importName = checker.getIndex().getImportName(type);
149
+ const tsTypeArguments = checker.getAllTypeArguments(type);
150
+ return { key: 'external', name, importName, tsTypeArguments };
149
151
  }
150
152
  },
151
153
  union: {
@@ -176,9 +178,10 @@ export const TypeBuilder: {
176
178
  },
177
179
  shape: {
178
180
  build: (checker, type, alias?) => {
179
- const fieldNodes: Record<string, ts.Type> = {};
180
- const name = CoreUtil.getSymbol(alias ?? type);
181
- const source = DeclarationUtil.getPrimaryDeclarationNode(type)?.getSourceFile();
181
+ const tsFieldTypes: Record<string, ts.Type> = {};
182
+ const name = CoreUtil.getSymbol(alias ?? type)?.getName();
183
+ const importName = checker.getIndex().getImportName(type);
184
+ const tsTypeArguments = checker.getAllTypeArguments(type);
182
185
  for (const member of checker.getPropertiesOfType(type)) {
183
186
  const dec = DeclarationUtil.getPrimaryDeclarationNode(member);
184
187
  if (DeclarationUtil.isPublic(dec)) { // If public
@@ -187,17 +190,11 @@ export const TypeBuilder: {
187
190
  !member.getName().includes('@') && // if not a symbol
188
191
  !memberType.getCallSignatures().length // if not a function
189
192
  ) {
190
- fieldNodes[member.getName()] = memberType;
193
+ tsFieldTypes[member.getName()] = memberType;
191
194
  }
192
195
  }
193
196
  }
194
- return {
195
- key: 'shape', name: name?.getName(),
196
- source: source?.fileName,
197
- tsFieldTypes: fieldNodes,
198
- tsTypeArguments: checker.getAllTypeArguments(type),
199
- fieldTypes: {}
200
- };
197
+ return { key: 'shape', name, importName, tsFieldTypes, tsTypeArguments, fieldTypes: {} };
201
198
  }
202
199
  },
203
200
  concrete: {
@@ -205,23 +202,26 @@ export const TypeBuilder: {
205
202
  const [tag] = DocUtil.readDocTag(type, 'concrete');
206
203
  if (tag) {
207
204
  // eslint-disable-next-line prefer-const
208
- let [source, name, ext] = tag.split(':');
205
+ let [importName, name] = tag.split(':');
209
206
  if (!name) {
210
- name = source;
211
- source = '.';
207
+ name = importName;
208
+ importName = '.';
212
209
  }
213
210
 
214
- const sourceFile: string = DeclarationUtil.getDeclarations(type)
215
- ?.find(x => ts.getAllJSDocTags(x, (t): t is ts.JSDocTag => t.tagName.getText() === 'concrete').length)
216
- ?.getSourceFile().fileName ?? '';
211
+ // Resolving relative to source file
212
+ if (importName.startsWith('.')) {
213
+ const rawSourceFile: string = DeclarationUtil.getDeclarations(type)
214
+ ?.find(x => ts.getAllJSDocTags(x, (t): t is ts.JSDocTag => t.tagName.getText() === 'concrete').length)
215
+ ?.getSourceFile().fileName ?? '';
217
216
 
218
- if (source === '.') {
219
- source = sourceFile;
220
- } else if (source.startsWith('.')) {
221
- source = PathUtil.resolveUnix(dirname(sourceFile), source);
217
+ if (importName === '.') {
218
+ importName = checker.getIndex().getImportName(rawSourceFile);
219
+ } else {
220
+ const base = path.dirname(rawSourceFile);
221
+ importName = checker.getIndex().getImportName(path.resolve(base, importName));
222
+ }
222
223
  }
223
-
224
- return { key: 'external', name, source: ext === 'node' ? source : PathUtil.resolveUnix(sourceFile, source) };
224
+ return { key: 'external', name, importName };
225
225
  }
226
226
  }
227
227
  }