@travetto/transformer 3.1.4 → 3.2.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/resolver/builder.ts +48 -3
- package/src/resolver/types.ts +18 -1
- package/src/types/shared.ts +5 -0
- package/src/util/literal.ts +31 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/transformer",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0-rc.0",
|
|
4
4
|
"description": "Functionality for AST transformations, with transformer registration, and general utils",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"directory": "module/transformer"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@travetto/manifest": "^3.
|
|
27
|
+
"@travetto/manifest": "^3.2.0-rc.0",
|
|
28
28
|
"tslib": "^2.5.2",
|
|
29
29
|
"typescript": "^5.0.4"
|
|
30
30
|
},
|
package/src/resolver/builder.ts
CHANGED
|
@@ -7,8 +7,9 @@ import { DocUtil } from '../util/doc';
|
|
|
7
7
|
import { CoreUtil } from '../util/core';
|
|
8
8
|
import { DeclarationUtil } from '../util/declaration';
|
|
9
9
|
import { LiteralUtil } from '../util/literal';
|
|
10
|
+
import { TemplateLiteralPart } from '../types/shared';
|
|
10
11
|
|
|
11
|
-
import { Type, AnyType, UnionType, TransformResolver } from './types';
|
|
12
|
+
import { Type, AnyType, UnionType, TransformResolver, TemplateType } from './types';
|
|
12
13
|
import { CoerceUtil } from './coerce';
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -33,7 +34,10 @@ const GLOBAL_SIMPLE: Record<string, Function> = {
|
|
|
33
34
|
PromiseConstructor: Promise.constructor
|
|
34
35
|
};
|
|
35
36
|
|
|
36
|
-
type Category =
|
|
37
|
+
type Category =
|
|
38
|
+
'void' | 'undefined' | 'concrete' | 'unknown' |
|
|
39
|
+
'tuple' | 'shape' | 'literal' | 'template' | 'managed' |
|
|
40
|
+
'union' | 'foreign';
|
|
37
41
|
|
|
38
42
|
/**
|
|
39
43
|
* Type categorizer, input for builder
|
|
@@ -82,6 +86,8 @@ export function TypeCategorize(resolver: TransformResolver, type: ts.Type): { ca
|
|
|
82
86
|
} else {
|
|
83
87
|
return { category: 'managed', type: resolvedType };
|
|
84
88
|
}
|
|
89
|
+
} else if (flags & (ts.TypeFlags.TemplateLiteral)) {
|
|
90
|
+
return { category: 'template', type };
|
|
85
91
|
} else if (flags & (
|
|
86
92
|
ts.TypeFlags.Boolean | ts.TypeFlags.BooleanLiteral |
|
|
87
93
|
ts.TypeFlags.Number | ts.TypeFlags.NumberLiteral |
|
|
@@ -120,6 +126,36 @@ export const TypeBuilder: {
|
|
|
120
126
|
tuple: {
|
|
121
127
|
build: (resolver, type) => ({ key: 'tuple', tsTupleTypes: resolver.getAllTypeArguments(type), subTypes: [] })
|
|
122
128
|
},
|
|
129
|
+
template: {
|
|
130
|
+
build: (resolver, type) => {
|
|
131
|
+
// If we are have a template literal type, we need to make our own type node
|
|
132
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
133
|
+
if (type.flags & ts.TypeFlags.TemplateLiteral) {
|
|
134
|
+
const values: TemplateLiteralPart[] = [];
|
|
135
|
+
const texts = 'texts' in type && (typeof type.texts === 'object') && Array.isArray(type.texts) ? type.texts : undefined;
|
|
136
|
+
const types = 'types' in type && (typeof type.types === 'object') && Array.isArray(type.types) ? type.types : undefined;
|
|
137
|
+
if (texts?.length && types?.length) {
|
|
138
|
+
for (let i = 0; i < texts?.length; i += 1) {
|
|
139
|
+
if (texts[i] && texts[i] !== 'undefined') {
|
|
140
|
+
values.push(texts[i]);
|
|
141
|
+
}
|
|
142
|
+
if (types[i]) {
|
|
143
|
+
switch (types[i].intrinsicName) {
|
|
144
|
+
case 'number': values.push(Number); break;
|
|
145
|
+
case 'string': values.push(String); break;
|
|
146
|
+
case 'boolean': values.push(Boolean); break;
|
|
147
|
+
case 'undefined': values.push(''); break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (values.length > 0) {
|
|
152
|
+
return ({ key: 'template', template: { op: 'and', values }, ctor: String });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return { key: 'literal', ctor: Object };
|
|
157
|
+
}
|
|
158
|
+
},
|
|
123
159
|
literal: {
|
|
124
160
|
build: (resolver, type) => {
|
|
125
161
|
// Handle void/undefined
|
|
@@ -182,7 +218,16 @@ export const TypeBuilder: {
|
|
|
182
218
|
const { undefinable, nullable, subTypes } = type;
|
|
183
219
|
const [first] = subTypes;
|
|
184
220
|
|
|
185
|
-
if (
|
|
221
|
+
if (first.key === 'template') {
|
|
222
|
+
return {
|
|
223
|
+
key: 'template',
|
|
224
|
+
ctor: String,
|
|
225
|
+
nullable: type.nullable,
|
|
226
|
+
undefinable: type.undefinable,
|
|
227
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
228
|
+
template: { op: 'or', values: subTypes.map(x => (x as TemplateType).template!) }
|
|
229
|
+
};
|
|
230
|
+
} else if (subTypes.length === 1) {
|
|
186
231
|
return { undefinable, nullable, ...first };
|
|
187
232
|
} else if (first.key === 'literal' && subTypes.every(el => el.name === first.name)) { // We have a common
|
|
188
233
|
type.commonType = first;
|
package/src/resolver/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type ts from 'typescript';
|
|
2
|
+
import { TemplateLiteral } from '../types/shared';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Base type for a simplistic type structure
|
|
@@ -99,6 +100,20 @@ export interface LiteralType extends Type<'literal'> {
|
|
|
99
100
|
tsTypeArguments?: ts.Type[];
|
|
100
101
|
}
|
|
101
102
|
|
|
103
|
+
/**
|
|
104
|
+
* A literal template type
|
|
105
|
+
*/
|
|
106
|
+
export interface TemplateType extends Type<'template'> {
|
|
107
|
+
/**
|
|
108
|
+
* Pointer to real type (String)
|
|
109
|
+
*/
|
|
110
|
+
ctor: Function;
|
|
111
|
+
/**
|
|
112
|
+
* Type arguments
|
|
113
|
+
*/
|
|
114
|
+
template?: TemplateLiteral;
|
|
115
|
+
}
|
|
116
|
+
|
|
102
117
|
/**
|
|
103
118
|
* Union type
|
|
104
119
|
*/
|
|
@@ -161,7 +176,9 @@ export interface ForeignType extends Type<'foreign'> {
|
|
|
161
176
|
*/
|
|
162
177
|
export interface UnknownType extends Type<'unknown'> { }
|
|
163
178
|
|
|
164
|
-
export type AnyType =
|
|
179
|
+
export type AnyType =
|
|
180
|
+
TupleType | ShapeType | UnionType | LiteralType |
|
|
181
|
+
ManagedType | PointerType | UnknownType | ForeignType | TemplateType;
|
|
165
182
|
|
|
166
183
|
/**
|
|
167
184
|
* Simple interface for checked methods
|
package/src/types/shared.ts
CHANGED
|
@@ -25,3 +25,8 @@ export type Import = {
|
|
|
25
25
|
ident: ts.Identifier;
|
|
26
26
|
stmt?: ts.ImportDeclaration;
|
|
27
27
|
};
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
/** Template Literal Types */
|
|
31
|
+
export type TemplateLiteralPart = string | NumberConstructor | StringConstructor | BooleanConstructor;
|
|
32
|
+
export type TemplateLiteral = { op: 'and' | 'or', values: (TemplateLiteralPart | TemplateLiteral)[] };
|
package/src/util/literal.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
|
|
3
|
+
import { TemplateLiteral } from '../types/shared';
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* Utilities for dealing with literals
|
|
5
7
|
*/
|
|
@@ -41,6 +43,8 @@ export class LiteralUtil {
|
|
|
41
43
|
val = factory.createNumericLiteral(val);
|
|
42
44
|
} else if (typeof val === 'boolean') {
|
|
43
45
|
val = val ? factory.createTrue() : factory.createFalse();
|
|
46
|
+
} else if (val instanceof RegExp) {
|
|
47
|
+
val = factory.createRegularExpressionLiteral(`/${val.source}/${val.flags ?? ''}`);
|
|
44
48
|
} else if (val === String || val === Number || val === Boolean || val === Date || val === RegExp) {
|
|
45
49
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
46
50
|
val = factory.createIdentifier((val as Function).name);
|
|
@@ -144,4 +148,31 @@ export class LiteralUtil {
|
|
|
144
148
|
}
|
|
145
149
|
return undefined;
|
|
146
150
|
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Flatten a template literal into a regex
|
|
154
|
+
*/
|
|
155
|
+
static templateLiteralToRegex(template: TemplateLiteral, exact = true): string {
|
|
156
|
+
const out: string[] = [];
|
|
157
|
+
for (const el of template.values) {
|
|
158
|
+
if (el === Number) {
|
|
159
|
+
out.push('\\d+');
|
|
160
|
+
} else if (el === Boolean) {
|
|
161
|
+
out.push('(?:true|false)');
|
|
162
|
+
} else if (el === String) {
|
|
163
|
+
out.push('.+');
|
|
164
|
+
} else if (typeof el === 'string' || typeof el === 'number' || typeof el === 'boolean') {
|
|
165
|
+
out.push(`${el}`);
|
|
166
|
+
} else {
|
|
167
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
168
|
+
out.push(`(?:${this.templateLiteralToRegex(el as TemplateLiteral, false)})`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const body = out.join(template.op === 'and' ? '' : '|');
|
|
172
|
+
if (exact) {
|
|
173
|
+
return `^(?:${body})$`;
|
|
174
|
+
} else {
|
|
175
|
+
return body;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
147
178
|
}
|