@travetto/transformer 6.0.0-rc.1 → 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 +73 -58
- 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,31 +91,21 @@ 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()) {
|
|
106
104
|
return { category: 'shape', type };
|
|
107
105
|
} else if ((objectFlags & ts.ObjectFlags.Mapped)) { // Mapped types: Pick, Omit, Exclude, Retain
|
|
108
|
-
|
|
106
|
+
if (type.getProperties().some(x => x.declarations || x.valueDeclaration)) {
|
|
107
|
+
return { category: 'shape', type };
|
|
108
|
+
}
|
|
109
109
|
}
|
|
110
110
|
return { category: 'literal', type };
|
|
111
111
|
}
|
|
@@ -120,13 +120,10 @@ export const TypeBuilder: {
|
|
|
120
120
|
}
|
|
121
121
|
} = {
|
|
122
122
|
unknown: {
|
|
123
|
-
build: (resolver, type) =>
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
},
|
|
128
|
-
void: {
|
|
129
|
-
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
|
+
}
|
|
130
127
|
},
|
|
131
128
|
tuple: {
|
|
132
129
|
build: (resolver, type) => ({ key: 'tuple', tsTupleTypes: resolver.getAllTypeArguments(type), subTypes: [] })
|
|
@@ -166,16 +163,18 @@ export const TypeBuilder: {
|
|
|
166
163
|
const name = resolver.getTypeAsString(type) ?? '';
|
|
167
164
|
const complexName = CoreUtil.getSymbol(type)?.getName() ?? '';
|
|
168
165
|
|
|
169
|
-
if (name in
|
|
166
|
+
if (name in UNDEFINED_GLOBAL) {
|
|
167
|
+
return { key: 'literal', ctor: undefined, name };
|
|
168
|
+
} else if (name in GLOBAL_SIMPLE) {
|
|
170
169
|
const cons = GLOBAL_SIMPLE[name];
|
|
171
|
-
const
|
|
170
|
+
const literal = LiteralUtil.isLiteralType(type) ? CoerceUtil.coerce(type.value, transformCast(cons), false) :
|
|
172
171
|
undefined;
|
|
173
172
|
|
|
174
173
|
return {
|
|
175
174
|
key: 'literal',
|
|
176
175
|
ctor: cons,
|
|
177
176
|
name: SIMPLE_NAMES[cons.name] ?? cons.name,
|
|
178
|
-
value:
|
|
177
|
+
value: literal
|
|
179
178
|
};
|
|
180
179
|
} else if (complexName in GLOBAL_COMPLEX) {
|
|
181
180
|
const cons = GLOBAL_COMPLEX[complexName];
|
|
@@ -225,6 +224,11 @@ export const TypeBuilder: {
|
|
|
225
224
|
},
|
|
226
225
|
finalize: (type: CompositionType) => {
|
|
227
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
|
+
|
|
228
232
|
const [first] = subTypes;
|
|
229
233
|
|
|
230
234
|
if (first.key === 'template') {
|
|
@@ -253,7 +257,7 @@ export const TypeBuilder: {
|
|
|
253
257
|
const tsTypeArguments = resolver.getAllTypeArguments(type);
|
|
254
258
|
const props = resolver.getPropertiesOfType(type);
|
|
255
259
|
if (props.length === 0) {
|
|
256
|
-
return { key: 'literal', name: 'Object', ctor: Object };
|
|
260
|
+
return { key: 'literal', name: 'Object', ctor: Object, importName };
|
|
257
261
|
}
|
|
258
262
|
|
|
259
263
|
for (const member of props) {
|
|
@@ -264,6 +268,9 @@ export const TypeBuilder: {
|
|
|
264
268
|
!member.getName().includes('@') && // if not a symbol
|
|
265
269
|
!memberType.getCallSignatures().length // if not a function
|
|
266
270
|
) {
|
|
271
|
+
if ((ts.isPropertySignature(dec) || ts.isPropertyDeclaration(dec)) && !!dec.questionToken) {
|
|
272
|
+
Object.defineProperty(memberType, UNDEFINED, { value: true });
|
|
273
|
+
}
|
|
267
274
|
tsFieldTypes[member.getName()] = memberType;
|
|
268
275
|
}
|
|
269
276
|
}
|
|
@@ -273,26 +280,34 @@ export const TypeBuilder: {
|
|
|
273
280
|
},
|
|
274
281
|
concrete: {
|
|
275
282
|
build: (resolver, type) => {
|
|
283
|
+
if (!DocUtil.hasDocTag(type, 'concrete')) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
276
287
|
const [tag] = DocUtil.readDocTag(type, 'concrete');
|
|
277
|
-
|
|
278
|
-
// eslint-disable-next-line prefer-const
|
|
279
|
-
let [importName, name] = tag.split('#');
|
|
288
|
+
let [importName, name] = tag?.split('#') ?? [];
|
|
280
289
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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 ?? '';
|
|
286
295
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
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));
|
|
293
301
|
}
|
|
294
|
-
return { key: 'managed', name, importName };
|
|
295
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 };
|
|
296
311
|
}
|
|
297
312
|
}
|
|
298
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;
|