@vkontakte/api-schema-typescript-generator 0.13.2 → 0.14.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/.eslintrc.json +3 -0
- package/.prettierrc.js +10 -0
- package/dist/generators/APITypingsGenerator.js +44 -41
- package/dist/generators/CommentCodeBlock.js +2 -9
- package/dist/generators/SchemaObject.js +5 -1
- package/dist/generators/TypeCodeBlock.js +22 -18
- package/dist/generators/enums.js +2 -5
- package/dist/generators/methods.js +3 -3
- package/dist/helpers.js +32 -20
- package/dist/index.js +4 -3
- package/package.json +5 -3
- package/src/generator.ts +5 -1
- package/src/generators/APITypingsGenerator.ts +84 -61
- package/src/generators/CommentCodeBlock.ts +2 -9
- package/src/generators/SchemaObject.ts +17 -8
- package/src/generators/TypeCodeBlock.ts +56 -47
- package/src/generators/enums.ts +11 -11
- package/src/generators/methods.ts +3 -3
- package/src/generators/typeString.ts +4 -1
- package/src/helpers.ts +36 -23
- package/src/index.ts +15 -5
- package/src/types/schema.ts +7 -0
package/.eslintrc.json
CHANGED
package/.prettierrc.js
ADDED
|
@@ -26,13 +26,14 @@ class APITypingsGenerator {
|
|
|
26
26
|
this.methodsList = options.methodsDefinitions.methods || [];
|
|
27
27
|
this.objects = this.convertJSONSchemaDictionary(options.objects);
|
|
28
28
|
this.responses = this.convertJSONSchemaDictionary(options.responses);
|
|
29
|
+
this.errors = options.errors;
|
|
29
30
|
this.visitedRefs = {};
|
|
30
31
|
this.generatedObjects = {};
|
|
31
32
|
this.methodFilesMap = {};
|
|
32
33
|
this.exports = {};
|
|
33
34
|
this.ignoredResponses = {
|
|
34
35
|
'storage.get': {
|
|
35
|
-
|
|
36
|
+
keysResponse: true,
|
|
36
37
|
},
|
|
37
38
|
};
|
|
38
39
|
this.resultFiles = {};
|
|
@@ -44,6 +45,7 @@ class APITypingsGenerator {
|
|
|
44
45
|
methodsList;
|
|
45
46
|
objects;
|
|
46
47
|
responses;
|
|
48
|
+
errors;
|
|
47
49
|
visitedRefs;
|
|
48
50
|
generatedObjects;
|
|
49
51
|
methodFilesMap;
|
|
@@ -80,10 +82,7 @@ class APITypingsGenerator {
|
|
|
80
82
|
...methodFile.imports,
|
|
81
83
|
...imports,
|
|
82
84
|
},
|
|
83
|
-
codeBlocks: [
|
|
84
|
-
...methodFile.codeBlocks,
|
|
85
|
-
...codeBlocks,
|
|
86
|
-
],
|
|
85
|
+
codeBlocks: [...methodFile.codeBlocks, ...codeBlocks],
|
|
87
86
|
};
|
|
88
87
|
}
|
|
89
88
|
collectAllOf(object, deep = 0) {
|
|
@@ -133,10 +132,7 @@ class APITypingsGenerator {
|
|
|
133
132
|
additionalProperties = this.getObjectProperties(refObject, deep + 1);
|
|
134
133
|
}
|
|
135
134
|
if (additionalProperties.length) {
|
|
136
|
-
properties = [
|
|
137
|
-
...properties,
|
|
138
|
-
...additionalProperties,
|
|
139
|
-
];
|
|
135
|
+
properties = [...properties, ...additionalProperties];
|
|
140
136
|
}
|
|
141
137
|
});
|
|
142
138
|
}
|
|
@@ -192,10 +188,7 @@ class APITypingsGenerator {
|
|
|
192
188
|
});
|
|
193
189
|
});
|
|
194
190
|
return {
|
|
195
|
-
codeBlocks: [
|
|
196
|
-
...codeBlocks,
|
|
197
|
-
codeBlock,
|
|
198
|
-
],
|
|
191
|
+
codeBlocks: [...codeBlocks, codeBlock],
|
|
199
192
|
imports,
|
|
200
193
|
value: '',
|
|
201
194
|
};
|
|
@@ -298,7 +291,9 @@ class APITypingsGenerator {
|
|
|
298
291
|
properties: [],
|
|
299
292
|
});
|
|
300
293
|
methodInfo.parameters.forEach((property) => {
|
|
301
|
-
const { imports: newImports, value, codeBlocks: newCodeBlocks, } = typeString_1.generateTypeString(property, this.objects, {
|
|
294
|
+
const { imports: newImports, value, codeBlocks: newCodeBlocks, } = typeString_1.generateTypeString(property, this.objects, {
|
|
295
|
+
needEnumNamesConstant: false,
|
|
296
|
+
});
|
|
302
297
|
imports = { ...imports, ...newImports };
|
|
303
298
|
codeBlocks = [...codeBlocks, ...newCodeBlocks];
|
|
304
299
|
codeBlock.addProperty({
|
|
@@ -323,12 +318,10 @@ class APITypingsGenerator {
|
|
|
323
318
|
let imports = {};
|
|
324
319
|
if (object.enum) {
|
|
325
320
|
const { codeBlocks: newCodeBlocks } = enums_1.generateEnumAsUnionType(object);
|
|
326
|
-
codeBlocks = [
|
|
327
|
-
...newCodeBlocks,
|
|
328
|
-
];
|
|
321
|
+
codeBlocks = [...newCodeBlocks];
|
|
329
322
|
}
|
|
330
323
|
else {
|
|
331
|
-
const { imports: newImports, value, codeBlocks: newCodeBlocks } = typeString_1.generateTypeString(object, this.objects);
|
|
324
|
+
const { imports: newImports, value, codeBlocks: newCodeBlocks, } = typeString_1.generateTypeString(object, this.objects);
|
|
332
325
|
const codeBlock = new TypeCodeBlock_1.TypeCodeBlock({
|
|
333
326
|
type: TypeCodeBlock_1.TypeScriptCodeTypes.Type,
|
|
334
327
|
refName: object.name,
|
|
@@ -339,11 +332,7 @@ class APITypingsGenerator {
|
|
|
339
332
|
value,
|
|
340
333
|
});
|
|
341
334
|
imports = newImports;
|
|
342
|
-
codeBlocks = [
|
|
343
|
-
...codeBlocks,
|
|
344
|
-
...newCodeBlocks,
|
|
345
|
-
codeBlock,
|
|
346
|
-
];
|
|
335
|
+
codeBlocks = [...codeBlocks, ...newCodeBlocks, codeBlock];
|
|
347
336
|
}
|
|
348
337
|
return {
|
|
349
338
|
codeBlocks,
|
|
@@ -352,26 +341,20 @@ class APITypingsGenerator {
|
|
|
352
341
|
};
|
|
353
342
|
}
|
|
354
343
|
getResponseCodeBlockAsType(object, response) {
|
|
355
|
-
const { imports, value, codeBlocks, description
|
|
344
|
+
const { imports, value, codeBlocks, description } = typeString_1.generateTypeString(response, this.objects, {
|
|
356
345
|
objectParentName: ' ', // TODO: Refactor
|
|
357
346
|
});
|
|
358
347
|
const codeBlock = new TypeCodeBlock_1.TypeCodeBlock({
|
|
359
348
|
type: TypeCodeBlock_1.TypeScriptCodeTypes.Type,
|
|
360
349
|
refName: object.name,
|
|
361
350
|
interfaceName: helpers_1.getInterfaceName(object.name),
|
|
362
|
-
description: [
|
|
363
|
-
object.description,
|
|
364
|
-
description || '',
|
|
365
|
-
].join(constants_1.newLineChar),
|
|
351
|
+
description: [object.description, description || ''].join(constants_1.newLineChar),
|
|
366
352
|
needExport: true,
|
|
367
353
|
properties: [],
|
|
368
354
|
value,
|
|
369
355
|
});
|
|
370
356
|
return {
|
|
371
|
-
codeBlocks: [
|
|
372
|
-
...codeBlocks,
|
|
373
|
-
codeBlock,
|
|
374
|
-
],
|
|
357
|
+
codeBlocks: [...codeBlocks, codeBlock],
|
|
375
358
|
imports,
|
|
376
359
|
value: '',
|
|
377
360
|
description,
|
|
@@ -441,13 +424,10 @@ class APITypingsGenerator {
|
|
|
441
424
|
// Comment with method name for visual sections in file
|
|
442
425
|
const methodNameComment = new CommentCodeBlock_1.CommentCodeBlock([methodName]);
|
|
443
426
|
if (method.description) {
|
|
444
|
-
methodNameComment.appendLines([
|
|
445
|
-
'',
|
|
446
|
-
method.description,
|
|
447
|
-
]);
|
|
427
|
+
methodNameComment.appendLines(['', method.description]);
|
|
448
428
|
}
|
|
449
429
|
this.appendToFileMap(section, {}, [methodNameComment]);
|
|
450
|
-
const { method: normalizedMethod, parameterRefs
|
|
430
|
+
const { method: normalizedMethod, parameterRefs } = methods_1.normalizeMethodInfo(method);
|
|
451
431
|
method = normalizedMethod;
|
|
452
432
|
this.generateObjectsFromRefs(parameterRefs);
|
|
453
433
|
this.generateMethodParams(new SchemaObject_1.SchemaObject(method.name, method));
|
|
@@ -473,13 +453,35 @@ class APITypingsGenerator {
|
|
|
473
453
|
this.registerExport(`./methods/${section}`, codeBlock.interfaceName);
|
|
474
454
|
}
|
|
475
455
|
});
|
|
476
|
-
const code = [
|
|
477
|
-
generator_1.generateImportsBlock(imports, null),
|
|
478
|
-
...codeBlocks,
|
|
479
|
-
];
|
|
456
|
+
const code = [generator_1.generateImportsBlock(imports, null), ...codeBlocks];
|
|
480
457
|
this.registerResultFile(path_1.default.join('methods', `${section}.ts`), code.join(constants_1.newLineChar.repeat(2)));
|
|
481
458
|
});
|
|
482
459
|
}
|
|
460
|
+
generateErrors() {
|
|
461
|
+
log_1.consoleLogInfo('creating errors...');
|
|
462
|
+
const code = [];
|
|
463
|
+
Object.entries(this.errors)
|
|
464
|
+
.reduce((acc, [name, error]) => {
|
|
465
|
+
acc.push({ name, ...error });
|
|
466
|
+
return acc;
|
|
467
|
+
}, [])
|
|
468
|
+
.sort((errorA, errorB) => {
|
|
469
|
+
return errorA.code - errorB.code;
|
|
470
|
+
})
|
|
471
|
+
.forEach((error) => {
|
|
472
|
+
const errorConstantName = error.name.toUpperCase();
|
|
473
|
+
code.push(new TypeCodeBlock_1.TypeCodeBlock({
|
|
474
|
+
type: TypeCodeBlock_1.TypeScriptCodeTypes.Const,
|
|
475
|
+
interfaceName: errorConstantName,
|
|
476
|
+
needExport: true,
|
|
477
|
+
value: String(error.code),
|
|
478
|
+
properties: [],
|
|
479
|
+
description: [error.description, error.$comment || ''].join(constants_1.newLineChar.repeat(2)),
|
|
480
|
+
}).toString());
|
|
481
|
+
this.registerExport('./common/errors', errorConstantName);
|
|
482
|
+
});
|
|
483
|
+
this.registerResultFile(path_1.default.join('common', 'errors.ts'), code.join(constants_1.newLineChar.repeat(2)));
|
|
484
|
+
}
|
|
483
485
|
createCommonTypes() {
|
|
484
486
|
log_1.consoleLogInfo('creating common types...');
|
|
485
487
|
const code = [];
|
|
@@ -545,6 +547,7 @@ class APITypingsGenerator {
|
|
|
545
547
|
generate() {
|
|
546
548
|
log_1.consoleLogInfo('generate');
|
|
547
549
|
this.generateMethods();
|
|
550
|
+
this.generateErrors();
|
|
548
551
|
if (this.needEmit) {
|
|
549
552
|
this.createCommonTypes();
|
|
550
553
|
this.createIndexExports();
|
|
@@ -10,18 +10,11 @@ class CommentCodeBlock extends BaseCodeBlock_1.BaseCodeBlock {
|
|
|
10
10
|
}
|
|
11
11
|
lines;
|
|
12
12
|
appendLines(lines) {
|
|
13
|
-
this.lines = [
|
|
14
|
-
...this.lines,
|
|
15
|
-
...lines,
|
|
16
|
-
];
|
|
13
|
+
this.lines = [...this.lines, ...lines];
|
|
17
14
|
}
|
|
18
15
|
toString() {
|
|
19
16
|
const inner = this.lines.map((line) => constants_1.spaceChar + `* ${line}`.trim());
|
|
20
|
-
return [
|
|
21
|
-
'/**',
|
|
22
|
-
...inner,
|
|
23
|
-
' */',
|
|
24
|
-
].join(constants_1.newLineChar);
|
|
17
|
+
return ['/**', ...inner, ' */'].join(constants_1.newLineChar);
|
|
25
18
|
}
|
|
26
19
|
}
|
|
27
20
|
exports.CommentCodeBlock = CommentCodeBlock;
|
|
@@ -7,7 +7,11 @@ const log_1 = require("../log");
|
|
|
7
7
|
class SchemaObject {
|
|
8
8
|
constructor(name, object, parentName) {
|
|
9
9
|
if (!utils_1.isObject(object)) {
|
|
10
|
-
log_1.consoleLogErrorAndExit(`[SchemaObject] "${name}" is not an object.`, {
|
|
10
|
+
log_1.consoleLogErrorAndExit(`[SchemaObject] "${name}" is not an object.`, {
|
|
11
|
+
name,
|
|
12
|
+
object,
|
|
13
|
+
parentName,
|
|
14
|
+
});
|
|
11
15
|
return;
|
|
12
16
|
}
|
|
13
17
|
this.name = name;
|
|
@@ -12,6 +12,7 @@ var TypeScriptCodeTypes;
|
|
|
12
12
|
TypeScriptCodeTypes["Enum"] = "enum";
|
|
13
13
|
TypeScriptCodeTypes["ConstantObject"] = "constant_object";
|
|
14
14
|
TypeScriptCodeTypes["Type"] = "type";
|
|
15
|
+
TypeScriptCodeTypes["Const"] = "const";
|
|
15
16
|
})(TypeScriptCodeTypes = exports.TypeScriptCodeTypes || (exports.TypeScriptCodeTypes = {}));
|
|
16
17
|
class TypeCodeBlock extends BaseCodeBlock_1.BaseCodeBlock {
|
|
17
18
|
constructor(options) {
|
|
@@ -39,8 +40,11 @@ class TypeCodeBlock extends BaseCodeBlock_1.BaseCodeBlock {
|
|
|
39
40
|
this.properties.push(property);
|
|
40
41
|
}
|
|
41
42
|
getPropertiesCode() {
|
|
42
|
-
const quoteChar = this.properties.some((property) => helpers_1.areQuotesNeededForProperty(property.name))
|
|
43
|
-
|
|
43
|
+
const quoteChar = this.properties.some((property) => helpers_1.areQuotesNeededForProperty(property.name))
|
|
44
|
+
? "'"
|
|
45
|
+
: '';
|
|
46
|
+
return this.properties
|
|
47
|
+
.map((property) => {
|
|
44
48
|
let divider = '';
|
|
45
49
|
let lineEnd = '';
|
|
46
50
|
switch (this.type) {
|
|
@@ -68,7 +72,8 @@ class TypeCodeBlock extends BaseCodeBlock_1.BaseCodeBlock {
|
|
|
68
72
|
}
|
|
69
73
|
}
|
|
70
74
|
return propertyCode.join(constants_1.newLineChar);
|
|
71
|
-
})
|
|
75
|
+
})
|
|
76
|
+
.join(constants_1.newLineChar);
|
|
72
77
|
}
|
|
73
78
|
toString() {
|
|
74
79
|
const hasProperties = this.properties.length > 0;
|
|
@@ -80,10 +85,7 @@ class TypeCodeBlock extends BaseCodeBlock_1.BaseCodeBlock {
|
|
|
80
85
|
before.push(`// ${this.refName}`);
|
|
81
86
|
}
|
|
82
87
|
if (this.description) {
|
|
83
|
-
before = [
|
|
84
|
-
...before,
|
|
85
|
-
...helpers_1.joinCommentLines(0, this.description),
|
|
86
|
-
];
|
|
88
|
+
before = [...before, ...helpers_1.joinCommentLines(0, this.description)];
|
|
87
89
|
}
|
|
88
90
|
switch (this.type) {
|
|
89
91
|
case TypeScriptCodeTypes.Interface: {
|
|
@@ -92,15 +94,12 @@ class TypeCodeBlock extends BaseCodeBlock_1.BaseCodeBlock {
|
|
|
92
94
|
propertiesCode = '';
|
|
93
95
|
}
|
|
94
96
|
else {
|
|
95
|
-
propertiesCode = [
|
|
96
|
-
' // empty interface',
|
|
97
|
-
' [key: string]: any;',
|
|
98
|
-
].join(constants_1.newLineChar);
|
|
97
|
+
propertiesCode = [' // empty interface', ' [key: string]: any;'].join(constants_1.newLineChar);
|
|
99
98
|
}
|
|
100
99
|
}
|
|
101
|
-
const extendsInterfaces = Array.isArray(this.extendsInterfaces) && this.extendsInterfaces.length
|
|
102
|
-
this.extendsInterfaces.join(', ')
|
|
103
|
-
'';
|
|
100
|
+
const extendsInterfaces = Array.isArray(this.extendsInterfaces) && this.extendsInterfaces.length
|
|
101
|
+
? this.extendsInterfaces.join(', ')
|
|
102
|
+
: '';
|
|
104
103
|
code = [
|
|
105
104
|
utils_1.trimStringDoubleSpaces(`${exportKeyword} interface ${this.interfaceName} ${extendsInterfaces} {`),
|
|
106
105
|
propertiesCode,
|
|
@@ -130,11 +129,16 @@ class TypeCodeBlock extends BaseCodeBlock_1.BaseCodeBlock {
|
|
|
130
129
|
utils_1.trimStringDoubleSpaces(`${exportKeyword} type ${this.interfaceName} = ${this.value};`),
|
|
131
130
|
].join(constants_1.newLineChar);
|
|
132
131
|
break;
|
|
132
|
+
case TypeScriptCodeTypes.Const:
|
|
133
|
+
if (!this.value) {
|
|
134
|
+
log_1.consoleLogErrorAndExit(`"${this.interfaceName}" type has empty value`);
|
|
135
|
+
}
|
|
136
|
+
code = [
|
|
137
|
+
utils_1.trimStringDoubleSpaces(`${exportKeyword} const ${this.interfaceName} = ${this.value};`),
|
|
138
|
+
].join(constants_1.newLineChar);
|
|
139
|
+
break;
|
|
133
140
|
}
|
|
134
|
-
return [
|
|
135
|
-
before.join(constants_1.newLineChar),
|
|
136
|
-
code,
|
|
137
|
-
].join(constants_1.newLineChar).trim();
|
|
141
|
+
return [before.join(constants_1.newLineChar), code].join(constants_1.newLineChar).trim();
|
|
138
142
|
}
|
|
139
143
|
}
|
|
140
144
|
exports.TypeCodeBlock = TypeCodeBlock;
|
package/dist/generators/enums.js
CHANGED
|
@@ -46,10 +46,7 @@ function generateEnumAsUnionType(object) {
|
|
|
46
46
|
type: TypeCodeBlock_1.TypeScriptCodeTypes.Type,
|
|
47
47
|
refName: object.name,
|
|
48
48
|
interfaceName: helpers_1.getInterfaceName(object.name),
|
|
49
|
-
description: [
|
|
50
|
-
object.description,
|
|
51
|
-
description,
|
|
52
|
-
].join(constants_1.newLineChar),
|
|
49
|
+
description: [object.description, description].join(constants_1.newLineChar),
|
|
53
50
|
needExport: true,
|
|
54
51
|
properties: [],
|
|
55
52
|
value,
|
|
@@ -79,7 +76,7 @@ function getEnumNames(object) {
|
|
|
79
76
|
};
|
|
80
77
|
}
|
|
81
78
|
function generateInlineEnum(object, options = {}) {
|
|
82
|
-
const { isNumericEnum, enumNames, needEnumNamesDescription
|
|
79
|
+
const { isNumericEnum, enumNames, needEnumNamesDescription } = getEnumNames(object);
|
|
83
80
|
options = {
|
|
84
81
|
needEnumNamesConstant: isNumericEnum,
|
|
85
82
|
...options,
|
|
@@ -28,9 +28,9 @@ function normalizeMethodInfo(method) {
|
|
|
28
28
|
if (parameter.items && parameter.items.$ref) {
|
|
29
29
|
const ref = parameter.items?.$ref;
|
|
30
30
|
parameterRefs[ref] = types_1.RefsDictionaryType.Generate;
|
|
31
|
-
parameter.description +=
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
parameter.description +=
|
|
32
|
+
constants_1.newLineChar.repeat(2) +
|
|
33
|
+
[`@see ${helpers_1.getInterfaceName(helpers_1.getObjectNameByRef(ref))} (${ref})`].join(constants_1.newLineChar);
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
36
|
return {
|
package/dist/helpers.js
CHANGED
|
@@ -28,6 +28,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
28
28
|
const utils_1 = require("./utils");
|
|
29
29
|
const constants_1 = require("./constants");
|
|
30
30
|
const log_1 = require("./log");
|
|
31
|
+
const prettier_1 = __importDefault(require("prettier"));
|
|
31
32
|
async function readJSONFile(path) {
|
|
32
33
|
const content = await fs_1.promises.readFile(path, 'utf-8');
|
|
33
34
|
return JSON.parse(content);
|
|
@@ -54,15 +55,27 @@ function prepareBuildDirectory(directoryPath) {
|
|
|
54
55
|
exports.prepareBuildDirectory = prepareBuildDirectory;
|
|
55
56
|
function writeFile(filePath, code, insertAutoGeneratedNote = true) {
|
|
56
57
|
if (insertAutoGeneratedNote) {
|
|
57
|
-
code =
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
code =
|
|
59
|
+
[
|
|
60
|
+
'/**',
|
|
61
|
+
" * This is auto-generated file, don't modify this file manually",
|
|
62
|
+
' */',
|
|
63
|
+
// '/* eslint-disable max-len */',
|
|
64
|
+
// '/* eslint-disable @typescript-eslint/no-empty-interface */',
|
|
65
|
+
].join(constants_1.newLineChar) +
|
|
66
|
+
constants_1.newLineChar.repeat(2) +
|
|
67
|
+
code.trim();
|
|
64
68
|
}
|
|
65
|
-
|
|
69
|
+
code = prettier_1.default.format(code, {
|
|
70
|
+
semi: true,
|
|
71
|
+
singleQuote: true,
|
|
72
|
+
trailingComma: 'all',
|
|
73
|
+
quoteProps: 'consistent',
|
|
74
|
+
parser: 'typescript',
|
|
75
|
+
});
|
|
76
|
+
fs_1.default.mkdirSync(filePath.replace(path_1.default.basename(filePath), ''), {
|
|
77
|
+
recursive: true,
|
|
78
|
+
});
|
|
66
79
|
fs_1.default.writeFileSync(filePath, code.trim() + constants_1.newLineChar);
|
|
67
80
|
}
|
|
68
81
|
exports.writeFile = writeFile;
|
|
@@ -70,7 +83,10 @@ function prepareMethodsPattern(methodsPattern) {
|
|
|
70
83
|
if (!methodsPattern) {
|
|
71
84
|
log_1.consoleLogErrorAndExit('methodsPattern is empty. Pass "*" to generate all methods');
|
|
72
85
|
}
|
|
73
|
-
return methodsPattern
|
|
86
|
+
return methodsPattern
|
|
87
|
+
.replace(/\s+/g, '')
|
|
88
|
+
.split(',')
|
|
89
|
+
.reduce((acc, pattern) => {
|
|
74
90
|
acc[pattern] = true;
|
|
75
91
|
return acc;
|
|
76
92
|
}, {});
|
|
@@ -95,17 +111,16 @@ function getMethodSection(methodName) {
|
|
|
95
111
|
}
|
|
96
112
|
exports.getMethodSection = getMethodSection;
|
|
97
113
|
function getInterfaceName(name) {
|
|
98
|
-
name = name
|
|
114
|
+
name = name
|
|
115
|
+
.replace(/\.|(\s+)|_/g, ' ')
|
|
99
116
|
.split(' ')
|
|
100
|
-
.map((v) => utils_1.capitalizeFirstLetter(v))
|
|
117
|
+
.map((v) => utils_1.capitalizeFirstLetter(v))
|
|
118
|
+
.join('');
|
|
101
119
|
return utils_1.capitalizeFirstLetter(name);
|
|
102
120
|
}
|
|
103
121
|
exports.getInterfaceName = getInterfaceName;
|
|
104
122
|
function getEnumPropertyName(name) {
|
|
105
|
-
return name.toUpperCase()
|
|
106
|
-
.replace(/\s+/g, '_')
|
|
107
|
-
.replace(/-/g, '_')
|
|
108
|
-
.replace(/\./g, '_');
|
|
123
|
+
return name.toUpperCase().replace(/\s+/g, '_').replace(/-/g, '_').replace(/\./g, '_');
|
|
109
124
|
}
|
|
110
125
|
exports.getEnumPropertyName = getEnumPropertyName;
|
|
111
126
|
function getObjectNameByRef(ref) {
|
|
@@ -149,10 +164,7 @@ function joinCommentLines(indent = 2, ...description) {
|
|
|
149
164
|
];
|
|
150
165
|
}
|
|
151
166
|
else if (Array.isArray(entry)) {
|
|
152
|
-
descriptionLines = [
|
|
153
|
-
...descriptionLines,
|
|
154
|
-
...entry,
|
|
155
|
-
];
|
|
167
|
+
descriptionLines = [...descriptionLines, ...entry];
|
|
156
168
|
}
|
|
157
169
|
});
|
|
158
170
|
descriptionLines = utils_1.trimArray(descriptionLines);
|
|
@@ -181,7 +193,7 @@ function joinOneOfValues(values, primitive) {
|
|
|
181
193
|
}
|
|
182
194
|
exports.joinOneOfValues = joinOneOfValues;
|
|
183
195
|
function formatArrayDepth(value, depth) {
|
|
184
|
-
if (value.endsWith('
|
|
196
|
+
if (value.endsWith("'") || value.includes('|')) {
|
|
185
197
|
return `Array<${value}>` + '[]'.repeat(depth - 1); // Need decrement depth value because of Array<T> has its own depth
|
|
186
198
|
}
|
|
187
199
|
else {
|
package/dist/index.js
CHANGED
|
@@ -24,8 +24,8 @@ const helpMessage = `
|
|
|
24
24
|
|
|
25
25
|
${chalk_1.default.greenBright('--methods')} List of methods to generate responses and all needed objects.
|
|
26
26
|
Example:
|
|
27
|
-
- ${chalk_1.default.bold('
|
|
28
|
-
- ${chalk_1.default.bold('
|
|
27
|
+
- ${chalk_1.default.bold("'*'")} - to generate all responses and objects.
|
|
28
|
+
- ${chalk_1.default.bold("'messages.*, users.get, groups.isMember'")} - to generate all methods from messages section, users.get and groups.isMember.
|
|
29
29
|
`;
|
|
30
30
|
async function main() {
|
|
31
31
|
console.log(chalk_1.default.bold('VK API Schema TypeScript generator'));
|
|
@@ -52,7 +52,7 @@ async function main() {
|
|
|
52
52
|
schemaDir = path_1.default.resolve(schemaDir);
|
|
53
53
|
outDir = outDir ? path_1.default.resolve(outDir) : '';
|
|
54
54
|
// Read and check required schema files
|
|
55
|
-
const [methodsDefinitions, { definitions: responsesDefinitions }, { definitions: objectsDefinitions },] = await Promise.all([
|
|
55
|
+
const [methodsDefinitions, { definitions: responsesDefinitions }, { definitions: objectsDefinitions }, { errors: errorsDefinitions },] = await Promise.all([
|
|
56
56
|
helpers_1.readJSONFile(path_1.default.resolve(schemaDir, 'methods.json')),
|
|
57
57
|
helpers_1.readJSONFile(path_1.default.resolve(schemaDir, 'responses.json')),
|
|
58
58
|
helpers_1.readJSONFile(path_1.default.resolve(schemaDir, 'objects.json')),
|
|
@@ -77,6 +77,7 @@ async function main() {
|
|
|
77
77
|
methodsDefinitions,
|
|
78
78
|
objects: objectsDefinitions,
|
|
79
79
|
responses: responsesDefinitions,
|
|
80
|
+
errors: errorsDefinitions,
|
|
80
81
|
methodsPattern: methods.join(','),
|
|
81
82
|
});
|
|
82
83
|
generator.generate();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vkontakte/api-schema-typescript-generator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "VK API TypeScript generator",
|
|
6
6
|
"author": {
|
|
@@ -33,14 +33,16 @@
|
|
|
33
33
|
"clear": "rimraf dist/*",
|
|
34
34
|
"build": "yarn clear && tsc",
|
|
35
35
|
"watch": "yarn clear && tsc --watch",
|
|
36
|
-
"
|
|
36
|
+
"prettier": "prettier --write \"src/**/*.ts\"",
|
|
37
|
+
"test": "jest && tsc --noEmit && eslint src --ext .ts && yarn prettier"
|
|
37
38
|
},
|
|
38
39
|
"pre-commit": [
|
|
39
40
|
"test"
|
|
40
41
|
],
|
|
41
42
|
"dependencies": {
|
|
42
43
|
"arg": "^4.1.3",
|
|
43
|
-
"chalk": "4.1.0"
|
|
44
|
+
"chalk": "4.1.0",
|
|
45
|
+
"prettier": "^2.7.1"
|
|
44
46
|
},
|
|
45
47
|
"devDependencies": {
|
|
46
48
|
"@types/jest": "^28.1.5",
|
package/src/generator.ts
CHANGED
|
@@ -3,7 +3,11 @@ import { getInterfaceName, getSectionFromObjectName } from './helpers';
|
|
|
3
3
|
import { Dictionary, ObjectType, RefsDictionary, RefsDictionaryType } from './types';
|
|
4
4
|
import { sortArrayAlphabetically, uniqueArray } from './utils';
|
|
5
5
|
|
|
6
|
-
export function generateImportsBlock(
|
|
6
|
+
export function generateImportsBlock(
|
|
7
|
+
refs: RefsDictionary,
|
|
8
|
+
section: string | null,
|
|
9
|
+
type?: ObjectType,
|
|
10
|
+
): string {
|
|
7
11
|
let importRefs = Object.entries(refs)
|
|
8
12
|
.filter(([, type]) => type === RefsDictionaryType.GenerateAndImport)
|
|
9
13
|
.map(([key]) => key);
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import * as Schema from '../types/schema';
|
|
2
|
-
import {
|
|
3
|
-
Dictionary,
|
|
4
|
-
ObjectType,
|
|
5
|
-
RefsDictionary,
|
|
6
|
-
} from '../types';
|
|
2
|
+
import { Dictionary, ObjectType, RefsDictionary } from '../types';
|
|
7
3
|
import { generateEnumAsUnionType } from './enums';
|
|
8
4
|
import { normalizeMethodInfo } from './methods';
|
|
9
5
|
import { SchemaObject } from './SchemaObject';
|
|
10
6
|
import {
|
|
11
|
-
getInterfaceName,
|
|
12
|
-
|
|
7
|
+
getInterfaceName,
|
|
8
|
+
getMethodSection,
|
|
9
|
+
getObjectNameByRef,
|
|
10
|
+
getSectionFromObjectName,
|
|
11
|
+
isMethodNeeded,
|
|
12
|
+
isPatternProperty,
|
|
13
|
+
prepareBuildDirectory,
|
|
13
14
|
prepareMethodsPattern,
|
|
14
15
|
writeFile,
|
|
15
16
|
} from '../helpers';
|
|
@@ -20,7 +21,8 @@ import {
|
|
|
20
21
|
baseAPIParamsInterfaceName,
|
|
21
22
|
baseBoolIntRef,
|
|
22
23
|
baseOkResponseRef,
|
|
23
|
-
basePropertyExistsRef,
|
|
24
|
+
basePropertyExistsRef,
|
|
25
|
+
DEFAULT_API_VERSION,
|
|
24
26
|
newLineChar,
|
|
25
27
|
} from '../constants';
|
|
26
28
|
import path from 'path';
|
|
@@ -28,6 +30,7 @@ import { CommentCodeBlock } from './CommentCodeBlock';
|
|
|
28
30
|
import { consoleLogError, consoleLogErrorAndExit, consoleLogInfo } from '../log';
|
|
29
31
|
import { generateImportsBlock } from '../generator';
|
|
30
32
|
import { generateTypeString } from './typeString';
|
|
33
|
+
import { ErrorInterface } from '../types/schema';
|
|
31
34
|
|
|
32
35
|
interface APITypingsGeneratorOptions {
|
|
33
36
|
needEmit: boolean;
|
|
@@ -47,6 +50,7 @@ interface APITypingsGeneratorOptions {
|
|
|
47
50
|
methodsDefinitions: Schema.API;
|
|
48
51
|
objects: Dictionary<SchemaObject>;
|
|
49
52
|
responses: Dictionary<SchemaObject>;
|
|
53
|
+
errors: Dictionary<ErrorInterface>;
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
export class APITypingsGenerator {
|
|
@@ -59,6 +63,7 @@ export class APITypingsGenerator {
|
|
|
59
63
|
this.methodsList = options.methodsDefinitions.methods || [];
|
|
60
64
|
this.objects = this.convertJSONSchemaDictionary(options.objects);
|
|
61
65
|
this.responses = this.convertJSONSchemaDictionary(options.responses);
|
|
66
|
+
this.errors = options.errors;
|
|
62
67
|
|
|
63
68
|
this.visitedRefs = {};
|
|
64
69
|
this.generatedObjects = {};
|
|
@@ -68,7 +73,7 @@ export class APITypingsGenerator {
|
|
|
68
73
|
|
|
69
74
|
this.ignoredResponses = {
|
|
70
75
|
'storage.get': {
|
|
71
|
-
|
|
76
|
+
keysResponse: true,
|
|
72
77
|
},
|
|
73
78
|
};
|
|
74
79
|
|
|
@@ -83,6 +88,7 @@ export class APITypingsGenerator {
|
|
|
83
88
|
methodsList!: NonNullable<Schema.API['methods']>;
|
|
84
89
|
objects!: Dictionary<SchemaObject>;
|
|
85
90
|
responses!: Dictionary<SchemaObject>;
|
|
91
|
+
errors!: Dictionary<ErrorInterface>;
|
|
86
92
|
|
|
87
93
|
visitedRefs!: Dictionary<boolean>;
|
|
88
94
|
generatedObjects!: Dictionary<boolean>;
|
|
@@ -129,10 +135,7 @@ export class APITypingsGenerator {
|
|
|
129
135
|
...methodFile.imports,
|
|
130
136
|
...imports,
|
|
131
137
|
},
|
|
132
|
-
codeBlocks: [
|
|
133
|
-
...methodFile.codeBlocks,
|
|
134
|
-
...codeBlocks,
|
|
135
|
-
],
|
|
138
|
+
codeBlocks: [...methodFile.codeBlocks, ...codeBlocks],
|
|
136
139
|
};
|
|
137
140
|
}
|
|
138
141
|
|
|
@@ -192,10 +195,7 @@ export class APITypingsGenerator {
|
|
|
192
195
|
}
|
|
193
196
|
|
|
194
197
|
if (additionalProperties.length) {
|
|
195
|
-
properties = [
|
|
196
|
-
...properties,
|
|
197
|
-
...additionalProperties,
|
|
198
|
-
];
|
|
198
|
+
properties = [...properties, ...additionalProperties];
|
|
199
199
|
}
|
|
200
200
|
});
|
|
201
201
|
}
|
|
@@ -265,10 +265,7 @@ export class APITypingsGenerator {
|
|
|
265
265
|
});
|
|
266
266
|
|
|
267
267
|
return {
|
|
268
|
-
codeBlocks: [
|
|
269
|
-
...codeBlocks,
|
|
270
|
-
codeBlock,
|
|
271
|
-
],
|
|
268
|
+
codeBlocks: [...codeBlocks, codeBlock],
|
|
272
269
|
imports,
|
|
273
270
|
value: '',
|
|
274
271
|
};
|
|
@@ -338,12 +335,18 @@ export class APITypingsGenerator {
|
|
|
338
335
|
|
|
339
336
|
if (stringCodeBlocks.length > 0) {
|
|
340
337
|
const code = stringCodeBlocks.join(newLineChar.repeat(2));
|
|
341
|
-
this.registerResultFile(
|
|
338
|
+
this.registerResultFile(
|
|
339
|
+
path.join('objects', section, `${getInterfaceName(object.name)}.ts`),
|
|
340
|
+
code,
|
|
341
|
+
);
|
|
342
342
|
}
|
|
343
343
|
|
|
344
344
|
codeBlocks.forEach((codeBlock) => {
|
|
345
345
|
if (codeBlock instanceof TypeCodeBlock && codeBlock.needExport && codeBlock.interfaceName) {
|
|
346
|
-
this.registerExport(
|
|
346
|
+
this.registerExport(
|
|
347
|
+
`./objects/${section}/${getInterfaceName(object.name)}.ts`,
|
|
348
|
+
codeBlock.interfaceName,
|
|
349
|
+
);
|
|
347
350
|
}
|
|
348
351
|
});
|
|
349
352
|
|
|
@@ -399,7 +402,9 @@ export class APITypingsGenerator {
|
|
|
399
402
|
imports: newImports,
|
|
400
403
|
value,
|
|
401
404
|
codeBlocks: newCodeBlocks,
|
|
402
|
-
} = generateTypeString(property, this.objects, {
|
|
405
|
+
} = generateTypeString(property, this.objects, {
|
|
406
|
+
needEnumNamesConstant: false,
|
|
407
|
+
});
|
|
403
408
|
|
|
404
409
|
imports = { ...imports, ...newImports };
|
|
405
410
|
codeBlocks = [...codeBlocks, ...newCodeBlocks];
|
|
@@ -432,11 +437,13 @@ export class APITypingsGenerator {
|
|
|
432
437
|
|
|
433
438
|
if (object.enum) {
|
|
434
439
|
const { codeBlocks: newCodeBlocks } = generateEnumAsUnionType(object);
|
|
435
|
-
codeBlocks = [
|
|
436
|
-
...newCodeBlocks,
|
|
437
|
-
];
|
|
440
|
+
codeBlocks = [...newCodeBlocks];
|
|
438
441
|
} else {
|
|
439
|
-
const {
|
|
442
|
+
const {
|
|
443
|
+
imports: newImports,
|
|
444
|
+
value,
|
|
445
|
+
codeBlocks: newCodeBlocks,
|
|
446
|
+
} = generateTypeString(object, this.objects);
|
|
440
447
|
const codeBlock = new TypeCodeBlock({
|
|
441
448
|
type: TypeScriptCodeTypes.Type,
|
|
442
449
|
refName: object.name,
|
|
@@ -448,11 +455,7 @@ export class APITypingsGenerator {
|
|
|
448
455
|
});
|
|
449
456
|
|
|
450
457
|
imports = newImports;
|
|
451
|
-
codeBlocks = [
|
|
452
|
-
...codeBlocks,
|
|
453
|
-
...newCodeBlocks,
|
|
454
|
-
codeBlock,
|
|
455
|
-
];
|
|
458
|
+
codeBlocks = [...codeBlocks, ...newCodeBlocks, codeBlock];
|
|
456
459
|
}
|
|
457
460
|
|
|
458
461
|
return {
|
|
@@ -462,13 +465,11 @@ export class APITypingsGenerator {
|
|
|
462
465
|
};
|
|
463
466
|
}
|
|
464
467
|
|
|
465
|
-
private getResponseCodeBlockAsType(
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
description,
|
|
471
|
-
} = generateTypeString(response, this.objects, {
|
|
468
|
+
private getResponseCodeBlockAsType(
|
|
469
|
+
object: SchemaObject,
|
|
470
|
+
response: SchemaObject,
|
|
471
|
+
): GeneratorResultInterface | false {
|
|
472
|
+
const { imports, value, codeBlocks, description } = generateTypeString(response, this.objects, {
|
|
472
473
|
objectParentName: ' ', // TODO: Refactor
|
|
473
474
|
});
|
|
474
475
|
|
|
@@ -476,20 +477,14 @@ export class APITypingsGenerator {
|
|
|
476
477
|
type: TypeScriptCodeTypes.Type,
|
|
477
478
|
refName: object.name,
|
|
478
479
|
interfaceName: getInterfaceName(object.name),
|
|
479
|
-
description: [
|
|
480
|
-
object.description,
|
|
481
|
-
description || '',
|
|
482
|
-
].join(newLineChar),
|
|
480
|
+
description: [object.description, description || ''].join(newLineChar),
|
|
483
481
|
needExport: true,
|
|
484
482
|
properties: [],
|
|
485
483
|
value,
|
|
486
484
|
});
|
|
487
485
|
|
|
488
486
|
return {
|
|
489
|
-
codeBlocks: [
|
|
490
|
-
...codeBlocks,
|
|
491
|
-
codeBlock,
|
|
492
|
-
],
|
|
487
|
+
codeBlocks: [...codeBlocks, codeBlock],
|
|
493
488
|
imports,
|
|
494
489
|
value: '',
|
|
495
490
|
description,
|
|
@@ -576,17 +571,11 @@ export class APITypingsGenerator {
|
|
|
576
571
|
// Comment with method name for visual sections in file
|
|
577
572
|
const methodNameComment = new CommentCodeBlock([methodName]);
|
|
578
573
|
if (method.description) {
|
|
579
|
-
methodNameComment.appendLines([
|
|
580
|
-
'',
|
|
581
|
-
method.description,
|
|
582
|
-
]);
|
|
574
|
+
methodNameComment.appendLines(['', method.description]);
|
|
583
575
|
}
|
|
584
576
|
this.appendToFileMap(section, {}, [methodNameComment]);
|
|
585
577
|
|
|
586
|
-
const {
|
|
587
|
-
method: normalizedMethod,
|
|
588
|
-
parameterRefs,
|
|
589
|
-
} = normalizeMethodInfo(method);
|
|
578
|
+
const { method: normalizedMethod, parameterRefs } = normalizeMethodInfo(method);
|
|
590
579
|
|
|
591
580
|
method = normalizedMethod;
|
|
592
581
|
this.generateObjectsFromRefs(parameterRefs);
|
|
@@ -619,15 +608,48 @@ export class APITypingsGenerator {
|
|
|
619
608
|
this.registerExport(`./methods/${section}`, codeBlock.interfaceName);
|
|
620
609
|
}
|
|
621
610
|
});
|
|
622
|
-
const code = [
|
|
623
|
-
generateImportsBlock(imports, null),
|
|
624
|
-
...codeBlocks,
|
|
625
|
-
];
|
|
611
|
+
const code = [generateImportsBlock(imports, null), ...codeBlocks];
|
|
626
612
|
|
|
627
|
-
this.registerResultFile(
|
|
613
|
+
this.registerResultFile(
|
|
614
|
+
path.join('methods', `${section}.ts`),
|
|
615
|
+
code.join(newLineChar.repeat(2)),
|
|
616
|
+
);
|
|
628
617
|
});
|
|
629
618
|
}
|
|
630
619
|
|
|
620
|
+
private generateErrors() {
|
|
621
|
+
consoleLogInfo('creating errors...');
|
|
622
|
+
|
|
623
|
+
const code: string[] = [];
|
|
624
|
+
|
|
625
|
+
Object.entries(this.errors)
|
|
626
|
+
.reduce<Array<ErrorInterface & { name: string }>>((acc, [name, error]) => {
|
|
627
|
+
acc.push({ name, ...error });
|
|
628
|
+
return acc;
|
|
629
|
+
}, [])
|
|
630
|
+
.sort((errorA, errorB) => {
|
|
631
|
+
return errorA.code - errorB.code;
|
|
632
|
+
})
|
|
633
|
+
.forEach((error) => {
|
|
634
|
+
const errorConstantName = error.name.toUpperCase();
|
|
635
|
+
|
|
636
|
+
code.push(
|
|
637
|
+
new TypeCodeBlock({
|
|
638
|
+
type: TypeScriptCodeTypes.Const,
|
|
639
|
+
interfaceName: errorConstantName,
|
|
640
|
+
needExport: true,
|
|
641
|
+
value: String(error.code),
|
|
642
|
+
properties: [],
|
|
643
|
+
description: [error.description, error.$comment || ''].join(newLineChar.repeat(2)),
|
|
644
|
+
}).toString(),
|
|
645
|
+
);
|
|
646
|
+
|
|
647
|
+
this.registerExport('./common/errors', errorConstantName);
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
this.registerResultFile(path.join('common', 'errors.ts'), code.join(newLineChar.repeat(2)));
|
|
651
|
+
}
|
|
652
|
+
|
|
631
653
|
private createCommonTypes() {
|
|
632
654
|
consoleLogInfo('creating common types...');
|
|
633
655
|
const code: string[] = [];
|
|
@@ -708,6 +730,7 @@ export class APITypingsGenerator {
|
|
|
708
730
|
consoleLogInfo('generate');
|
|
709
731
|
|
|
710
732
|
this.generateMethods();
|
|
733
|
+
this.generateErrors();
|
|
711
734
|
|
|
712
735
|
if (this.needEmit) {
|
|
713
736
|
this.createCommonTypes();
|
|
@@ -10,19 +10,12 @@ export class CommentCodeBlock extends BaseCodeBlock {
|
|
|
10
10
|
lines: string[];
|
|
11
11
|
|
|
12
12
|
appendLines(lines: string[]) {
|
|
13
|
-
this.lines = [
|
|
14
|
-
...this.lines,
|
|
15
|
-
...lines,
|
|
16
|
-
];
|
|
13
|
+
this.lines = [...this.lines, ...lines];
|
|
17
14
|
}
|
|
18
15
|
|
|
19
16
|
toString(): string {
|
|
20
17
|
const inner = this.lines.map((line) => spaceChar + `* ${line}`.trim());
|
|
21
18
|
|
|
22
|
-
return [
|
|
23
|
-
'/**',
|
|
24
|
-
...inner,
|
|
25
|
-
' */',
|
|
26
|
-
].join(newLineChar);
|
|
19
|
+
return ['/**', ...inner, ' */'].join(newLineChar);
|
|
27
20
|
}
|
|
28
21
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { EnumLikeArray } from '../types';
|
|
2
2
|
import { isObject, isString } from '../utils';
|
|
3
|
-
import {
|
|
4
|
-
transformPatternPropertyName,
|
|
5
|
-
} from '../helpers';
|
|
3
|
+
import { transformPatternPropertyName } from '../helpers';
|
|
6
4
|
import { consoleLogErrorAndExit } from '../log';
|
|
7
5
|
|
|
8
6
|
export class SchemaObject {
|
|
9
7
|
constructor(name: string, object: any, parentName?: string) {
|
|
10
8
|
if (!isObject(object)) {
|
|
11
|
-
consoleLogErrorAndExit(`[SchemaObject] "${name}" is not an object.`, {
|
|
9
|
+
consoleLogErrorAndExit(`[SchemaObject] "${name}" is not an object.`, {
|
|
10
|
+
name,
|
|
11
|
+
object,
|
|
12
|
+
parentName,
|
|
13
|
+
});
|
|
12
14
|
return;
|
|
13
15
|
}
|
|
14
16
|
|
|
@@ -67,9 +69,13 @@ export class SchemaObject {
|
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
if (object.patternProperties) {
|
|
70
|
-
Object.entries(object.patternProperties).forEach(
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
Object.entries(object.patternProperties).forEach(
|
|
73
|
+
([propertyName, property]: [string, any]) => {
|
|
74
|
+
this.properties.push(
|
|
75
|
+
new SchemaObject(transformPatternPropertyName(propertyName), property, name),
|
|
76
|
+
);
|
|
77
|
+
},
|
|
78
|
+
);
|
|
73
79
|
}
|
|
74
80
|
|
|
75
81
|
if (isObject(object.items)) {
|
|
@@ -112,6 +118,9 @@ export class SchemaObject {
|
|
|
112
118
|
}
|
|
113
119
|
|
|
114
120
|
public clone() {
|
|
115
|
-
return Object.assign(
|
|
121
|
+
return Object.assign(
|
|
122
|
+
Object.create(Object.getPrototypeOf(this)),
|
|
123
|
+
this,
|
|
124
|
+
) as NonNullable<SchemaObject>;
|
|
116
125
|
}
|
|
117
126
|
}
|
|
@@ -9,6 +9,7 @@ export enum TypeScriptCodeTypes {
|
|
|
9
9
|
Enum = 'enum',
|
|
10
10
|
ConstantObject = 'constant_object',
|
|
11
11
|
Type = 'type',
|
|
12
|
+
Const = 'const',
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export interface TypeCodeBlockProperty {
|
|
@@ -63,41 +64,45 @@ export class TypeCodeBlock extends BaseCodeBlock {
|
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
private getPropertiesCode() {
|
|
66
|
-
const quoteChar = this.properties.some((property) => areQuotesNeededForProperty(property.name))
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
67
|
+
const quoteChar = this.properties.some((property) => areQuotesNeededForProperty(property.name))
|
|
68
|
+
? "'"
|
|
69
|
+
: '';
|
|
70
|
+
|
|
71
|
+
return this.properties
|
|
72
|
+
.map((property) => {
|
|
73
|
+
let divider = '';
|
|
74
|
+
let lineEnd = '';
|
|
75
|
+
|
|
76
|
+
switch (this.type) {
|
|
77
|
+
case TypeScriptCodeTypes.Interface:
|
|
78
|
+
divider = property.isRequired ? ':' : '?:';
|
|
79
|
+
lineEnd = ';';
|
|
80
|
+
break;
|
|
81
|
+
case TypeScriptCodeTypes.ConstantObject:
|
|
82
|
+
divider = ':';
|
|
83
|
+
lineEnd = ',';
|
|
84
|
+
break;
|
|
85
|
+
case TypeScriptCodeTypes.Enum:
|
|
86
|
+
divider = ' =';
|
|
87
|
+
lineEnd = ',';
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
86
90
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
let value = property.wrapValue ? quoteJavaScriptValue(property.value) : property.value;
|
|
92
|
+
let propertyCode = [
|
|
93
|
+
` ${quoteChar}${property.name}${quoteChar}${divider} ${value}${lineEnd}`,
|
|
94
|
+
];
|
|
91
95
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
if (property.description) {
|
|
97
|
+
const commentLines = joinCommentLines(2, property.description);
|
|
98
|
+
if (commentLines.length) {
|
|
99
|
+
propertyCode.unshift(commentLines.join(newLineChar));
|
|
100
|
+
}
|
|
96
101
|
}
|
|
97
|
-
}
|
|
98
102
|
|
|
99
|
-
|
|
100
|
-
|
|
103
|
+
return propertyCode.join(newLineChar);
|
|
104
|
+
})
|
|
105
|
+
.join(newLineChar);
|
|
101
106
|
}
|
|
102
107
|
|
|
103
108
|
toString(): string {
|
|
@@ -113,10 +118,7 @@ export class TypeCodeBlock extends BaseCodeBlock {
|
|
|
113
118
|
}
|
|
114
119
|
|
|
115
120
|
if (this.description) {
|
|
116
|
-
before = [
|
|
117
|
-
...before,
|
|
118
|
-
...joinCommentLines(0, this.description),
|
|
119
|
-
];
|
|
121
|
+
before = [...before, ...joinCommentLines(0, this.description)];
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
switch (this.type) {
|
|
@@ -125,19 +127,19 @@ export class TypeCodeBlock extends BaseCodeBlock {
|
|
|
125
127
|
if (this.options.allowEmptyInterface) {
|
|
126
128
|
propertiesCode = '';
|
|
127
129
|
} else {
|
|
128
|
-
propertiesCode = [
|
|
129
|
-
' // empty interface',
|
|
130
|
-
' [key: string]: any;',
|
|
131
|
-
].join(newLineChar);
|
|
130
|
+
propertiesCode = [' // empty interface', ' [key: string]: any;'].join(newLineChar);
|
|
132
131
|
}
|
|
133
132
|
}
|
|
134
133
|
|
|
135
|
-
const extendsInterfaces =
|
|
136
|
-
this.extendsInterfaces
|
|
137
|
-
|
|
134
|
+
const extendsInterfaces =
|
|
135
|
+
Array.isArray(this.extendsInterfaces) && this.extendsInterfaces.length
|
|
136
|
+
? this.extendsInterfaces.join(', ')
|
|
137
|
+
: '';
|
|
138
138
|
|
|
139
139
|
code = [
|
|
140
|
-
trimStringDoubleSpaces(
|
|
140
|
+
trimStringDoubleSpaces(
|
|
141
|
+
`${exportKeyword} interface ${this.interfaceName} ${extendsInterfaces} {`,
|
|
142
|
+
),
|
|
141
143
|
propertiesCode,
|
|
142
144
|
'}',
|
|
143
145
|
].join(propertiesCode.length ? newLineChar : '');
|
|
@@ -169,11 +171,18 @@ export class TypeCodeBlock extends BaseCodeBlock {
|
|
|
169
171
|
trimStringDoubleSpaces(`${exportKeyword} type ${this.interfaceName} = ${this.value};`),
|
|
170
172
|
].join(newLineChar);
|
|
171
173
|
break;
|
|
174
|
+
|
|
175
|
+
case TypeScriptCodeTypes.Const:
|
|
176
|
+
if (!this.value) {
|
|
177
|
+
consoleLogErrorAndExit(`"${this.interfaceName}" type has empty value`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
code = [
|
|
181
|
+
trimStringDoubleSpaces(`${exportKeyword} const ${this.interfaceName} = ${this.value};`),
|
|
182
|
+
].join(newLineChar);
|
|
183
|
+
break;
|
|
172
184
|
}
|
|
173
185
|
|
|
174
|
-
return [
|
|
175
|
-
before.join(newLineChar),
|
|
176
|
-
code,
|
|
177
|
-
].join(newLineChar).trim();
|
|
186
|
+
return [before.join(newLineChar), code].join(newLineChar).trim();
|
|
178
187
|
}
|
|
179
188
|
}
|
package/src/generators/enums.ts
CHANGED
|
@@ -18,7 +18,11 @@ export function getEnumNamesIdentifier(name: string) {
|
|
|
18
18
|
return `${name} enumNames`.trim();
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export function generateEnumConstantObject(
|
|
21
|
+
export function generateEnumConstantObject(
|
|
22
|
+
object: SchemaObject,
|
|
23
|
+
objectName: string,
|
|
24
|
+
enumNames: Array<string | number>,
|
|
25
|
+
) {
|
|
22
26
|
const enumInterfaceName = getInterfaceName(objectName);
|
|
23
27
|
|
|
24
28
|
const codeBlock = new TypeCodeBlock({
|
|
@@ -52,10 +56,7 @@ export function generateEnumAsUnionType(object: SchemaObject): GeneratorResultIn
|
|
|
52
56
|
type: TypeScriptCodeTypes.Type,
|
|
53
57
|
refName: object.name,
|
|
54
58
|
interfaceName: getInterfaceName(object.name),
|
|
55
|
-
description: [
|
|
56
|
-
object.description,
|
|
57
|
-
description,
|
|
58
|
-
].join(newLineChar),
|
|
59
|
+
description: [object.description, description].join(newLineChar),
|
|
59
60
|
needExport: true,
|
|
60
61
|
properties: [],
|
|
61
62
|
value,
|
|
@@ -97,12 +98,11 @@ interface GenerateInlineEnumOptions {
|
|
|
97
98
|
refName?: string;
|
|
98
99
|
}
|
|
99
100
|
|
|
100
|
-
export function generateInlineEnum(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
} = getEnumNames(object);
|
|
101
|
+
export function generateInlineEnum(
|
|
102
|
+
object: SchemaObject,
|
|
103
|
+
options: GenerateInlineEnumOptions = {},
|
|
104
|
+
): GeneratorResultInterface {
|
|
105
|
+
const { isNumericEnum, enumNames, needEnumNamesDescription } = getEnumNames(object);
|
|
106
106
|
|
|
107
107
|
options = {
|
|
108
108
|
needEnumNamesConstant: isNumericEnum,
|
|
@@ -37,9 +37,9 @@ export function normalizeMethodInfo(method: Schema.Method): NormalizeMethodInfoR
|
|
|
37
37
|
const ref = parameter.items?.$ref;
|
|
38
38
|
parameterRefs[ref] = RefsDictionaryType.Generate;
|
|
39
39
|
|
|
40
|
-
parameter.description +=
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
parameter.description +=
|
|
41
|
+
newLineChar.repeat(2) +
|
|
42
|
+
[`@see ${getInterfaceName(getObjectNameByRef(ref))} (${ref})`].join(newLineChar);
|
|
43
43
|
}
|
|
44
44
|
});
|
|
45
45
|
|
|
@@ -28,7 +28,10 @@ interface GenerateTypeStringOptions {
|
|
|
28
28
|
needEnumNamesConstant?: boolean;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
function generateBaseType(
|
|
31
|
+
function generateBaseType(
|
|
32
|
+
object: SchemaObject,
|
|
33
|
+
options: GenerateTypeStringOptions,
|
|
34
|
+
): GeneratorResultInterface {
|
|
32
35
|
let codeBlocks: CodeBlocksArray = [];
|
|
33
36
|
let typeString = 'any /* default type */';
|
|
34
37
|
let imports: RefsDictionary = {};
|
package/src/helpers.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { capitalizeFirstLetter, trimArray } from './utils';
|
|
|
4
4
|
import { newLineChar, primitiveTypes, spaceChar } from './constants';
|
|
5
5
|
import { Dictionary } from './types';
|
|
6
6
|
import { consoleLogErrorAndExit } from './log';
|
|
7
|
+
import prettier from 'prettier';
|
|
7
8
|
|
|
8
9
|
export async function readJSONFile(path: string): Promise<any> {
|
|
9
10
|
const content = await fsPromises.readFile(path, 'utf-8');
|
|
@@ -31,16 +32,29 @@ export function prepareBuildDirectory(directoryPath: string) {
|
|
|
31
32
|
|
|
32
33
|
export function writeFile(filePath: string, code: string, insertAutoGeneratedNote = true) {
|
|
33
34
|
if (insertAutoGeneratedNote) {
|
|
34
|
-
code =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
code =
|
|
36
|
+
[
|
|
37
|
+
'/**',
|
|
38
|
+
" * This is auto-generated file, don't modify this file manually",
|
|
39
|
+
' */',
|
|
40
|
+
// '/* eslint-disable max-len */',
|
|
41
|
+
// '/* eslint-disable @typescript-eslint/no-empty-interface */',
|
|
42
|
+
].join(newLineChar) +
|
|
43
|
+
newLineChar.repeat(2) +
|
|
44
|
+
code.trim();
|
|
41
45
|
}
|
|
42
46
|
|
|
43
|
-
|
|
47
|
+
code = prettier.format(code, {
|
|
48
|
+
semi: true,
|
|
49
|
+
singleQuote: true,
|
|
50
|
+
trailingComma: 'all',
|
|
51
|
+
quoteProps: 'consistent',
|
|
52
|
+
parser: 'typescript',
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
fs.mkdirSync(filePath.replace(path.basename(filePath), ''), {
|
|
56
|
+
recursive: true,
|
|
57
|
+
});
|
|
44
58
|
fs.writeFileSync(filePath, code.trim() + newLineChar);
|
|
45
59
|
}
|
|
46
60
|
|
|
@@ -49,10 +63,13 @@ export function prepareMethodsPattern(methodsPattern: string): Dictionary<boolea
|
|
|
49
63
|
consoleLogErrorAndExit('methodsPattern is empty. Pass "*" to generate all methods');
|
|
50
64
|
}
|
|
51
65
|
|
|
52
|
-
return methodsPattern
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
66
|
+
return methodsPattern
|
|
67
|
+
.replace(/\s+/g, '')
|
|
68
|
+
.split(',')
|
|
69
|
+
.reduce<Dictionary<boolean>>((acc, pattern) => {
|
|
70
|
+
acc[pattern] = true;
|
|
71
|
+
return acc;
|
|
72
|
+
}, {});
|
|
56
73
|
}
|
|
57
74
|
|
|
58
75
|
export function isMethodNeeded(methodsPattern: Dictionary<boolean>, method: string): boolean {
|
|
@@ -77,18 +94,17 @@ export function getMethodSection(methodName: string): string {
|
|
|
77
94
|
}
|
|
78
95
|
|
|
79
96
|
export function getInterfaceName(name: string): string {
|
|
80
|
-
name = name
|
|
97
|
+
name = name
|
|
98
|
+
.replace(/\.|(\s+)|_/g, ' ')
|
|
81
99
|
.split(' ')
|
|
82
|
-
.map((v) => capitalizeFirstLetter(v))
|
|
100
|
+
.map((v) => capitalizeFirstLetter(v))
|
|
101
|
+
.join('');
|
|
83
102
|
|
|
84
103
|
return capitalizeFirstLetter(name);
|
|
85
104
|
}
|
|
86
105
|
|
|
87
106
|
export function getEnumPropertyName(name: string): string {
|
|
88
|
-
return name.toUpperCase()
|
|
89
|
-
.replace(/\s+/g, '_')
|
|
90
|
-
.replace(/-/g, '_')
|
|
91
|
-
.replace(/\./g, '_');
|
|
107
|
+
return name.toUpperCase().replace(/\s+/g, '_').replace(/-/g, '_').replace(/\./g, '_');
|
|
92
108
|
}
|
|
93
109
|
|
|
94
110
|
export function getObjectNameByRef(ref: string): string {
|
|
@@ -136,10 +152,7 @@ export function joinCommentLines(indent = 2, ...description: Array<string | stri
|
|
|
136
152
|
...trimArray((entry || '').trim().split(newLineChar)),
|
|
137
153
|
];
|
|
138
154
|
} else if (Array.isArray(entry)) {
|
|
139
|
-
descriptionLines = [
|
|
140
|
-
...descriptionLines,
|
|
141
|
-
...entry,
|
|
142
|
-
];
|
|
155
|
+
descriptionLines = [...descriptionLines, ...entry];
|
|
143
156
|
}
|
|
144
157
|
});
|
|
145
158
|
|
|
@@ -171,7 +184,7 @@ export function joinOneOfValues(values: Array<string | number>, primitive?: bool
|
|
|
171
184
|
}
|
|
172
185
|
|
|
173
186
|
export function formatArrayDepth(value: string, depth: number) {
|
|
174
|
-
if (value.endsWith('
|
|
187
|
+
if (value.endsWith("'") || value.includes('|')) {
|
|
175
188
|
return `Array<${value}>` + '[]'.repeat(depth - 1); // Need decrement depth value because of Array<T> has its own depth
|
|
176
189
|
} else {
|
|
177
190
|
return value + '[]'.repeat(depth);
|
package/src/index.ts
CHANGED
|
@@ -11,16 +11,24 @@ const helpMessage = `
|
|
|
11
11
|
|
|
12
12
|
${chalk.greenBright('--help')} Shows this help.
|
|
13
13
|
|
|
14
|
-
${chalk.greenBright('--schemaDir')} The relative path to directory with ${chalk.bold(
|
|
14
|
+
${chalk.greenBright('--schemaDir')} The relative path to directory with ${chalk.bold(
|
|
15
|
+
'methods.json',
|
|
16
|
+
)}, ${chalk.bold('objects.json')} and ${chalk.bold('responses.json')} files.
|
|
15
17
|
|
|
16
18
|
${chalk.greenBright('--outDir')} The directory where the files will be generated.
|
|
17
19
|
If you skip this param, script will work in linter mode without emitting files to file system.
|
|
18
|
-
${chalk.bold(
|
|
20
|
+
${chalk.bold(
|
|
21
|
+
'Please note',
|
|
22
|
+
)} that this folder will be cleared after starting the generation.
|
|
19
23
|
|
|
20
|
-
${chalk.greenBright(
|
|
24
|
+
${chalk.greenBright(
|
|
25
|
+
'--methods',
|
|
26
|
+
)} List of methods to generate responses and all needed objects.
|
|
21
27
|
Example:
|
|
22
|
-
- ${chalk.bold('
|
|
23
|
-
- ${chalk.bold(
|
|
28
|
+
- ${chalk.bold("'*'")} - to generate all responses and objects.
|
|
29
|
+
- ${chalk.bold(
|
|
30
|
+
"'messages.*, users.get, groups.isMember'",
|
|
31
|
+
)} - to generate all methods from messages section, users.get and groups.isMember.
|
|
24
32
|
`;
|
|
25
33
|
|
|
26
34
|
export async function main() {
|
|
@@ -61,6 +69,7 @@ export async function main() {
|
|
|
61
69
|
methodsDefinitions,
|
|
62
70
|
{ definitions: responsesDefinitions },
|
|
63
71
|
{ definitions: objectsDefinitions },
|
|
72
|
+
{ errors: errorsDefinitions },
|
|
64
73
|
] = await Promise.all([
|
|
65
74
|
readJSONFile(path.resolve(schemaDir, 'methods.json')),
|
|
66
75
|
readJSONFile(path.resolve(schemaDir, 'responses.json')),
|
|
@@ -91,6 +100,7 @@ export async function main() {
|
|
|
91
100
|
methodsDefinitions,
|
|
92
101
|
objects: objectsDefinitions,
|
|
93
102
|
responses: responsesDefinitions,
|
|
103
|
+
errors: errorsDefinitions,
|
|
94
104
|
methodsPattern: methods.join(','),
|
|
95
105
|
});
|
|
96
106
|
|
package/src/types/schema.ts
CHANGED