@travetto/schema 6.0.1 → 7.0.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/README.md +30 -26
- package/__index__.ts +4 -2
- package/package.json +3 -3
- package/src/bind-util.ts +40 -37
- package/src/decorator/common.ts +32 -16
- package/src/decorator/field.ts +21 -184
- package/src/decorator/input.ts +207 -0
- package/src/decorator/method.ts +28 -0
- package/src/decorator/schema.ts +36 -29
- package/src/name.ts +2 -2
- package/src/service/changes.ts +27 -30
- package/src/service/registry-adapter.ts +340 -0
- package/src/service/registry-index.ts +240 -0
- package/src/service/types.ts +113 -63
- package/src/validate/types.ts +4 -0
- package/src/validate/validator.ts +70 -64
- package/support/transformer/util.ts +147 -61
- package/support/transformer.schema.ts +137 -49
- package/src/service/registry.ts +0 -501
|
@@ -4,10 +4,14 @@ import {
|
|
|
4
4
|
DecoratorUtil, DocUtil, ParamDocumentation, TransformerState, transformCast,
|
|
5
5
|
} from '@travetto/transformer';
|
|
6
6
|
|
|
7
|
+
export type ComputeConfig = { type?: AnyType, root?: ts.Node, name?: string, index?: number };
|
|
8
|
+
|
|
7
9
|
export class SchemaTransformUtil {
|
|
8
10
|
|
|
9
11
|
static SCHEMA_IMPORT = '@travetto/schema/src/decorator/schema.ts';
|
|
12
|
+
static METHOD_IMPORT = '@travetto/schema/src/decorator/method.ts';
|
|
10
13
|
static FIELD_IMPORT = '@travetto/schema/src/decorator/field.ts';
|
|
14
|
+
static INPUT_IMPORT = '@travetto/schema/src/decorator/input.ts';
|
|
11
15
|
static COMMON_IMPORT = '@travetto/schema/src/decorator/common.ts';
|
|
12
16
|
static TYPES_IMPORT = '@travetto/schema/src/types.ts';
|
|
13
17
|
|
|
@@ -28,6 +32,32 @@ export class SchemaTransformUtil {
|
|
|
28
32
|
}
|
|
29
33
|
break;
|
|
30
34
|
}
|
|
35
|
+
case 'mapped': {
|
|
36
|
+
const base = state.getOrImport(type);
|
|
37
|
+
const uniqueId = state.generateUniqueIdentifier(node, type, 'Δ');
|
|
38
|
+
const [id, existing] = state.registerIdentifier(uniqueId);
|
|
39
|
+
if (!existing) {
|
|
40
|
+
const cls = state.factory.createClassDeclaration(
|
|
41
|
+
[
|
|
42
|
+
state.createDecorator(this.SCHEMA_IMPORT, 'Schema', state.fromLiteral({
|
|
43
|
+
description: type.comment,
|
|
44
|
+
mappedOperation: type.operation,
|
|
45
|
+
mappedFields: type.fields,
|
|
46
|
+
})),
|
|
47
|
+
],
|
|
48
|
+
id, [], [state.factory.createHeritageClause(
|
|
49
|
+
ts.SyntaxKind.ExtendsKeyword, [state.factory.createExpressionWithTypeArguments(base, [])]
|
|
50
|
+
)], []
|
|
51
|
+
);
|
|
52
|
+
cls.getText = (): string => `
|
|
53
|
+
class ${uniqueId} extends ${type.mappedClassName} {
|
|
54
|
+
fields: ${type.fields?.join(', ')}
|
|
55
|
+
operation: ${type.operation}
|
|
56
|
+
}`;
|
|
57
|
+
state.addStatements([cls], root || node);
|
|
58
|
+
}
|
|
59
|
+
return id;
|
|
60
|
+
}
|
|
31
61
|
case 'unknown': {
|
|
32
62
|
const imp = state.importFile(this.TYPES_IMPORT);
|
|
33
63
|
return state.createAccess(imp.ident, 'UnknownType');
|
|
@@ -40,18 +70,14 @@ export class SchemaTransformUtil {
|
|
|
40
70
|
if (!existing) {
|
|
41
71
|
const cls = state.factory.createClassDeclaration(
|
|
42
72
|
[
|
|
43
|
-
state.createDecorator(this.SCHEMA_IMPORT, 'Schema'
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
title: type.name,
|
|
47
|
-
description: type.comment
|
|
48
|
-
})
|
|
49
|
-
)
|
|
73
|
+
state.createDecorator(this.SCHEMA_IMPORT, 'Schema', state.fromLiteral({
|
|
74
|
+
description: type.comment
|
|
75
|
+
})),
|
|
50
76
|
],
|
|
51
77
|
id, [], [],
|
|
52
78
|
Object.entries(type.fieldTypes)
|
|
53
79
|
.map(([k, v]) =>
|
|
54
|
-
this.
|
|
80
|
+
this.computeInput(state, state.factory.createPropertyDeclaration(
|
|
55
81
|
[], /\W/.test(k) ? state.factory.createComputedPropertyName(state.fromLiteral(k)) : k,
|
|
56
82
|
v.undefinable || v.nullable ? state.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined,
|
|
57
83
|
v.key === 'unknown' ? state.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword) : undefined, undefined
|
|
@@ -83,45 +109,50 @@ export class SchemaTransformUtil {
|
|
|
83
109
|
}
|
|
84
110
|
|
|
85
111
|
/**
|
|
86
|
-
* Compute
|
|
112
|
+
* Compute decorator params from property/parameter/getter/setter
|
|
87
113
|
*/
|
|
88
|
-
static
|
|
89
|
-
state: TransformerState,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
114
|
+
static computeInputDecoratorParams<T extends ts.PropertyDeclaration | ts.ParameterDeclaration | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration>(
|
|
115
|
+
state: TransformerState,
|
|
116
|
+
node: T,
|
|
117
|
+
config?: ComputeConfig
|
|
118
|
+
): ts.Expression[] {
|
|
119
|
+
const typeExpr = config?.type ?? state.resolveType(ts.isSetAccessor(node) ? node.parameters[0] : node);
|
|
120
|
+
const attrs: Record<string, string | boolean | object | number | ts.Expression> = {};
|
|
94
121
|
|
|
95
122
|
if (!ts.isGetAccessorDeclaration(node) && !ts.isSetAccessorDeclaration(node)) {
|
|
96
123
|
// eslint-disable-next-line no-bitwise
|
|
97
124
|
if ((ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Readonly) > 0) {
|
|
98
|
-
attrs.
|
|
99
|
-
} else if (
|
|
100
|
-
attrs.
|
|
125
|
+
attrs.access = 'readonly';
|
|
126
|
+
} else if (!!node.questionToken || !!typeExpr.undefinable || !!node.initializer) {
|
|
127
|
+
attrs.required = { active: false };
|
|
101
128
|
}
|
|
102
|
-
if (node.initializer && (
|
|
129
|
+
if (node.initializer !== undefined && (
|
|
103
130
|
ts.isLiteralExpression(node.initializer) ||
|
|
131
|
+
node.initializer.kind === ts.SyntaxKind.TrueKeyword ||
|
|
132
|
+
node.initializer.kind === ts.SyntaxKind.FalseKeyword ||
|
|
104
133
|
(ts.isArrayLiteralExpression(node.initializer) && node.initializer.elements.length === 0)
|
|
105
134
|
)) {
|
|
106
|
-
attrs.
|
|
135
|
+
attrs.default = node.initializer;
|
|
107
136
|
}
|
|
108
137
|
} else {
|
|
109
138
|
const acc = DeclarationUtil.getAccessorPair(node);
|
|
110
|
-
attrs.
|
|
139
|
+
attrs.accessor = true;
|
|
111
140
|
if (!acc.setter) {
|
|
112
|
-
attrs.
|
|
141
|
+
attrs.access = 'readonly';
|
|
113
142
|
}
|
|
114
143
|
if (!acc.getter) {
|
|
115
|
-
attrs.
|
|
116
|
-
} else if (
|
|
117
|
-
attrs.
|
|
144
|
+
attrs.access = 'writeonly';
|
|
145
|
+
} else if (!!typeExpr.undefinable) {
|
|
146
|
+
attrs.required = { active: false };
|
|
118
147
|
}
|
|
119
148
|
}
|
|
120
149
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
150
|
+
const rawName = node.getSourceFile()?.text ? node.name.getText() ?? undefined : undefined;
|
|
151
|
+
const providedName = config?.name ?? rawName!;
|
|
152
|
+
attrs.name = providedName;
|
|
153
|
+
|
|
154
|
+
if (rawName !== providedName && rawName) {
|
|
155
|
+
attrs.sourceText = rawName;
|
|
125
156
|
}
|
|
126
157
|
|
|
127
158
|
const primaryExpr = typeExpr.key === 'literal' && typeExpr.typeArguments?.[0] ? typeExpr.typeArguments[0] : typeExpr;
|
|
@@ -133,79 +164,107 @@ export class SchemaTransformUtil {
|
|
|
133
164
|
.filter(x => x !== undefined && x !== null);
|
|
134
165
|
|
|
135
166
|
if (values.length === primaryExpr.subTypes.length) {
|
|
136
|
-
attrs.
|
|
167
|
+
attrs.enum = {
|
|
137
168
|
values,
|
|
138
169
|
message: `{path} is only allowed to be "${values.join('" or "')}"`
|
|
139
|
-
}
|
|
170
|
+
};
|
|
140
171
|
}
|
|
141
172
|
} else if (primaryExpr.key === 'template' && primaryExpr.template) {
|
|
142
173
|
const re = LiteralUtil.templateLiteralToRegex(primaryExpr.template);
|
|
143
|
-
attrs.
|
|
174
|
+
attrs.match = {
|
|
144
175
|
re: new RegExp(re),
|
|
145
176
|
template: primaryExpr.template,
|
|
146
177
|
message: `{path} must match "${re}"`
|
|
147
|
-
}
|
|
178
|
+
};
|
|
148
179
|
}
|
|
149
180
|
|
|
150
181
|
if (ts.isParameter(node)) {
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
if (
|
|
154
|
-
attrs.
|
|
182
|
+
const parentComments = DocUtil.describeDocs(node.parent);
|
|
183
|
+
const paramComments: Partial<ParamDocumentation> = (parentComments.params ?? []).find(x => x.name === node.name.getText()) || {};
|
|
184
|
+
if (paramComments.description) {
|
|
185
|
+
attrs.description = paramComments.description;
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
const comments = DocUtil.describeDocs(node);
|
|
189
|
+
if (comments.description) {
|
|
190
|
+
attrs.description = comments.description;
|
|
155
191
|
}
|
|
156
192
|
}
|
|
157
193
|
|
|
158
194
|
const tags = ts.getJSDocTags(node);
|
|
159
195
|
const aliases = tags.filter(x => x.tagName.getText() === 'alias');
|
|
160
196
|
if (aliases.length) {
|
|
161
|
-
attrs.
|
|
197
|
+
attrs.aliases = aliases.map(x => x.comment).filter(x => !!x);
|
|
162
198
|
}
|
|
163
199
|
|
|
164
200
|
const params: ts.Expression[] = [];
|
|
165
201
|
|
|
166
|
-
const existing =
|
|
202
|
+
const existing =
|
|
203
|
+
state.findDecorator('@travetto/schema', node, 'Field', this.FIELD_IMPORT) ??
|
|
204
|
+
state.findDecorator('@travetto/schema', node, 'Input', this.INPUT_IMPORT);
|
|
205
|
+
|
|
206
|
+
if (config?.index !== undefined) {
|
|
207
|
+
attrs.index = config.index;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (Object.keys(attrs).length) {
|
|
211
|
+
params.push(state.fromLiteral(attrs));
|
|
212
|
+
}
|
|
213
|
+
|
|
167
214
|
if (!existing) {
|
|
168
|
-
const resolved = this.toConcreteType(state, typeExpr, node, config
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
215
|
+
const resolved = this.toConcreteType(state, typeExpr, node, config?.root ?? node);
|
|
216
|
+
const type = typeExpr.key === 'foreign' ? state.getConcreteType(node) :
|
|
217
|
+
ts.isArrayLiteralExpression(resolved) ? resolved.elements[0] : resolved;
|
|
218
|
+
|
|
219
|
+
params.unshift(LiteralUtil.fromLiteral(state.factory, {
|
|
220
|
+
array: ts.isArrayLiteralExpression(resolved),
|
|
221
|
+
type
|
|
222
|
+
}));
|
|
173
223
|
} else {
|
|
174
224
|
const args = DecoratorUtil.getArguments(existing) ?? [];
|
|
175
225
|
if (args.length > 0) {
|
|
176
|
-
params.
|
|
226
|
+
params.unshift(args[0]);
|
|
177
227
|
}
|
|
178
|
-
params.push(state.factory.createObjectLiteralExpression(attrs));
|
|
179
228
|
if (args.length > 1) {
|
|
180
229
|
params.push(...args.slice(1));
|
|
181
230
|
}
|
|
182
231
|
}
|
|
183
232
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
233
|
+
return params;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Compute property information from declaration
|
|
238
|
+
*/
|
|
239
|
+
static computeInput<T extends ts.PropertyDeclaration | ts.ParameterDeclaration | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration>(
|
|
240
|
+
state: TransformerState, node: T, config?: ComputeConfig
|
|
241
|
+
): T {
|
|
242
|
+
const existingField = state.findDecorator('@travetto/schema', node, 'Field', this.FIELD_IMPORT);
|
|
243
|
+
const existingInput = state.findDecorator('@travetto/schema', node, 'Input', this.INPUT_IMPORT);
|
|
244
|
+
const decParams = this.computeInputDecoratorParams(state, node, config);
|
|
245
|
+
|
|
246
|
+
let modifiers: ts.ModifierLike[];
|
|
247
|
+
if (existingField) {
|
|
248
|
+
const dec = state.createDecorator(this.FIELD_IMPORT, 'Field', ...decParams);
|
|
249
|
+
modifiers = DecoratorUtil.spliceDecorators(node, existingField, [dec]);
|
|
250
|
+
} else {
|
|
251
|
+
const dec = state.createDecorator(this.INPUT_IMPORT, 'Input', ...decParams);
|
|
252
|
+
modifiers = DecoratorUtil.spliceDecorators(node, existingInput, [dec]);
|
|
253
|
+
}
|
|
188
254
|
|
|
189
255
|
let result: unknown;
|
|
190
256
|
if (ts.isPropertyDeclaration(node)) {
|
|
191
|
-
const comments = DocUtil.describeDocs(node);
|
|
192
|
-
if (comments.description) {
|
|
193
|
-
newModifiers.push(state.createDecorator(this.COMMON_IMPORT, 'Describe', state.fromLiteral({
|
|
194
|
-
description: comments.description
|
|
195
|
-
})));
|
|
196
|
-
}
|
|
197
|
-
|
|
198
257
|
result = state.factory.updatePropertyDeclaration(node,
|
|
199
|
-
|
|
258
|
+
modifiers, node.name, node.questionToken, node.type, node.initializer);
|
|
200
259
|
} else if (ts.isParameter(node)) {
|
|
201
260
|
result = state.factory.updateParameterDeclaration(node,
|
|
202
|
-
|
|
261
|
+
modifiers, node.dotDotDotToken, node.name, node.questionToken, node.type, node.initializer);
|
|
203
262
|
} else if (ts.isGetAccessorDeclaration(node)) {
|
|
204
263
|
result = state.factory.updateGetAccessorDeclaration(node,
|
|
205
|
-
|
|
264
|
+
modifiers, node.name, node.parameters, node.type, node.body);
|
|
206
265
|
} else {
|
|
207
266
|
result = state.factory.updateSetAccessorDeclaration(node,
|
|
208
|
-
|
|
267
|
+
modifiers, node.name, node.parameters, node.body);
|
|
209
268
|
}
|
|
210
269
|
return transformCast(result);
|
|
211
270
|
}
|
|
@@ -233,7 +292,12 @@ export class SchemaTransformUtil {
|
|
|
233
292
|
static ensureType(state: TransformerState, anyType: AnyType, target: ts.Node): Record<string, unknown> {
|
|
234
293
|
const { out, type } = this.unwrapType(anyType);
|
|
235
294
|
switch (type?.key) {
|
|
295
|
+
case 'foreign': {
|
|
296
|
+
out.type = state.getForeignTarget(type);
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
236
299
|
case 'managed': out.type = state.typeToIdentifier(type); break;
|
|
300
|
+
case 'mapped': out.type = this.toConcreteType(state, type, target); break;
|
|
237
301
|
case 'shape': out.type = this.toConcreteType(state, type, target); break;
|
|
238
302
|
case 'template': out.type = state.factory.createIdentifier(type.ctor.name); break;
|
|
239
303
|
case 'literal': {
|
|
@@ -270,4 +334,26 @@ export class SchemaTransformUtil {
|
|
|
270
334
|
return state.findMethodByName(cls, methodName);
|
|
271
335
|
}
|
|
272
336
|
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Compute return type decorator params
|
|
340
|
+
*/
|
|
341
|
+
static computeReturnTypeDecoratorParams(state: TransformerState, node: ts.MethodDeclaration): ts.Expression[] {
|
|
342
|
+
// If we have a valid response type, declare it
|
|
343
|
+
const returnType = state.resolveReturnType(node);
|
|
344
|
+
let targetType = returnType;
|
|
345
|
+
|
|
346
|
+
if (returnType.key === 'literal' && returnType.typeArguments?.length && returnType.name === 'Promise') {
|
|
347
|
+
targetType = returnType.typeArguments[0];
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// TODO: Standardize this using jsdoc
|
|
351
|
+
let innerReturnType: AnyType | undefined;
|
|
352
|
+
if (targetType.key === 'managed' && targetType.importName.startsWith('@travetto/')) {
|
|
353
|
+
innerReturnType = state.getApparentTypeOfField(targetType.original!, 'body');
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const finalReturnType = SchemaTransformUtil.ensureType(state, innerReturnType ?? returnType, node);
|
|
357
|
+
return finalReturnType ? [state.fromLiteral({ returnType: finalReturnType })] : [];
|
|
358
|
+
}
|
|
273
359
|
}
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
TransformerState, OnProperty, OnClass, AfterClass, DocUtil, DeclarationUtil,
|
|
5
|
-
DecoratorUtil
|
|
4
|
+
TransformerState, OnProperty, OnClass, AfterClass, DocUtil, DeclarationUtil,
|
|
5
|
+
OnGetter, OnSetter, OnMethod, DecoratorUtil, OnStaticMethod
|
|
6
6
|
} from '@travetto/transformer';
|
|
7
7
|
|
|
8
8
|
import { SchemaTransformUtil } from './transformer/util.ts';
|
|
9
9
|
|
|
10
|
+
const CONSTRUCTOR_PROPERTY = 'CONSTRUCTOR';
|
|
10
11
|
const InSchemaSymbol = Symbol();
|
|
11
12
|
const AccessorsSymbol = Symbol();
|
|
13
|
+
const AutoEnrollMethods = Symbol();
|
|
12
14
|
|
|
13
15
|
interface AutoState {
|
|
14
16
|
[InSchemaSymbol]?: boolean;
|
|
17
|
+
[AutoEnrollMethods]?: Set<string>;
|
|
15
18
|
[AccessorsSymbol]?: Set<string>;
|
|
16
19
|
}
|
|
17
20
|
|
|
@@ -20,6 +23,30 @@ interface AutoState {
|
|
|
20
23
|
*/
|
|
21
24
|
export class SchemaTransformer {
|
|
22
25
|
|
|
26
|
+
static isInvisible(state: AutoState & TransformerState, node: ts.Declaration): boolean {
|
|
27
|
+
const ignore = state.findDecorator(this, node, 'Ignore');
|
|
28
|
+
if (ignore) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const manuallyOpted = !!(
|
|
33
|
+
state.findDecorator(this, node, 'Input') ??
|
|
34
|
+
state.findDecorator(this, node, 'Method')
|
|
35
|
+
);
|
|
36
|
+
if (manuallyOpted) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
if (ts.isMethodDeclaration(node)) {
|
|
40
|
+
if (!node.body || !state[AutoEnrollMethods]?.has(node.name.getText())) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (!state[InSchemaSymbol] || !DeclarationUtil.isPublic(node)) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
23
50
|
/**
|
|
24
51
|
* Track schema on start
|
|
25
52
|
*/
|
|
@@ -27,6 +54,17 @@ export class SchemaTransformer {
|
|
|
27
54
|
static startSchema(state: AutoState & TransformerState, node: ts.ClassDeclaration): ts.ClassDeclaration {
|
|
28
55
|
state[InSchemaSymbol] = true;
|
|
29
56
|
state[AccessorsSymbol] = new Set();
|
|
57
|
+
state[AutoEnrollMethods] = new Set();
|
|
58
|
+
|
|
59
|
+
// Determine auto enrol methods
|
|
60
|
+
for (const item of state.getDecoratorList(node)) {
|
|
61
|
+
if (item.targets?.includes('@travetto/schema:Schema')) {
|
|
62
|
+
for (const opt of item.options ?? []) {
|
|
63
|
+
state[AutoEnrollMethods].add(opt);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
30
68
|
return node;
|
|
31
69
|
}
|
|
32
70
|
|
|
@@ -35,56 +73,99 @@ export class SchemaTransformer {
|
|
|
35
73
|
*/
|
|
36
74
|
@AfterClass('Schema')
|
|
37
75
|
static finalizeSchema(state: AutoState & TransformerState, node: ts.ClassDeclaration): ts.ClassDeclaration {
|
|
38
|
-
const modifiers = (node.modifiers ?? []).slice(0);
|
|
39
|
-
|
|
40
76
|
const comments = DocUtil.describeDocs(node);
|
|
41
77
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
78
|
+
const existing = state.findDecorator(this, node, 'Schema', SchemaTransformUtil.SCHEMA_IMPORT);
|
|
79
|
+
const cons = node.members.find(x => ts.isConstructorDeclaration(x));
|
|
80
|
+
|
|
81
|
+
const attrs: Record<string, string | boolean | ts.Expression | number | object | unknown[]> = {};
|
|
45
82
|
|
|
46
83
|
if (comments.description) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
84
|
+
attrs.description = comments.description;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Extract all interfaces
|
|
88
|
+
const interfaces: ts.Node[] = [];
|
|
89
|
+
for (const clause of node.heritageClauses ?? []) {
|
|
90
|
+
if (clause.token === ts.SyntaxKind.ImplementsKeyword) {
|
|
91
|
+
for (const typeExpression of clause.types) {
|
|
92
|
+
const resolvedType = state.resolveType(typeExpression);
|
|
93
|
+
if (resolvedType.key === 'managed') {
|
|
94
|
+
const resolved = state.getOrImport(resolvedType);
|
|
95
|
+
interfaces.push(resolved);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (interfaces.length > 0) {
|
|
102
|
+
attrs.interfaces = interfaces;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (cons) {
|
|
106
|
+
attrs.methods = {
|
|
107
|
+
[CONSTRUCTOR_PROPERTY]: {
|
|
108
|
+
parameters: cons.parameters.map((p, i) => SchemaTransformUtil.computeInputDecoratorParams(state, p, { index: i })).map(x =>
|
|
109
|
+
state.extendObjectLiteral({}, ...x)
|
|
110
|
+
),
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let params = DecoratorUtil.getArguments(existing) ?? [];
|
|
116
|
+
if (Object.keys(attrs).length) {
|
|
117
|
+
params = [...params, state.fromLiteral(attrs)];
|
|
50
118
|
}
|
|
51
119
|
|
|
52
120
|
delete state[InSchemaSymbol];
|
|
53
121
|
delete state[AccessorsSymbol];
|
|
54
|
-
let members = node.members;
|
|
55
|
-
|
|
56
|
-
const schemaMethods = [
|
|
57
|
-
...node.modifiers?.filter(x => ts.isDecorator(x))
|
|
58
|
-
.flatMap(x => state.getDeclarations(DecoratorUtil.getDecoratorIdent(x))) ?? [],
|
|
59
|
-
node
|
|
60
|
-
]
|
|
61
|
-
.flatMap(v => state.readDocTagList(v, 'schemaMethods'));
|
|
62
|
-
|
|
63
|
-
if (schemaMethods.length) {
|
|
64
|
-
const methodSet = new Set(schemaMethods.flatMap(x => x.split(/\s*,\s*/g)));
|
|
65
|
-
members = state.factory.createNodeArray(
|
|
66
|
-
node.members.map(x => ts.isMethodDeclaration(x) && methodSet.has(x.name.getText()) ?
|
|
67
|
-
state.factory.updateMethodDeclaration(
|
|
68
|
-
x,
|
|
69
|
-
x.modifiers,
|
|
70
|
-
x.asteriskToken,
|
|
71
|
-
x.name,
|
|
72
|
-
x.questionToken,
|
|
73
|
-
x.typeParameters,
|
|
74
|
-
x.parameters.map(y => SchemaTransformUtil.computeField(state, y)),
|
|
75
|
-
x.type,
|
|
76
|
-
x.body
|
|
77
|
-
) : x)
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
122
|
|
|
81
123
|
return state.factory.updateClassDeclaration(
|
|
82
124
|
node,
|
|
83
|
-
|
|
125
|
+
DecoratorUtil.spliceDecorators(node, existing, [
|
|
126
|
+
state.createDecorator(SchemaTransformUtil.SCHEMA_IMPORT, 'Schema', ...params)
|
|
127
|
+
]),
|
|
84
128
|
node.name,
|
|
85
129
|
node.typeParameters,
|
|
86
130
|
node.heritageClauses,
|
|
87
|
-
members
|
|
131
|
+
node.members
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Handle explicitly registered methods
|
|
137
|
+
*/
|
|
138
|
+
@OnMethod()
|
|
139
|
+
@OnStaticMethod()
|
|
140
|
+
static processSchemaMethod(state: TransformerState & AutoState, node: ts.MethodDeclaration): ts.MethodDeclaration {
|
|
141
|
+
if (this.isInvisible(state, node) && !state[AutoEnrollMethods]?.has(node.name.getText())) {
|
|
142
|
+
return node;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const existing = state.findDecorator(this, node, 'Method', SchemaTransformUtil.METHOD_IMPORT);
|
|
146
|
+
const comments = DocUtil.describeDocs(node);
|
|
147
|
+
const params = DecoratorUtil.getArguments(existing) ?? [];
|
|
148
|
+
|
|
149
|
+
if (comments.description) {
|
|
150
|
+
params.unshift(state.fromLiteral({ description: comments.description }));
|
|
151
|
+
}
|
|
152
|
+
if (DeclarationUtil.isStatic(node)) {
|
|
153
|
+
params.push(state.fromLiteral({ isStatic: true }));
|
|
154
|
+
}
|
|
155
|
+
params.push(...SchemaTransformUtil.computeReturnTypeDecoratorParams(state, node));
|
|
156
|
+
|
|
157
|
+
return state.factory.updateMethodDeclaration(
|
|
158
|
+
node,
|
|
159
|
+
DecoratorUtil.spliceDecorators(node, existing, [
|
|
160
|
+
state.createDecorator(SchemaTransformUtil.METHOD_IMPORT, 'Method', ...params)
|
|
161
|
+
]),
|
|
162
|
+
node.asteriskToken,
|
|
163
|
+
node.name,
|
|
164
|
+
node.questionToken,
|
|
165
|
+
node.typeParameters,
|
|
166
|
+
node.parameters.map((y, i) => SchemaTransformUtil.computeInput(state, y, { index: i })),
|
|
167
|
+
node.type,
|
|
168
|
+
node.body
|
|
88
169
|
);
|
|
89
170
|
}
|
|
90
171
|
|
|
@@ -93,9 +174,10 @@ export class SchemaTransformer {
|
|
|
93
174
|
*/
|
|
94
175
|
@OnProperty()
|
|
95
176
|
static processSchemaField(state: TransformerState & AutoState, node: ts.PropertyDeclaration): ts.PropertyDeclaration {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
177
|
+
if (this.isInvisible(state, node)) {
|
|
178
|
+
return node;
|
|
179
|
+
}
|
|
180
|
+
return SchemaTransformUtil.computeInput(state, node);
|
|
99
181
|
}
|
|
100
182
|
|
|
101
183
|
/**
|
|
@@ -103,12 +185,15 @@ export class SchemaTransformer {
|
|
|
103
185
|
*/
|
|
104
186
|
@OnGetter()
|
|
105
187
|
static processSchemaGetter(state: TransformerState & AutoState, node: ts.GetAccessorDeclaration): ts.GetAccessorDeclaration {
|
|
106
|
-
|
|
107
|
-
|
|
188
|
+
if (this.isInvisible(state, node) || DeclarationUtil.isStatic(node)) {
|
|
189
|
+
return node;
|
|
190
|
+
}
|
|
191
|
+
if (state[AccessorsSymbol]?.has(node.name.getText())) {
|
|
192
|
+
return node;
|
|
193
|
+
} else {
|
|
108
194
|
state[AccessorsSymbol]?.add(node.name.getText());
|
|
109
|
-
return SchemaTransformUtil.
|
|
195
|
+
return SchemaTransformUtil.computeInput(state, node);
|
|
110
196
|
}
|
|
111
|
-
return node;
|
|
112
197
|
}
|
|
113
198
|
|
|
114
199
|
/**
|
|
@@ -116,11 +201,14 @@ export class SchemaTransformer {
|
|
|
116
201
|
*/
|
|
117
202
|
@OnSetter()
|
|
118
203
|
static processSchemaSetter(state: TransformerState & AutoState, node: ts.SetAccessorDeclaration): ts.SetAccessorDeclaration {
|
|
119
|
-
|
|
120
|
-
|
|
204
|
+
if (this.isInvisible(state, node) || DeclarationUtil.isStatic(node)) {
|
|
205
|
+
return node;
|
|
206
|
+
}
|
|
207
|
+
if (state[AccessorsSymbol]?.has(node.name.getText())) {
|
|
208
|
+
return node;
|
|
209
|
+
} else {
|
|
121
210
|
state[AccessorsSymbol]?.add(node.name.getText());
|
|
122
|
-
return SchemaTransformUtil.
|
|
211
|
+
return SchemaTransformUtil.computeInput(state, node);
|
|
123
212
|
}
|
|
124
|
-
return node;
|
|
125
213
|
}
|
|
126
214
|
}
|