@opra/cli 0.33.11 → 1.0.0-alpha.7

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 (63) hide show
  1. package/bin/bin/oprimp.mjs +1 -1
  2. package/bin/oprimp.mjs +1 -1
  3. package/cjs/code-block.js +17 -0
  4. package/cjs/index.js +1 -1
  5. package/cjs/oprimp-cli.js +9 -9
  6. package/cjs/ts-generator/http-controller-node.js +21 -0
  7. package/cjs/{api-exporter → ts-generator}/index.js +1 -1
  8. package/cjs/ts-generator/processors/clean-directory.js +35 -0
  9. package/cjs/ts-generator/processors/process-data-types.js +252 -0
  10. package/cjs/ts-generator/processors/process-document.js +60 -0
  11. package/cjs/ts-generator/processors/process-http-api.js +45 -0
  12. package/cjs/ts-generator/processors/process-http-controller.js +189 -0
  13. package/cjs/ts-generator/ts-file.js +101 -0
  14. package/cjs/ts-generator/ts-generator.js +108 -0
  15. package/cjs/ts-generator/utils/locate-named-type.js +16 -0
  16. package/cjs/{utils → ts-generator/utils}/string-utils.js +17 -16
  17. package/esm/code-block.js +13 -0
  18. package/esm/index.js +1 -1
  19. package/esm/oprimp-cli.js +9 -9
  20. package/esm/ts-generator/http-controller-node.js +18 -0
  21. package/esm/ts-generator/index.js +1 -0
  22. package/esm/ts-generator/processors/clean-directory.js +30 -0
  23. package/esm/ts-generator/processors/process-data-types.js +241 -0
  24. package/esm/ts-generator/processors/process-document.js +55 -0
  25. package/esm/ts-generator/processors/process-http-api.js +40 -0
  26. package/esm/ts-generator/processors/process-http-controller.js +184 -0
  27. package/esm/ts-generator/ts-file.js +96 -0
  28. package/esm/ts-generator/ts-generator.js +103 -0
  29. package/esm/ts-generator/utils/locate-named-type.js +12 -0
  30. package/esm/{utils → ts-generator/utils}/string-utils.js +17 -16
  31. package/package.json +8 -6
  32. package/types/code-block.d.ts +5 -0
  33. package/types/{api-exporter/file-writer.d.ts → file-writer.d.ts} +1 -1
  34. package/types/index.d.ts +1 -1
  35. package/types/ts-generator/http-controller-node.d.ts +1 -0
  36. package/types/ts-generator/index.d.ts +1 -0
  37. package/types/ts-generator/processors/clean-directory.d.ts +2 -0
  38. package/types/ts-generator/processors/process-data-types.d.ts +30 -0
  39. package/types/ts-generator/processors/process-document.d.ts +8 -0
  40. package/types/ts-generator/processors/process-http-api.d.ts +3 -0
  41. package/types/ts-generator/processors/process-http-controller.d.ts +3 -0
  42. package/types/{api-exporter → ts-generator}/ts-file.d.ts +4 -4
  43. package/types/ts-generator/ts-generator.d.ts +70 -0
  44. package/types/ts-generator/utils/locate-named-type.d.ts +2 -0
  45. package/cjs/api-exporter/api-exporter.js +0 -115
  46. package/cjs/api-exporter/process-resources.js +0 -125
  47. package/cjs/api-exporter/process-types.js +0 -261
  48. package/cjs/api-exporter/ts-file.js +0 -104
  49. package/cjs/utils/get-caller-file.util.js +0 -24
  50. package/esm/api-exporter/api-exporter.js +0 -110
  51. package/esm/api-exporter/index.js +0 -1
  52. package/esm/api-exporter/process-resources.js +0 -120
  53. package/esm/api-exporter/process-types.js +0 -249
  54. package/esm/api-exporter/ts-file.js +0 -99
  55. package/esm/utils/get-caller-file.util.js +0 -20
  56. package/types/api-exporter/api-exporter.d.ts +0 -46
  57. package/types/api-exporter/index.d.ts +0 -1
  58. package/types/api-exporter/process-resources.d.ts +0 -4
  59. package/types/api-exporter/process-types.d.ts +0 -62
  60. package/types/utils/get-caller-file.util.d.ts +0 -1
  61. /package/cjs/{api-exporter/file-writer.js → file-writer.js} +0 -0
  62. /package/esm/{api-exporter/file-writer.js → file-writer.js} +0 -0
  63. /package/types/{utils → ts-generator/utils}/string-utils.d.ts +0 -0
