@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,236 @@
1
+ import { dirname, resolve } from 'path';
2
+ import { ensureDirSync, writeFileSync } from 'fs-extra';
3
+ import { getEndpointUrlPreview, notNullish, toCasing } from '@goast/core';
4
+ import { TypeScriptFileBuilder } from '../../../file-builder';
5
+ import { TypeScriptFileGenerator } from '../../file-generator';
6
+ export class DefaultTypeScriptFetchClientGenerator extends TypeScriptFileGenerator {
7
+ generate(ctx) {
8
+ const result = {};
9
+ let builder;
10
+ if (this.shouldGenerateInterface(ctx)) {
11
+ const filePath = this.getInterfaceFilePath(ctx);
12
+ const name = this.getInterfaceName(ctx);
13
+ console.log(`Generating interface ${name} in ${filePath}...`);
14
+ ensureDirSync(dirname(filePath));
15
+ builder = new TypeScriptFileBuilder(filePath, ctx.config);
16
+ this.generateInterface(ctx, builder);
17
+ writeFileSync(filePath, builder.toString());
18
+ result.interface = { filePath, component: name, imports: [{ kind: 'file', name, modulePath: filePath }] };
19
+ }
20
+ if (this.shouldGenerateClass(ctx)) {
21
+ const filePath = this.getClassFilePath(ctx);
22
+ const name = this.getClassName(ctx);
23
+ console.log(`Generating class ${name} in ${filePath}...`);
24
+ ensureDirSync(dirname(filePath));
25
+ if (!builder || builder.filePath !== filePath) {
26
+ builder = new TypeScriptFileBuilder(filePath, ctx.config);
27
+ }
28
+ this.generateDefaultOptions(ctx, builder);
29
+ this.generateClass(ctx, builder);
30
+ writeFileSync(filePath, builder.toString());
31
+ result.class = { filePath, component: name, imports: [{ kind: 'file', name, modulePath: filePath }] };
32
+ }
33
+ return result;
34
+ }
35
+ generateInterface(ctx, builder) {
36
+ builder
37
+ .ensurePreviousLineEmpty()
38
+ .append((builder) => this.generateInterfaceDocumentation(ctx, builder))
39
+ .ensureCurrentLineEmpty()
40
+ .append((builder) => this.generateInterfaceSignature(ctx, builder))
41
+ .append(' ')
42
+ .parenthesize('{}', (builder) => this.generateInterfaceContent(ctx, builder))
43
+ .appendLine();
44
+ }
45
+ generateInterfaceDocumentation(ctx, builder) {
46
+ if (ctx.service.description) {
47
+ builder.appendLine('/**').appendLineWithLinePrefix(' *', ctx.service.description).appendLine(' */');
48
+ }
49
+ }
50
+ generateInterfaceSignature(ctx, builder) {
51
+ builder.append('export interface ').append(this.getInterfaceName(ctx));
52
+ }
53
+ generateInterfaceContent(ctx, builder) {
54
+ builder.forEach(ctx.service.endpoints, (builder, endpoint) => this.generateInterfaceServiceMethod(ctx, builder, endpoint));
55
+ }
56
+ generateInterfaceServiceMethod(ctx, builder, endpoint) {
57
+ builder
58
+ .ensurePreviousLineEmpty()
59
+ .append((builder) => this.generateServiceMethodDocumentation(ctx, builder, endpoint))
60
+ .ensureCurrentLineEmpty()
61
+ .append((builder) => this.generateServiceMethodSignature(ctx, builder, endpoint))
62
+ .appendLine(';');
63
+ }
64
+ generateDefaultOptions(ctx, builder) {
65
+ const baseUrl = ctx.service.endpoints
66
+ .map((x) => { var _a, _b, _c; return (_c = (_b = (_a = x.$src) === null || _a === void 0 ? void 0 : _a.document.servers) === null || _b === void 0 ? void 0 : _b.find((x) => x.url)) === null || _c === void 0 ? void 0 : _c.url; })
67
+ .find((x) => !!x);
68
+ builder
69
+ .ensurePreviousLineEmpty()
70
+ .addImport('FetchClientOptions', this.getUtilPath(ctx, 'types.ts'))
71
+ .append('export const ', this.getDefaultOptionsConstantName(ctx), ': FetchClientOptions = ')
72
+ .parenthesize('{}', (builder) => builder.appendLineIf(notNullish(baseUrl), 'baseUrl: ', this.toStringLiteral(ctx, baseUrl !== null && baseUrl !== void 0 ? baseUrl : ''), ','), { indent: true, multiline: true });
73
+ }
74
+ generateClass(ctx, builder) {
75
+ builder
76
+ .ensurePreviousLineEmpty()
77
+ .append((builder) => this.generateClassDocumentation(ctx, builder))
78
+ .ensureCurrentLineEmpty()
79
+ .append((builder) => this.generateClassSignature(ctx, builder))
80
+ .append(' ')
81
+ .parenthesize('{}', (builder) => this.generateClassContent(ctx, builder))
82
+ .appendLine();
83
+ }
84
+ generateClassDocumentation(ctx, builder) {
85
+ if (ctx.service.description) {
86
+ builder.appendLine('/**').appendLineWithLinePrefix(' *', ctx.service.description).appendLine(' */');
87
+ }
88
+ }
89
+ generateClassSignature(ctx, builder) {
90
+ builder
91
+ .append('export class ')
92
+ .append(this.getClassName(ctx))
93
+ .appendIf(this.shouldGenerateInterface(ctx), (builder) => builder.addImport(this.getInterfaceName(ctx), this.getInterfaceFilePath(ctx)))
94
+ .append(' implements ', this.getInterfaceName(ctx));
95
+ }
96
+ generateClassContent(ctx, builder) {
97
+ builder
98
+ .append((builder) => this.generateClassFields(ctx, builder))
99
+ .append((builder) => this.generateClassConstructor(ctx, builder))
100
+ .forEach(ctx.service.endpoints, (builder, endpoint) => this.generateClassServiceMethod(ctx, builder, endpoint));
101
+ }
102
+ generateClassFields(ctx, builder) {
103
+ builder.ensureCurrentLineEmpty().appendLine('private readonly _options: FetchClientOptions;');
104
+ }
105
+ generateClassConstructor(ctx, builder) {
106
+ builder
107
+ .ensurePreviousLineEmpty()
108
+ .append('constructor(options?: FetchClientOptions)')
109
+ .parenthesize('{}', (builder) => this.generateClassConstructorContent(ctx, builder), {
110
+ indent: true,
111
+ multiline: true,
112
+ });
113
+ }
114
+ generateClassConstructorContent(ctx, builder) {
115
+ builder.appendLine('this._options = { ...', this.getDefaultOptionsConstantName(ctx), ', ...options };');
116
+ }
117
+ generateClassServiceMethod(ctx, builder, endpoint) {
118
+ builder
119
+ .ensurePreviousLineEmpty()
120
+ .append((builder) => this.generateServiceMethodDocumentation(ctx, builder, endpoint))
121
+ .ensureCurrentLineEmpty()
122
+ .append((builder) => this.generateServiceMethodSignature(ctx, builder, endpoint))
123
+ .append(' ')
124
+ .parenthesize('{}', (builder) => this.generateServiceMethodContent(ctx, builder, endpoint), {
125
+ indent: true,
126
+ multiline: true,
127
+ })
128
+ .appendLine();
129
+ }
130
+ generateServiceMethodDocumentation(ctx, builder, endpoint) {
131
+ var _a;
132
+ const hasParams = endpoint.parameters.some((p) => p.target === 'path' || p.target === 'query') ||
133
+ (!!endpoint.requestBody && ((_a = endpoint.requestBody) === null || _a === void 0 ? void 0 : _a.content.length) > 0);
134
+ builder.appendComment('/***/', (builder) => {
135
+ var _a;
136
+ return builder
137
+ .appendLine((_a = endpoint.description) !== null && _a !== void 0 ? _a : '[No description was provided by the API]')
138
+ .appendLine(`@see ${getEndpointUrlPreview(endpoint)}`)
139
+ .appendLineIf(hasParams, `@param params Parameters for the endpoint.`)
140
+ .append(`@returns The response of the call to the endpoint.`);
141
+ });
142
+ }
143
+ generateServiceMethodSignature(ctx, builder, endpoint) {
144
+ builder
145
+ .append(toCasing(endpoint.name, ctx.config.methodCasing))
146
+ .parenthesize('()', (builder) => this.generateServiceMethodParameters(ctx, builder, endpoint))
147
+ .append(': ')
148
+ .append((builder) => this.generateServiceMethodReturnValue(ctx, builder, endpoint));
149
+ }
150
+ generateServiceMethodParameters(ctx, builder, endpoint) {
151
+ var _a, _b, _c;
152
+ const params = endpoint.parameters.filter((x) => x.target === 'path' || x.target === 'query');
153
+ const bodySchemaId = (_c = (_b = (_a = endpoint.requestBody) === null || _a === void 0 ? void 0 : _a.content[0]) === null || _b === void 0 ? void 0 : _b.schema) === null || _c === void 0 ? void 0 : _c.id;
154
+ builder
155
+ .appendLineIf(params.length > 0 && !!bodySchemaId)
156
+ .if(params.length > 0, (builder) => builder
157
+ .append('params: ')
158
+ .parenthesize('{}', (builder) => builder.forEach(params, (builder, parameter) => {
159
+ var _a;
160
+ return builder.appendLine(`${this.toPropertyName(ctx, parameter.name)}: ${this.getTypeName(ctx, builder, (_a = parameter.schema) === null || _a === void 0 ? void 0 : _a.id)};`);
161
+ }), { indent: true, multiline: true }))
162
+ .appendLineIf(params.length > 0 && !!bodySchemaId, ',')
163
+ .appendIf(!!bodySchemaId, (builder) => builder.append(`body: ${this.getTypeName(ctx, builder, bodySchemaId)}`))
164
+ .appendLineIf(params.length > 0 && !!bodySchemaId);
165
+ }
166
+ generateServiceMethodReturnValue(ctx, builder, endpoint) {
167
+ var _a, _b, _c;
168
+ const successResponse = (_a = endpoint.responses.find((x) => x.statusCode === 200)) !== null && _a !== void 0 ? _a : endpoint.responses.find((x) => x.statusCode && x.statusCode > 200 && x.statusCode < 300);
169
+ const schema = (_c = (_b = successResponse === null || successResponse === void 0 ? void 0 : successResponse.contentOptions) === null || _b === void 0 ? void 0 : _b.find((x) => x.schema !== undefined)) === null || _c === void 0 ? void 0 : _c.schema;
170
+ builder.append('Promise').parenthesize('<>', (builder) => this.generateTypedResponse(ctx, builder, schema));
171
+ }
172
+ generateTypedResponse(ctx, builder, schema) {
173
+ builder
174
+ .addImport('TypedResponse', this.getUtilPath(ctx, 'types.ts'))
175
+ .append('TypedResponse')
176
+ .parenthesize('<>', (builder) => builder.append(this.getTypeName(ctx, builder, schema === null || schema === void 0 ? void 0 : schema.id)));
177
+ }
178
+ generateServiceMethodContent(ctx, builder, endpoint) {
179
+ builder
180
+ .addImport('UrlBuilder', this.getUtilPath(ctx, 'url-builder.ts'))
181
+ .appendLine('const url = new UrlBuilder(this._options.baseUrl)')
182
+ .indent((builder) => builder
183
+ .appendLine(`.withPath(${this.toStringLiteral(ctx, endpoint.path)})`)
184
+ .forEach(endpoint.parameters.filter((x) => x.target === 'path' || x.target === 'query'), (builder, parameter) => builder
185
+ .appendIf(parameter.target === 'path', `.withPathParam`)
186
+ .appendIf(parameter.target === 'query', `.withQueryParam`)
187
+ .parenthesize('()', `${this.toStringLiteral(ctx, parameter.name)}, params.${this.toPropertyName(ctx, parameter.name)}`)
188
+ .appendLine())
189
+ .appendLine('.build();'))
190
+ .append('return (this._options.fetch ?? fetch)')
191
+ .parenthesize('()', (builder) => builder.append('url, ').parenthesize('{}', (builder) => {
192
+ var _a, _b;
193
+ return builder
194
+ .appendLine('method: ', this.toStringLiteral(ctx, toCasing(endpoint.method, 'all-upper')), ',')
195
+ .appendLine('headers: this._options.headers,')
196
+ .appendLineIf(!!((_b = (_a = endpoint.requestBody) === null || _a === void 0 ? void 0 : _a.content[0]) === null || _b === void 0 ? void 0 : _b.schema), 'body: JSON.stringify(body),');
197
+ }, { indent: true, multiline: true }), { indent: false })
198
+ .appendLine(' as ', (builder) => this.generateServiceMethodReturnValue(ctx, builder, endpoint), ';');
199
+ }
200
+ getTypeName(ctx, builder, schemaId) {
201
+ if (!schemaId) {
202
+ return this.getAnyType(ctx);
203
+ }
204
+ const modelInfo = ctx.input.models[schemaId];
205
+ if (!modelInfo) {
206
+ return this.getAnyType(ctx);
207
+ }
208
+ modelInfo.imports.forEach((x) => builder.addImport(x.name, x.modulePath));
209
+ return modelInfo.component;
210
+ }
211
+ shouldGenerateInterface(ctx) {
212
+ return ctx.config.clientFileKind === 'interface' || ctx.config.clientFileKind === 'class-and-interface';
213
+ }
214
+ shouldGenerateClass(ctx) {
215
+ return ctx.config.clientFileKind === 'class' || ctx.config.clientFileKind === 'class-and-interface';
216
+ }
217
+ getInterfaceName(ctx) {
218
+ return toCasing(ctx.service.name, ctx.config.interfaceNameCasing);
219
+ }
220
+ getClassName(ctx) {
221
+ return toCasing(ctx.service.name, ctx.config.classNameCasing);
222
+ }
223
+ getDefaultOptionsConstantName(ctx) {
224
+ return toCasing(this.getClassName(ctx) + '-default-options', ctx.config.constantCasing);
225
+ }
226
+ getInterfaceFilePath(ctx) {
227
+ var _a, _b;
228
+ return resolve(ctx.config.outputDir, (_a = ctx.config.clientInterfaceDirPath) !== null && _a !== void 0 ? _a : ctx.config.clientDirPath, `${toCasing(ctx.service.name, (_b = ctx.config.interfaceFileNameCasing) !== null && _b !== void 0 ? _b : ctx.config.fileNameCasing)}.ts`);
229
+ }
230
+ getClassFilePath(ctx) {
231
+ return resolve(ctx.config.outputDir, ctx.config.clientDirPath, `${toCasing(ctx.service.name, ctx.config.fileNameCasing)}.ts`);
232
+ }
233
+ getUtilPath(ctx, fileName) {
234
+ return resolve(ctx.config.outputDir, ctx.config.utilsDirPath, fileName);
235
+ }
236
+ }
@@ -0,0 +1,79 @@
1
+ import { dirname, resolve } from 'path';
2
+ import { ensureDirSync, writeFileSync, copyFileSync, readdirSync } from 'fs-extra';
3
+ import { OpenApiServicesGenerationProviderBase, Factory } from '@goast/core';
4
+ import { DefaultTypeScriptFetchClientGenerator } from './fetch-client-generator';
5
+ import { defaultTypeScriptFetchClientsGeneratorConfig, } from './models';
6
+ import { TypeScriptFileBuilder } from '../../../file-builder';
7
+ import { ImportExportCollection } from '../../../import-collection';
8
+ export class TypeScriptClientsGenerator extends OpenApiServicesGenerationProviderBase {
9
+ constructor(clientGeneratorFactory) {
10
+ super();
11
+ this._clientGeneratorFactory =
12
+ clientGeneratorFactory !== null && clientGeneratorFactory !== void 0 ? clientGeneratorFactory : Factory.fromValue(new DefaultTypeScriptFetchClientGenerator());
13
+ }
14
+ initResult() {
15
+ return {
16
+ clients: {},
17
+ clientIndexFilePath: undefined,
18
+ clientInterfaceIndexFilePath: undefined,
19
+ };
20
+ }
21
+ buildContext(context, config) {
22
+ return this.getProviderContext(context, config, defaultTypeScriptFetchClientsGeneratorConfig);
23
+ }
24
+ onGenerate(ctx) {
25
+ this.copyUtilsFiles(ctx);
26
+ const output = super.onGenerate(ctx);
27
+ output.clientIndexFilePath = this.generateIndexFile(ctx);
28
+ return output;
29
+ }
30
+ generateService(ctx, service) {
31
+ const clientGenerator = this._clientGeneratorFactory.create();
32
+ return clientGenerator.generate(Object.assign(Object.assign({}, ctx), { service }));
33
+ }
34
+ addServiceResult(ctx, service, result) {
35
+ ctx.output.clients[service.id] = result;
36
+ }
37
+ generateIndexFile(ctx) {
38
+ if (!this.shouldGenerateIndexFile(ctx)) {
39
+ return undefined;
40
+ }
41
+ const filePath = this.getIndexFilePath(ctx);
42
+ console.log(`Generating index file to ${filePath}...`);
43
+ ensureDirSync(dirname(filePath));
44
+ const builder = new TypeScriptFileBuilder(filePath, ctx.config);
45
+ this.generateIndexFileContent(ctx, builder);
46
+ writeFileSync(filePath, builder.toString());
47
+ return filePath;
48
+ }
49
+ getIndexFilePath(ctx) {
50
+ var _a;
51
+ return resolve(ctx.config.outputDir, (_a = ctx.config.indexFilePath) !== null && _a !== void 0 ? _a : 'clients.ts');
52
+ }
53
+ shouldGenerateIndexFile(ctx) {
54
+ return ctx.config.indexFilePath !== null;
55
+ }
56
+ generateIndexFileContent(ctx, builder) {
57
+ var _a, _b;
58
+ const exports = new ImportExportCollection();
59
+ for (const clientId in ctx.output.clients) {
60
+ const client = ctx.output.clients[clientId];
61
+ if ((_a = client.class) === null || _a === void 0 ? void 0 : _a.filePath) {
62
+ exports.addExport(client.class.component, client.class.filePath);
63
+ }
64
+ if ((_b = client.interface) === null || _b === void 0 ? void 0 : _b.filePath) {
65
+ exports.addExport(client.interface.component, client.interface.filePath);
66
+ }
67
+ }
68
+ exports.writeTo(builder);
69
+ }
70
+ copyUtilsFiles(ctx) {
71
+ const sourceDir = resolve(dirname(require.resolve('@goast/typescript')), '../assets/client/fetch');
72
+ const targetDir = resolve(ctx.config.outputDir, ctx.config.utilsDirPath);
73
+ ensureDirSync(targetDir);
74
+ const files = readdirSync(sourceDir);
75
+ for (const file of files) {
76
+ copyFileSync(resolve(sourceDir, file), resolve(targetDir, file));
77
+ }
78
+ }
79
+ }
@@ -0,0 +1,3 @@
1
+ export * from './fetch-client-generator';
2
+ export * from './fetch-clients-generator';
3
+ export * from './models';
@@ -0,0 +1,2 @@
1
+ import { defaultTypeScriptGeneratorConfig } from '../../../config';
2
+ export const defaultTypeScriptFetchClientsGeneratorConfig = Object.assign(Object.assign({}, defaultTypeScriptGeneratorConfig), { fileNameCasing: { casing: 'kebab', suffix: '-client' }, interfaceFileNameCasing: null, classNameCasing: { casing: 'pascal', suffix: 'Client' }, interfaceNameCasing: { prefix: 'I', casing: 'pascal', suffix: 'Client' }, methodCasing: 'camel', clientFileKind: 'class', clientDirPath: 'clients', clientInterfaceDirPath: 'clients/interfaces', indexFilePath: 'clients.ts', interfaceIndexFilePath: 'clients.ts', utilsDirPath: 'utils' });
@@ -0,0 +1,112 @@
1
+ import { extname } from 'path';
2
+ import { StringBuilder } from '@goast/core';
3
+ import { getModulePathRelativeToFile } from './utils';
4
+ export class ImportExportCollection {
5
+ constructor() {
6
+ this._imports = new Map();
7
+ this._exports = new Map();
8
+ }
9
+ get hasImports() {
10
+ return this._imports.size > 0;
11
+ }
12
+ get hasExports() {
13
+ return this._exports.size > 0;
14
+ }
15
+ get imports() {
16
+ return Array.from(this._imports.entries())
17
+ .map(([fromModule, importNames]) => Array.from(importNames).map((importName) => ({
18
+ kind: this.getImportKind(fromModule),
19
+ modulePath: fromModule,
20
+ name: importName,
21
+ })))
22
+ .flat();
23
+ }
24
+ addImport(importName, fromModule) {
25
+ const existingImport = this._imports.get(fromModule);
26
+ if (existingImport) {
27
+ existingImport.add(importName);
28
+ }
29
+ else {
30
+ this._imports.set(fromModule, new Set([importName]));
31
+ }
32
+ }
33
+ addExport(exportName, fromModule) {
34
+ const existingExport = this._exports.get(fromModule);
35
+ if (existingExport) {
36
+ existingExport.add(exportName);
37
+ }
38
+ else {
39
+ this._exports.set(fromModule, new Set([exportName]));
40
+ }
41
+ }
42
+ clear() {
43
+ this._imports.clear();
44
+ this._exports.clear();
45
+ }
46
+ toString(options, filePath, importModuleTransformer) {
47
+ const builder = new StringBuilder(options);
48
+ this.writeTo(builder, filePath, importModuleTransformer);
49
+ return builder.toString();
50
+ }
51
+ writeTo(builder, filePath, importModuleTransformer) {
52
+ if (!filePath) {
53
+ filePath = builder.filePath;
54
+ }
55
+ if (!importModuleTransformer) {
56
+ importModuleTransformer = builder.options.importModuleTransformer;
57
+ if (!importModuleTransformer) {
58
+ throw new Error('importModuleTransformer is required');
59
+ }
60
+ }
61
+ if (this._imports.size > 0) {
62
+ const sortedImports = this.sortAndResolve(this._imports, filePath, importModuleTransformer);
63
+ this.writeImportsExports(builder, 'import', sortedImports);
64
+ }
65
+ if (this._imports.size > 0 && this._exports.size > 0) {
66
+ builder.appendLine();
67
+ }
68
+ if (this._exports.size > 0) {
69
+ const sortedExports = this.sortAndResolve(this._exports, filePath, importModuleTransformer);
70
+ this.writeImportsExports(builder, 'export', sortedExports);
71
+ }
72
+ }
73
+ writeImportsExports(builder, keyword, data) {
74
+ const moduleData = [];
75
+ const fileData = [];
76
+ for (const x of data) {
77
+ if (x[1] === 'module') {
78
+ moduleData.push(x);
79
+ }
80
+ else {
81
+ fileData.push(x);
82
+ }
83
+ }
84
+ for (const [fromModule, _, names] of moduleData) {
85
+ builder.appendLine(`${keyword} { ${names.join(', ')} } from '${fromModule}';`);
86
+ }
87
+ if (moduleData.length > 0 && fileData.length > 0) {
88
+ builder.appendLine();
89
+ }
90
+ for (const [fromModule, _, names] of fileData) {
91
+ builder.appendLine(`${keyword} { ${names.join(', ')} } from '${fromModule}';`);
92
+ }
93
+ }
94
+ sortAndResolve(data, filePath, importModuleTransformer) {
95
+ return Array.from(data.entries())
96
+ .sort(([fromModuleA], [fromModuleB]) => fromModuleA.localeCompare(fromModuleB))
97
+ .map(([fromModule, names]) => [...this.resolveModulePath(fromModule, filePath, importModuleTransformer), Array.from(names).sort()]);
98
+ }
99
+ getImportKind(fromModule) {
100
+ const extName = extname(fromModule).toUpperCase();
101
+ return extName === '.TS' || extName === '.JS' || extName === '.JSON' ? 'file' : 'module';
102
+ }
103
+ resolveModulePath(fromModule, filePath, importModuleTransformer) {
104
+ const kind = this.getImportKind(fromModule);
105
+ return [
106
+ filePath && kind === 'file'
107
+ ? getModulePathRelativeToFile(filePath, fromModule, importModuleTransformer)
108
+ : fromModule,
109
+ kind,
110
+ ];
111
+ }
112
+ }
@@ -0,0 +1,48 @@
1
+ import { dirname, relative } from 'path';
2
+ function doubleQuotedToSingleQuoted(value) {
3
+ return `'${value.slice(1, -1).replace(/'/g, "\\'")}'`;
4
+ }
5
+ export function toTypeScriptStringLiteral(value, useSingleQuotes) {
6
+ return useSingleQuotes ? doubleQuotedToSingleQuoted(JSON.stringify(value)) : JSON.stringify(value);
7
+ }
8
+ export function toTypeScriptPropertyName(value, useSingleQuotes) {
9
+ if (value.match(/^[a-zA-Z_$][a-zA-Z_$0-9]*$/)) {
10
+ return value;
11
+ }
12
+ return useSingleQuotes ? doubleQuotedToSingleQuoted(JSON.stringify(value)) : JSON.stringify(value);
13
+ }
14
+ export function getModulePathRelativeToFile(from, to, transformer) {
15
+ return transformModulePath(relative(dirname(from), to).replace(/\\/g, '/'), transformer);
16
+ }
17
+ export function getModulePathRelativeToDirectory(from, to, transformer) {
18
+ return transformModulePath(relative(from, to).replace(/\\/g, '/'), transformer);
19
+ }
20
+ function transformModulePath(modulePath, transformer) {
21
+ if (!modulePath.startsWith('.')) {
22
+ modulePath = `./${modulePath}`;
23
+ }
24
+ if (transformer === 'omit-extension') {
25
+ return modulePath.replace(/\.[^/.]+$/, '');
26
+ }
27
+ else if (transformer === 'js-extension') {
28
+ return modulePath.replace(/\.[^/.]+$/, '.js');
29
+ }
30
+ else if (typeof transformer === 'function') {
31
+ return transformer(modulePath);
32
+ }
33
+ return modulePath;
34
+ }
35
+ export function modifyString(value, modifier, ...args) {
36
+ var _a;
37
+ if (typeof modifier === 'string') {
38
+ return modifier;
39
+ }
40
+ if (modifier instanceof RegExp) {
41
+ const match = value.match(modifier);
42
+ return (_a = match === null || match === void 0 ? void 0 : match[0]) !== null && _a !== void 0 ? _a : value;
43
+ }
44
+ if (typeof modifier === 'function') {
45
+ return modifier(value, ...args);
46
+ }
47
+ return value;
48
+ }
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@goast/typescript",
3
+ "version": "0.0.1",
4
+ "repository": "https://github.com/MaSch0212/goast.git",
5
+ "author": {
6
+ "name": "Marc Schmidt (MaSch0212)",
7
+ "url": "https://github.com/MaSch0212"
8
+ },
9
+ "license": "MIT",
10
+ "type": "module",
11
+ "dependencies": {
12
+ "fs-extra": "^11.1.1",
13
+ "node-fetch": "^2.6.11",
14
+ "yaml": "^2.3.1",
15
+ "@goast/core": "0.1.1",
16
+ "tslib": "^2.3.0"
17
+ },
18
+ "module": "./esm/index.js",
19
+ "main": "./cjs/index.js",
20
+ "types": "./types/index.d.ts",
21
+ "exports": {
22
+ ".": {
23
+ "import": "./esm/index.js",
24
+ "require": "./cjs/index.js",
25
+ "types": "./types/index.d.ts"
26
+ },
27
+ "./package.json": "./package.json"
28
+ }
29
+ }
@@ -0,0 +1,6 @@
1
+ export * from './lib/common-results';
2
+ export * from './lib/config';
3
+ export * from './lib/generators';
4
+ export * from './lib/file-builder';
5
+ export * from './lib/import-collection';
6
+ export * from './lib/utils';
@@ -0,0 +1,11 @@
1
+ export type TypeScriptComponentOutput = {
2
+ component: string;
3
+ filePath?: string;
4
+ imports: TypeScriptImport[];
5
+ };
6
+ export type TypeScriptImportKind = 'module' | 'file';
7
+ export type TypeScriptImport = {
8
+ kind: TypeScriptImportKind;
9
+ modulePath: string;
10
+ name: string;
11
+ };
@@ -0,0 +1,13 @@
1
+ import { DefaultGenerationProviderConfig, OpenApiGeneratorConfig, StringCasing, StringCasingWithOptions } from '@goast/core';
2
+ import { ImportModuleTransformer } from './utils';
3
+ export type TypeScriptGeneratorConfig = OpenApiGeneratorConfig & {
4
+ importModuleTransformer: ImportModuleTransformer;
5
+ typeNameCasing: StringCasing | StringCasingWithOptions;
6
+ methodNameCasing: StringCasing | StringCasingWithOptions;
7
+ propertyNameCasing: StringCasing | StringCasingWithOptions;
8
+ enumValueNameCasing: StringCasing | StringCasingWithOptions;
9
+ constantCasing: StringCasing | StringCasingWithOptions;
10
+ preferUnknown: boolean;
11
+ useSingleQuotes: boolean;
12
+ };
13
+ export declare const defaultTypeScriptGeneratorConfig: DefaultGenerationProviderConfig<TypeScriptGeneratorConfig>;
@@ -0,0 +1,110 @@
1
+ import { Nullable, SourceBuilder, TextOrBuilderFn } from '@goast/core';
2
+ import { TypeScriptGeneratorConfig } from './config';
3
+ import { TypeScriptModelGeneratorOutput } from './generators';
4
+ import { ImportExportCollection } from './import-collection';
5
+ export declare class TypeScriptFileBuilder extends SourceBuilder {
6
+ readonly filePath: string | undefined;
7
+ readonly imports: ImportExportCollection;
8
+ get options(): TypeScriptGeneratorConfig;
9
+ constructor(filePath: string | undefined, options: TypeScriptGeneratorConfig);
10
+ addImport(name: string, moduleNameOrfilePath: string): this;
11
+ addExport(name: string, filePath: string): this;
12
+ clear(): void;
13
+ toString(addPadding?: boolean): string;
14
+ appendGenericArguments(...genericArguments: Nullable<TextOrBuilderFn<this>>[]): this;
15
+ appendParameter(parameter: Nullable<TextOrBuilderFn<this>> | TypeScriptParameterOptions): this | undefined;
16
+ appendParameters(...parameters: (Nullable<TextOrBuilderFn<this>> | TypeScriptParameterOptions)[]): this;
17
+ appendObjectLiteral(...properties: Nullable<TextOrBuilderFn<this>>[]): this;
18
+ appendArrayLiteral(...items: Nullable<TextOrBuilderFn<this>>[]): this;
19
+ appendCodeBlock(...content: Nullable<TextOrBuilderFn<this>>[]): this;
20
+ appendComment(style: '/***/' | '/**/' | '//', comment: Nullable<TextOrBuilderFn<this>>): this;
21
+ appendDocumentation(documentation: Nullable<TextOrBuilderFn<this>>): this;
22
+ appendModelUsage(type: TypeScriptModelGeneratorOutput): this;
23
+ appendExternalTypeUsage(type: TypeScriptExternalTypeOptions): this;
24
+ appendTypeDeclaration(options: TypeScriptTypeDeclarationOptions): this;
25
+ appendAnnotation(options: TypeScriptAnnotationOptions): this;
26
+ appendAnnotations(annotations: TypeScriptAnnotationOptions[], separator: string): this;
27
+ appendClass(options: TypeScriptClassOptions): this;
28
+ appendConstructor(options: TypeScriptConstructorOptions): this;
29
+ appendMethod(options: TypeScriptMethodOptions): this;
30
+ appendProperty(options: TypeScriptPropertyOptions): this;
31
+ appendFunction(options: TypeScriptFunctionOptions): this;
32
+ }
33
+ export type TypeScriptTypeDeclarationOptions = {
34
+ documentation?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
35
+ omitExport?: boolean;
36
+ name: string;
37
+ type: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
38
+ };
39
+ export type TypeScriptExternalTypeOptions = {
40
+ name: string;
41
+ module: string;
42
+ };
43
+ export type TypeScriptAnnotationOptions = {
44
+ name: string;
45
+ module?: string;
46
+ args?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>[];
47
+ };
48
+ export type TypeScriptClassOptions = {
49
+ documentation?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
50
+ annotations?: TypeScriptAnnotationOptions[];
51
+ omitExport?: boolean;
52
+ isAbstract?: boolean;
53
+ name: string;
54
+ genericArguments?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>[];
55
+ extends?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
56
+ implements?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>[];
57
+ body?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
58
+ };
59
+ export type TypeScriptConstructorOptions = {
60
+ documentation?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
61
+ annotations?: TypeScriptAnnotationOptions[];
62
+ parameters?: (Nullable<TextOrBuilderFn<TypeScriptFileBuilder>> | TypeScriptParameterOptions)[];
63
+ body?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
64
+ };
65
+ export type TypeScriptParameterOptions = {
66
+ name: string;
67
+ isOptional?: boolean;
68
+ type?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>> | TypeScriptExternalTypeOptions;
69
+ genericArguments?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>[];
70
+ defaultValue?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
71
+ };
72
+ export type TypeScriptMethodOptions = {
73
+ documentation?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
74
+ annotations?: TypeScriptAnnotationOptions[];
75
+ accessibility?: 'public' | 'protected' | 'private';
76
+ isAbstract?: boolean;
77
+ isStatic?: boolean;
78
+ isAsync?: boolean;
79
+ name: string;
80
+ genericArguments?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>[];
81
+ parameters?: (Nullable<TextOrBuilderFn<TypeScriptFileBuilder>> | TypeScriptParameterOptions)[];
82
+ returnType?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
83
+ body?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
84
+ };
85
+ export type TypeScriptPropertyOptions = {
86
+ documentation?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
87
+ annotations?: TypeScriptAnnotationOptions[];
88
+ accessibility?: 'public' | 'protected' | 'private';
89
+ isAbstract?: boolean;
90
+ isStatic?: boolean;
91
+ isReadonly?: boolean;
92
+ isOptional?: boolean;
93
+ kind?: 'property' | 'getter' | 'setter' | 'getter-setter';
94
+ name: string;
95
+ type?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
96
+ initializer?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
97
+ getter?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
98
+ getterAnnotations?: TypeScriptAnnotationOptions[];
99
+ setter?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
100
+ setterAnnotations?: TypeScriptAnnotationOptions[];
101
+ };
102
+ export type TypeScriptFunctionOptions = {
103
+ kind?: 'function' | 'arrow-function';
104
+ isAsync?: boolean;
105
+ name?: string;
106
+ parameters: (Nullable<TextOrBuilderFn<TypeScriptFileBuilder>> | TypeScriptParameterOptions)[];
107
+ returnType?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
108
+ body?: Nullable<TextOrBuilderFn<TypeScriptFileBuilder>>;
109
+ singleLine?: boolean;
110
+ };