@goast/typescript 0.0.1

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.
Files changed (70) hide show
  1. package/README.md +11 -0
  2. package/assets/client/fetch/types.ts +9 -0
  3. package/assets/client/fetch/url-builder.ts +36 -0
  4. package/assets/service/angular/api-configuration.ts +18 -0
  5. package/assets/service/angular/base-service.ts +29 -0
  6. package/assets/service/angular/request-builder.ts +368 -0
  7. package/assets/service/angular/response-handlers.ts +127 -0
  8. package/assets/service/angular/strict-http-response.ts +8 -0
  9. package/cjs/index.js +9 -0
  10. package/cjs/lib/common-results.js +2 -0
  11. package/cjs/lib/config.js +15 -0
  12. package/cjs/lib/file-builder.js +244 -0
  13. package/cjs/lib/generators/file-generator.js +26 -0
  14. package/cjs/lib/generators/index.js +7 -0
  15. package/cjs/lib/generators/models/index.js +6 -0
  16. package/cjs/lib/generators/models/model-generator.js +317 -0
  17. package/cjs/lib/generators/models/models-generator.js +67 -0
  18. package/cjs/lib/generators/models/models.js +6 -0
  19. package/cjs/lib/generators/services/angular-services/angular-service-generator.js +360 -0
  20. package/cjs/lib/generators/services/angular-services/angular-services-generator.js +122 -0
  21. package/cjs/lib/generators/services/angular-services/index.js +6 -0
  22. package/cjs/lib/generators/services/angular-services/models.js +5 -0
  23. package/cjs/lib/generators/services/fetch-clients/fetch-client-generator.js +240 -0
  24. package/cjs/lib/generators/services/fetch-clients/fetch-clients-generator.js +83 -0
  25. package/cjs/lib/generators/services/fetch-clients/index.js +6 -0
  26. package/cjs/lib/generators/services/fetch-clients/models.js +5 -0
  27. package/cjs/lib/import-collection.js +116 -0
  28. package/cjs/lib/utils.js +56 -0
  29. package/cjs/package.json +1 -0
  30. package/esm/index.js +6 -0
  31. package/esm/lib/common-results.js +1 -0
  32. package/esm/lib/config.js +12 -0
  33. package/esm/lib/file-builder.js +240 -0
  34. package/esm/lib/generators/file-generator.js +22 -0
  35. package/esm/lib/generators/index.js +4 -0
  36. package/esm/lib/generators/models/index.js +3 -0
  37. package/esm/lib/generators/models/model-generator.js +313 -0
  38. package/esm/lib/generators/models/models-generator.js +63 -0
  39. package/esm/lib/generators/models/models.js +3 -0
  40. package/esm/lib/generators/services/angular-services/angular-service-generator.js +356 -0
  41. package/esm/lib/generators/services/angular-services/angular-services-generator.js +118 -0
  42. package/esm/lib/generators/services/angular-services/index.js +3 -0
  43. package/esm/lib/generators/services/angular-services/models.js +2 -0
  44. package/esm/lib/generators/services/fetch-clients/fetch-client-generator.js +236 -0
  45. package/esm/lib/generators/services/fetch-clients/fetch-clients-generator.js +79 -0
  46. package/esm/lib/generators/services/fetch-clients/index.js +3 -0
  47. package/esm/lib/generators/services/fetch-clients/models.js +2 -0
  48. package/esm/lib/import-collection.js +112 -0
  49. package/esm/lib/utils.js +48 -0
  50. package/package.json +29 -0
  51. package/types/index.d.ts +6 -0
  52. package/types/lib/common-results.d.ts +11 -0
  53. package/types/lib/config.d.ts +13 -0
  54. package/types/lib/file-builder.d.ts +110 -0
  55. package/types/lib/generators/file-generator.d.ts +11 -0
  56. package/types/lib/generators/index.d.ts +4 -0
  57. package/types/lib/generators/models/index.d.ts +3 -0
  58. package/types/lib/generators/models/model-generator.d.ts +40 -0
  59. package/types/lib/generators/models/models-generator.d.ts +23 -0
  60. package/types/lib/generators/models/models.d.ts +29 -0
  61. package/types/lib/generators/services/angular-services/angular-service-generator.d.ts +53 -0
  62. package/types/lib/generators/services/angular-services/angular-services-generator.d.ts +29 -0
  63. package/types/lib/generators/services/angular-services/index.d.ts +3 -0
  64. package/types/lib/generators/services/angular-services/models.d.ts +41 -0
  65. package/types/lib/generators/services/fetch-clients/fetch-client-generator.d.ts +43 -0
  66. package/types/lib/generators/services/fetch-clients/fetch-clients-generator.d.ts +24 -0
  67. package/types/lib/generators/services/fetch-clients/index.d.ts +3 -0
  68. package/types/lib/generators/services/fetch-clients/models.d.ts +34 -0
  69. package/types/lib/import-collection.d.ts +21 -0
  70. package/types/lib/utils.d.ts +6 -0