@@ -1,261 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateMappedTypeDefinition = exports.generateMixinTypeDefinition = exports.generateSimpleTypeDefinition = exports.generateComplexTypeDefinition = exports.generateEnumTypeDefinition = exports.resolveTypeNameOrDef = exports.generateTypeFile = exports.processTypes = void 0;
4
- const tslib_1 = require("tslib");
5
- const node_path_1 = tslib_1.__importDefault(require("node:path"));
6
- const common_1 = require("@opra/common");
7
- const string_utils_js_1 = require("../utils/string-utils.js");
8
- const internalTypeNames = ['any', 'boolean', 'bigint', 'number', 'null', 'string', 'object'];
9
- /**
10
- *
11
- */
12
- async function processTypes() {
13
- const { document } = this;
14
- for (const dataType of document.types.values()) {
15
- await this.generateTypeFile(dataType);
16
- }
17
- }
18
- exports.processTypes = processTypes;
19
- /**
20
- *
21
- * @param dataType
22
- */
23
- async function generateTypeFile(dataType) {
24
- const typeName = dataType.name;
25
- if (typeName === 'any')
26
- return;
27
- if (!typeName)
28
- throw new TypeError(`DataType has no name`);
29
- const typesTs = this.addFile('/types.ts', true);
30
- let filePath = '';
31
- if (dataType instanceof common_1.SimpleType)
32
- filePath = '/simple-types.ts';
33
- else {
34
- if (dataType instanceof common_1.EnumType)
35
- filePath = node_path_1.default.join('/enums', typeName + '.ts');
36
- else
37
- filePath = node_path_1.default.join('/types', typeName + '.ts');
38
- }
39
- const file = this.addFile(filePath, true);
40
- if (file.exportTypes.includes(typeName))
41
- return file;
42
- file.exportTypes.push(typeName);
43
- const indexTs = this.addFile('/index.ts', true);
44
- indexTs.addExport(file.filename);
45
- file.content += `
46
- /**
47
- * ${typeName}`;
48
- if (dataType.description)
49
- file.content += `
50
- * ${(0, string_utils_js_1.wrapJSDocString)(dataType.description || '')}`;
51
- file.content += `
52
- * @url ${node_path_1.default.posix.join(this.client.serviceUrl, '#types/' + typeName)}
53
- */
54
- export `;
55
- if (dataType instanceof common_1.EnumType)
56
- file.content += await this.generateEnumTypeDefinition({ file, dataType, intent: 'scope' });
57
- else if (dataType instanceof common_1.ComplexType)
58
- file.content += await this.generateComplexTypeDefinition({ file, dataType, intent: 'scope' });
59
- else if (dataType instanceof common_1.SimpleType)
60
- file.content += await this.generateSimpleTypeDefinition({ file, dataType, intent: 'scope' });
61
- else
62
- throw new TypeError(`${dataType.kind} data type (${typeName}) can not be directly exported`);
63
- file.content += '\n';
64
- typesTs.addExport(file.filename);
65
- return file;
66
- }
67
- exports.generateTypeFile = generateTypeFile;
68
- /**
69
- *
70
- */
71
- async function resolveTypeNameOrDef(args) {
72
- const { dataType } = args;
73
- if (dataType.name && !dataType.isEmbedded) {
74
- if (internalTypeNames.includes(dataType.name))
75
- return dataType.name;
76
- const f = await this.generateTypeFile(dataType);
77
- if (!f)
78
- return '';
79
- args.file.addImport(f.filename, [dataType.name], true);
80
- return dataType.name;
81
- }
82
- if (dataType instanceof common_1.SimpleType)
83
- return this.generateSimpleTypeDefinition({ ...args, dataType });
84
- if (dataType instanceof common_1.EnumType)
85
- return this.generateEnumTypeDefinition({ ...args, dataType });
86
- if (dataType instanceof common_1.MixinType)
87
- return this.generateMixinTypeDefinition({ ...args, dataType });
88
- if (dataType instanceof common_1.MappedType)
89
- return this.generateMappedTypeDefinition({ ...args, dataType });
90
- if (dataType instanceof common_1.ComplexType)
91
- return this.generateComplexTypeDefinition({ ...args, dataType });
92
- return '';
93
- }
94
- exports.resolveTypeNameOrDef = resolveTypeNameOrDef;
95
- /**
96
- *
97
- */
98
- async function generateEnumTypeDefinition(args) {
99
- const { dataType } = args;
100
- if (args.intent === 'field')
101
- return '(' +
102
- Object.keys(dataType.values)
103
- .map(t => `'${t}'`)
104
- .join(' | ') +
105
- ')';
106
- if (args.intent !== 'scope')
107
- throw new TypeError(`Can't generate EnumType for "${args.intent}" intent`);
108
- if (!dataType.name)
109
- throw new TypeError(`Name required to generate EnumType for "${args.intent}" intent`);
110
- let out = `enum ${dataType.name} {\n\t`;
111
- for (const [value, info] of Object.entries(dataType.values)) {
112
- // Print JSDoc
113
- let jsDoc = '';
114
- if (dataType.values[value].description)
115
- jsDoc += ` * ${dataType.values[value].description}\n`;
116
- if (jsDoc)
117
- out += `/**\n${jsDoc} */\n`;
118
- out += `${info.key || value} = ` +
119
- (typeof value === 'number' ? value : ('\'' + (String(value)).replace('\'', '\\\'')) + '\'');
120
- out += ',\n\n';
121
- }
122
- return out + '\b}';
123
- }
124
- exports.generateEnumTypeDefinition = generateEnumTypeDefinition;
125
- /**
126
- *
127
- */
128
- async function generateComplexTypeDefinition(args) {
129
- const { intent, dataType } = args;
130
- if (intent === 'scope' && !dataType.name)
131
- throw new TypeError(`Name required to generate ComplexType for "${args.intent}" intent`);
132
- let out = intent === 'scope' ? `interface ${dataType.name} ` : '';
133
- if (dataType.base) {
134
- const base = await this.resolveTypeNameOrDef({ file: args.file, dataType: dataType.base, intent: 'extends' });
135
- const omitFields = [...dataType.own.fields.keys()]
136
- .filter(k => dataType.base?.fields.has(k));
137
- const baseDef = omitFields.length
138
- ? `Omit<${base}, ${omitFields.map(x => "'" + x + "'").join(' | ')}>`
139
- : `${base}`;
140
- if (intent === 'scope')
141
- out += `extends ${baseDef} `;
142
- else {
143
- out += baseDef;
144
- if (!dataType.own.fields.size)
145
- return out;
146
- out += ' & ';
147
- }
148
- }
149
- out += '{\n\t';
150
- let i = 0;
151
- for (const field of dataType.own.fields.values()) {
152
- if (i++)
153
- out += '\n';
154
- // Print JSDoc
155
- out += `/**\n * ${field.description || ''}\n`;
156
- if (field.default)
157
- out += ` * @default ` + field.default + '\n';
158
- // if (field.format)
159
- // jsDoc += ` * @format ` + field.format + '\n';
160
- if (field.exclusive)
161
- out += ` * @exclusive\n`;
162
- if (field.readonly)
163
- out += ` * @readonly\n`;
164
- if (field.writeonly)
165
- out += ` * @writeonly\n`;
166
- if (field.deprecated)
167
- out += ` * @deprecated ` + (typeof field.deprecated === 'string' ? field.deprecated : '') + '\n';
168
- out += ' */\n';
169
- // Print field name
170
- if (field.readonly)
171
- out += 'readonly ';
172
- out += `${field.name}${field.required ? '' : '?'}: `;
173
- if (field.fixed) {
174
- const t = typeof field.fixed;
175
- out += `${t === 'number' || t === 'boolean' || t === 'bigint' ? field.fixed : "'" + field.fixed + "'"}\n`;
176
- }
177
- else {
178
- out += await this.resolveTypeNameOrDef({ file: args.file, dataType: field.type, intent: 'field' }) +
179
- `${field.isArray ? '[]' : ''};\n`;
180
- }
181
- }
182
- if (dataType.additionalFields)
183
- out += '[key: string]: any;\n';
184
- return out + '\b}';
185
- }
186
- exports.generateComplexTypeDefinition = generateComplexTypeDefinition;
187
- /**
188
- *
189
- */
190
- async function generateSimpleTypeDefinition(args) {
191
- const { intent, dataType } = args;
192
- if (intent === 'scope' && !dataType.name)
193
- throw new TypeError(`Name required to generate SimpleType for "${args.intent}" intent`);
194
- let out = intent === 'scope' ? `type ${dataType.name} = ` : '';
195
- if (dataType.extendsFrom('boolean'))
196
- out += 'boolean';
197
- else if (dataType.extendsFrom('string'))
198
- out += 'string';
199
- else if (dataType.extendsFrom('number') || dataType.extendsFrom('integer'))
200
- out += 'number';
201
- else if (dataType.extendsFrom('date') || dataType.extendsFrom('datetime'))
202
- out += 'Date';
203
- else if (dataType.extendsFrom('approxdate') || dataType.extendsFrom('approxdatetime'))
204
- out += 'string';
205
- else if (dataType.extendsFrom('bigint'))
206
- out += 'bigint';
207
- else if (dataType.extendsFrom('object'))
208
- out += 'object';
209
- else
210
- out += 'any';
211
- return intent === 'scope' ? out + ';' : out;
212
- }
213
- exports.generateSimpleTypeDefinition = generateSimpleTypeDefinition;
214
- /**
215
- *
216
- */
217
- async function generateMixinTypeDefinition(args) {
218
- const { file, dataType, intent } = args;
219
- return (await Promise.all(dataType.types
220
- .map(t => this.resolveTypeNameOrDef({ file, dataType: t, intent })))).map(t => t.includes('|') ? '(' + t + ')' : t)
221
- .join(intent === 'extends' ? ', ' : ' & ');
222
- }
223
- exports.generateMixinTypeDefinition = generateMixinTypeDefinition;
224
- /**
225
- *
226
- */
227
- async function generateMappedTypeDefinition(args) {
228
- const { dataType } = args;
229
- const typeDef = await this.resolveTypeNameOrDef({ ...args, dataType: dataType.base });
230
- const pick = dataType.pick?.length ? dataType.pick : undefined;
231
- const omit = !pick && dataType.omit?.length ? dataType.omit : undefined;
232
- const partial = dataType.partial === true || Array.isArray(dataType.partial) && dataType.partial.length > 0
233
- ? dataType.partial
234
- : undefined;
235
- if (!(pick || omit || partial))
236
- return typeDef;
237
- let out = '';
238
- if (partial)
239
- out += 'Partial<';
240
- if (pick)
241
- out += 'Pick<';
242
- else if (omit)
243
- out += 'Omit<';
244
- out += typeDef;
245
- if (omit || pick)
246
- out += ', ' + (omit || pick)
247
- .filter(x => !!x)
248
- .map(x => `'${x}'`)
249
- .join(' | ') +
250
- '>';
251
- if (partial) {
252
- if (Array.isArray(partial))
253
- out += ', ' + partial
254
- .filter(x => !!x)
255
- .map(x => `'${x}'`)
256
- .join(' | ');
257
- out += '>';
258
- }
259
- return out;
260
- }
261
- exports.generateMappedTypeDefinition = generateMappedTypeDefinition;
@@ -1,104 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TsFile = void 0;
4
- const tslib_1 = require("tslib");
5
- const path_1 = tslib_1.__importDefault(require("path"));
6
- const putil_flattentext_1 = tslib_1.__importDefault(require("putil-flattentext"));
7
- class TsFile {
8
- constructor(filename) {
9
- this.filename = filename;
10
- this.imports = {};
11
- this.exportFiles = {};
12
- this.exportTypes = [];
13
- this.header = '';
14
- this.content = '';
15
- this.addImport = (filename, items, typeImport) => {
16
- if (isLocalFile(filename)) {
17
- filename = path_1.default.relative(this.dirname, path_1.default.resolve(this.dirname, filename));
18
- if (!filename.startsWith('.'))
19
- filename = './' + filename;
20
- }
21
- if (filename.endsWith('.d.ts'))
22
- filename = filename.substring(0, filename.length - 5);
23
- if (filename.endsWith('.ts') || filename.endsWith('.js'))
24
- filename = filename.substring(0, filename.length - 3);
25
- const imp = (this.imports[filename] = this.imports[filename] || { items: [], typeImport });
26
- if (!typeImport)
27
- imp.typeImport = false;
28
- items?.forEach(x => {
29
- if (!imp.items.includes(x))
30
- imp.items.push(x);
31
- });
32
- };
33
- this.addExport = (filename, types) => {
34
- if (isLocalFile(filename)) {
35
- filename = path_1.default.relative(this.dirname, path_1.default.resolve(this.dirname, filename));
36
- if (!filename.startsWith('.'))
37
- filename = './' + filename;
38
- }
39
- if (filename.endsWith('.d.ts'))
40
- filename = filename.substring(0, filename.length - 5);
41
- if (filename.endsWith('.ts') || filename.endsWith('.js'))
42
- filename = filename.substring(0, filename.length - 3);
43
- this.exportFiles[filename] = this.exportFiles[filename] || [];
44
- types?.forEach(x => {
45
- if (!this.exportFiles[filename].includes(x))
46
- this.exportFiles[filename].push(x);
47
- });
48
- };
49
- this.dirname = path_1.default.dirname(filename);
50
- }
51
- generate(options) {
52
- let output = '/* #!oprimp_auto_generated!# !! Do NOT remove this line */\n' +
53
- (this.header ? (0, putil_flattentext_1.default)(this.header) + '\n\n' : '\n');
54
- const importStr = Object.keys(this.imports)
55
- .sort((a, b) => {
56
- if (a.startsWith('@'))
57
- return -1;
58
- if (b.startsWith('@'))
59
- return 1;
60
- if (!a.startsWith('.'))
61
- return -1;
62
- if (!b.startsWith('.'))
63
- return 1;
64
- return a.toLowerCase().localeCompare(b.toLowerCase());
65
- })
66
- .map(filename => {
67
- const imp = this.imports[filename];
68
- let relFile = filename;
69
- if (!isPackageName(filename)) {
70
- if (options?.importExt)
71
- relFile += '.js';
72
- }
73
- return `import${imp.typeImport ? ' type' : ''} ${imp.items.length ? '{ ' + imp.items.join(', ') + ' } from ' : ''}'${relFile}';`;
74
- })
75
- .join('\n');
76
- if (importStr)
77
- output += (0, putil_flattentext_1.default)(importStr) + '\n';
78
- output += (0, putil_flattentext_1.default)(this.content);
79
- const exportStr = Object.keys(this.exportFiles)
80
- .sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
81
- .map(filename => {
82
- const types = this.exportFiles[filename];
83
- let relFile = filename;
84
- if (!isPackageName(filename)) {
85
- if (options?.importExt)
86
- relFile += '.js';
87
- }
88
- return `export ${types.length ? '{ ' + types.join(', ') + ' }' : '*'} from '${relFile}';`;
89
- })
90
- .join('\n');
91
- if (exportStr)
92
- output += (0, putil_flattentext_1.default)(exportStr) + '\n';
93
- return output;
94
- }
95
- }
96
- exports.TsFile = TsFile;
97
- function isLocalFile(s) {
98
- return typeof s === 'string' &&
99
- (s.startsWith('.') || s.startsWith('/'));
100
- }
101
- const PACKAGENAME_PATTERN = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
102
- function isPackageName(s) {
103
- return PACKAGENAME_PATTERN.test(s);
104
- }
@@ -1,24 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getCallerFile = void 0;
4
- const PATH_PATTERN = /^(?:file:\/\/)?(.+)$/;
5
- function getCallerFile(position = 1) {
6
- if (position >= Error.stackTraceLimit) {
7
- throw new TypeError('getCallerFile(position) requires position be less then Error.stackTraceLimit but position was: `' +
8
- position + '` and Error.stackTraceLimit was: `' + Error.stackTraceLimit + '`');
9
- }
10
- const oldPrepareStackTrace = Error.prepareStackTrace;
11
- Error.prepareStackTrace = (_, stack) => stack;
12
- const stack = new Error().stack;
13
- Error.prepareStackTrace = oldPrepareStackTrace;
14
- if (stack !== null && typeof stack === 'object') {
15
- // stack[0] holds this file
16
- // stack[1] holds where this function was called
17
- const s = stack[position] ?
18
- stack[position].getFileName() : undefined;
19
- const m = s ? PATH_PATTERN.exec(s) : undefined;
20
- return m ? m[1] : '';
21
- }
22
- return '';
23
- }
24
- exports.getCallerFile = getCallerFile;
@@ -1,110 +0,0 @@
1
- import chalk from 'chalk';
2
- import fs from 'node:fs';
3
- import path from 'node:path';
4
- import process from 'node:process';
5
- import { OpraHttpClient } from '@opra/client';
6
- import { FileWriter } from './file-writer.js';
7
- import { processResource } from './process-resources.js';
8
- import { generateComplexTypeDefinition, generateEnumTypeDefinition, generateMappedTypeDefinition, generateMixinTypeDefinition, generateSimpleTypeDefinition, generateTypeFile, processTypes, resolveTypeNameOrDef } from './process-types.js';
9
- import { TsFile } from './ts-file.js';
10
- export class ApiExporter {
11
- constructor(config) {
12
- this.files = {};
13
- this.client = new OpraHttpClient(config.serviceUrl);
14
- this.cwd = config.cwd || process.cwd();
15
- this.outDir = path.resolve(this.cwd, config.outDir);
16
- this.logger = config.logger || {
17
- log: () => void 0,
18
- error: () => void 0,
19
- debug: () => void 0,
20
- warn: () => void 0,
21
- verbose: () => void 0,
22
- };
23
- this.fileHeader = config.fileHeader || '';
24
- this.writer = config.writer || new FileWriter();
25
- this.serviceClassName = config.name;
26
- this.importExt = config.importExt;
27
- // this.nsMap = nsMap || new ResponsiveMap(); // implement references later
28
- }
29
- async execute() {
30
- this.logger.log(chalk.cyan('Fetching service metadata from'), chalk.whiteBright(this.client.serviceUrl));
31
- this.document = await this.client.getMetadata();
32
- this.logger.log(chalk.cyan('Retrieved service info:\n'), chalk.white('Title:'), chalk.whiteBright(this.document.info.title), '\n', chalk.white('Version:'), chalk.whiteBright(this.document.info.version), '\n');
33
- this.serviceClassName = (this.serviceClassName || this.document.info.title || 'Service1').replace(/[^\w_$]*/g, '');
34
- this.serviceClassName = this.serviceClassName.charAt(0).toUpperCase() + this.serviceClassName.substring(1);
35
- this.fileHeader += `/*
36
- * ${this.document.info.title}
37
- * Version ${this.document.info.version}
38
- * ${this.client.serviceUrl}
39
- */`;
40
- this.logger.log(chalk.cyan('Removing old files..'));
41
- this.cleanDirectory(this.outDir);
42
- this.logger.log(chalk.cyan(`Generating service interface ( ${chalk.whiteBright(this.serviceClassName)} )`));
43
- fs.mkdirSync(this.outDir, { recursive: true });
44
- this.logger.log(chalk.cyan('Processing types'));
45
- await this.processTypes();
46
- this.logger.log(chalk.cyan('Processing resources'));
47
- const rootTs = this.addFile('/' + this.serviceClassName + '.ts');
48
- await this.processResource(this.document.root, this.serviceClassName, rootTs);
49
- const { importExt } = this;
50
- // Write files
51
- for (const file of Object.values(this.files)) {
52
- const filename = path.join(this.outDir, file.filename);
53
- const targetDir = path.dirname(filename);
54
- fs.mkdirSync(targetDir, { recursive: true });
55
- await this.writer.writeFile(filename, file.generate({ importExt }));
56
- }
57
- }
58
- getFile(filePath) {
59
- return this.files[filePath];
60
- }
61
- addFile(filePath, returnExists) {
62
- if (!(filePath.startsWith('.') || filePath.startsWith('/')))
63
- filePath = './' + filePath;
64
- let file = this.getFile(filePath);
65
- if (file) {
66
- if (returnExists)
67
- return file;
68
- throw new Error(`File "${filePath}" already exists`);
69
- }
70
- file = new TsFile(filePath);
71
- file.header = this.fileHeader;
72
- this.files[file.filename] = file;
73
- return file;
74
- }
75
- cleanDirectory(dirname) {
76
- if (!fs.existsSync(dirname))
77
- return;
78
- const files = fs.readdirSync(dirname);
79
- for (const f of files) {
80
- const absolutePath = path.join(dirname, f);
81
- if (fs.statSync(absolutePath).isDirectory()) {
82
- this.cleanDirectory(absolutePath);
83
- if (!fs.readdirSync(absolutePath).length)
84
- fs.rmdirSync(absolutePath);
85
- continue;
86
- }
87
- if (path.extname(f) === '.ts') {
88
- const contents = fs.readFileSync(absolutePath, 'utf-8');
89
- if (contents.includes('#!oprimp_auto_generated!#')) {
90
- fs.unlinkSync(absolutePath);
91
- }
92
- }
93
- }
94
- }
95
- static async execute(config) {
96
- const exporter = new ApiExporter(config);
97
- await exporter.execute();
98
- }
99
- }
100
- (() => {
101
- ApiExporter.prototype.processResource = processResource;
102
- ApiExporter.prototype.processTypes = processTypes;
103
- ApiExporter.prototype.generateTypeFile = generateTypeFile;
104
- ApiExporter.prototype.generateComplexTypeDefinition = generateComplexTypeDefinition;
105
- ApiExporter.prototype.generateSimpleTypeDefinition = generateSimpleTypeDefinition;
106
- ApiExporter.prototype.resolveTypeNameOrDef = resolveTypeNameOrDef;
107
- ApiExporter.prototype.generateEnumTypeDefinition = generateEnumTypeDefinition;
108
- ApiExporter.prototype.generateMixinTypeDefinition = generateMixinTypeDefinition;
109
- ApiExporter.prototype.generateMappedTypeDefinition = generateMappedTypeDefinition;
110
- })();
@@ -1 +0,0 @@
1
- export * from './api-exporter.js';
@@ -1,120 +0,0 @@
1
- import path from 'node:path';
2
- import { Collection, Container, Singleton, Storage } from '@opra/common';
3
- import { wrapJSDocString } from '../utils/string-utils.js';
4
- export async function processResource(resource, className, tsFile) {
5
- tsFile.addImport('@opra/client', ['kClient', 'OpraHttpClient']);
6
- tsFile.content = `\n
7
- /**
8
- * ${wrapJSDocString(resource.description || '')}
9
- * @class ${className}
10
- * @url ${path.posix.join(this.client.serviceUrl, '#resources/' + className)}
11
- */
12
- export class ${className} {
13
- readonly [kClient]: OpraHttpClient;\n`;
14
- let constructorBody = `
15
- constructor(client: OpraHttpClient) {
16
- this[kClient] = client;\n`;
17
- if (resource instanceof Container) {
18
- for (const child of resource.resources.values()) {
19
- // Determine class name of child resource
20
- let childClassName = child.name.charAt(0).toUpperCase() + child.name.substring(1);
21
- if (child instanceof Container)
22
- childClassName += 'Container';
23
- else
24
- childClassName += 'Resource';
25
- // Create TsFile for child resource
26
- const dir = path.dirname(tsFile.filename);
27
- const basename = dir === '/'
28
- ? 'root'
29
- : path.basename(tsFile.filename, path.extname(tsFile.filename));
30
- const childFile = this.addFile(path.join(dir, basename, child.name + '.ts'));
31
- await this.processResource(child, childClassName, childFile);
32
- tsFile.addImport(childFile.filename, [childClassName]);
33
- tsFile.content += `
34
- /**
35
- * ${wrapJSDocString(child.description || '')}
36
- * @url ${path.posix.join(this.client.serviceUrl, '#resources/' + child.name)}
37
- */
38
- readonly ${child.name}: ${childClassName};\n`;
39
- constructorBody += ` this.${child.name} = new ${childClassName}(client);\n`;
40
- }
41
- }
42
- else if (resource instanceof Collection) {
43
- tsFile.addImport('@opra/client', ['HttpCollectionNode']);
44
- const typeName = resource.type.name || '';
45
- tsFile.addImport(`/types/${typeName}`, [typeName], true);
46
- constructorBody += ` const node = this[kClient].collection('${resource.getFullPath()}');\n`;
47
- for (const [operation, endpoint] of resource.operations.entries()) {
48
- tsFile.content += `
49
- /**
50
- * ${wrapJSDocString(endpoint.description || '')}
51
- */
52
- readonly ${operation}: HttpCollectionNode<${typeName}>['${operation}'];\n`;
53
- constructorBody += ` this.${operation} = node.${operation}.bind(node);\n`;
54
- }
55
- }
56
- else if (resource instanceof Singleton) {
57
- tsFile.addImport('@opra/client', ['HttpSingletonNode']);
58
- const typeName = resource.type.name || '';
59
- tsFile.addImport(`/types/${typeName}`, [typeName], true);
60
- constructorBody += ` const node = this[kClient].singleton('${resource.getFullPath()}');\n`;
61
- for (const [operation, endpoint] of resource.operations.entries()) {
62
- tsFile.content += `
63
- /**
64
- * ${wrapJSDocString(endpoint.description || '')}
65
- */
66
- readonly ${operation}: HttpSingletonNode<${typeName}>['${operation}'];\n`;
67
- constructorBody += ` this.${operation} = node.${operation}.bind(node);\n`;
68
- }
69
- }
70
- else if (resource instanceof Storage) {
71
- tsFile.addImport('@opra/client', ['HttpStorageNode']);
72
- constructorBody += ` const node = this[kClient].storage('${resource.getFullPath()}');\n`;
73
- for (const [operation, endpoint] of resource.operations.entries()) {
74
- tsFile.content += `
75
- /**
76
- * ${wrapJSDocString(endpoint.description || '')}
77
- */
78
- readonly ${operation}: HttpStorageNode['${operation}'];\n`;
79
- constructorBody += ` this.${operation} = node.${operation}.bind(node);\n`;
80
- }
81
- }
82
- if (resource.actions.size) {
83
- tsFile.addImport('@opra/client', ['HttpRequestObservable']);
84
- for (const [action, endpoint] of resource.actions.entries()) {
85
- let returnTypeDef = endpoint.returnType ?
86
- await this.resolveTypeNameOrDef({
87
- file: tsFile,
88
- dataType: endpoint.returnType,
89
- intent: 'field'
90
- })
91
- : 'any';
92
- if (returnTypeDef.length > 40)
93
- returnTypeDef = '\n\t\t' + returnTypeDef + '\n\b\b';
94
- const actionPath = resource.getFullPath() + '/' + action;
95
- let params = '';
96
- for (const prm of endpoint.parameters.values()) {
97
- const paramTypeDef = await this.resolveTypeNameOrDef({
98
- file: tsFile,
99
- dataType: prm.type,
100
- intent: 'field'
101
- }) || 'any';
102
- params += `${prm.name}: ${paramTypeDef}`;
103
- if (prm.isArray)
104
- params += '[]';
105
- params += ';\n';
106
- }
107
- params = params ? '\n\t\t\tparams: {\n\t' + params + '\b}\n\b\b' : '';
108
- tsFile.content += `
109
- /**
110
- * ${wrapJSDocString(endpoint.description || '')}
111
- */
112
- ${action}(${params}): HttpRequestObservable<${returnTypeDef}> {\b
113
- return this[kClient].action('${actionPath}'${params ? ', params' : ''});
114
- }
115
- `;
116
- }
117
- }
118
- tsFile.content += constructorBody + ` }\n\n}\n`;
119
- return tsFile.content;
120
- }