@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.
- package/bin/bin/oprimp.mjs +1 -1
- package/bin/oprimp.mjs +1 -1
- package/cjs/code-block.js +17 -0
- package/cjs/index.js +1 -1
- package/cjs/oprimp-cli.js +9 -9
- package/cjs/ts-generator/http-controller-node.js +21 -0
- package/cjs/{api-exporter → ts-generator}/index.js +1 -1
- package/cjs/ts-generator/processors/clean-directory.js +35 -0
- package/cjs/ts-generator/processors/process-data-types.js +252 -0
- package/cjs/ts-generator/processors/process-document.js +60 -0
- package/cjs/ts-generator/processors/process-http-api.js +45 -0
- package/cjs/ts-generator/processors/process-http-controller.js +189 -0
- package/cjs/ts-generator/ts-file.js +101 -0
- package/cjs/ts-generator/ts-generator.js +108 -0
- package/cjs/ts-generator/utils/locate-named-type.js +16 -0
- package/cjs/{utils → ts-generator/utils}/string-utils.js +17 -16
- package/esm/code-block.js +13 -0
- package/esm/index.js +1 -1
- package/esm/oprimp-cli.js +9 -9
- package/esm/ts-generator/http-controller-node.js +18 -0
- package/esm/ts-generator/index.js +1 -0
- package/esm/ts-generator/processors/clean-directory.js +30 -0
- package/esm/ts-generator/processors/process-data-types.js +241 -0
- package/esm/ts-generator/processors/process-document.js +55 -0
- package/esm/ts-generator/processors/process-http-api.js +40 -0
- package/esm/ts-generator/processors/process-http-controller.js +184 -0
- package/esm/ts-generator/ts-file.js +96 -0
- package/esm/ts-generator/ts-generator.js +103 -0
- package/esm/ts-generator/utils/locate-named-type.js +12 -0
- package/esm/{utils → ts-generator/utils}/string-utils.js +17 -16
- package/package.json +8 -6
- package/types/code-block.d.ts +5 -0
- package/types/{api-exporter/file-writer.d.ts → file-writer.d.ts} +1 -1
- package/types/index.d.ts +1 -1
- package/types/ts-generator/http-controller-node.d.ts +1 -0
- package/types/ts-generator/index.d.ts +1 -0
- package/types/ts-generator/processors/clean-directory.d.ts +2 -0
- package/types/ts-generator/processors/process-data-types.d.ts +30 -0
- package/types/ts-generator/processors/process-document.d.ts +8 -0
- package/types/ts-generator/processors/process-http-api.d.ts +3 -0
- package/types/ts-generator/processors/process-http-controller.d.ts +3 -0
- package/types/{api-exporter → ts-generator}/ts-file.d.ts +4 -4
- package/types/ts-generator/ts-generator.d.ts +70 -0
- package/types/ts-generator/utils/locate-named-type.d.ts +2 -0
- package/cjs/api-exporter/api-exporter.js +0 -115
- package/cjs/api-exporter/process-resources.js +0 -125
- package/cjs/api-exporter/process-types.js +0 -261
- package/cjs/api-exporter/ts-file.js +0 -104
- package/cjs/utils/get-caller-file.util.js +0 -24
- package/esm/api-exporter/api-exporter.js +0 -110
- package/esm/api-exporter/index.js +0 -1
- package/esm/api-exporter/process-resources.js +0 -120
- package/esm/api-exporter/process-types.js +0 -249
- package/esm/api-exporter/ts-file.js +0 -99
- package/esm/utils/get-caller-file.util.js +0 -20
- package/types/api-exporter/api-exporter.d.ts +0 -46
- package/types/api-exporter/index.d.ts +0 -1
- package/types/api-exporter/process-resources.d.ts +0 -4
- package/types/api-exporter/process-types.d.ts +0 -62
- package/types/utils/get-caller-file.util.d.ts +0 -1
- /package/cjs/{api-exporter/file-writer.js → file-writer.js} +0 -0
- /package/esm/{api-exporter/file-writer.js → file-writer.js} +0 -0
- /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
|
-
}
|