rads-db 3.0.18 → 3.0.20
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/dist/index.d.ts +2 -0
- package/integrations/lib.cjs +229 -171
- package/integrations/lib.d.ts +1 -1
- package/integrations/lib.mjs +198 -130
- package/integrations/restEndpointsDev.cjs +5 -0
- package/integrations/restEndpointsDev.d.ts +1 -0
- package/integrations/restEndpointsDev.mjs +5 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -206,6 +206,7 @@ interface TypeDefinition {
|
|
|
206
206
|
enumValues?: Record<string, EnumDefinition>;
|
|
207
207
|
handle?: string;
|
|
208
208
|
handlePlural?: string;
|
|
209
|
+
isExtending?: string;
|
|
209
210
|
}
|
|
210
211
|
interface FileUploadResult {
|
|
211
212
|
url: string;
|
|
@@ -256,6 +257,7 @@ interface FieldDefinition {
|
|
|
256
257
|
name: string;
|
|
257
258
|
type: string;
|
|
258
259
|
defaultValue?: any;
|
|
260
|
+
defaultValueClass?: string;
|
|
259
261
|
defaultValueCopyFrom?: string;
|
|
260
262
|
isRequired?: boolean;
|
|
261
263
|
isArray?: boolean;
|
package/integrations/lib.cjs
CHANGED
|
@@ -10,8 +10,77 @@ var _pluralize = _interopRequireDefault(require("pluralize"));
|
|
|
10
10
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
11
|
const supportedPrimitiveTypes = ["string", "number", "boolean", "Record<string, string>", "Record<string, any>"];
|
|
12
12
|
function parseSchema(typescriptFiles) {
|
|
13
|
+
const typeNodesMap = getTypeNodesMap(typescriptFiles);
|
|
14
|
+
const schema = getSchema(typeNodesMap);
|
|
15
|
+
resolveIsExtending(schema);
|
|
16
|
+
fillComputedDefinitionsForType(schema);
|
|
17
|
+
verifyDefaultValueTypes(schema, typeNodesMap);
|
|
18
|
+
verifyRelationFields(schema);
|
|
19
|
+
return schema;
|
|
20
|
+
}
|
|
21
|
+
function resolveIsExtending(schema) {
|
|
22
|
+
for (const key in schema) {
|
|
23
|
+
const isExtendingType = schema[key].isExtending;
|
|
24
|
+
if (!isExtendingType) continue;
|
|
25
|
+
if (!schema[isExtendingType]) throw new Error(`Unknown type: "${isExtendingType}"`);
|
|
26
|
+
schema[key].fields = {
|
|
27
|
+
...schema[isExtendingType].fields,
|
|
28
|
+
...schema[key].fields
|
|
29
|
+
};
|
|
30
|
+
schema[key].decorators = {
|
|
31
|
+
...schema[isExtendingType].decorators,
|
|
32
|
+
...schema[key].decorators
|
|
33
|
+
};
|
|
34
|
+
if (schema[key].decorators.entity && !schema[key].fields?.id) {
|
|
35
|
+
throw new Error(`Entity "${key}" must have an id`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function getSchema(typeNodesMap) {
|
|
13
40
|
const result = {};
|
|
14
|
-
const
|
|
41
|
+
for (const key in typeNodesMap) {
|
|
42
|
+
const {
|
|
43
|
+
node,
|
|
44
|
+
name
|
|
45
|
+
} = typeNodesMap[key];
|
|
46
|
+
if (![_typescript.SyntaxKind.ClassDeclaration, _typescript.SyntaxKind.TypeAliasDeclaration].includes(node.kind)) {
|
|
47
|
+
throw new Error(`Unexpected type kind - "${name}"`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const key in typeNodesMap) {
|
|
51
|
+
if (result[key]) continue;
|
|
52
|
+
const {
|
|
53
|
+
node,
|
|
54
|
+
sourceFile,
|
|
55
|
+
name
|
|
56
|
+
} = typeNodesMap[key];
|
|
57
|
+
if (node.kind !== _typescript.SyntaxKind.ClassDeclaration) continue;
|
|
58
|
+
const parsedClass = parseClassDeclaration(node, name, {
|
|
59
|
+
typeNodesMap,
|
|
60
|
+
sourceFile,
|
|
61
|
+
result
|
|
62
|
+
});
|
|
63
|
+
result[key] = parsedClass;
|
|
64
|
+
}
|
|
65
|
+
for (const key in typeNodesMap) {
|
|
66
|
+
if (result[key]) continue;
|
|
67
|
+
const {
|
|
68
|
+
node,
|
|
69
|
+
sourceFile,
|
|
70
|
+
name
|
|
71
|
+
} = typeNodesMap[key];
|
|
72
|
+
if (node.kind !== _typescript.SyntaxKind.TypeAliasDeclaration) continue;
|
|
73
|
+
const parsedClass = parseTypeAliasDeclaration(node, name, {
|
|
74
|
+
typeNodesMap,
|
|
75
|
+
sourceFile,
|
|
76
|
+
result
|
|
77
|
+
});
|
|
78
|
+
result[key] = parsedClass;
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
function getTypeNodesMap(typescriptFiles) {
|
|
83
|
+
const typeNodesMap = {};
|
|
15
84
|
for (const key in typescriptFiles) {
|
|
16
85
|
const text = typescriptFiles[key];
|
|
17
86
|
const sourceFile = (0, _typescript.createSourceFile)(`${key}.ts`, text, _typescript.ScriptTarget.Latest);
|
|
@@ -20,75 +89,44 @@ function parseSchema(typescriptFiles) {
|
|
|
20
89
|
const nameNode = cd.name;
|
|
21
90
|
if (!nameNode || nameNode.kind !== _typescript.SyntaxKind.Identifier) throw new Error("Cannot detect class name");
|
|
22
91
|
const name = nameNode.text;
|
|
23
|
-
|
|
92
|
+
typeNodesMap[name] = {
|
|
24
93
|
name,
|
|
25
94
|
node: cd,
|
|
26
95
|
sourceFile
|
|
27
96
|
};
|
|
28
97
|
}
|
|
29
|
-
if (!
|
|
98
|
+
if (!typeNodesMap[key]) {
|
|
30
99
|
throw new Error(`File ${key}.ts must contain class declaration with name "${key}"`);
|
|
31
100
|
}
|
|
32
101
|
}
|
|
33
|
-
|
|
34
|
-
if (result[key]) continue;
|
|
35
|
-
const {
|
|
36
|
-
node,
|
|
37
|
-
sourceFile,
|
|
38
|
-
name
|
|
39
|
-
} = typesMap[key];
|
|
40
|
-
const parsedClass = parseType(node, name, {
|
|
41
|
-
typesMap,
|
|
42
|
-
sourceFile,
|
|
43
|
-
result
|
|
44
|
-
});
|
|
45
|
-
if (parsedClass.name !== key) throw new Error(`File name must correspond to exported class name ("${key}", "${parsedClass.name}")`);
|
|
46
|
-
result[key] = parsedClass;
|
|
47
|
-
}
|
|
48
|
-
for (const key in result) {
|
|
49
|
-
const isExtendingType = result[key].isExtending;
|
|
50
|
-
if (!isExtendingType) continue;
|
|
51
|
-
if (!result[isExtendingType]) throw new Error(`Unknown type: "${isExtendingType}"`);
|
|
52
|
-
result[key].fields = {
|
|
53
|
-
...result[isExtendingType].fields,
|
|
54
|
-
...result[key].fields
|
|
55
|
-
};
|
|
56
|
-
result[key].decorators = {
|
|
57
|
-
...result[isExtendingType].decorators,
|
|
58
|
-
...result[key].decorators
|
|
59
|
-
};
|
|
60
|
-
if (result[key].decorators.entity && !result[key].fields?.id) {
|
|
61
|
-
throw new Error(`Entity "${key}" must have an id`);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
fillComputedDefinitionsForType(result);
|
|
65
|
-
processThisReferenceDefaultValues(result, typesMap);
|
|
66
|
-
verifyRelationFields(result);
|
|
67
|
-
return result;
|
|
102
|
+
return typeNodesMap;
|
|
68
103
|
}
|
|
69
|
-
function
|
|
70
|
-
for (const key in
|
|
71
|
-
const type =
|
|
104
|
+
function verifyDefaultValueTypes(schema, typeNodesMap) {
|
|
105
|
+
for (const key in schema) {
|
|
106
|
+
const type = schema[key];
|
|
72
107
|
const fields = type.fields;
|
|
73
108
|
if (!fields) continue;
|
|
74
109
|
for (const fName in fields) {
|
|
75
110
|
const field = fields[fName];
|
|
76
|
-
if (field.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
field.isArray = sourceField.isArray;
|
|
82
|
-
}
|
|
83
|
-
verifyDefaultValueType(field.isArray || false, {
|
|
84
|
-
type: sourceField.type,
|
|
85
|
-
value: sourceField.defaultValue
|
|
86
|
-
}, field.type, supportedPrimitiveTypes, {
|
|
87
|
-
typesMap,
|
|
88
|
-
result,
|
|
89
|
-
sourceFile: typesMap[key]?.sourceFile
|
|
111
|
+
if (field.defaultValue) {
|
|
112
|
+
verifyDefaultValueType(field, {
|
|
113
|
+
typeNodesMap,
|
|
114
|
+
result: schema,
|
|
115
|
+
sourceFile: typeNodesMap[key]?.sourceFile
|
|
90
116
|
});
|
|
91
117
|
}
|
|
118
|
+
for (const fName2 in fields) {
|
|
119
|
+
const field2 = fields[fName2];
|
|
120
|
+
if (field2.defaultValueCopyFrom) {
|
|
121
|
+
const sourceField = fields[field2.defaultValueCopyFrom];
|
|
122
|
+
if (!sourceField) throw new Error(`Cannot find field ${key}.${field2.defaultValueCopyFrom}"`);
|
|
123
|
+
verifyDefaultValueTypeCopyFrom(field2, sourceField, {
|
|
124
|
+
typeNodesMap,
|
|
125
|
+
result: schema,
|
|
126
|
+
sourceFile: typeNodesMap[key]?.sourceFile
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
92
130
|
}
|
|
93
131
|
}
|
|
94
132
|
}
|
|
@@ -113,7 +151,12 @@ function getOrder(f) {
|
|
|
113
151
|
};
|
|
114
152
|
return decorator.order ?? defaultOrders[decorator.preset] ?? 0;
|
|
115
153
|
}
|
|
116
|
-
function
|
|
154
|
+
function parseClassOrTypeDeclaration(typeDeclaration, typeName, ctx) {
|
|
155
|
+
if (typeDeclaration.kind === _typescript.SyntaxKind.ClassDeclaration) return parseClassDeclaration(typeDeclaration, typeName, ctx);
|
|
156
|
+
if (typeDeclaration.kind === _typescript.SyntaxKind.TypeAliasDeclaration) return parseTypeAliasDeclaration(typeDeclaration, typeName, ctx);
|
|
157
|
+
throw new Error(`Unexpected type definition - ${typeName}`);
|
|
158
|
+
}
|
|
159
|
+
function parseClassDeclaration(typeDeclaration, typeName, ctx) {
|
|
117
160
|
if (ctx.result[typeName]) return ctx.result[typeName];
|
|
118
161
|
const {
|
|
119
162
|
modifiers
|
|
@@ -123,62 +166,64 @@ function parseType(typeDeclaration, typeName, ctx) {
|
|
|
123
166
|
const name = nameNode.text;
|
|
124
167
|
const comment = typeDeclaration.jsDoc?.[0]?.comment;
|
|
125
168
|
const decorators = parseDecorators(modifiers, ctx);
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
throw new Error(`Unexpected class member - only properties are allowed("${m.getText(ctx.sourceFile)}")`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
const fieldsArray = members.map(m => parseClassMember(m, name, ctx));
|
|
140
|
-
const fields = {};
|
|
141
|
-
for (const f of fieldsArray) {
|
|
142
|
-
fields[f.name] = f;
|
|
169
|
+
const classDeclaration = typeDeclaration;
|
|
170
|
+
const {
|
|
171
|
+
members,
|
|
172
|
+
heritageClauses
|
|
173
|
+
} = classDeclaration;
|
|
174
|
+
const isExtendingExpr = heritageClauses?.[0]?.types?.[0]?.expression;
|
|
175
|
+
const isExtending = isExtendingExpr?.text;
|
|
176
|
+
for (const m of members) {
|
|
177
|
+
if (m.kind !== _typescript.SyntaxKind.PropertyDeclaration) {
|
|
178
|
+
throw new Error(`Unexpected class member - only properties are allowed("${m.getText(ctx.sourceFile)}")`);
|
|
143
179
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
180
|
+
}
|
|
181
|
+
const fields = {};
|
|
182
|
+
for (const m of members) {
|
|
183
|
+
const field = parseClassMember(m, fields, name, ctx);
|
|
184
|
+
fields[field.name] = field;
|
|
185
|
+
}
|
|
186
|
+
const handle = _lodash.default.lowerFirst(name);
|
|
187
|
+
const handlePlural = (0, _pluralize.default)(handle);
|
|
188
|
+
const result = {
|
|
189
|
+
name,
|
|
190
|
+
handle,
|
|
191
|
+
handlePlural,
|
|
192
|
+
decorators,
|
|
193
|
+
fields,
|
|
194
|
+
isExtending,
|
|
195
|
+
comment
|
|
196
|
+
};
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
function parseTypeAliasDeclaration(typeDeclaration, typeName, ctx) {
|
|
200
|
+
if (ctx.result[typeName]) return ctx.result[typeName];
|
|
201
|
+
const nameNode = typeDeclaration.name;
|
|
202
|
+
if (!nameNode || nameNode.kind !== _typescript.SyntaxKind.Identifier) throw new Error("Cannot detect class name");
|
|
203
|
+
const name = nameNode.text;
|
|
204
|
+
const comment = typeDeclaration.jsDoc?.[0]?.comment;
|
|
205
|
+
const typeAliasDeclaration = typeDeclaration;
|
|
206
|
+
const typeAliasType = typeAliasDeclaration.type;
|
|
207
|
+
if (typeAliasType.kind === _typescript.SyntaxKind.UnionType) {
|
|
208
|
+
const typeAliasValue = typeAliasDeclaration.type;
|
|
209
|
+
const enumValues = getEnumValues(typeAliasValue, typeDeclaration, ctx);
|
|
210
|
+
return {
|
|
147
211
|
name,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
decorators
|
|
151
|
-
fields,
|
|
152
|
-
isExtending,
|
|
153
|
-
comment
|
|
212
|
+
enumValues,
|
|
213
|
+
comment,
|
|
214
|
+
decorators: {}
|
|
154
215
|
};
|
|
155
|
-
return result;
|
|
156
216
|
}
|
|
157
|
-
if (
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
decorators,
|
|
166
|
-
enumValues,
|
|
167
|
-
comment
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
if (typeAliasType.kind === _typescript.SyntaxKind.TypeOperator && typeAliasType.operator === _typescript.SyntaxKind.KeyOfKeyword) {
|
|
171
|
-
const enumValues = getEnumValuesFromKeyOf(typeAliasType, ctx);
|
|
172
|
-
return {
|
|
173
|
-
name,
|
|
174
|
-
decorators,
|
|
175
|
-
enumValues,
|
|
176
|
-
comment
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
throw new Error(`Unexpected type definition - ${typeDeclaration.getText(ctx.sourceFile)}. Did you mean 'class'?`);
|
|
217
|
+
if (typeAliasType.kind === _typescript.SyntaxKind.TypeOperator && typeAliasType.operator === _typescript.SyntaxKind.KeyOfKeyword) {
|
|
218
|
+
const enumValues = getEnumValuesFromKeyOf(typeAliasType, ctx);
|
|
219
|
+
return {
|
|
220
|
+
name,
|
|
221
|
+
enumValues,
|
|
222
|
+
comment,
|
|
223
|
+
decorators: {}
|
|
224
|
+
};
|
|
180
225
|
}
|
|
181
|
-
throw new Error(`Unexpected type
|
|
226
|
+
throw new Error(`Unexpected type definition - ${typeDeclaration.getText(ctx.sourceFile)}. Did you mean 'class'?`);
|
|
182
227
|
}
|
|
183
228
|
function parseDecorators(modifiers, ctx) {
|
|
184
229
|
if (!modifiers) return {};
|
|
@@ -205,28 +250,29 @@ function getEnumValues(node, parentNode, ctx) {
|
|
|
205
250
|
});
|
|
206
251
|
return _lodash.default.keyBy(enumValuesArray, "name");
|
|
207
252
|
}
|
|
208
|
-
function parseClassMember(node, parentName, ctx) {
|
|
253
|
+
function parseClassMember(node, parentFields, parentName, ctx) {
|
|
209
254
|
const name = node.name.getText(ctx.sourceFile);
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
defaultValueCopyFrom = defaultValueDescription.value;
|
|
216
|
-
} else {
|
|
217
|
-
defaultValue = defaultValueDescription.value;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
255
|
+
const {
|
|
256
|
+
defaultValue,
|
|
257
|
+
defaultValueCopyFrom,
|
|
258
|
+
defaultValueClass
|
|
259
|
+
} = parseDefaultValueExpression(node.initializer, ctx);
|
|
220
260
|
const isRequired = !node.questionToken;
|
|
221
261
|
const comment = node.jsDoc?.[0]?.comment;
|
|
222
262
|
const decorators = parseDecorators(node.modifiers, ctx);
|
|
263
|
+
let defaultValueType = defaultValueClass || getPrimitiveTypeFromDefaultValue(defaultValue);
|
|
264
|
+
if (defaultValueCopyFrom) {
|
|
265
|
+
const parentField = parentFields[defaultValueCopyFrom];
|
|
266
|
+
if (!parentField) throw new Error(`Cannot find field ${parentName}.${defaultValueCopyFrom}"`);
|
|
267
|
+
defaultValueType = parentField.type;
|
|
268
|
+
}
|
|
223
269
|
const {
|
|
224
270
|
isArray,
|
|
225
271
|
isRelation,
|
|
226
272
|
isChange,
|
|
227
273
|
relationDenormFields,
|
|
228
274
|
type
|
|
229
|
-
} = parseFieldType(ctx, parentName, name, node,
|
|
275
|
+
} = parseFieldType(ctx, parentName, name, node, defaultValueType);
|
|
230
276
|
const result = {
|
|
231
277
|
type,
|
|
232
278
|
defaultValue,
|
|
@@ -240,9 +286,18 @@ function parseClassMember(node, parentName, ctx) {
|
|
|
240
286
|
comment
|
|
241
287
|
};
|
|
242
288
|
if (!_lodash.default.isEmpty(decorators)) result.decorators = decorators;
|
|
289
|
+
if (defaultValueClass) result.defaultValueClass = defaultValueClass;
|
|
243
290
|
return result;
|
|
244
291
|
}
|
|
245
|
-
function
|
|
292
|
+
function getPrimitiveTypeFromDefaultValue(value) {
|
|
293
|
+
if (_lodash.default.isString(value)) return "string";
|
|
294
|
+
if (_lodash.default.isNumber(value)) return "number";
|
|
295
|
+
if (_lodash.default.isBoolean(value)) return "boolean";
|
|
296
|
+
if (_lodash.default.isArray(value)) return "array";
|
|
297
|
+
if (_lodash.default.isObject(value)) return "object";
|
|
298
|
+
return void 0;
|
|
299
|
+
}
|
|
300
|
+
function parseFieldType(ctx, parentName, fieldName, node, defaultValueType) {
|
|
246
301
|
const parsedType = {
|
|
247
302
|
isArray: false,
|
|
248
303
|
isRelation: false,
|
|
@@ -257,20 +312,17 @@ function parseFieldType(ctx, parentName, fieldName, node, defaultValueDescriptio
|
|
|
257
312
|
parseFieldTypeInlineEnum(parsedType, parentName, fieldName, ctx);
|
|
258
313
|
parseFieldTypeKeyofEnum(parsedType, parentName, fieldName, ctx);
|
|
259
314
|
parseFieldTypeRecordEnum(parsedType, parentName, fieldName, ctx);
|
|
260
|
-
parsedType.type = parsedType.type ?? parsedType.nodeType?.getText(ctx.sourceFile) ??
|
|
315
|
+
parsedType.type = parsedType.type ?? parsedType.nodeType?.getText(ctx.sourceFile) ?? defaultValueType;
|
|
261
316
|
if (!parsedType.type) throw new Error(`Cannot detect property type: '${node.getText(ctx.sourceFile)}'`);
|
|
262
317
|
if (parsedType.type.startsWith("Change<")) {
|
|
263
318
|
parsedType.type = parsedType.type.slice(7, -1);
|
|
264
|
-
if (!ctx.
|
|
319
|
+
if (!ctx.typeNodesMap[parsedType.type]) throw new Error(`Unexpected property type: '${parsedType.type}'`);
|
|
265
320
|
parsedType.isChange = true;
|
|
266
321
|
} else {
|
|
267
|
-
if (!supportedPrimitiveTypes.includes(parsedType.type) && !ctx.
|
|
322
|
+
if (!supportedPrimitiveTypes.includes(parsedType.type) && !ctx.typeNodesMap[parsedType.type] && !ctx.result[parsedType.type]) {
|
|
268
323
|
throw new Error(`Unexpected property type: '${parsedType.type}'`);
|
|
269
324
|
}
|
|
270
325
|
}
|
|
271
|
-
if (defaultValueDescription && defaultValueDescription.type !== "thisReference") {
|
|
272
|
-
verifyDefaultValueType(parsedType.isArray, defaultValueDescription, parsedType.type, supportedPrimitiveTypes, ctx);
|
|
273
|
-
}
|
|
274
326
|
return {
|
|
275
327
|
isArray: parsedType.isArray || void 0,
|
|
276
328
|
isRelation: parsedType.isRelation || void 0,
|
|
@@ -286,9 +338,9 @@ function parseFieldTypeRecordEnum(parsedType, parentName, fieldName, ctx) {
|
|
|
286
338
|
if (nt.typeArguments?.length !== 2) return;
|
|
287
339
|
const keyTypeName = nt.typeArguments[0].getText(ctx.sourceFile);
|
|
288
340
|
const valueTypeName = nt.typeArguments[1].getText(ctx.sourceFile);
|
|
289
|
-
const keyType = ctx.
|
|
341
|
+
const keyType = ctx.typeNodesMap[keyTypeName];
|
|
290
342
|
if (!keyType) return;
|
|
291
|
-
if (!ctx.result[keyTypeName]) ctx.result[keyTypeName] =
|
|
343
|
+
if (!ctx.result[keyTypeName]) ctx.result[keyTypeName] = parseClassOrTypeDeclaration(keyType.node, keyTypeName, ctx);
|
|
292
344
|
const enumValues = ctx.result[keyTypeName].enumValues;
|
|
293
345
|
if (!enumValues) throw new Error(`Unexpected type - ${keyTypeName}`);
|
|
294
346
|
const newTypeName = `${parentName}_${_lodash.default.upperFirst(fieldName)}`;
|
|
@@ -362,11 +414,11 @@ function getEnumValuesFromKeyOf(nodeType, ctx) {
|
|
|
362
414
|
throw new Error(`Unexpected type - ${typeReferenceNode.getText(ctx.sourceFile)}`);
|
|
363
415
|
}
|
|
364
416
|
const typeName = typeReferenceNode.typeName.text;
|
|
365
|
-
const type = ctx.
|
|
417
|
+
const type = ctx.typeNodesMap[typeName];
|
|
366
418
|
if (!type) {
|
|
367
419
|
throw new Error(`Unexpected type - ${typeName}`);
|
|
368
420
|
}
|
|
369
|
-
if (!ctx.result[typeName]) ctx.result[typeName] =
|
|
421
|
+
if (!ctx.result[typeName]) ctx.result[typeName] = parseClassOrTypeDeclaration(type.node, typeName, ctx);
|
|
370
422
|
if (!ctx.result[typeName].fields) throw new Error(`Unexpected type - ${typeName}`);
|
|
371
423
|
return _lodash.default.mapValues(ctx.result[typeName].fields || {}, v => ({
|
|
372
424
|
name: v.name,
|
|
@@ -388,23 +440,43 @@ function getRelationDenormFields(ctx, node) {
|
|
|
388
440
|
}
|
|
389
441
|
throw new Error(`Unexpected type - ${node.getText(ctx.sourceFile)}`);
|
|
390
442
|
}
|
|
391
|
-
function verifyDefaultValueType(
|
|
443
|
+
function verifyDefaultValueType(field, ctx) {
|
|
444
|
+
const {
|
|
445
|
+
isArray,
|
|
446
|
+
defaultValue,
|
|
447
|
+
type
|
|
448
|
+
} = field;
|
|
392
449
|
if (isArray) {
|
|
393
|
-
if (
|
|
394
|
-
throw new
|
|
450
|
+
if (!_lodash.default.isArray(defaultValue)) {
|
|
451
|
+
throw new TypeError(`Default value type is different from field type: '${type}'`);
|
|
395
452
|
}
|
|
396
453
|
} else {
|
|
397
|
-
if (
|
|
454
|
+
if (supportedPrimitiveTypes.includes(type) && getPrimitiveTypeFromDefaultValue(defaultValue) !== type) {
|
|
398
455
|
throw new Error(`Default value type is different from field type: '${type}'`);
|
|
399
456
|
}
|
|
400
|
-
if (!ctx.result[type] && ctx.
|
|
457
|
+
if (!ctx.result[type] && ctx.typeNodesMap[type]) ctx.result[type] = parseClassOrTypeDeclaration(ctx.typeNodesMap[type].node, type, ctx);
|
|
401
458
|
const enumValues = ctx.result[type]?.enumValues;
|
|
402
|
-
if (enumValues && !enumValues[
|
|
459
|
+
if (enumValues && !enumValues[defaultValue]) {
|
|
403
460
|
const enumValuesStr = _lodash.default.keys(enumValues).map(x => `'x'`).join(", ");
|
|
404
461
|
throw new Error(`Default value must be one of: ${enumValuesStr}`);
|
|
405
462
|
}
|
|
406
463
|
}
|
|
407
464
|
}
|
|
465
|
+
function verifyDefaultValueTypeCopyFrom(field, sourceField, ctx) {
|
|
466
|
+
const {
|
|
467
|
+
isArray,
|
|
468
|
+
type
|
|
469
|
+
} = field;
|
|
470
|
+
if (isArray) {
|
|
471
|
+
if (!sourceField.isArray) {
|
|
472
|
+
throw new TypeError(`Default value type is not an array: '${field.name}'`);
|
|
473
|
+
}
|
|
474
|
+
} else {
|
|
475
|
+
if (sourceField.type !== type) {
|
|
476
|
+
throw new Error(`Default value type is different from field type: '${field.name}'`);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
408
480
|
function parseDecorator(decoratorNode, ctx) {
|
|
409
481
|
const expr = decoratorNode.expression;
|
|
410
482
|
if (expr.kind !== _typescript.SyntaxKind.CallExpression) throw new Error(`Unexpected decorator format: "${expr.getText(ctx.sourceFile)}"`);
|
|
@@ -420,40 +492,35 @@ function parseDecoratorArguments(expr, ctx) {
|
|
|
420
492
|
if (args.length === 0) return {};
|
|
421
493
|
if (args.length > 1) throw new Error(`Too many arguments - one expected: "${expr.getText(ctx.sourceFile)}"`);
|
|
422
494
|
const arg = args[0];
|
|
423
|
-
return parseLiteralNode(arg, ctx)
|
|
495
|
+
return parseLiteralNode(arg, ctx) ?? {};
|
|
424
496
|
}
|
|
425
|
-
function
|
|
426
|
-
if (!expr) return
|
|
497
|
+
function parseDefaultValueExpression(expr, ctx) {
|
|
498
|
+
if (!expr) return {};
|
|
427
499
|
if (expr.kind === _typescript.SyntaxKind.NewExpression) {
|
|
428
500
|
const identifier = expr.expression;
|
|
429
501
|
const type = identifier?.text;
|
|
430
502
|
if (type) {
|
|
431
503
|
return {
|
|
432
|
-
type,
|
|
433
|
-
|
|
504
|
+
defaultValueClass: type,
|
|
505
|
+
defaultValue: {}
|
|
434
506
|
};
|
|
435
507
|
}
|
|
508
|
+
} else if (expr.kind === _typescript.SyntaxKind.PropertyAccessExpression && expr.expression.kind === _typescript.SyntaxKind.ThisKeyword) {
|
|
509
|
+
return {
|
|
510
|
+
defaultValueCopyFrom: expr.name?.text
|
|
511
|
+
};
|
|
436
512
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
value: expr.text
|
|
440
|
-
};
|
|
441
|
-
if (expr.kind === _typescript.SyntaxKind.FalseKeyword) return {
|
|
442
|
-
type: "boolean",
|
|
443
|
-
value: false
|
|
444
|
-
};
|
|
445
|
-
if (expr.kind === _typescript.SyntaxKind.TrueKeyword) return {
|
|
446
|
-
type: "boolean",
|
|
447
|
-
value: true
|
|
448
|
-
};
|
|
449
|
-
if (expr.kind === _typescript.SyntaxKind.NumericLiteral) return {
|
|
450
|
-
type: "number",
|
|
451
|
-
value: Number.parseFloat(expr.text)
|
|
452
|
-
};
|
|
453
|
-
if (expr.kind === _typescript.SyntaxKind.ObjectLiteralExpression) return {
|
|
454
|
-
type: "object",
|
|
455
|
-
value: parseObjectLiteral(expr, ctx)
|
|
513
|
+
return {
|
|
514
|
+
defaultValue: parseLiteralNode(expr, ctx)
|
|
456
515
|
};
|
|
516
|
+
}
|
|
517
|
+
function parseLiteralNode(expr, ctx) {
|
|
518
|
+
if (!expr) return void 0;
|
|
519
|
+
if (expr.kind === _typescript.SyntaxKind.StringLiteral) return expr.text;
|
|
520
|
+
if (expr.kind === _typescript.SyntaxKind.FalseKeyword) return false;
|
|
521
|
+
if (expr.kind === _typescript.SyntaxKind.TrueKeyword) return true;
|
|
522
|
+
if (expr.kind === _typescript.SyntaxKind.NumericLiteral) return Number.parseFloat(expr.text);
|
|
523
|
+
if (expr.kind === _typescript.SyntaxKind.ObjectLiteralExpression) return parseObjectLiteral(expr, ctx);
|
|
457
524
|
if (expr.kind === _typescript.SyntaxKind.ArrayLiteralExpression) {
|
|
458
525
|
const defaultValueStr = expr.getText(ctx.sourceFile);
|
|
459
526
|
let defaultValue;
|
|
@@ -463,16 +530,7 @@ function parseLiteralNode(expr, ctx) {
|
|
|
463
530
|
throw new Error("Value must be valid array");
|
|
464
531
|
}
|
|
465
532
|
if (!_lodash.default.isArray(defaultValue)) throw new Error("Value must be valid array");
|
|
466
|
-
return
|
|
467
|
-
type: "array",
|
|
468
|
-
value: defaultValue
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
if (expr.kind === _typescript.SyntaxKind.PropertyAccessExpression && expr.expression.kind === _typescript.SyntaxKind.ThisKeyword) {
|
|
472
|
-
return {
|
|
473
|
-
type: "thisReference",
|
|
474
|
-
value: expr.name?.text
|
|
475
|
-
};
|
|
533
|
+
return defaultValue;
|
|
476
534
|
}
|
|
477
535
|
throw new Error(`Unexpected property expression: "${expr.getText(ctx.sourceFile)}"`);
|
|
478
536
|
}
|
|
@@ -485,7 +543,7 @@ function parseObjectLiteral(arg, ctx) {
|
|
|
485
543
|
if (p.kind !== _typescript.SyntaxKind.PropertyAssignment) throw new Error(`Unexpected property value: "${p.getText(ctx.sourceFile)}"`);
|
|
486
544
|
const p2 = p;
|
|
487
545
|
const valueExpression = p2.initializer;
|
|
488
|
-
const value = parseLiteralNode(valueExpression, ctx)
|
|
546
|
+
const value = parseLiteralNode(valueExpression, ctx);
|
|
489
547
|
result[name] = value;
|
|
490
548
|
}
|
|
491
549
|
return result;
|
package/integrations/lib.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function parseSchema(typescriptFiles: Record<string, string>): Record<string,
|
|
1
|
+
export declare function parseSchema(typescriptFiles: Record<string, string>): Record<string, TypeDefinition>;
|
package/integrations/lib.mjs
CHANGED
|
@@ -3,8 +3,58 @@ import _ from "lodash";
|
|
|
3
3
|
import pluralize from "pluralize";
|
|
4
4
|
const supportedPrimitiveTypes = ["string", "number", "boolean", "Record<string, string>", "Record<string, any>"];
|
|
5
5
|
export function parseSchema(typescriptFiles) {
|
|
6
|
+
const typeNodesMap = getTypeNodesMap(typescriptFiles);
|
|
7
|
+
const schema = getSchema(typeNodesMap);
|
|
8
|
+
resolveIsExtending(schema);
|
|
9
|
+
fillComputedDefinitionsForType(schema);
|
|
10
|
+
verifyDefaultValueTypes(schema, typeNodesMap);
|
|
11
|
+
verifyRelationFields(schema);
|
|
12
|
+
return schema;
|
|
13
|
+
}
|
|
14
|
+
function resolveIsExtending(schema) {
|
|
15
|
+
for (const key in schema) {
|
|
16
|
+
const isExtendingType = schema[key].isExtending;
|
|
17
|
+
if (!isExtendingType)
|
|
18
|
+
continue;
|
|
19
|
+
if (!schema[isExtendingType])
|
|
20
|
+
throw new Error(`Unknown type: "${isExtendingType}"`);
|
|
21
|
+
schema[key].fields = { ...schema[isExtendingType].fields, ...schema[key].fields };
|
|
22
|
+
schema[key].decorators = { ...schema[isExtendingType].decorators, ...schema[key].decorators };
|
|
23
|
+
if (schema[key].decorators.entity && !schema[key].fields?.id) {
|
|
24
|
+
throw new Error(`Entity "${key}" must have an id`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function getSchema(typeNodesMap) {
|
|
6
29
|
const result = {};
|
|
7
|
-
const
|
|
30
|
+
for (const key in typeNodesMap) {
|
|
31
|
+
const { node, name } = typeNodesMap[key];
|
|
32
|
+
if (![SyntaxKind.ClassDeclaration, SyntaxKind.TypeAliasDeclaration].includes(node.kind)) {
|
|
33
|
+
throw new Error(`Unexpected type kind - "${name}"`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
for (const key in typeNodesMap) {
|
|
37
|
+
if (result[key])
|
|
38
|
+
continue;
|
|
39
|
+
const { node, sourceFile, name } = typeNodesMap[key];
|
|
40
|
+
if (node.kind !== SyntaxKind.ClassDeclaration)
|
|
41
|
+
continue;
|
|
42
|
+
const parsedClass = parseClassDeclaration(node, name, { typeNodesMap, sourceFile, result });
|
|
43
|
+
result[key] = parsedClass;
|
|
44
|
+
}
|
|
45
|
+
for (const key in typeNodesMap) {
|
|
46
|
+
if (result[key])
|
|
47
|
+
continue;
|
|
48
|
+
const { node, sourceFile, name } = typeNodesMap[key];
|
|
49
|
+
if (node.kind !== SyntaxKind.TypeAliasDeclaration)
|
|
50
|
+
continue;
|
|
51
|
+
const parsedClass = parseTypeAliasDeclaration(node, name, { typeNodesMap, sourceFile, result });
|
|
52
|
+
result[key] = parsedClass;
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
function getTypeNodesMap(typescriptFiles) {
|
|
57
|
+
const typeNodesMap = {};
|
|
8
58
|
for (const key in typescriptFiles) {
|
|
9
59
|
const text = typescriptFiles[key];
|
|
10
60
|
const sourceFile = createSourceFile(`${key}.ts`, text, ScriptTarget.Latest);
|
|
@@ -14,61 +64,41 @@ export function parseSchema(typescriptFiles) {
|
|
|
14
64
|
if (!nameNode || nameNode.kind !== SyntaxKind.Identifier)
|
|
15
65
|
throw new Error("Cannot detect class name");
|
|
16
66
|
const name = nameNode.text;
|
|
17
|
-
|
|
67
|
+
typeNodesMap[name] = { name, node: cd, sourceFile };
|
|
18
68
|
}
|
|
19
|
-
if (!
|
|
69
|
+
if (!typeNodesMap[key]) {
|
|
20
70
|
throw new Error(`File ${key}.ts must contain class declaration with name "${key}"`);
|
|
21
71
|
}
|
|
22
72
|
}
|
|
23
|
-
|
|
24
|
-
if (result[key])
|
|
25
|
-
continue;
|
|
26
|
-
const { node, sourceFile, name } = typesMap[key];
|
|
27
|
-
const parsedClass = parseType(node, name, { typesMap, sourceFile, result });
|
|
28
|
-
if (parsedClass.name !== key)
|
|
29
|
-
throw new Error(`File name must correspond to exported class name ("${key}", "${parsedClass.name}")`);
|
|
30
|
-
result[key] = parsedClass;
|
|
31
|
-
}
|
|
32
|
-
for (const key in result) {
|
|
33
|
-
const isExtendingType = result[key].isExtending;
|
|
34
|
-
if (!isExtendingType)
|
|
35
|
-
continue;
|
|
36
|
-
if (!result[isExtendingType])
|
|
37
|
-
throw new Error(`Unknown type: "${isExtendingType}"`);
|
|
38
|
-
result[key].fields = { ...result[isExtendingType].fields, ...result[key].fields };
|
|
39
|
-
result[key].decorators = { ...result[isExtendingType].decorators, ...result[key].decorators };
|
|
40
|
-
if (result[key].decorators.entity && !result[key].fields?.id) {
|
|
41
|
-
throw new Error(`Entity "${key}" must have an id`);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
fillComputedDefinitionsForType(result);
|
|
45
|
-
processThisReferenceDefaultValues(result, typesMap);
|
|
46
|
-
verifyRelationFields(result);
|
|
47
|
-
return result;
|
|
73
|
+
return typeNodesMap;
|
|
48
74
|
}
|
|
49
|
-
function
|
|
50
|
-
for (const key in
|
|
51
|
-
const type =
|
|
75
|
+
function verifyDefaultValueTypes(schema, typeNodesMap) {
|
|
76
|
+
for (const key in schema) {
|
|
77
|
+
const type = schema[key];
|
|
52
78
|
const fields = type.fields;
|
|
53
79
|
if (!fields)
|
|
54
80
|
continue;
|
|
55
81
|
for (const fName in fields) {
|
|
56
82
|
const field = fields[fName];
|
|
57
|
-
if (field.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
83
|
+
if (field.defaultValue) {
|
|
84
|
+
verifyDefaultValueType(field, {
|
|
85
|
+
typeNodesMap,
|
|
86
|
+
result: schema,
|
|
87
|
+
sourceFile: typeNodesMap[key]?.sourceFile
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
for (const fName2 in fields) {
|
|
91
|
+
const field2 = fields[fName2];
|
|
92
|
+
if (field2.defaultValueCopyFrom) {
|
|
93
|
+
const sourceField = fields[field2.defaultValueCopyFrom];
|
|
94
|
+
if (!sourceField)
|
|
95
|
+
throw new Error(`Cannot find field ${key}.${field2.defaultValueCopyFrom}"`);
|
|
96
|
+
verifyDefaultValueTypeCopyFrom(field2, sourceField, {
|
|
97
|
+
typeNodesMap,
|
|
98
|
+
result: schema,
|
|
99
|
+
sourceFile: typeNodesMap[key]?.sourceFile
|
|
100
|
+
});
|
|
64
101
|
}
|
|
65
|
-
verifyDefaultValueType(
|
|
66
|
-
field.isArray || false,
|
|
67
|
-
{ type: sourceField.type, value: sourceField.defaultValue },
|
|
68
|
-
field.type,
|
|
69
|
-
supportedPrimitiveTypes,
|
|
70
|
-
{ typesMap, result, sourceFile: typesMap[key]?.sourceFile }
|
|
71
|
-
);
|
|
72
102
|
}
|
|
73
103
|
}
|
|
74
104
|
}
|
|
@@ -98,7 +128,14 @@ function getOrder(f) {
|
|
|
98
128
|
};
|
|
99
129
|
return decorator.order ?? defaultOrders[decorator.preset] ?? 0;
|
|
100
130
|
}
|
|
101
|
-
function
|
|
131
|
+
function parseClassOrTypeDeclaration(typeDeclaration, typeName, ctx) {
|
|
132
|
+
if (typeDeclaration.kind === SyntaxKind.ClassDeclaration)
|
|
133
|
+
return parseClassDeclaration(typeDeclaration, typeName, ctx);
|
|
134
|
+
if (typeDeclaration.kind === SyntaxKind.TypeAliasDeclaration)
|
|
135
|
+
return parseTypeAliasDeclaration(typeDeclaration, typeName, ctx);
|
|
136
|
+
throw new Error(`Unexpected type definition - ${typeName}`);
|
|
137
|
+
}
|
|
138
|
+
function parseClassDeclaration(typeDeclaration, typeName, ctx) {
|
|
102
139
|
if (ctx.result[typeName])
|
|
103
140
|
return ctx.result[typeName];
|
|
104
141
|
const { modifiers } = typeDeclaration;
|
|
@@ -108,49 +145,53 @@ function parseType(typeDeclaration, typeName, ctx) {
|
|
|
108
145
|
const name = nameNode.text;
|
|
109
146
|
const comment = typeDeclaration.jsDoc?.[0]?.comment;
|
|
110
147
|
const decorators = parseDecorators(modifiers, ctx);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
throw new Error(`Unexpected class member - only properties are allowed("${m.getText(ctx.sourceFile)}")`);
|
|
119
|
-
}
|
|
148
|
+
const classDeclaration = typeDeclaration;
|
|
149
|
+
const { members, heritageClauses } = classDeclaration;
|
|
150
|
+
const isExtendingExpr = heritageClauses?.[0]?.types?.[0]?.expression;
|
|
151
|
+
const isExtending = isExtendingExpr?.text;
|
|
152
|
+
for (const m of members) {
|
|
153
|
+
if (m.kind !== SyntaxKind.PropertyDeclaration) {
|
|
154
|
+
throw new Error(`Unexpected class member - only properties are allowed("${m.getText(ctx.sourceFile)}")`);
|
|
120
155
|
}
|
|
121
|
-
const fieldsArray = members.map((m) => parseClassMember(m, name, ctx));
|
|
122
|
-
const fields = {};
|
|
123
|
-
for (const f of fieldsArray) {
|
|
124
|
-
fields[f.name] = f;
|
|
125
|
-
}
|
|
126
|
-
const handle = _.lowerFirst(name);
|
|
127
|
-
const handlePlural = pluralize(handle);
|
|
128
|
-
const result = {
|
|
129
|
-
name,
|
|
130
|
-
handle,
|
|
131
|
-
handlePlural,
|
|
132
|
-
decorators,
|
|
133
|
-
fields,
|
|
134
|
-
isExtending,
|
|
135
|
-
comment
|
|
136
|
-
};
|
|
137
|
-
return result;
|
|
138
156
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
const typeAliasValue = typeAliasDeclaration.type;
|
|
144
|
-
const enumValues = getEnumValues(typeAliasValue, typeDeclaration, ctx);
|
|
145
|
-
return { name, decorators, enumValues, comment };
|
|
146
|
-
}
|
|
147
|
-
if (typeAliasType.kind === SyntaxKind.TypeOperator && typeAliasType.operator === SyntaxKind.KeyOfKeyword) {
|
|
148
|
-
const enumValues = getEnumValuesFromKeyOf(typeAliasType, ctx);
|
|
149
|
-
return { name, decorators, enumValues, comment };
|
|
150
|
-
}
|
|
151
|
-
throw new Error(`Unexpected type definition - ${typeDeclaration.getText(ctx.sourceFile)}. Did you mean 'class'?`);
|
|
157
|
+
const fields = {};
|
|
158
|
+
for (const m of members) {
|
|
159
|
+
const field = parseClassMember(m, fields, name, ctx);
|
|
160
|
+
fields[field.name] = field;
|
|
152
161
|
}
|
|
153
|
-
|
|
162
|
+
const handle = _.lowerFirst(name);
|
|
163
|
+
const handlePlural = pluralize(handle);
|
|
164
|
+
const result = {
|
|
165
|
+
name,
|
|
166
|
+
handle,
|
|
167
|
+
handlePlural,
|
|
168
|
+
decorators,
|
|
169
|
+
fields,
|
|
170
|
+
isExtending,
|
|
171
|
+
comment
|
|
172
|
+
};
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
function parseTypeAliasDeclaration(typeDeclaration, typeName, ctx) {
|
|
176
|
+
if (ctx.result[typeName])
|
|
177
|
+
return ctx.result[typeName];
|
|
178
|
+
const nameNode = typeDeclaration.name;
|
|
179
|
+
if (!nameNode || nameNode.kind !== SyntaxKind.Identifier)
|
|
180
|
+
throw new Error("Cannot detect class name");
|
|
181
|
+
const name = nameNode.text;
|
|
182
|
+
const comment = typeDeclaration.jsDoc?.[0]?.comment;
|
|
183
|
+
const typeAliasDeclaration = typeDeclaration;
|
|
184
|
+
const typeAliasType = typeAliasDeclaration.type;
|
|
185
|
+
if (typeAliasType.kind === SyntaxKind.UnionType) {
|
|
186
|
+
const typeAliasValue = typeAliasDeclaration.type;
|
|
187
|
+
const enumValues = getEnumValues(typeAliasValue, typeDeclaration, ctx);
|
|
188
|
+
return { name, enumValues, comment, decorators: {} };
|
|
189
|
+
}
|
|
190
|
+
if (typeAliasType.kind === SyntaxKind.TypeOperator && typeAliasType.operator === SyntaxKind.KeyOfKeyword) {
|
|
191
|
+
const enumValues = getEnumValuesFromKeyOf(typeAliasType, ctx);
|
|
192
|
+
return { name, enumValues, comment, decorators: {} };
|
|
193
|
+
}
|
|
194
|
+
throw new Error(`Unexpected type definition - ${typeDeclaration.getText(ctx.sourceFile)}. Did you mean 'class'?`);
|
|
154
195
|
}
|
|
155
196
|
function parseDecorators(modifiers, ctx) {
|
|
156
197
|
if (!modifiers)
|
|
@@ -175,27 +216,25 @@ function getEnumValues(node, parentNode, ctx) {
|
|
|
175
216
|
});
|
|
176
217
|
return _.keyBy(enumValuesArray, "name");
|
|
177
218
|
}
|
|
178
|
-
function parseClassMember(node, parentName, ctx) {
|
|
219
|
+
function parseClassMember(node, parentFields, parentName, ctx) {
|
|
179
220
|
const name = node.name.getText(ctx.sourceFile);
|
|
180
|
-
const
|
|
181
|
-
let defaultValue;
|
|
182
|
-
let defaultValueCopyFrom;
|
|
183
|
-
if (defaultValueDescription) {
|
|
184
|
-
if (defaultValueDescription.type === "thisReference") {
|
|
185
|
-
defaultValueCopyFrom = defaultValueDescription.value;
|
|
186
|
-
} else {
|
|
187
|
-
defaultValue = defaultValueDescription.value;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
221
|
+
const { defaultValue, defaultValueCopyFrom, defaultValueClass } = parseDefaultValueExpression(node.initializer, ctx);
|
|
190
222
|
const isRequired = !node.questionToken;
|
|
191
223
|
const comment = node.jsDoc?.[0]?.comment;
|
|
192
224
|
const decorators = parseDecorators(node.modifiers, ctx);
|
|
225
|
+
let defaultValueType = defaultValueClass || getPrimitiveTypeFromDefaultValue(defaultValue);
|
|
226
|
+
if (defaultValueCopyFrom) {
|
|
227
|
+
const parentField = parentFields[defaultValueCopyFrom];
|
|
228
|
+
if (!parentField)
|
|
229
|
+
throw new Error(`Cannot find field ${parentName}.${defaultValueCopyFrom}"`);
|
|
230
|
+
defaultValueType = parentField.type;
|
|
231
|
+
}
|
|
193
232
|
const { isArray, isRelation, isChange, relationDenormFields, type } = parseFieldType(
|
|
194
233
|
ctx,
|
|
195
234
|
parentName,
|
|
196
235
|
name,
|
|
197
236
|
node,
|
|
198
|
-
|
|
237
|
+
defaultValueType
|
|
199
238
|
);
|
|
200
239
|
const result = {
|
|
201
240
|
type,
|
|
@@ -211,9 +250,24 @@ function parseClassMember(node, parentName, ctx) {
|
|
|
211
250
|
};
|
|
212
251
|
if (!_.isEmpty(decorators))
|
|
213
252
|
result.decorators = decorators;
|
|
253
|
+
if (defaultValueClass)
|
|
254
|
+
result.defaultValueClass = defaultValueClass;
|
|
214
255
|
return result;
|
|
215
256
|
}
|
|
216
|
-
function
|
|
257
|
+
function getPrimitiveTypeFromDefaultValue(value) {
|
|
258
|
+
if (_.isString(value))
|
|
259
|
+
return "string";
|
|
260
|
+
if (_.isNumber(value))
|
|
261
|
+
return "number";
|
|
262
|
+
if (_.isBoolean(value))
|
|
263
|
+
return "boolean";
|
|
264
|
+
if (_.isArray(value))
|
|
265
|
+
return "array";
|
|
266
|
+
if (_.isObject(value))
|
|
267
|
+
return "object";
|
|
268
|
+
return void 0;
|
|
269
|
+
}
|
|
270
|
+
function parseFieldType(ctx, parentName, fieldName, node, defaultValueType) {
|
|
217
271
|
const parsedType = {
|
|
218
272
|
isArray: false,
|
|
219
273
|
isRelation: false,
|
|
@@ -228,22 +282,19 @@ function parseFieldType(ctx, parentName, fieldName, node, defaultValueDescriptio
|
|
|
228
282
|
parseFieldTypeInlineEnum(parsedType, parentName, fieldName, ctx);
|
|
229
283
|
parseFieldTypeKeyofEnum(parsedType, parentName, fieldName, ctx);
|
|
230
284
|
parseFieldTypeRecordEnum(parsedType, parentName, fieldName, ctx);
|
|
231
|
-
parsedType.type = parsedType.type ?? parsedType.nodeType?.getText(ctx.sourceFile) ??
|
|
285
|
+
parsedType.type = parsedType.type ?? parsedType.nodeType?.getText(ctx.sourceFile) ?? defaultValueType;
|
|
232
286
|
if (!parsedType.type)
|
|
233
287
|
throw new Error(`Cannot detect property type: '${node.getText(ctx.sourceFile)}'`);
|
|
234
288
|
if (parsedType.type.startsWith("Change<")) {
|
|
235
289
|
parsedType.type = parsedType.type.slice(7, -1);
|
|
236
|
-
if (!ctx.
|
|
290
|
+
if (!ctx.typeNodesMap[parsedType.type])
|
|
237
291
|
throw new Error(`Unexpected property type: '${parsedType.type}'`);
|
|
238
292
|
parsedType.isChange = true;
|
|
239
293
|
} else {
|
|
240
|
-
if (!supportedPrimitiveTypes.includes(parsedType.type) && !ctx.
|
|
294
|
+
if (!supportedPrimitiveTypes.includes(parsedType.type) && !ctx.typeNodesMap[parsedType.type] && !ctx.result[parsedType.type]) {
|
|
241
295
|
throw new Error(`Unexpected property type: '${parsedType.type}'`);
|
|
242
296
|
}
|
|
243
297
|
}
|
|
244
|
-
if (defaultValueDescription && defaultValueDescription.type !== "thisReference") {
|
|
245
|
-
verifyDefaultValueType(parsedType.isArray, defaultValueDescription, parsedType.type, supportedPrimitiveTypes, ctx);
|
|
246
|
-
}
|
|
247
298
|
return {
|
|
248
299
|
isArray: parsedType.isArray || void 0,
|
|
249
300
|
isRelation: parsedType.isRelation || void 0,
|
|
@@ -262,11 +313,11 @@ function parseFieldTypeRecordEnum(parsedType, parentName, fieldName, ctx) {
|
|
|
262
313
|
return;
|
|
263
314
|
const keyTypeName = nt.typeArguments[0].getText(ctx.sourceFile);
|
|
264
315
|
const valueTypeName = nt.typeArguments[1].getText(ctx.sourceFile);
|
|
265
|
-
const keyType = ctx.
|
|
316
|
+
const keyType = ctx.typeNodesMap[keyTypeName];
|
|
266
317
|
if (!keyType)
|
|
267
318
|
return;
|
|
268
319
|
if (!ctx.result[keyTypeName])
|
|
269
|
-
ctx.result[keyTypeName] =
|
|
320
|
+
ctx.result[keyTypeName] = parseClassOrTypeDeclaration(keyType.node, keyTypeName, ctx);
|
|
270
321
|
const enumValues = ctx.result[keyTypeName].enumValues;
|
|
271
322
|
if (!enumValues)
|
|
272
323
|
throw new Error(`Unexpected type - ${keyTypeName}`);
|
|
@@ -335,12 +386,12 @@ function getEnumValuesFromKeyOf(nodeType, ctx) {
|
|
|
335
386
|
throw new Error(`Unexpected type - ${typeReferenceNode.getText(ctx.sourceFile)}`);
|
|
336
387
|
}
|
|
337
388
|
const typeName = typeReferenceNode.typeName.text;
|
|
338
|
-
const type = ctx.
|
|
389
|
+
const type = ctx.typeNodesMap[typeName];
|
|
339
390
|
if (!type) {
|
|
340
391
|
throw new Error(`Unexpected type - ${typeName}`);
|
|
341
392
|
}
|
|
342
393
|
if (!ctx.result[typeName])
|
|
343
|
-
ctx.result[typeName] =
|
|
394
|
+
ctx.result[typeName] = parseClassOrTypeDeclaration(type.node, typeName, ctx);
|
|
344
395
|
if (!ctx.result[typeName].fields)
|
|
345
396
|
throw new Error(`Unexpected type - ${typeName}`);
|
|
346
397
|
return _.mapValues(ctx.result[typeName].fields || {}, (v) => ({
|
|
@@ -363,24 +414,37 @@ function getRelationDenormFields(ctx, node) {
|
|
|
363
414
|
}
|
|
364
415
|
throw new Error(`Unexpected type - ${node.getText(ctx.sourceFile)}`);
|
|
365
416
|
}
|
|
366
|
-
function verifyDefaultValueType(
|
|
417
|
+
function verifyDefaultValueType(field, ctx) {
|
|
418
|
+
const { isArray, defaultValue, type } = field;
|
|
367
419
|
if (isArray) {
|
|
368
|
-
if (
|
|
369
|
-
throw new
|
|
420
|
+
if (!_.isArray(defaultValue)) {
|
|
421
|
+
throw new TypeError(`Default value type is different from field type: '${type}'`);
|
|
370
422
|
}
|
|
371
423
|
} else {
|
|
372
|
-
if (
|
|
424
|
+
if (supportedPrimitiveTypes.includes(type) && getPrimitiveTypeFromDefaultValue(defaultValue) !== type) {
|
|
373
425
|
throw new Error(`Default value type is different from field type: '${type}'`);
|
|
374
426
|
}
|
|
375
|
-
if (!ctx.result[type] && ctx.
|
|
376
|
-
ctx.result[type] =
|
|
427
|
+
if (!ctx.result[type] && ctx.typeNodesMap[type])
|
|
428
|
+
ctx.result[type] = parseClassOrTypeDeclaration(ctx.typeNodesMap[type].node, type, ctx);
|
|
377
429
|
const enumValues = ctx.result[type]?.enumValues;
|
|
378
|
-
if (enumValues && !enumValues[
|
|
430
|
+
if (enumValues && !enumValues[defaultValue]) {
|
|
379
431
|
const enumValuesStr = _.keys(enumValues).map((x) => `'x'`).join(", ");
|
|
380
432
|
throw new Error(`Default value must be one of: ${enumValuesStr}`);
|
|
381
433
|
}
|
|
382
434
|
}
|
|
383
435
|
}
|
|
436
|
+
function verifyDefaultValueTypeCopyFrom(field, sourceField, ctx) {
|
|
437
|
+
const { isArray, type } = field;
|
|
438
|
+
if (isArray) {
|
|
439
|
+
if (!sourceField.isArray) {
|
|
440
|
+
throw new TypeError(`Default value type is not an array: '${field.name}'`);
|
|
441
|
+
}
|
|
442
|
+
} else {
|
|
443
|
+
if (sourceField.type !== type) {
|
|
444
|
+
throw new Error(`Default value type is different from field type: '${field.name}'`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
384
448
|
function parseDecorator(decoratorNode, ctx) {
|
|
385
449
|
const expr = decoratorNode.expression;
|
|
386
450
|
if (expr.kind !== SyntaxKind.CallExpression)
|
|
@@ -397,28 +461,35 @@ function parseDecoratorArguments(expr, ctx) {
|
|
|
397
461
|
if (args.length > 1)
|
|
398
462
|
throw new Error(`Too many arguments - one expected: "${expr.getText(ctx.sourceFile)}"`);
|
|
399
463
|
const arg = args[0];
|
|
400
|
-
return parseLiteralNode(arg, ctx)
|
|
464
|
+
return parseLiteralNode(arg, ctx) ?? {};
|
|
401
465
|
}
|
|
402
|
-
function
|
|
466
|
+
function parseDefaultValueExpression(expr, ctx) {
|
|
403
467
|
if (!expr)
|
|
404
|
-
return
|
|
468
|
+
return {};
|
|
405
469
|
if (expr.kind === SyntaxKind.NewExpression) {
|
|
406
470
|
const identifier = expr.expression;
|
|
407
471
|
const type = identifier?.text;
|
|
408
472
|
if (type) {
|
|
409
|
-
return { type,
|
|
473
|
+
return { defaultValueClass: type, defaultValue: {} };
|
|
410
474
|
}
|
|
475
|
+
} else if (expr.kind === SyntaxKind.PropertyAccessExpression && expr.expression.kind === SyntaxKind.ThisKeyword) {
|
|
476
|
+
return { defaultValueCopyFrom: expr.name?.text };
|
|
411
477
|
}
|
|
478
|
+
return { defaultValue: parseLiteralNode(expr, ctx) };
|
|
479
|
+
}
|
|
480
|
+
function parseLiteralNode(expr, ctx) {
|
|
481
|
+
if (!expr)
|
|
482
|
+
return void 0;
|
|
412
483
|
if (expr.kind === SyntaxKind.StringLiteral)
|
|
413
|
-
return
|
|
484
|
+
return expr.text;
|
|
414
485
|
if (expr.kind === SyntaxKind.FalseKeyword)
|
|
415
|
-
return
|
|
486
|
+
return false;
|
|
416
487
|
if (expr.kind === SyntaxKind.TrueKeyword)
|
|
417
|
-
return
|
|
488
|
+
return true;
|
|
418
489
|
if (expr.kind === SyntaxKind.NumericLiteral)
|
|
419
|
-
return
|
|
490
|
+
return Number.parseFloat(expr.text);
|
|
420
491
|
if (expr.kind === SyntaxKind.ObjectLiteralExpression)
|
|
421
|
-
return
|
|
492
|
+
return parseObjectLiteral(expr, ctx);
|
|
422
493
|
if (expr.kind === SyntaxKind.ArrayLiteralExpression) {
|
|
423
494
|
const defaultValueStr = expr.getText(ctx.sourceFile);
|
|
424
495
|
let defaultValue;
|
|
@@ -429,10 +500,7 @@ function parseLiteralNode(expr, ctx) {
|
|
|
429
500
|
}
|
|
430
501
|
if (!_.isArray(defaultValue))
|
|
431
502
|
throw new Error("Value must be valid array");
|
|
432
|
-
return
|
|
433
|
-
}
|
|
434
|
-
if (expr.kind === SyntaxKind.PropertyAccessExpression && expr.expression.kind === SyntaxKind.ThisKeyword) {
|
|
435
|
-
return { type: "thisReference", value: expr.name?.text };
|
|
503
|
+
return defaultValue;
|
|
436
504
|
}
|
|
437
505
|
throw new Error(`Unexpected property expression: "${expr.getText(ctx.sourceFile)}"`);
|
|
438
506
|
}
|
|
@@ -447,7 +515,7 @@ function parseObjectLiteral(arg, ctx) {
|
|
|
447
515
|
throw new Error(`Unexpected property value: "${p.getText(ctx.sourceFile)}"`);
|
|
448
516
|
const p2 = p;
|
|
449
517
|
const valueExpression = p2.initializer;
|
|
450
|
-
const value = parseLiteralNode(valueExpression, ctx)
|
|
518
|
+
const value = parseLiteralNode(valueExpression, ctx);
|
|
451
519
|
result[name] = value;
|
|
452
520
|
}
|
|
453
521
|
return result;
|
|
@@ -77,6 +77,11 @@ var _default = {
|
|
|
77
77
|
async unlink(filepath) {
|
|
78
78
|
sanitizeFilepath(filepath);
|
|
79
79
|
return await _promises.default.unlink(filepath);
|
|
80
|
+
},
|
|
81
|
+
async rename(oldFilepath, newFilepath) {
|
|
82
|
+
sanitizeFilepath(oldFilepath);
|
|
83
|
+
sanitizeFilepath(newFilepath);
|
|
84
|
+
return await _promises.default.rename(oldFilepath, newFilepath);
|
|
80
85
|
}
|
|
81
86
|
};
|
|
82
87
|
module.exports = _default;
|
|
@@ -69,6 +69,11 @@ export default {
|
|
|
69
69
|
async unlink(filepath) {
|
|
70
70
|
sanitizeFilepath(filepath);
|
|
71
71
|
return await fs.unlink(filepath);
|
|
72
|
+
},
|
|
73
|
+
async rename(oldFilepath, newFilepath) {
|
|
74
|
+
sanitizeFilepath(oldFilepath);
|
|
75
|
+
sanitizeFilepath(newFilepath);
|
|
76
|
+
return await fs.rename(oldFilepath, newFilepath);
|
|
72
77
|
}
|
|
73
78
|
};
|
|
74
79
|
async function dirExists(path2) {
|