@@ -0,0 +1,240 @@
1
+ import { SourceBuilder, notNullish } from '@goast/core';
2
+ import { ImportExportCollection } from './import-collection';
3
+ export class TypeScriptFileBuilder extends SourceBuilder {
4
+ get options() {
5
+ return super.options;
6
+ }
7
+ constructor(filePath, options) {
8
+ super(options);
9
+ this.imports = new ImportExportCollection();
10
+ this.filePath = filePath;
11
+ }
12
+ addImport(name, moduleNameOrfilePath) {
13
+ this.imports.addImport(name, moduleNameOrfilePath);
14
+ return this;
15
+ }
16
+ addExport(name, filePath) {
17
+ this.imports.addExport(name, filePath);
18
+ return this;
19
+ }
20
+ clear() {
21
+ super.clear();
22
+ this.imports.clear();
23
+ }
24
+ toString(addPadding = true) {
25
+ return new SourceBuilder(this.options)
26
+ .append((builder) => this.imports.writeTo(builder, this.filePath, this.options.importModuleTransformer))
27
+ .appendIf(addPadding, (builder) => builder.ensurePreviousLineEmpty())
28
+ .append(super.toString())
29
+ .appendIf(addPadding, (builder) => builder.ensureCurrentLineEmpty())
30
+ .toString();
31
+ }
32
+ appendGenericArguments(...genericArguments) {
33
+ genericArguments = genericArguments.filter(notNullish);
34
+ if (genericArguments.length === 0)
35
+ return this;
36
+ const multiline = genericArguments.length > 1;
37
+ return this.parenthesize('<>', (builder) => builder.appendSeparated(genericArguments, multiline ? ',\n' : ', '), {
38
+ multiline,
39
+ indent: multiline,
40
+ });
41
+ }
42
+ appendParameter(parameter) {
43
+ var _a;
44
+ if (!parameter)
45
+ return;
46
+ if (typeof parameter === 'object') {
47
+ return this.append(parameter.name)
48
+ .appendIf(parameter.isOptional === true, '?')
49
+ .appendIf(parameter.type !== undefined, ': ', (builder) => {
50
+ if (!parameter.type)
51
+ throw new Error('Unreachable error');
52
+ return typeof parameter.type === 'object'
53
+ ? builder.appendExternalTypeUsage(parameter.type)
54
+ : builder.append(parameter.type);
55
+ })
56
+ .appendGenericArguments(...((_a = parameter.genericArguments) !== null && _a !== void 0 ? _a : []))
57
+ .appendIf(parameter.defaultValue !== undefined, ' = ', parameter.defaultValue);
58
+ }
59
+ else {
60
+ return this.append(parameter);
61
+ }
62
+ }
63
+ appendParameters(...parameters) {
64
+ parameters = parameters.filter(notNullish);
65
+ if (parameters.length === 0)
66
+ return this.append('()');
67
+ const multiline = parameters.length > 1;
68
+ return this.parenthesize('()', (builder) => builder.forEach(parameters, (builder, param) => builder.appendParameter(param), {
69
+ separator: multiline ? ',\n' : ', ',
70
+ }), { multiline, indent: multiline });
71
+ }
72
+ appendObjectLiteral(...properties) {
73
+ properties = properties.filter(notNullish);
74
+ if (properties.length === 0)
75
+ return this.append('{}');
76
+ const multiline = properties.length > 1;
77
+ return this.parenthesize('{}', (builder) => builder
78
+ .appendIf(!multiline, ' ')
79
+ .appendSeparated(properties, multiline ? ',\n' : ', ')
80
+ .appendIf(!multiline, ' '), { multiline, indent: multiline });
81
+ }
82
+ appendArrayLiteral(...items) {
83
+ items = items.filter(notNullish);
84
+ if (items.length === 0)
85
+ return this.append('[]');
86
+ const multiline = items.length > 1;
87
+ return this.parenthesize('[]', (builder) => builder.appendSeparated(items, multiline ? ',\n' : ', '), {
88
+ multiline,
89
+ indent: multiline,
90
+ });
91
+ }
92
+ appendCodeBlock(...content) {
93
+ return this.parenthesize('{}', (builder) => builder.append(...content).ensureCurrentLineEmpty(), {
94
+ multiline: true,
95
+ });
96
+ }
97
+ appendComment(style, comment) {
98
+ if (!comment)
99
+ return this;
100
+ if (typeof comment === 'string') {
101
+ comment = comment.trim();
102
+ }
103
+ return style === '//'
104
+ ? this.appendLineWithLinePrefix('// ', comment)
105
+ : this.appendLine(style === '/**/' ? '/*' : '/**')
106
+ .appendWithLinePrefix(' * ', comment)
107
+ .ensureCurrentLineEmpty()
108
+ .appendLine(' */');
109
+ }
110
+ appendDocumentation(documentation) {
111
+ return this.appendComment('/***/', documentation);
112
+ }
113
+ appendModelUsage(type) {
114
+ this.append(type.component);
115
+ for (const i of type.imports) {
116
+ this.addImport(i.name, i.modulePath);
117
+ }
118
+ return this;
119
+ }
120
+ appendExternalTypeUsage(type) {
121
+ return this.append(type.name).addImport(type.name, type.module);
122
+ }
123
+ appendTypeDeclaration(options) {
124
+ return this.appendDocumentation(options.documentation)
125
+ .appendIf(options.omitExport !== true, 'export ')
126
+ .append('type ')
127
+ .append(options.name)
128
+ .append(' = ')
129
+ .append(options.type)
130
+ .appendLine(';');
131
+ }
132
+ appendAnnotation(options) {
133
+ var _a;
134
+ if (options.module)
135
+ this.addImport(options.name, options.module);
136
+ return this.append(`@${options.name}`).appendParameters(...((_a = options.args) !== null && _a !== void 0 ? _a : []));
137
+ }
138
+ appendAnnotations(annotations, separator) {
139
+ if (annotations.length === 0)
140
+ return this;
141
+ return this.forEach(annotations, (builder, annotation) => builder.appendAnnotation(annotation).append(separator));
142
+ }
143
+ appendClass(options) {
144
+ var _a;
145
+ return this.appendComment('/***/', options.documentation)
146
+ .appendAnnotations((_a = options.annotations) !== null && _a !== void 0 ? _a : [], '\n')
147
+ .appendIf(options.omitExport !== true, 'export ')
148
+ .appendIf(options.isAbstract === true, 'abstract ')
149
+ .append('class ')
150
+ .append(options.name)
151
+ .indent((builder) => {
152
+ var _a;
153
+ return builder
154
+ .appendGenericArguments(...((_a = options === null || options === void 0 ? void 0 : options.genericArguments) !== null && _a !== void 0 ? _a : []))
155
+ .appendIf(options.extends !== undefined, '\nextends ', options.extends)
156
+ .appendIf(options.implements !== undefined && options.implements.length > 0, ' implements ', (builder) => { var _a; return builder.appendSeparated((_a = options.implements) !== null && _a !== void 0 ? _a : [], ', '); });
157
+ })
158
+ .append(options.extends || (options.implements !== undefined && options.implements.length > 0) ? '\n' : ' ')
159
+ .appendCodeBlock(options.body)
160
+ .appendLine();
161
+ }
162
+ appendConstructor(options) {
163
+ var _a, _b;
164
+ return this.appendComment('/***/', options.documentation)
165
+ .appendAnnotations((_a = options.annotations) !== null && _a !== void 0 ? _a : [], '\n')
166
+ .append('constructor')
167
+ .appendParameters(...((_b = options === null || options === void 0 ? void 0 : options.parameters) !== null && _b !== void 0 ? _b : []))
168
+ .append(' ')
169
+ .appendCodeBlock(options.body)
170
+ .appendLine();
171
+ }
172
+ appendMethod(options) {
173
+ var _a, _b, _c;
174
+ return this.appendComment('/***/', options.documentation)
175
+ .appendAnnotations((_a = options.annotations) !== null && _a !== void 0 ? _a : [], '\n')
176
+ .appendIf(options.accessibility !== undefined, options.accessibility, ' ')
177
+ .appendIf(options.isAbstract === true, 'abstract ')
178
+ .appendIf(options.isStatic === true, 'static ')
179
+ .appendIf(options.isAsync === true, 'async ')
180
+ .append(options.name)
181
+ .appendGenericArguments(...((_b = options === null || options === void 0 ? void 0 : options.genericArguments) !== null && _b !== void 0 ? _b : []))
182
+ .appendParameters(...((_c = options === null || options === void 0 ? void 0 : options.parameters) !== null && _c !== void 0 ? _c : []))
183
+ .appendIf(options.returnType !== undefined, ': ', options.returnType)
184
+ .append(' ')
185
+ .appendCodeBlock(options.body)
186
+ .appendLine();
187
+ }
188
+ appendProperty(options) {
189
+ var _a, _b, _c, _d, _e;
190
+ if (options.kind === 'getter' || options.kind === 'setter' || options.kind === 'getter-setter') {
191
+ if (options.kind === 'getter-setter' || options.kind === 'getter') {
192
+ this.appendComment('/***/', options.documentation)
193
+ .appendAnnotations([...((_a = options.annotations) !== null && _a !== void 0 ? _a : []), ...((_b = options.getterAnnotations) !== null && _b !== void 0 ? _b : [])], '\n')
194
+ .appendIf(options.accessibility !== undefined, options.accessibility, ' ')
195
+ .appendIf(options.isAbstract === true, 'abstract ')
196
+ .appendIf(options.isStatic === true, 'static ')
197
+ .append('get ', options.name, '()')
198
+ .appendIf(options.type !== undefined, ': ', options.type)
199
+ .if(options.isAbstract === true, (builder) => builder.append(';'), (builder) => builder.append(' ').appendCodeBlock(options.getter))
200
+ .ensureCurrentLineEmpty();
201
+ }
202
+ if (options.kind === 'getter-setter' || options.kind === 'setter') {
203
+ this.appendComment('/***/', options.documentation)
204
+ .appendAnnotations([...((_c = options.annotations) !== null && _c !== void 0 ? _c : []), ...((_d = options.setterAnnotations) !== null && _d !== void 0 ? _d : [])], '\n')
205
+ .appendIf(options.accessibility !== undefined, options.accessibility, ' ')
206
+ .appendIf(options.isAbstract === true, 'abstract ')
207
+ .appendIf(options.isStatic === true, 'static ')
208
+ .append('set ', options.name)
209
+ .parenthesize('()', (builder) => builder.append('value: ', options.type))
210
+ .if(options.isAbstract === true, (builder) => builder.append(';'), (builder) => builder.append(' ').appendCodeBlock(options.setter))
211
+ .ensureCurrentLineEmpty();
212
+ }
213
+ return this;
214
+ }
215
+ return this.appendComment('/***/', options.documentation)
216
+ .appendAnnotations((_e = options.annotations) !== null && _e !== void 0 ? _e : [], '\n')
217
+ .appendIf(options.accessibility !== undefined, options.accessibility, ' ')
218
+ .appendIf(options.isAbstract === true, 'abstract ')
219
+ .appendIf(options.isStatic === true, 'static ')
220
+ .appendIf(options.isReadonly === true, 'readonly ')
221
+ .append(options.name)
222
+ .appendIf(options.isOptional === true, '?')
223
+ .appendIf(options.type !== undefined, ': ', options.type)
224
+ .appendIf(options.initializer !== undefined, ' = ', options.initializer)
225
+ .appendLine(';');
226
+ }
227
+ appendFunction(options) {
228
+ var _a, _b;
229
+ const kind = (_a = options.kind) !== null && _a !== void 0 ? _a : 'arrow-function';
230
+ return this.appendIf(kind === 'arrow-function' && !!options.name, 'const ', options.name, ' = ')
231
+ .appendIf(options.isAsync === true, 'async ')
232
+ .appendIf(kind === 'function', 'function')
233
+ .appendIf(kind === 'function' && !!options.name, ' ', options.name)
234
+ .appendParameters(...((_b = options === null || options === void 0 ? void 0 : options.parameters) !== null && _b !== void 0 ? _b : []))
235
+ .appendIf(options.returnType !== undefined, ': ', options.returnType)
236
+ .append(' ')
237
+ .appendIf(kind === 'arrow-function', '=> ')
238
+ .parenthesizeIf(kind === 'function' || options.singleLine !== true, '{}', (builder) => builder.append(options.body), { multiline: true });
239
+ }
240
+ }
@@ -0,0 +1,22 @@
1
+ import { toCasing } from '@goast/core';
2
+ import { toTypeScriptPropertyName, toTypeScriptStringLiteral } from '../utils';
3
+ export class TypeScriptFileGenerator {
4
+ getAnyType(context) {
5
+ return context.config.preferUnknown ? 'unknown' : 'any';
6
+ }
7
+ toTypeName(context, name) {
8
+ return toCasing(name, context.config.typeNameCasing);
9
+ }
10
+ toMethodName(context, name) {
11
+ return toTypeScriptPropertyName(toCasing(name, context.config.methodNameCasing), context.config.useSingleQuotes);
12
+ }
13
+ toPropertyName(context, name, keepCasing = false) {
14
+ return toTypeScriptPropertyName(keepCasing ? name : toCasing(name, context.config.propertyNameCasing), context.config.useSingleQuotes);
15
+ }
16
+ toEnumValueName(context, name) {
17
+ return toTypeScriptPropertyName(toCasing(name, context.config.enumValueNameCasing), context.config.useSingleQuotes);
18
+ }
19
+ toStringLiteral(context, text) {
20
+ return toTypeScriptStringLiteral(text, context.config.useSingleQuotes);
21
+ }
22
+ }
@@ -0,0 +1,4 @@
1
+ export * from './file-generator';
2
+ export * from './models';
3
+ export * from './services/angular-services';
4
+ export * from './services/fetch-clients';
@@ -0,0 +1,3 @@
1
+ export * from './model-generator';
2
+ export * from './models';
3
+ export * from './models-generator';
@@ -0,0 +1,313 @@
1
+ import { dirname, resolve } from 'path';
2
+ import { ensureDirSync, writeFileSync } from 'fs-extra';
3
+ import { getSchemaReference, resolveAnyOfAndAllOf, toCasing, } from '@goast/core';
4
+ import { TypeScriptFileBuilder } from '../../file-builder';
5
+ import { toTypeScriptPropertyName } from '../../utils';
6
+ import { TypeScriptFileGenerator } from '../file-generator';
7
+ export class DefaultTypeScriptModelGenerator extends TypeScriptFileGenerator {
8
+ generate(ctx) {
9
+ if (this.shouldGenerateTypeDeclaration(ctx, ctx.schema)) {
10
+ const name = this.getDeclarationTypeName(ctx, ctx.schema);
11
+ const filePath = this.getFilePath(ctx, ctx.schema);
12
+ console.log(`Generating model ${name} to ${filePath}...`);
13
+ ensureDirSync(dirname(filePath));
14
+ const builder = new TypeScriptFileBuilder(filePath, ctx.config);
15
+ this.generateFileContent(ctx, builder);
16
+ writeFileSync(filePath, builder.toString());
17
+ return { component: name, filePath, imports: [{ kind: 'file', name, modulePath: filePath }] };
18
+ }
19
+ else {
20
+ const builder = new TypeScriptFileBuilder(undefined, ctx.config);
21
+ this.generateType(ctx, builder, ctx.schema);
22
+ const imports = builder.imports.imports;
23
+ builder.imports.clear();
24
+ return { component: builder.toString(false), imports };
25
+ }
26
+ }
27
+ generateFileContent(ctx, builder) {
28
+ const interfaceSchema = this.resolveInterfaceSchema(ctx);
29
+ if (interfaceSchema) {
30
+ this.generateInterface(ctx, builder, interfaceSchema);
31
+ }
32
+ else if (ctx.config.enumGeneration !== 'union' &&
33
+ ctx.schema.kind === 'string' &&
34
+ ctx.schema.enum !== undefined &&
35
+ ctx.schema.enum.length > 0) {
36
+ this.generateEnum(ctx, builder);
37
+ }
38
+ else {
39
+ this.generateTypeAlias(ctx, builder);
40
+ }
41
+ }
42
+ generateEnum(ctx, builder) {
43
+ builder
44
+ .append((builder) => this.generateTypeDocumentation(ctx, builder))
45
+ .ensureCurrentLineEmpty()
46
+ .append((builder) => this.generateEnumSignature(ctx, builder))
47
+ .parenthesize('{}', (builder) => this.generateEnumValues(ctx, builder), { multiline: true })
48
+ .ensureCurrentLineEmpty();
49
+ }
50
+ generateEnumSignature(ctx, builder) {
51
+ builder.append('export enum ').append(this.getDeclarationTypeName(ctx, ctx.schema));
52
+ }
53
+ generateEnumValues(ctx, builder) {
54
+ var _a, _b;
55
+ builder.forEach((_b = (_a = ctx.schema.enum) === null || _a === void 0 ? void 0 : _a.map((x) => String(x))) !== null && _b !== void 0 ? _b : [], (builder, enumValue) => builder.append(`${this.toEnumValueName(ctx, enumValue)} = ${this.toStringLiteral(ctx, enumValue)}`), { separator: ',\n' });
56
+ }
57
+ generateInterface(ctx, builder, schema) {
58
+ builder
59
+ .append((builder) => this.generateTypeDocumentation(ctx, builder))
60
+ .ensureCurrentLineEmpty()
61
+ .append((builder) => this.generateInterfaceSignature(ctx, builder))
62
+ .parenthesize('{}', (builder) => builder.append((builder) => this.generateInterfaceContent(ctx, builder, schema)), { multiline: true })
63
+ .ensureCurrentLineEmpty();
64
+ }
65
+ generateInterfaceSignature(ctx, builder) {
66
+ builder.append('export interface ').append(this.getDeclarationTypeName(ctx, ctx.schema));
67
+ }
68
+ generateInterfaceContent(ctx, builder, schema) {
69
+ this.generateObjectTypeProperties(ctx, builder, schema);
70
+ if (schema.additionalProperties) {
71
+ builder.append('[key: string]: ');
72
+ if (schema.additionalProperties === true) {
73
+ builder.append(this.getAnyType(ctx));
74
+ }
75
+ else {
76
+ this.generateTypeUsage(ctx, builder, schema.additionalProperties);
77
+ }
78
+ builder.appendLine(';');
79
+ }
80
+ }
81
+ generateTypeAlias(ctx, builder) {
82
+ builder
83
+ .append((builder) => this.generateTypeDocumentation(ctx, builder))
84
+ .ensureCurrentLineEmpty()
85
+ .append((builder) => this.generateTypeAliasSignature(ctx, builder))
86
+ .append(' = ')
87
+ .append((builder) => this.generateType(ctx, builder, ctx.schema))
88
+ .appendLine(';');
89
+ }
90
+ generateTypeAliasSignature(ctx, builder) {
91
+ builder.append('export type ').append(this.getDeclarationTypeName(ctx, ctx.schema));
92
+ }
93
+ generateTypeUsage(ctx, builder, schema) {
94
+ schema = getSchemaReference(schema, ['description']);
95
+ if (this.shouldGenerateTypeDeclaration(ctx, schema)) {
96
+ const name = this.getDeclarationTypeName(ctx, schema);
97
+ const filePath = this.getFilePath(ctx, schema);
98
+ if (filePath) {
99
+ builder.addImport(name, filePath);
100
+ }
101
+ builder.append(name);
102
+ }
103
+ else {
104
+ this.generateType(ctx, builder, schema);
105
+ }
106
+ }
107
+ generateType(ctx, builder, schema) {
108
+ if (schema.enum !== undefined && schema.enum.length > 0) {
109
+ this.generateEnumType(ctx, builder, schema);
110
+ return;
111
+ }
112
+ switch (schema.kind) {
113
+ case 'boolean':
114
+ builder.append('boolean');
115
+ break;
116
+ case 'integer':
117
+ case 'number':
118
+ builder.append('number');
119
+ break;
120
+ case 'string':
121
+ builder.append('string');
122
+ break;
123
+ case 'null':
124
+ builder.append('null');
125
+ break;
126
+ case 'unknown':
127
+ builder.append(this.getAnyType(ctx));
128
+ break;
129
+ case 'array':
130
+ this.generateArrayType(ctx, builder, schema);
131
+ break;
132
+ case 'object':
133
+ this.generateObjectType(ctx, builder, schema);
134
+ break;
135
+ case 'combined':
136
+ this.generateCombinedType(ctx, builder, schema);
137
+ break;
138
+ case 'multi-type':
139
+ this.generateMultiType(ctx, builder, schema);
140
+ break;
141
+ case 'oneOf':
142
+ this.generateOneOfType(ctx, builder, schema);
143
+ break;
144
+ default:
145
+ builder.append(this.getAnyType(ctx));
146
+ break;
147
+ }
148
+ }
149
+ generateArrayType(ctx, builder, schema) {
150
+ builder
151
+ .appendIf(ctx.config.immutableTypes, 'readonly ')
152
+ .parenthesize('()', (builder) => schema.items ? this.generateTypeUsage(ctx, builder, schema.items) : builder.append(this.getAnyType(ctx)))
153
+ .append('[]');
154
+ }
155
+ generateCombinedType(ctx, builder, schema) {
156
+ if (schema.allOf.length === 0 && schema.anyOf.length === 0) {
157
+ builder.append(this.getAnyType(ctx));
158
+ return;
159
+ }
160
+ builder
161
+ .forEach(schema.allOf, (builder, schema) => this.generateTypeUsage(ctx, builder, schema), { separator: ' & ' })
162
+ .appendIf(schema.anyOf.length > 0, (builder) => builder
163
+ .appendIf(schema.allOf.length > 0, ' & ')
164
+ .forEach(schema.anyOf, (builder, schema) => builder
165
+ .append('Partial')
166
+ .parenthesize('<>', (builder) => this.generateTypeUsage(ctx, builder, schema), { indent: false }), { separator: ' & ' }));
167
+ }
168
+ generateOneOfType(ctx, builder, schema) {
169
+ if (schema.oneOf.length === 0) {
170
+ builder.append(this.getAnyType(ctx));
171
+ return;
172
+ }
173
+ builder.parenthesizeIf(schema.oneOf.length > 1, '()', (builder) => builder.forEach(schema.oneOf, (builder, schema) => this.generateTypeUsage(ctx, builder, schema), {
174
+ separator: ' | ',
175
+ }));
176
+ }
177
+ generateMultiType(ctx, builder, schema) {
178
+ if (schema.type.length === 0) {
179
+ builder.append(this.getAnyType(ctx));
180
+ return;
181
+ }
182
+ const parenthesize = schema.type.length > 1;
183
+ builder.parenthesizeIf(parenthesize, '()', (builder) => builder
184
+ .appendIf(parenthesize, (builder) => builder.appendLine().append('| '))
185
+ .forEach(schema.type, (builder, type) => {
186
+ switch (type) {
187
+ case 'string':
188
+ builder.append('string');
189
+ break;
190
+ case 'number':
191
+ case 'integer':
192
+ builder.append('number');
193
+ break;
194
+ case 'boolean':
195
+ builder.append('boolean');
196
+ break;
197
+ case 'null':
198
+ builder.append('null');
199
+ break;
200
+ case 'array':
201
+ this.generateArrayType(ctx, builder, schema);
202
+ break;
203
+ case 'object':
204
+ this.generateObjectType(ctx, builder, schema);
205
+ break;
206
+ default:
207
+ builder.append('never');
208
+ break;
209
+ }
210
+ }, { separator: (builder) => builder.appendLine().append('| ') }));
211
+ }
212
+ generateObjectType(ctx, builder, schema) {
213
+ if (schema.properties.size === 0 &&
214
+ !schema.additionalProperties &&
215
+ schema.allOf.length === 0 &&
216
+ schema.anyOf.length === 0 &&
217
+ schema.discriminator === undefined) {
218
+ builder.append('{}');
219
+ return;
220
+ }
221
+ // properties
222
+ if (schema.properties.size > 0) {
223
+ builder
224
+ .parenthesize('{}', (builder) => builder.appendLine().append((builder) => this.generateObjectTypeProperties(ctx, builder, schema)))
225
+ .appendIf(!!schema.additionalProperties || schema.allOf.length > 0 || schema.anyOf.length > 0, ' & ');
226
+ }
227
+ // additional properties
228
+ if (schema.additionalProperties) {
229
+ this.generateObjectTypeAdditionalProperties(ctx, builder, schema);
230
+ if (schema.allOf.length > 0 || schema.anyOf.length > 0) {
231
+ builder.append(' & ');
232
+ }
233
+ }
234
+ // allOf & anyOf
235
+ if (schema.allOf.length > 0 || schema.anyOf.length > 0) {
236
+ this.generateCombinedType(ctx, builder, schema);
237
+ }
238
+ }
239
+ generateObjectTypeProperties(ctx, builder, schema) {
240
+ builder.forEach(schema.properties.values(), (builder, property) => {
241
+ this.generatePropertyDocumentation(ctx, builder, property);
242
+ builder
243
+ .appendIf(ctx.config.immutableTypes, 'readonly ')
244
+ .append(toTypeScriptPropertyName(property.name, ctx.config.useSingleQuotes))
245
+ .appendIf(!schema.required.has(property.name), '?')
246
+ .append(': ')
247
+ .append((builder) => this.generateTypeUsage(ctx, builder, property.schema))
248
+ .appendIf(property.schema.nullable === true, ' | null')
249
+ .appendLine(';');
250
+ });
251
+ }
252
+ generateObjectTypeAdditionalProperties(ctx, builder, schema) {
253
+ if (!schema.additionalProperties)
254
+ return;
255
+ builder
256
+ .appendIf(ctx.config.immutableTypes, 'Readonly')
257
+ .parenthesizeIf(ctx.config.immutableTypes, '<>', (builder) => builder
258
+ .append('Record')
259
+ .appendGenericArguments('string', schema.additionalProperties === true
260
+ ? this.getAnyType(ctx)
261
+ : (builder) => this.generateTypeUsage(ctx, builder, schema.additionalProperties)));
262
+ }
263
+ generateEnumType(ctx, builder, schema) {
264
+ builder.indent((builder) => {
265
+ var _a;
266
+ builder.forEach((_a = schema.enum) !== null && _a !== void 0 ? _a : [], (builder, item) => builder.append(this.toStringLiteral(ctx, String(item))), {
267
+ separator: ' | ',
268
+ });
269
+ });
270
+ }
271
+ generateTypeDocumentation(ctx, builder) {
272
+ this.generateDocumentation(ctx, builder, ctx.schema);
273
+ }
274
+ generatePropertyDocumentation(ctx, builder, property) {
275
+ this.generateDocumentation(ctx, builder, property.schema);
276
+ }
277
+ generateDocumentation(ctx, builder, schema) {
278
+ builder.appendComment('/***/', schema.description);
279
+ }
280
+ shouldGenerateTypeDeclaration(ctx, schema) {
281
+ // All named schemas should have its own type declaration
282
+ if (!schema.isNameGenerated) {
283
+ return true;
284
+ }
285
+ // All enum types should have its own type declaration
286
+ if (schema.kind === 'string' && schema.enum !== undefined && schema.enum.length > 0) {
287
+ return true;
288
+ }
289
+ // All primitive types already exist and do not need its own type declaration
290
+ if (schema.kind !== 'array' &&
291
+ schema.kind !== 'combined' &&
292
+ schema.kind !== 'multi-type' &&
293
+ schema.kind !== 'object' &&
294
+ schema.kind !== 'oneOf') {
295
+ return false;
296
+ }
297
+ // For all other types, check if the user has enabled inline unnamed schemas
298
+ return !ctx.config.inlineUnnamedSchemas;
299
+ }
300
+ resolveInterfaceSchema(ctx) {
301
+ if (ctx.config.typeDeclaration === 'prefer-interface' &&
302
+ (ctx.schema.kind === 'object' || ctx.schema.kind === 'combined')) {
303
+ return resolveAnyOfAndAllOf(ctx.schema, true);
304
+ }
305
+ return undefined;
306
+ }
307
+ getDeclarationTypeName(ctx, schema) {
308
+ return toCasing(schema.name, ctx.config.typeNameCasing);
309
+ }
310
+ getFilePath(ctx, schema) {
311
+ return resolve(ctx.config.outputDir, ctx.config.modelsDirPath, `${toCasing(schema.name, ctx.config.fileNameCasing)}.ts`);
312
+ }
313
+ }
@@ -0,0 +1,63 @@
1
+ import { dirname, resolve } from 'path';
2
+ import { ensureDirSync, writeFileSync } from 'fs-extra';
3
+ import { OpenApiSchemasGenerationProviderBase, Factory } from '@goast/core';
4
+ import { DefaultTypeScriptModelGenerator } from './model-generator';
5
+ import { defaultTypeScriptModelsGeneratorConfig, } from './models';
6
+ import { TypeScriptFileBuilder } from '../../file-builder';
7
+ import { ImportExportCollection } from '../../import-collection';
8
+ export class TypeScriptModelsGenerator extends OpenApiSchemasGenerationProviderBase {
9
+ constructor(modelGeneratorFactory) {
10
+ super();
11
+ this._modelGeneratorFactory = modelGeneratorFactory !== null && modelGeneratorFactory !== void 0 ? modelGeneratorFactory : Factory.fromValue(new DefaultTypeScriptModelGenerator());
12
+ }
13
+ initResult() {
14
+ return {
15
+ models: {},
16
+ modelIndexFilePath: undefined,
17
+ };
18
+ }
19
+ buildContext(context, config) {
20
+ return this.getProviderContext(context, config, defaultTypeScriptModelsGeneratorConfig);
21
+ }
22
+ onGenerate(ctx) {
23
+ const output = super.onGenerate(ctx);
24
+ output.modelIndexFilePath = this.generateIndexFile(ctx);
25
+ return output;
26
+ }
27
+ generateSchema(ctx, schema) {
28
+ const modelGenerator = this._modelGeneratorFactory.create();
29
+ return modelGenerator.generate(Object.assign(Object.assign({}, ctx), { schema }));
30
+ }
31
+ addSchemaResult(ctx, schema, result) {
32
+ ctx.output.models[schema.id] = result;
33
+ }
34
+ generateIndexFile(ctx) {
35
+ if (!this.shouldGenerateIndexFile(ctx)) {
36
+ return undefined;
37
+ }
38
+ const filePath = this.getIndexFilePath(ctx);
39
+ console.log(`Generating model index file to ${filePath}...`);
40
+ ensureDirSync(dirname(filePath));
41
+ const builder = new TypeScriptFileBuilder(filePath, ctx.config);
42
+ this.generateIndexFileContent(ctx, builder);
43
+ writeFileSync(filePath, builder.toString());
44
+ return filePath;
45
+ }
46
+ getIndexFilePath(ctx) {
47
+ var _a;
48
+ return resolve(ctx.config.outputDir, (_a = ctx.config.indexFilePath) !== null && _a !== void 0 ? _a : 'models.ts');
49
+ }
50
+ shouldGenerateIndexFile(ctx) {
51
+ return ctx.config.indexFilePath !== null;
52
+ }
53
+ generateIndexFileContent(ctx, builder) {
54
+ const exports = new ImportExportCollection();
55
+ for (const modelId in ctx.output.models) {
56
+ const model = ctx.output.models[modelId];
57
+ if (model.filePath) {
58
+ exports.addExport(model.component, model.filePath);
59
+ }
60
+ }
61
+ exports.writeTo(builder);
62
+ }
63
+ }
@@ -0,0 +1,3 @@
1
+ import { defaultTypeScriptGeneratorConfig } from '../../config';
2
+ export const defaultTypeScriptModelGeneratorConfig = Object.assign(Object.assign({}, defaultTypeScriptGeneratorConfig), { fileNameCasing: 'kebab', typeNameCasing: 'pascal', enumGeneration: 'union', immutableTypes: false, inlineUnnamedSchemas: true, modelsDirPath: 'models', typeDeclaration: 'type' });
3
+ export const defaultTypeScriptModelsGeneratorConfig = Object.assign(Object.assign({}, defaultTypeScriptModelGeneratorConfig), { indexFilePath: 'models.ts' });