@travetto/transformer 6.0.0-rc.2 → 6.0.0-rc.3
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 +18 -14
- package/package.json +3 -3
- package/src/importer.ts +30 -28
- package/src/manager.ts +4 -4
- package/src/register.ts +44 -7
- package/src/resolver/builder.ts +70 -57
- package/src/resolver/cache.ts +1 -1
- package/src/resolver/coerce.ts +6 -6
- package/src/resolver/service.ts +12 -10
- package/src/resolver/types.ts +1 -1
- package/src/state.ts +54 -20
- package/src/types/visitor.ts +1 -1
- package/src/util/declaration.ts +1 -1
- package/src/util/decorator.ts +1 -1
- package/src/util/doc.ts +12 -3
- package/src/util/import.ts +1 -1
- package/src/util/literal.ts +8 -6
- package/src/visitor.ts +13 -9
package/README.md
CHANGED
|
@@ -33,12 +33,13 @@ import { OnProperty, TransformerState, OnMethod, OnClass } from '@travetto/trans
|
|
|
33
33
|
|
|
34
34
|
export class MakeUpper {
|
|
35
35
|
|
|
36
|
+
static isValid(state: TransformerState): boolean {
|
|
37
|
+
return state.importName !== '@travetto/transformer/doc/upper.ts';
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
@OnProperty()
|
|
37
41
|
static handleProperty(state: TransformerState, node: ts.PropertyDeclaration): ts.PropertyDeclaration {
|
|
38
|
-
|
|
39
|
-
return node;
|
|
40
|
-
}
|
|
41
|
-
return state.factory.updatePropertyDeclaration(
|
|
42
|
+
return !this.isValid(state) ? node : state.factory.updatePropertyDeclaration(
|
|
42
43
|
node,
|
|
43
44
|
node.modifiers,
|
|
44
45
|
node.name.getText().toUpperCase(),
|
|
@@ -50,10 +51,7 @@ export class MakeUpper {
|
|
|
50
51
|
|
|
51
52
|
@OnClass()
|
|
52
53
|
static handleClass(state: TransformerState, node: ts.ClassDeclaration): ts.ClassDeclaration {
|
|
53
|
-
|
|
54
|
-
return node;
|
|
55
|
-
}
|
|
56
|
-
return state.factory.updateClassDeclaration(
|
|
54
|
+
return !this.isValid(state) ? node : state.factory.updateClassDeclaration(
|
|
57
55
|
node,
|
|
58
56
|
node.modifiers,
|
|
59
57
|
state.createIdentifier(node.name!.getText().toUpperCase()),
|
|
@@ -65,10 +63,7 @@ export class MakeUpper {
|
|
|
65
63
|
|
|
66
64
|
@OnMethod()
|
|
67
65
|
static handleMethod(state: TransformerState, node: ts.MethodDeclaration): ts.MethodDeclaration {
|
|
68
|
-
|
|
69
|
-
return node;
|
|
70
|
-
}
|
|
71
|
-
return state.factory.updateMethodDeclaration(
|
|
66
|
+
return !this.isValid(state) ? node : state.factory.updateMethodDeclaration(
|
|
72
67
|
node,
|
|
73
68
|
node.modifiers,
|
|
74
69
|
undefined,
|
package/__index__.ts
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
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
|
|
7
|
-
export * from './src/manager';
|
|
1
|
+
export * from './src/state.ts';
|
|
2
|
+
export * from './src/visitor.ts';
|
|
3
|
+
export * from './src/register.ts';
|
|
4
|
+
export * from './src/types/visitor.ts';
|
|
5
|
+
export * from './src/types/shared.ts';
|
|
6
|
+
export * from './src/manager.ts';
|
|
8
7
|
|
|
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';
|
|
8
|
+
export * from './src/util/core.ts';
|
|
9
|
+
export * from './src/util/declaration.ts';
|
|
10
|
+
export * from './src/util/decorator.ts';
|
|
11
|
+
export * from './src/util/doc.ts';
|
|
12
|
+
export * from './src/util/literal.ts';
|
|
13
|
+
export * from './src/util/log.ts';
|
|
14
|
+
export * from './src/util/system.ts';
|
|
15
|
+
|
|
16
|
+
export type {
|
|
17
|
+
AnyType, ForeignType, ManagedType, PointerType, LiteralType, ShapeType,
|
|
18
|
+
CompositionType, TupleType, UnknownType, TemplateType
|
|
19
|
+
} from './src/resolver/types.ts';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/transformer",
|
|
3
|
-
"version": "6.0.0-rc.
|
|
3
|
+
"version": "6.0.0-rc.3",
|
|
4
4
|
"description": "Functionality for AST transformations, with transformer registration, and general utils",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"directory": "module/transformer"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@travetto/manifest": "^6.0.0-rc.
|
|
27
|
+
"@travetto/manifest": "^6.0.0-rc.2",
|
|
28
28
|
"tslib": "^2.8.1",
|
|
29
|
-
"typescript": "^5.
|
|
29
|
+
"typescript": "^5.8.3"
|
|
30
30
|
},
|
|
31
31
|
"travetto": {
|
|
32
32
|
"displayName": "Transformation",
|
package/src/importer.ts
CHANGED
|
@@ -2,12 +2,12 @@ import ts from 'typescript';
|
|
|
2
2
|
|
|
3
3
|
import { ManifestModuleUtil, PackageUtil, path } from '@travetto/manifest';
|
|
4
4
|
|
|
5
|
-
import { AnyType, TransformResolver, ManagedType } from './resolver/types';
|
|
6
|
-
import { ImportUtil } from './util/import';
|
|
7
|
-
import { CoreUtil } from './util/core';
|
|
8
|
-
import { Import } from './types/shared';
|
|
9
|
-
import { LiteralUtil } from './util/literal';
|
|
10
|
-
import { DeclarationUtil } from './util/declaration';
|
|
5
|
+
import { AnyType, TransformResolver, ManagedType } from './resolver/types.ts';
|
|
6
|
+
import { ImportUtil } from './util/import.ts';
|
|
7
|
+
import { CoreUtil } from './util/core.ts';
|
|
8
|
+
import { Import } from './types/shared.ts';
|
|
9
|
+
import { LiteralUtil } from './util/literal.ts';
|
|
10
|
+
import { DeclarationUtil } from './util/declaration.ts';
|
|
11
11
|
|
|
12
12
|
const D_OR_D_TS_EXT_RE = /[.]d([.]ts)?$/;
|
|
13
13
|
|
|
@@ -34,19 +34,12 @@ export class ImportManager {
|
|
|
34
34
|
this.factory = factory;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
#rewriteModuleSpecifier(spec: ts.Expression | undefined): ts.Expression | undefined {
|
|
38
|
-
if (spec && ts.isStringLiteral(spec) && this.isUntypedImport(spec)) {
|
|
39
|
-
return LiteralUtil.fromLiteral(this.factory, `${spec.text.replace(/['"]/g, '')}.js`);
|
|
40
|
-
}
|
|
41
|
-
return spec;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
37
|
#rewriteImportClause(spec: ts.Expression | undefined, clause: ts.ImportClause | undefined): ts.ImportClause | undefined {
|
|
45
38
|
if (!(spec && clause?.namedBindings && ts.isNamedImports(clause.namedBindings))) {
|
|
46
39
|
return clause;
|
|
47
40
|
}
|
|
48
41
|
|
|
49
|
-
if (spec && ts.isStringLiteral(spec) && !this
|
|
42
|
+
if (spec && ts.isStringLiteral(spec) && !this.#isKnownImport(spec)) {
|
|
50
43
|
return clause;
|
|
51
44
|
}
|
|
52
45
|
|
|
@@ -80,7 +73,7 @@ export class ImportManager {
|
|
|
80
73
|
* Is a known import an untyped file access
|
|
81
74
|
* @param fileOrImport
|
|
82
75
|
*/
|
|
83
|
-
isKnownImport(fileOrImport: ts.StringLiteral | string | undefined): boolean {
|
|
76
|
+
#isKnownImport(fileOrImport: ts.StringLiteral | string | undefined): boolean {
|
|
84
77
|
if (fileOrImport && typeof fileOrImport !== 'string') {
|
|
85
78
|
if (ts.isStringLiteral(fileOrImport)) {
|
|
86
79
|
fileOrImport = fileOrImport.text.replace(/['"]g/, '');
|
|
@@ -95,13 +88,22 @@ export class ImportManager {
|
|
|
95
88
|
}
|
|
96
89
|
|
|
97
90
|
/**
|
|
98
|
-
*
|
|
99
|
-
* @param fileOrImport
|
|
91
|
+
* Normalize module specifier
|
|
100
92
|
*/
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
93
|
+
normalizeModuleSpecifier<T extends ts.Expression | undefined>(spec: T): T {
|
|
94
|
+
if (spec && ts.isStringLiteral(spec) && this.#isKnownImport(spec.text)) {
|
|
95
|
+
const specText = spec.text.replace(/['"]/g, '');
|
|
96
|
+
|
|
97
|
+
const type = ManifestModuleUtil.getFileType(specText);
|
|
98
|
+
if (type === 'js' || type === 'ts') {
|
|
99
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
100
|
+
return LiteralUtil.fromLiteral(this.factory, ManifestModuleUtil.withOutputExtension(specText)) as unknown as T;
|
|
101
|
+
} else {
|
|
102
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
103
|
+
return LiteralUtil.fromLiteral(this.factory, `${specText}${ManifestModuleUtil.OUTPUT_EXT}`) as unknown as T;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return spec;
|
|
105
107
|
}
|
|
106
108
|
|
|
107
109
|
/**
|
|
@@ -126,7 +128,7 @@ export class ImportManager {
|
|
|
126
128
|
importFile(file: string, name?: string): Import {
|
|
127
129
|
file = this.#resolver.getFileImportName(file);
|
|
128
130
|
|
|
129
|
-
if (file.endsWith(
|
|
131
|
+
if (file.endsWith(ManifestModuleUtil.SOURCE_DEF_EXT) && !file.endsWith(ManifestModuleUtil.TYPINGS_EXT)) {
|
|
130
132
|
file = ManifestModuleUtil.withOutputExtension(file);
|
|
131
133
|
}
|
|
132
134
|
|
|
@@ -211,7 +213,7 @@ export class ImportManager {
|
|
|
211
213
|
stmt.modifiers,
|
|
212
214
|
stmt.isTypeOnly,
|
|
213
215
|
stmt.exportClause,
|
|
214
|
-
this
|
|
216
|
+
this.normalizeModuleSpecifier(stmt.moduleSpecifier),
|
|
215
217
|
stmt.attributes
|
|
216
218
|
));
|
|
217
219
|
}
|
|
@@ -221,7 +223,7 @@ export class ImportManager {
|
|
|
221
223
|
stmt,
|
|
222
224
|
stmt.modifiers,
|
|
223
225
|
this.#rewriteImportClause(stmt.moduleSpecifier, stmt.importClause)!,
|
|
224
|
-
this
|
|
226
|
+
this.normalizeModuleSpecifier(stmt.moduleSpecifier)!,
|
|
225
227
|
stmt.attributes
|
|
226
228
|
));
|
|
227
229
|
}
|
|
@@ -235,10 +237,10 @@ export class ImportManager {
|
|
|
235
237
|
/**
|
|
236
238
|
* Reset the imports into the source file
|
|
237
239
|
*/
|
|
238
|
-
finalize(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return
|
|
240
|
+
finalize(src: ts.SourceFile): ts.SourceFile {
|
|
241
|
+
let node = this.finalizeNewImports(src) ?? src;
|
|
242
|
+
node = this.finalizeImportExportExtension(node) ?? node;
|
|
243
|
+
return node;
|
|
242
244
|
}
|
|
243
245
|
|
|
244
246
|
/**
|
package/src/manager.ts
CHANGED
|
@@ -2,10 +2,10 @@ import ts from 'typescript';
|
|
|
2
2
|
|
|
3
3
|
import { ManifestIndex, ManifestModuleUtil } from '@travetto/manifest';
|
|
4
4
|
|
|
5
|
-
import { NodeTransformer } from './types/visitor';
|
|
6
|
-
import { VisitorFactory } from './visitor';
|
|
7
|
-
import { TransformerState } from './state';
|
|
8
|
-
import { getAllTransformers } from './register';
|
|
5
|
+
import { NodeTransformer } from './types/visitor.ts';
|
|
6
|
+
import { VisitorFactory } from './visitor.ts';
|
|
7
|
+
import { TransformerState } from './state.ts';
|
|
8
|
+
import { getAllTransformers } from './register.ts';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Manages the typescript transformers
|
package/src/register.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
|
|
3
|
-
import { DecoratorMeta, NodeTransformer, State, TransformPhase, TransformerType, Transformer, ModuleNameSymbol } from './types/visitor';
|
|
3
|
+
import { DecoratorMeta, NodeTransformer, State, TransformPhase, TransformerType, Transformer, ModuleNameSymbol } from './types/visitor.ts';
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const HandlersSymbol = Symbol();
|
|
6
6
|
|
|
7
|
-
type TransformerWithHandlers = Transformer & { [
|
|
7
|
+
type TransformerWithHandlers = Transformer & { [HandlersSymbol]?: NodeTransformer[] };
|
|
8
8
|
|
|
9
9
|
function isTransformer(x: unknown): x is Transformer {
|
|
10
10
|
return x !== null && x !== undefined && typeof x === 'function';
|
|
@@ -14,13 +14,13 @@ function isTransformer(x: unknown): x is Transformer {
|
|
|
14
14
|
* Get all transformers
|
|
15
15
|
* @param obj Object to search for transformers
|
|
16
16
|
*/
|
|
17
|
-
export function getAllTransformers(obj: Record<string, { [
|
|
17
|
+
export function getAllTransformers(obj: Record<string, { [HandlersSymbol]?: NodeTransformer[] }>, module: string): NodeTransformer[] {
|
|
18
18
|
return Object.values(obj)
|
|
19
19
|
.flatMap(x => {
|
|
20
20
|
if (isTransformer(x)) {
|
|
21
21
|
x[ModuleNameSymbol] = module;
|
|
22
22
|
}
|
|
23
|
-
return (x[
|
|
23
|
+
return (x[HandlersSymbol] ?? []);
|
|
24
24
|
})
|
|
25
25
|
.map(handler => ({
|
|
26
26
|
...handler,
|
|
@@ -31,7 +31,7 @@ export function getAllTransformers(obj: Record<string, { [HandlersProp]?: NodeTr
|
|
|
31
31
|
|
|
32
32
|
// Store handlers in class
|
|
33
33
|
function storeHandler(cls: TransformerWithHandlers, fn: Function, phase: TransformPhase, type: TransformerType, target?: string[]): void {
|
|
34
|
-
(cls[
|
|
34
|
+
(cls[HandlersSymbol] ??= []).push({ key: fn.name, [phase]: fn.bind(cls), type, target });
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
@@ -133,6 +133,24 @@ export function OnClass(...target: string[]) {
|
|
|
133
133
|
): void => storeHandler(inst, d.value!, 'before', 'class', target);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Listens for a `ts.TypeAliasDeclaration` on descent
|
|
138
|
+
*/
|
|
139
|
+
export function OnTypeAlias(...target: string[]) {
|
|
140
|
+
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
141
|
+
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.TypeAliasDeclaration) => R>
|
|
142
|
+
): void => storeHandler(inst, d.value!, 'before', 'type', target);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Listens for a `ts.InterfaceDeclaration` on descent
|
|
147
|
+
*/
|
|
148
|
+
export function OnInterface(...target: string[]) {
|
|
149
|
+
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
150
|
+
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.InterfaceDeclaration) => R>
|
|
151
|
+
): void => storeHandler(inst, d.value!, 'before', 'interface', target);
|
|
152
|
+
}
|
|
153
|
+
|
|
136
154
|
/**
|
|
137
155
|
* Listens for a `ts.CallExpression`, on ascent
|
|
138
156
|
*/
|
|
@@ -212,4 +230,23 @@ export function AfterClass(...target: string[]) {
|
|
|
212
230
|
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
213
231
|
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.ClassDeclaration, dm?: DecoratorMeta) => R>
|
|
214
232
|
): void => storeHandler(inst, d.value!, 'after', 'class', target);
|
|
215
|
-
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Listens for a `ts.TypeAliasDeclaration` on ascent
|
|
238
|
+
*/
|
|
239
|
+
export function AfterTypeAlias(...target: string[]) {
|
|
240
|
+
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
241
|
+
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.TypeAliasDeclaration) => R>
|
|
242
|
+
): void => storeHandler(inst, d.value!, 'after', 'type', target);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Listens for a `ts.InterfaceDeclaration` on ascent
|
|
247
|
+
*/
|
|
248
|
+
export function AfterInterface(...target: string[]) {
|
|
249
|
+
return <S extends State = State, R extends ts.Node = ts.Node>(
|
|
250
|
+
inst: Transformer, __: unknown, d: TypedPropertyDescriptor<(state: S, node: ts.InterfaceDeclaration) => R>
|
|
251
|
+
): void => storeHandler(inst, d.value!, 'after', 'interface', target);
|
|
252
|
+
}
|
package/src/resolver/builder.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/* eslint-disable no-bitwise */
|
|
2
2
|
import ts from 'typescript';
|
|
3
3
|
|
|
4
|
-
import { path } from '@travetto/manifest';
|
|
4
|
+
import { path, ManifestModuleUtil } from '@travetto/manifest';
|
|
5
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';
|
|
10
|
-
import { transformCast, TemplateLiteralPart } from '../types/shared';
|
|
6
|
+
import { DocUtil } from '../util/doc.ts';
|
|
7
|
+
import { CoreUtil } from '../util/core.ts';
|
|
8
|
+
import { DeclarationUtil } from '../util/declaration.ts';
|
|
9
|
+
import { LiteralUtil } from '../util/literal.ts';
|
|
10
|
+
import { transformCast, TemplateLiteralPart } from '../types/shared.ts';
|
|
11
11
|
|
|
12
|
-
import { Type, AnyType, CompositionType, TransformResolver, TemplateType } from './types';
|
|
13
|
-
import { CoerceUtil } from './coerce';
|
|
12
|
+
import { Type, AnyType, CompositionType, TransformResolver, TemplateType } from './types.ts';
|
|
13
|
+
import { CoerceUtil } from './coerce.ts';
|
|
14
14
|
|
|
15
|
-
const
|
|
15
|
+
const UNDEFINED = Symbol();
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* List of global types that can be parameterized
|
|
@@ -30,6 +30,7 @@ const GLOBAL_COMPLEX: Record<string, Function> = {
|
|
|
30
30
|
/**
|
|
31
31
|
* List of global types that are simple
|
|
32
32
|
*/
|
|
33
|
+
const UNDEFINED_GLOBAL = { undefined: 1, void: 1, null: 1 };
|
|
33
34
|
const SIMPLE_NAMES: Record<string, string> = { String: 'string', Number: 'number', Boolean: 'boolean', Object: 'object' };
|
|
34
35
|
const GLOBAL_SIMPLE: Record<string, Function> = {
|
|
35
36
|
RegExp, Date, Number, Boolean, String, Function, Object, Error,
|
|
@@ -37,9 +38,8 @@ const GLOBAL_SIMPLE: Record<string, Function> = {
|
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
type Category =
|
|
40
|
-
'void' | 'undefined' | 'concrete' | 'unknown' |
|
|
41
41
|
'tuple' | 'shape' | 'literal' | 'template' | 'managed' |
|
|
42
|
-
'composition' | 'foreign';
|
|
42
|
+
'composition' | 'foreign' | 'concrete' | 'unknown';
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* Type categorizer, input for builder
|
|
@@ -48,21 +48,31 @@ export function TypeCategorize(resolver: TransformResolver, type: ts.Type): { ca
|
|
|
48
48
|
const flags = type.getFlags();
|
|
49
49
|
const objectFlags = DeclarationUtil.getObjectFlags(type) ?? 0;
|
|
50
50
|
|
|
51
|
-
if (flags & ts.TypeFlags.
|
|
52
|
-
return { category: '
|
|
53
|
-
} else if (flags &
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
if (flags & (ts.TypeFlags.TemplateLiteral)) {
|
|
52
|
+
return { category: 'template', type };
|
|
53
|
+
} else if (flags & (
|
|
54
|
+
ts.TypeFlags.BigIntLike |
|
|
55
|
+
ts.TypeFlags.BooleanLike |
|
|
56
|
+
ts.TypeFlags.NumberLike |
|
|
57
|
+
ts.TypeFlags.StringLike |
|
|
58
|
+
ts.TypeFlags.Null |
|
|
59
|
+
ts.TypeFlags.Undefined |
|
|
60
|
+
ts.TypeFlags.Void
|
|
61
|
+
)) {
|
|
62
|
+
return { category: 'literal', type };
|
|
63
|
+
} else if (DocUtil.hasDocTag(type, 'concrete')) {
|
|
56
64
|
return { category: 'concrete', type };
|
|
57
65
|
} else if (flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.Never)) { // Any or unknown
|
|
58
66
|
return { category: 'unknown', type };
|
|
59
67
|
} else if (objectFlags & ts.ObjectFlags.Reference && !CoreUtil.getSymbol(type)) { // Tuple type?
|
|
60
68
|
return { category: 'tuple', type };
|
|
69
|
+
} else if (type.isUnionOrIntersection()) {
|
|
70
|
+
return { category: 'composition', type };
|
|
61
71
|
} else if (objectFlags & ts.ObjectFlags.Anonymous) {
|
|
62
72
|
try {
|
|
63
73
|
const source = DeclarationUtil.getPrimaryDeclarationNode(type).getSourceFile();
|
|
64
74
|
const sourceFile = source.fileName;
|
|
65
|
-
if (sourceFile &&
|
|
75
|
+
if (sourceFile && ManifestModuleUtil.TYPINGS_EXT_RE.test(sourceFile) && !resolver.isKnownFile(sourceFile)) {
|
|
66
76
|
return { category: 'foreign', type };
|
|
67
77
|
}
|
|
68
78
|
} catch { }
|
|
@@ -72,7 +82,7 @@ export function TypeCategorize(resolver: TransformResolver, type: ts.Type): { ca
|
|
|
72
82
|
if (CoreUtil.hasTarget(resolvedType)) {
|
|
73
83
|
resolvedType = resolvedType.target;
|
|
74
84
|
// If resolved target has a concrete type
|
|
75
|
-
if (DocUtil.
|
|
85
|
+
if (DocUtil.hasDocTag(resolvedType, 'concrete')) {
|
|
76
86
|
return { category: 'concrete', type: resolvedType };
|
|
77
87
|
}
|
|
78
88
|
}
|
|
@@ -81,25 +91,13 @@ export function TypeCategorize(resolver: TransformResolver, type: ts.Type): { ca
|
|
|
81
91
|
const sourceFile = source.fileName;
|
|
82
92
|
if (sourceFile?.includes('typescript/lib')) {
|
|
83
93
|
return { category: 'literal', type };
|
|
84
|
-
} else if (sourceFile &&
|
|
94
|
+
} else if (sourceFile && ManifestModuleUtil.TYPINGS_EXT_RE.test(sourceFile) && !resolver.isKnownFile(sourceFile)) {
|
|
85
95
|
return { category: 'foreign', type: resolvedType };
|
|
86
96
|
} else if (!resolvedType.isClass()) { // Not a real type
|
|
87
97
|
return { category: 'shape', type: resolvedType };
|
|
88
98
|
} else {
|
|
89
99
|
return { category: 'managed', type: resolvedType };
|
|
90
100
|
}
|
|
91
|
-
} else if (flags & (ts.TypeFlags.TemplateLiteral)) {
|
|
92
|
-
return { category: 'template', type };
|
|
93
|
-
} else if (flags & (
|
|
94
|
-
ts.TypeFlags.BigIntLike |
|
|
95
|
-
ts.TypeFlags.BooleanLike |
|
|
96
|
-
ts.TypeFlags.NumberLike |
|
|
97
|
-
ts.TypeFlags.StringLike |
|
|
98
|
-
ts.TypeFlags.Void | ts.TypeFlags.Undefined
|
|
99
|
-
)) {
|
|
100
|
-
return { category: 'literal', type };
|
|
101
|
-
} else if (type.isUnionOrIntersection()) {
|
|
102
|
-
return { category: 'composition', type };
|
|
103
101
|
} else if (objectFlags & ts.ObjectFlags.Tuple) {
|
|
104
102
|
return { category: 'tuple', type };
|
|
105
103
|
} else if (type.isLiteral()) {
|
|
@@ -122,13 +120,10 @@ export const TypeBuilder: {
|
|
|
122
120
|
}
|
|
123
121
|
} = {
|
|
124
122
|
unknown: {
|
|
125
|
-
build: (resolver, type) =>
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
},
|
|
130
|
-
void: {
|
|
131
|
-
build: (resolver, type) => ({ key: 'literal', name: 'void', ctor: undefined })
|
|
123
|
+
build: (resolver, type) => {
|
|
124
|
+
const optional = UNDEFINED in type;
|
|
125
|
+
return { key: 'unknown', nullable: optional, undefinable: optional };
|
|
126
|
+
}
|
|
132
127
|
},
|
|
133
128
|
tuple: {
|
|
134
129
|
build: (resolver, type) => ({ key: 'tuple', tsTupleTypes: resolver.getAllTypeArguments(type), subTypes: [] })
|
|
@@ -168,16 +163,18 @@ export const TypeBuilder: {
|
|
|
168
163
|
const name = resolver.getTypeAsString(type) ?? '';
|
|
169
164
|
const complexName = CoreUtil.getSymbol(type)?.getName() ?? '';
|
|
170
165
|
|
|
171
|
-
if (name in
|
|
166
|
+
if (name in UNDEFINED_GLOBAL) {
|
|
167
|
+
return { key: 'literal', ctor: undefined, name };
|
|
168
|
+
} else if (name in GLOBAL_SIMPLE) {
|
|
172
169
|
const cons = GLOBAL_SIMPLE[name];
|
|
173
|
-
const
|
|
170
|
+
const literal = LiteralUtil.isLiteralType(type) ? CoerceUtil.coerce(type.value, transformCast(cons), false) :
|
|
174
171
|
undefined;
|
|
175
172
|
|
|
176
173
|
return {
|
|
177
174
|
key: 'literal',
|
|
178
175
|
ctor: cons,
|
|
179
176
|
name: SIMPLE_NAMES[cons.name] ?? cons.name,
|
|
180
|
-
value:
|
|
177
|
+
value: literal
|
|
181
178
|
};
|
|
182
179
|
} else if (complexName in GLOBAL_COMPLEX) {
|
|
183
180
|
const cons = GLOBAL_COMPLEX[complexName];
|
|
@@ -227,6 +224,11 @@ export const TypeBuilder: {
|
|
|
227
224
|
},
|
|
228
225
|
finalize: (type: CompositionType) => {
|
|
229
226
|
const { undefinable, nullable, subTypes } = type;
|
|
227
|
+
|
|
228
|
+
if (subTypes.length === 0) { // We have an unknown type?
|
|
229
|
+
return { key: 'unknown', nullable, undefinable };
|
|
230
|
+
}
|
|
231
|
+
|
|
230
232
|
const [first] = subTypes;
|
|
231
233
|
|
|
232
234
|
if (first.key === 'template') {
|
|
@@ -255,7 +257,7 @@ export const TypeBuilder: {
|
|
|
255
257
|
const tsTypeArguments = resolver.getAllTypeArguments(type);
|
|
256
258
|
const props = resolver.getPropertiesOfType(type);
|
|
257
259
|
if (props.length === 0) {
|
|
258
|
-
return { key: 'literal', name: 'Object', ctor: Object };
|
|
260
|
+
return { key: 'literal', name: 'Object', ctor: Object, importName };
|
|
259
261
|
}
|
|
260
262
|
|
|
261
263
|
for (const member of props) {
|
|
@@ -266,6 +268,9 @@ export const TypeBuilder: {
|
|
|
266
268
|
!member.getName().includes('@') && // if not a symbol
|
|
267
269
|
!memberType.getCallSignatures().length // if not a function
|
|
268
270
|
) {
|
|
271
|
+
if ((ts.isPropertySignature(dec) || ts.isPropertyDeclaration(dec)) && !!dec.questionToken) {
|
|
272
|
+
Object.defineProperty(memberType, UNDEFINED, { value: true });
|
|
273
|
+
}
|
|
269
274
|
tsFieldTypes[member.getName()] = memberType;
|
|
270
275
|
}
|
|
271
276
|
}
|
|
@@ -275,26 +280,34 @@ export const TypeBuilder: {
|
|
|
275
280
|
},
|
|
276
281
|
concrete: {
|
|
277
282
|
build: (resolver, type) => {
|
|
283
|
+
if (!DocUtil.hasDocTag(type, 'concrete')) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
278
287
|
const [tag] = DocUtil.readDocTag(type, 'concrete');
|
|
279
|
-
|
|
280
|
-
// eslint-disable-next-line prefer-const
|
|
281
|
-
let [importName, name] = tag.split('#');
|
|
288
|
+
let [importName, name] = tag?.split('#') ?? [];
|
|
282
289
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
290
|
+
// Resolving relative to source file
|
|
291
|
+
if (!importName || importName.startsWith('.')) {
|
|
292
|
+
const rawSourceFile: string = DeclarationUtil.getDeclarations(type)
|
|
293
|
+
?.find(x => ts.getAllJSDocTags(x, (t): t is ts.JSDocTag => t.tagName.getText() === 'concrete').length)
|
|
294
|
+
?.getSourceFile().fileName ?? '';
|
|
288
295
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
296
|
+
if (!importName || importName === '.') {
|
|
297
|
+
importName = resolver.getFileImportName(rawSourceFile);
|
|
298
|
+
} else {
|
|
299
|
+
const base = path.dirname(rawSourceFile);
|
|
300
|
+
importName = resolver.getFileImportName(path.resolve(base, importName));
|
|
295
301
|
}
|
|
296
|
-
return { key: 'managed', name, importName };
|
|
297
302
|
}
|
|
303
|
+
|
|
304
|
+
// Convert name to $Concrete suffix if not provided
|
|
305
|
+
if (!name) {
|
|
306
|
+
const [decl] = DeclarationUtil.getDeclarations(type).filter(x => ts.isInterfaceDeclaration(x) || ts.isTypeAliasDeclaration(x));
|
|
307
|
+
name = `${decl.name.text}$Concrete`;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return { key: 'managed', name, importName };
|
|
298
311
|
}
|
|
299
312
|
}
|
|
300
313
|
};
|
package/src/resolver/cache.ts
CHANGED
package/src/resolver/coerce.ts
CHANGED
|
@@ -50,19 +50,19 @@ export class CoerceUtil {
|
|
|
50
50
|
|
|
51
51
|
switch (type) {
|
|
52
52
|
case Date: {
|
|
53
|
-
const
|
|
53
|
+
const value = typeof input === 'number' || /^[-]?\d+$/.test(`${input}`) ?
|
|
54
54
|
new Date(parseInt(`${input}`, 10)) : new Date(`${input}`);
|
|
55
|
-
if (strict && Number.isNaN(
|
|
55
|
+
if (strict && Number.isNaN(value.getTime())) {
|
|
56
56
|
throw new Error(`Invalid date value: ${input}`);
|
|
57
57
|
}
|
|
58
|
-
return
|
|
58
|
+
return value;
|
|
59
59
|
}
|
|
60
60
|
case Number: {
|
|
61
|
-
const
|
|
62
|
-
if (strict && Number.isNaN(
|
|
61
|
+
const value = `${input}`.includes('.') ? parseFloat(`${input}`) : parseInt(`${input}`, 10);
|
|
62
|
+
if (strict && Number.isNaN(value)) {
|
|
63
63
|
throw new Error(`Invalid numeric value: ${input}`);
|
|
64
64
|
}
|
|
65
|
-
return
|
|
65
|
+
return value;
|
|
66
66
|
}
|
|
67
67
|
case BigInt: {
|
|
68
68
|
try {
|
package/src/resolver/service.ts
CHANGED
|
@@ -2,12 +2,12 @@ import ts from 'typescript';
|
|
|
2
2
|
|
|
3
3
|
import { path, ManifestIndex, ManifestModuleUtil, IndexedFile } from '@travetto/manifest';
|
|
4
4
|
|
|
5
|
-
import type { AnyType, TransformResolver } from './types';
|
|
6
|
-
import { TypeCategorize, TypeBuilder } from './builder';
|
|
7
|
-
import { VisitCache } from './cache';
|
|
8
|
-
import { DocUtil } from '../util/doc';
|
|
9
|
-
import { DeclarationUtil } from '../util/declaration';
|
|
10
|
-
import { transformCast } from '../types/shared';
|
|
5
|
+
import type { AnyType, TransformResolver } from './types.ts';
|
|
6
|
+
import { TypeCategorize, TypeBuilder } from './builder.ts';
|
|
7
|
+
import { VisitCache } from './cache.ts';
|
|
8
|
+
import { DocUtil } from '../util/doc.ts';
|
|
9
|
+
import { DeclarationUtil } from '../util/declaration.ts';
|
|
10
|
+
import { transformCast } from '../types/shared.ts';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Implementation of TransformResolver
|
|
@@ -38,10 +38,12 @@ export class SimpleResolver implements TransformResolver {
|
|
|
38
38
|
const type = ManifestModuleUtil.getFileType(file);
|
|
39
39
|
|
|
40
40
|
if (type !== 'js' && type !== 'ts') {
|
|
41
|
-
sourceFile = `${sourceFile}.
|
|
41
|
+
sourceFile = `${sourceFile}${ManifestModuleUtil.SOURCE_DEF_EXT}`;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
const sourceType = ManifestModuleUtil.getFileType(sourceFile);
|
|
45
|
+
|
|
46
|
+
return this.#manifestIndex.getEntry((sourceType === 'ts' || sourceType === 'js') ? sourceFile : undefined!) ??
|
|
45
47
|
this.#manifestIndex.getFromImport(ManifestModuleUtil.withoutSourceExtension(sourceFile).replace(/^.*node_modules\//, ''));
|
|
46
48
|
}
|
|
47
49
|
|
|
@@ -132,7 +134,7 @@ export class SimpleResolver implements TransformResolver {
|
|
|
132
134
|
result.comment = DocUtil.describeDocs(type).description;
|
|
133
135
|
|
|
134
136
|
if ('tsTypeArguments' in result) {
|
|
135
|
-
result.typeArguments = result.tsTypeArguments!.map((elType
|
|
137
|
+
result.typeArguments = result.tsTypeArguments!.map((elType) => resolve(elType, type.aliasSymbol, depth + 1));
|
|
136
138
|
delete result.tsTypeArguments;
|
|
137
139
|
}
|
|
138
140
|
if ('tsFieldTypes' in result) {
|
|
@@ -144,7 +146,7 @@ export class SimpleResolver implements TransformResolver {
|
|
|
144
146
|
delete result.tsFieldTypes;
|
|
145
147
|
}
|
|
146
148
|
if ('tsSubTypes' in result) {
|
|
147
|
-
result.subTypes = result.tsSubTypes!.map((elType
|
|
149
|
+
result.subTypes = result.tsSubTypes!.map((elType) => resolve(elType, type.aliasSymbol, depth + 1));
|
|
148
150
|
delete result.tsSubTypes;
|
|
149
151
|
}
|
|
150
152
|
if (finalize) {
|
package/src/resolver/types.ts
CHANGED
package/src/state.ts
CHANGED
|
@@ -2,18 +2,18 @@ import ts from 'typescript';
|
|
|
2
2
|
|
|
3
3
|
import { path, ManifestIndex } from '@travetto/manifest';
|
|
4
4
|
|
|
5
|
-
import { ManagedType, AnyType } from './resolver/types';
|
|
6
|
-
import { State, DecoratorMeta, Transformer, ModuleNameSymbol } from './types/visitor';
|
|
7
|
-
import { SimpleResolver } from './resolver/service';
|
|
8
|
-
import { ImportManager } from './importer';
|
|
9
|
-
import { Import } from './types/shared';
|
|
10
|
-
|
|
11
|
-
import { DocUtil } from './util/doc';
|
|
12
|
-
import { DecoratorUtil } from './util/decorator';
|
|
13
|
-
import { DeclarationUtil } from './util/declaration';
|
|
14
|
-
import { CoreUtil } from './util/core';
|
|
15
|
-
import { LiteralUtil } from './util/literal';
|
|
16
|
-
import { SystemUtil } from './util/system';
|
|
5
|
+
import { ManagedType, AnyType, ForeignType } from './resolver/types.ts';
|
|
6
|
+
import { State, DecoratorMeta, Transformer, ModuleNameSymbol } from './types/visitor.ts';
|
|
7
|
+
import { SimpleResolver } from './resolver/service.ts';
|
|
8
|
+
import { ImportManager } from './importer.ts';
|
|
9
|
+
import { Import } from './types/shared.ts';
|
|
10
|
+
|
|
11
|
+
import { DocUtil } from './util/doc.ts';
|
|
12
|
+
import { DecoratorUtil } from './util/decorator.ts';
|
|
13
|
+
import { DeclarationUtil } from './util/declaration.ts';
|
|
14
|
+
import { CoreUtil } from './util/core.ts';
|
|
15
|
+
import { LiteralUtil } from './util/literal.ts';
|
|
16
|
+
import { SystemUtil } from './util/system.ts';
|
|
17
17
|
|
|
18
18
|
function hasOriginal(n: ts.Node): n is ts.Node & { original: ts.Node } {
|
|
19
19
|
return !!n && !n.parent && 'original' in n && !!n.original;
|
|
@@ -49,17 +49,16 @@ export class TransformerState implements State {
|
|
|
49
49
|
this.#resolver = new SimpleResolver(checker, manifestIndex);
|
|
50
50
|
this.#imports = new ImportManager(source, factory, this.#resolver);
|
|
51
51
|
this.file = path.toPosix(source.fileName);
|
|
52
|
-
this.importName = this.#resolver.getFileImportName(this.file
|
|
52
|
+
this.importName = this.#resolver.getFileImportName(this.file);
|
|
53
53
|
this.source = source;
|
|
54
54
|
this.factory = factory;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
59
|
-
* @param fileOrImport
|
|
58
|
+
* Rewrite module specifier normalizing output
|
|
60
59
|
*/
|
|
61
|
-
|
|
62
|
-
return this.#imports.
|
|
60
|
+
normalizeModuleSpecifier<T extends ts.Expression | undefined>(spec: T): T {
|
|
61
|
+
return this.#imports.normalizeModuleSpecifier(spec);
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
/**
|
|
@@ -223,9 +222,8 @@ export class TransformerState implements State {
|
|
|
223
222
|
/**
|
|
224
223
|
* Finalize the source file for emission
|
|
225
224
|
*/
|
|
226
|
-
finalize(
|
|
227
|
-
|
|
228
|
-
return ret;
|
|
225
|
+
finalize(source: ts.SourceFile): ts.SourceFile {
|
|
226
|
+
return this.#imports.finalize(source);
|
|
229
227
|
}
|
|
230
228
|
|
|
231
229
|
/**
|
|
@@ -295,6 +293,7 @@ export class TransformerState implements State {
|
|
|
295
293
|
* @param module
|
|
296
294
|
*/
|
|
297
295
|
findDecorator(mod: string | Transformer, node: ts.Node, name: string, module?: string): ts.Decorator | undefined {
|
|
296
|
+
module = module?.replace(/[.]ts$/, ''); // Replace extension if exists
|
|
298
297
|
mod = typeof mod === 'string' ? mod : mod[ModuleNameSymbol]!;
|
|
299
298
|
const target = `${mod}:${name}`;
|
|
300
299
|
const list = this.getDecoratorList(node);
|
|
@@ -388,4 +387,39 @@ export class TransformerState implements State {
|
|
|
388
387
|
getFileImportName(file: string): string {
|
|
389
388
|
return this.#resolver.getFileImportName(file);
|
|
390
389
|
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Produce a foreign target type
|
|
393
|
+
*/
|
|
394
|
+
getForeignTarget(ret: ForeignType): ts.Expression {
|
|
395
|
+
return this.fromLiteral({
|
|
396
|
+
Ⲑid: `${ret.source.split('node_modules/')[1]}+${ret.name}`
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Return a concrete type the given type of a node
|
|
402
|
+
*/
|
|
403
|
+
getConcreteType(node: ts.Node): ts.Expression {
|
|
404
|
+
const type = this.resolveType(node);
|
|
405
|
+
|
|
406
|
+
if (type.key === 'managed') {
|
|
407
|
+
return this.getOrImport(type);
|
|
408
|
+
} else if (type.key === 'foreign') {
|
|
409
|
+
return this.getForeignTarget(type);
|
|
410
|
+
} else {
|
|
411
|
+
const file = node.getSourceFile().fileName;
|
|
412
|
+
const src = this.getFileImportName(file);
|
|
413
|
+
throw new Error(`Unable to import non-external type: ${node.getText()} ${type.key}: ${src}`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Get apparent type of requested field
|
|
419
|
+
*/
|
|
420
|
+
getApparentTypeOfField(value: ts.Type, field: string): AnyType | undefined {
|
|
421
|
+
const checker = this.#resolver.getChecker();
|
|
422
|
+
const props = checker.getApparentType(value).getApparentProperties().find(x => x.escapedName === field);
|
|
423
|
+
return props ? this.resolveType(checker.getTypeOfSymbol(props)) : undefined;
|
|
424
|
+
}
|
|
391
425
|
}
|
package/src/types/visitor.ts
CHANGED
|
@@ -24,7 +24,7 @@ export type TransformPhase = 'before' | 'after';
|
|
|
24
24
|
|
|
25
25
|
export type TransformerType =
|
|
26
26
|
'class' | 'method' | 'property' | 'getter' | 'setter' | 'parameter' |
|
|
27
|
-
'static-method' | 'call' | 'function' | 'file';
|
|
27
|
+
'static-method' | 'call' | 'function' | 'file' | 'type' | 'interface';
|
|
28
28
|
|
|
29
29
|
export const ModuleNameSymbol = Symbol.for('@travetto/transformer:id');
|
|
30
30
|
|
package/src/util/declaration.ts
CHANGED
package/src/util/decorator.ts
CHANGED
package/src/util/doc.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
|
|
3
|
-
import { transformCast, DeclDocumentation } from '../types/shared';
|
|
4
|
-
import { CoreUtil } from './core';
|
|
5
|
-
import { DeclarationUtil } from './declaration';
|
|
3
|
+
import { transformCast, DeclDocumentation } from '../types/shared.ts';
|
|
4
|
+
import { CoreUtil } from './core.ts';
|
|
5
|
+
import { DeclarationUtil } from './declaration.ts';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Utilities for dealing with docs
|
|
@@ -76,6 +76,15 @@ export class DocUtil {
|
|
|
76
76
|
.map(el => el.text!.map(x => x.text).join('')); // Join all text
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Has JS Doc tags for a type
|
|
81
|
+
*/
|
|
82
|
+
static hasDocTag(type: ts.Type | ts.Symbol, name: string): boolean {
|
|
83
|
+
const tags = CoreUtil.getSymbol(type)?.getJsDocTags() ?? [];
|
|
84
|
+
return tags.some(el => el.name === name);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
79
88
|
/**
|
|
80
89
|
* Read augments information
|
|
81
90
|
* @param type
|
package/src/util/import.ts
CHANGED
package/src/util/literal.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
|
|
3
|
-
import { transformCast, TemplateLiteral } from '../types/shared';
|
|
3
|
+
import { transformCast, TemplateLiteral } from '../types/shared.ts';
|
|
4
4
|
|
|
5
5
|
const TypedObject: {
|
|
6
6
|
keys<T = unknown, K extends keyof T = keyof T>(o: T): K[];
|
|
@@ -39,7 +39,9 @@ export class LiteralUtil {
|
|
|
39
39
|
static fromLiteral(factory: ts.NodeFactory, val: null): ts.NullLiteral;
|
|
40
40
|
static fromLiteral(factory: ts.NodeFactory, val: object): ts.ObjectLiteralExpression;
|
|
41
41
|
static fromLiteral(factory: ts.NodeFactory, val: unknown[]): ts.ArrayLiteralExpression;
|
|
42
|
-
static fromLiteral(factory: ts.NodeFactory, val: string
|
|
42
|
+
static fromLiteral(factory: ts.NodeFactory, val: string): ts.StringLiteral;
|
|
43
|
+
static fromLiteral(factory: ts.NodeFactory, val: number): ts.NumericLiteral;
|
|
44
|
+
static fromLiteral(factory: ts.NodeFactory, val: boolean): ts.BooleanLiteral;
|
|
43
45
|
static fromLiteral(factory: ts.NodeFactory, val: unknown): ts.Node {
|
|
44
46
|
if (isNode(val)) { // If already a node
|
|
45
47
|
return val;
|
|
@@ -137,14 +139,14 @@ export class LiteralUtil {
|
|
|
137
139
|
* Extend object literal, whether JSON or ts.ObjectLiteralExpression
|
|
138
140
|
*/
|
|
139
141
|
static extendObjectLiteral(factory: ts.NodeFactory, src: object | ts.Expression, ...rest: (object | ts.Expression)[]): ts.ObjectLiteralExpression {
|
|
140
|
-
let
|
|
142
|
+
let literal = this.fromLiteral(factory, src);
|
|
141
143
|
if (rest.find(x => !!x)) {
|
|
142
|
-
|
|
143
|
-
factory.createSpreadAssignment(
|
|
144
|
+
literal = factory.createObjectLiteralExpression([
|
|
145
|
+
factory.createSpreadAssignment(literal),
|
|
144
146
|
...(rest.filter(x => !!x).map(r => factory.createSpreadAssignment(this.fromLiteral(factory, r))))
|
|
145
147
|
]);
|
|
146
148
|
}
|
|
147
|
-
return
|
|
149
|
+
return literal;
|
|
148
150
|
}
|
|
149
151
|
|
|
150
152
|
/**
|
package/src/visitor.ts
CHANGED
|
@@ -2,8 +2,8 @@ import ts from 'typescript';
|
|
|
2
2
|
|
|
3
3
|
import { ManifestModuleFolderType, ManifestModuleUtil } from '@travetto/manifest';
|
|
4
4
|
|
|
5
|
-
import { DecoratorMeta, TransformerType, NodeTransformer, TransformerSet, State, TransformPhase } from './types/visitor';
|
|
6
|
-
import { CoreUtil } from './util/core';
|
|
5
|
+
import { DecoratorMeta, TransformerType, NodeTransformer, TransformerSet, State, TransformPhase } from './types/visitor.ts';
|
|
6
|
+
import { CoreUtil } from './util/core.ts';
|
|
7
7
|
|
|
8
8
|
const COMPILER_SRC = new Set<ManifestModuleFolderType>(['support', 'src', '$index']);
|
|
9
9
|
|
|
@@ -35,6 +35,10 @@ export class VisitorFactory<S extends State = State> {
|
|
|
35
35
|
return 'setter';
|
|
36
36
|
} else if (ts.isSourceFile(node)) {
|
|
37
37
|
return 'file';
|
|
38
|
+
} else if (ts.isInterfaceDeclaration(node)) {
|
|
39
|
+
return 'interface';
|
|
40
|
+
} else if (ts.isTypeAliasDeclaration(node)) {
|
|
41
|
+
return 'type';
|
|
38
42
|
}
|
|
39
43
|
}
|
|
40
44
|
|
|
@@ -58,7 +62,7 @@ export class VisitorFactory<S extends State = State> {
|
|
|
58
62
|
this.#transformers.set(trn.type, {});
|
|
59
63
|
}
|
|
60
64
|
const set = this.#transformers.get(trn.type)!;
|
|
61
|
-
const targets = trn.target && trn.target.length ? trn.target : ['
|
|
65
|
+
const targets = trn.target && trn.target.length ? trn.target : ['__all__'];
|
|
62
66
|
|
|
63
67
|
for (const target of targets) {
|
|
64
68
|
for (const phase of ['before', 'after'] as const) {
|
|
@@ -96,13 +100,13 @@ export class VisitorFactory<S extends State = State> {
|
|
|
96
100
|
return state.finalize(file);
|
|
97
101
|
}
|
|
98
102
|
|
|
99
|
-
let
|
|
103
|
+
let node = this.visit(state, context, file);
|
|
100
104
|
|
|
101
105
|
// Process added content
|
|
102
106
|
const changed = state.added.size;
|
|
103
|
-
let statements: ts.NodeArray<ts.Statement> | ts.Statement[] =
|
|
107
|
+
let statements: ts.NodeArray<ts.Statement> | ts.Statement[] = node.statements;
|
|
104
108
|
while (state.added.size) {
|
|
105
|
-
for (const [idx, all] of [...state.added].
|
|
109
|
+
for (const [idx, all] of [...state.added].toSorted(([idxA], [idxB]) => idxB - idxA)) {
|
|
106
110
|
statements = [
|
|
107
111
|
...statements.slice(0, Math.max(idx, 0)),
|
|
108
112
|
...all.map(v => this.visit(state, context, v)),
|
|
@@ -113,9 +117,9 @@ export class VisitorFactory<S extends State = State> {
|
|
|
113
117
|
}
|
|
114
118
|
|
|
115
119
|
if (changed) {
|
|
116
|
-
|
|
120
|
+
node = CoreUtil.updateSource(context.factory, node, statements);
|
|
117
121
|
}
|
|
118
|
-
return state.finalize(
|
|
122
|
+
return state.finalize(node);
|
|
119
123
|
} catch (err) {
|
|
120
124
|
if (!(err instanceof Error)) {
|
|
121
125
|
throw err;
|
|
@@ -136,7 +140,7 @@ export class VisitorFactory<S extends State = State> {
|
|
|
136
140
|
return;
|
|
137
141
|
}
|
|
138
142
|
|
|
139
|
-
for (const all of set[phase]!.get('
|
|
143
|
+
for (const all of set[phase]!.get('__all__') ?? []) {
|
|
140
144
|
node = all[phase]!<T>(state, node) ?? node;
|
|
141
145
|
}
|
|
142
146
|
return node;
|