@opra/cli 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/bin/oprimp.mjs +2 -0
- package/cjs/index.js +4 -0
- package/cjs/interfaces/file-writer.interface.js +2 -0
- package/cjs/interfaces/logger.interface.js +2 -0
- package/cjs/interfaces/service-generation-context.interface.js +2 -0
- package/cjs/oprimp/delete-files.js +25 -0
- package/cjs/oprimp/file-writer.js +11 -0
- package/cjs/oprimp/generate-service.js +46 -0
- package/cjs/oprimp/generate-types.js +112 -0
- package/cjs/oprimp/index.js +4 -0
- package/cjs/oprimp.js +41 -0
- package/cjs/package.json +3 -0
- package/cjs/utils/get-caller-file.util.js +24 -0
- package/cjs/utils/string-utils.js +64 -0
- package/cjs/utils/ts-file.js +51 -0
- package/esm/index.d.ts +1 -0
- package/esm/index.js +1 -0
- package/esm/interfaces/file-writer.interface.d.ts +3 -0
- package/esm/interfaces/file-writer.interface.js +1 -0
- package/esm/interfaces/logger.interface.d.ts +7 -0
- package/esm/interfaces/logger.interface.js +1 -0
- package/esm/interfaces/service-generation-context.interface.d.ts +16 -0
- package/esm/interfaces/service-generation-context.interface.js +1 -0
- package/esm/oprimp/delete-files.d.ts +1 -0
- package/esm/oprimp/delete-files.js +20 -0
- package/esm/oprimp/file-writer.d.ts +4 -0
- package/esm/oprimp/file-writer.js +6 -0
- package/esm/oprimp/generate-service.d.ts +13 -0
- package/esm/oprimp/generate-service.js +41 -0
- package/esm/oprimp/generate-types.d.ts +2 -0
- package/esm/oprimp/generate-types.js +107 -0
- package/esm/oprimp/index.d.ts +1 -0
- package/esm/oprimp/index.js +1 -0
- package/esm/oprimp.d.ts +1 -0
- package/esm/oprimp.js +38 -0
- package/esm/utils/get-caller-file.util.d.ts +1 -0
- package/esm/utils/get-caller-file.util.js +20 -0
- package/esm/utils/string-utils.d.ts +4 -0
- package/esm/utils/string-utils.js +56 -0
- package/esm/utils/ts-file.d.ts +11 -0
- package/esm/utils/ts-file.js +46 -0
- package/package.json +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Panates
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
package/bin/oprimp.mjs
ADDED
package/cjs/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deleteFiles = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs_1 = tslib_1.__importDefault(require("fs"));
|
|
6
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
7
|
+
function deleteFiles(dirname) {
|
|
8
|
+
const files = fs_1.default.readdirSync(dirname);
|
|
9
|
+
for (const f of files) {
|
|
10
|
+
const filename = path_1.default.join(dirname, f);
|
|
11
|
+
if (fs_1.default.statSync(filename).isDirectory()) {
|
|
12
|
+
deleteFiles(filename);
|
|
13
|
+
if (!fs_1.default.readdirSync(filename).length)
|
|
14
|
+
fs_1.default.rmdirSync(filename);
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (path_1.default.extname(f) === '.ts') {
|
|
18
|
+
const contents = fs_1.default.readFileSync(filename, 'utf-8');
|
|
19
|
+
if (contents.includes('#!oprimp_auto_generated!#')) {
|
|
20
|
+
fs_1.default.unlinkSync(filename);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.deleteFiles = deleteFiles;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FileWriter = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs_1 = tslib_1.__importDefault(require("fs"));
|
|
6
|
+
class FileWriter {
|
|
7
|
+
writeFile(filename, contents) {
|
|
8
|
+
fs_1.default.writeFileSync(filename, contents, 'utf-8');
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
exports.FileWriter = FileWriter;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateService = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
6
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
7
|
+
const process = tslib_1.__importStar(require("process"));
|
|
8
|
+
const ts_file_js_1 = require("../utils/ts-file.js");
|
|
9
|
+
const file_writer_js_1 = require("./file-writer.js");
|
|
10
|
+
const generate_types_js_1 = require("./generate-types.js");
|
|
11
|
+
const builtinsMap = {
|
|
12
|
+
base64Binary: 'Buffer',
|
|
13
|
+
dateString: 'string',
|
|
14
|
+
guid: 'string',
|
|
15
|
+
integer: 'number'
|
|
16
|
+
};
|
|
17
|
+
async function generateService(config) {
|
|
18
|
+
const cwd = config.cwd || process.cwd();
|
|
19
|
+
const logger = config.logger || console;
|
|
20
|
+
const ctx = {
|
|
21
|
+
serviceUrl: config.serviceUrl,
|
|
22
|
+
document: config.document,
|
|
23
|
+
logger,
|
|
24
|
+
cwd,
|
|
25
|
+
relativeDir: config.outDir,
|
|
26
|
+
absoluteDir: path_1.default.resolve(cwd, config.outDir),
|
|
27
|
+
fileHeader: config.fileHeader || '',
|
|
28
|
+
writer: config.writer || new file_writer_js_1.FileWriter(),
|
|
29
|
+
indexTs: new ts_file_js_1.TsFile(),
|
|
30
|
+
builtins: {},
|
|
31
|
+
};
|
|
32
|
+
fs.mkdirSync(ctx.absoluteDir, { recursive: true });
|
|
33
|
+
await (0, generate_types_js_1.generateTypes)(ctx);
|
|
34
|
+
const builtinsTs = new ts_file_js_1.TsFile();
|
|
35
|
+
builtinsTs.header = ctx.fileHeader;
|
|
36
|
+
builtinsTs.content = Object.keys(ctx.builtins)
|
|
37
|
+
.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
|
|
38
|
+
.map(s => `export type ${s} = ${builtinsMap[s] || 'any'};`)
|
|
39
|
+
.join('\n');
|
|
40
|
+
if (builtinsTs.content) {
|
|
41
|
+
await builtinsTs.writeFile(ctx, path_1.default.join(ctx.absoluteDir, 'builtins.ts'));
|
|
42
|
+
ctx.indexTs.addExport('./builtins.js');
|
|
43
|
+
}
|
|
44
|
+
await ctx.indexTs.writeFile(ctx, path_1.default.join(ctx.absoluteDir, 'index.ts'));
|
|
45
|
+
}
|
|
46
|
+
exports.generateService = generateService;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateTypes = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
6
|
+
const fs_1 = tslib_1.__importDefault(require("fs"));
|
|
7
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
8
|
+
const common_1 = require("@opra/common");
|
|
9
|
+
const string_utils_js_1 = require("../utils/string-utils.js");
|
|
10
|
+
const ts_file_js_1 = require("../utils/ts-file.js");
|
|
11
|
+
async function generateTypes(ctx) {
|
|
12
|
+
const targetDir = path_1.default.join(ctx.absoluteDir, 'types');
|
|
13
|
+
ctx.logger.log(chalk_1.default.yellow('Generating types'));
|
|
14
|
+
fs_1.default.mkdirSync(targetDir, { recursive: true });
|
|
15
|
+
const typeNames = Array.from(ctx.document.types.keys()).sort();
|
|
16
|
+
for (const typeName of typeNames) {
|
|
17
|
+
const dataType = ctx.document.getDataType(typeName);
|
|
18
|
+
if (dataType.isBuiltin)
|
|
19
|
+
continue;
|
|
20
|
+
const tsFile = new ts_file_js_1.TsFile();
|
|
21
|
+
tsFile.header = ctx.fileHeader;
|
|
22
|
+
tsFile.content = `\n/**\n * ${(0, string_utils_js_1.wrapJSDocString)(dataType.description || dataType.name)}
|
|
23
|
+
* @type ${dataType.name}
|
|
24
|
+
* @kind ${dataType.kind}
|
|
25
|
+
* @url ${(0, common_1.joinPath)(ctx.serviceUrl, '$metadata/types/' + dataType.name)}
|
|
26
|
+
*/\n`;
|
|
27
|
+
if (dataType instanceof common_1.ComplexType) {
|
|
28
|
+
await generateComplexType(ctx, dataType, tsFile);
|
|
29
|
+
await tsFile.writeFile(ctx, path_1.default.join(targetDir, dataType.name + '.ts'));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.generateTypes = generateTypes;
|
|
34
|
+
async function generateComplexType(ctx, dataType, tsFile) {
|
|
35
|
+
const filename = `./types/${dataType.name}`;
|
|
36
|
+
ctx.indexTs.addExport(`${filename}.js`);
|
|
37
|
+
tsFile.header = ctx.fileHeader;
|
|
38
|
+
tsFile.content = `
|
|
39
|
+
export class ${dataType.name} {
|
|
40
|
+
constructor(init?: Partial<I${dataType.name}>) {
|
|
41
|
+
if (init)
|
|
42
|
+
Object.assign(this, init);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ${dataType.name} extends I${dataType.name} {
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface I${dataType.name}`;
|
|
50
|
+
if (dataType.extends) {
|
|
51
|
+
tsFile.content += ' extends ' +
|
|
52
|
+
dataType.extends.map(ex => {
|
|
53
|
+
tsFile.addImport('./' + ex.type + '.js', ex.type);
|
|
54
|
+
let s = '';
|
|
55
|
+
if (ex.omit)
|
|
56
|
+
s += 'Omit<';
|
|
57
|
+
if (ex.pick)
|
|
58
|
+
s += 'Pick<';
|
|
59
|
+
s += ex.type;
|
|
60
|
+
if (ex.pick)
|
|
61
|
+
s += ex.pick.map(x => `'${x}`).join(' | ') + '>';
|
|
62
|
+
if (ex.omit)
|
|
63
|
+
s += ex.omit.map(x => `'${x}`).join(' | ') + '>';
|
|
64
|
+
return s;
|
|
65
|
+
}).join(', ');
|
|
66
|
+
}
|
|
67
|
+
const getTypeName = (dt) => {
|
|
68
|
+
if (dt.isBuiltin) {
|
|
69
|
+
if (dt.name === 'any' || dt.name === 'string' || dt.name === 'number' ||
|
|
70
|
+
dt.name === 'boolean' || dt.name === 'object')
|
|
71
|
+
return dt.name;
|
|
72
|
+
if (dt.name === 'date')
|
|
73
|
+
return 'Date';
|
|
74
|
+
tsFile.addImport('../builtins.js', dt.name);
|
|
75
|
+
ctx.builtins[dt.name] = true;
|
|
76
|
+
return dt.name;
|
|
77
|
+
}
|
|
78
|
+
tsFile.addImport('./' + dt.name + '.js', dt.name);
|
|
79
|
+
return dt.name;
|
|
80
|
+
};
|
|
81
|
+
tsFile.content += ' {\n\t';
|
|
82
|
+
for (const f of dataType.ownFields.values()) {
|
|
83
|
+
const fieldType = ctx.document.getDataType(f.type);
|
|
84
|
+
// Print JSDoc
|
|
85
|
+
tsFile.content += `/**\n * ${f.description || f.name}\n`;
|
|
86
|
+
if (f.default)
|
|
87
|
+
tsFile.content += ` * @default ` + f.default + '\n';
|
|
88
|
+
if (f.format)
|
|
89
|
+
tsFile.content += ` * @format ` + f.format + '\n';
|
|
90
|
+
if (f.exclusive)
|
|
91
|
+
tsFile.content += ` * @exclusive\n`;
|
|
92
|
+
if (f.deprecated)
|
|
93
|
+
tsFile.content += ` * @deprecated ` + (typeof f.deprecated === 'string' ? f.deprecated : '') + '\n';
|
|
94
|
+
tsFile.content += ` */\n`;
|
|
95
|
+
// Print field name
|
|
96
|
+
tsFile.content += `${f.name}${f.required ? '' : '?'}: `;
|
|
97
|
+
if (f.fixed)
|
|
98
|
+
tsFile.content += `${f.fixed}`;
|
|
99
|
+
else {
|
|
100
|
+
if (fieldType instanceof common_1.UnionType) {
|
|
101
|
+
const s = fieldType.types.map(t => getTypeName(t)).join(' | ');
|
|
102
|
+
tsFile.content += `(${s})`;
|
|
103
|
+
}
|
|
104
|
+
else
|
|
105
|
+
tsFile.content += `${getTypeName(fieldType)}`;
|
|
106
|
+
tsFile.content += `${f.isArray ? '[]' : ''};\n\n`;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (dataType.additionalFields)
|
|
110
|
+
tsFile.content += '[key: string]: any;\n';
|
|
111
|
+
tsFile.content += '\b\b}\n';
|
|
112
|
+
}
|
package/cjs/oprimp.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
5
|
+
const commander_1 = require("commander");
|
|
6
|
+
const console = tslib_1.__importStar(require("console"));
|
|
7
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
8
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
9
|
+
const process = tslib_1.__importStar(require("process"));
|
|
10
|
+
const node_client_1 = require("@opra/node-client");
|
|
11
|
+
const delete_files_js_1 = require("./oprimp/delete-files.js");
|
|
12
|
+
const generate_service_js_1 = require("./oprimp/generate-service.js");
|
|
13
|
+
const get_caller_file_util_js_1 = require("./utils/get-caller-file.util.js");
|
|
14
|
+
const pkgJson = JSON.parse(fs.readFileSync(path_1.default.resolve(path_1.default.dirname((0, get_caller_file_util_js_1.getCallerFile)()), '../package.json'), 'utf-8'));
|
|
15
|
+
commander_1.program
|
|
16
|
+
.version(pkgJson.version)
|
|
17
|
+
.argument('<serviceUrl>', 'OPRA service url')
|
|
18
|
+
.argument('<outDir>', 'Output directory')
|
|
19
|
+
.option('--no-color', 'Disables colors in logs messages')
|
|
20
|
+
.action(async (serviceUrl, outDir, options) => {
|
|
21
|
+
if (!options.color)
|
|
22
|
+
chalk_1.default.level = 0;
|
|
23
|
+
console.log(chalk_1.default.yellow('Fetching service metadata from'), chalk_1.default.whiteBright(serviceUrl));
|
|
24
|
+
const client = await node_client_1.OpraHttpClient.create(serviceUrl);
|
|
25
|
+
const metadata = client.metadata;
|
|
26
|
+
console.log(chalk_1.default.yellow('Retrieved service info:'), chalk_1.default.whiteBright(metadata.info.title), '-', chalk_1.default.whiteBright(metadata.info.version));
|
|
27
|
+
console.log(chalk_1.default.yellow('Removing old files..'));
|
|
28
|
+
(0, delete_files_js_1.deleteFiles)(outDir);
|
|
29
|
+
await (0, generate_service_js_1.generateService)({
|
|
30
|
+
serviceUrl,
|
|
31
|
+
document: metadata,
|
|
32
|
+
outDir,
|
|
33
|
+
fileHeader: '/*\n' +
|
|
34
|
+
' * Generated by OPRA Service Generator \n' +
|
|
35
|
+
' * Version: ' + pkgJson.version + '\n' +
|
|
36
|
+
' * ' + new Date() + '\n' +
|
|
37
|
+
' */'
|
|
38
|
+
});
|
|
39
|
+
console.log(chalk_1.default.greenBright('Completed'));
|
|
40
|
+
});
|
|
41
|
+
commander_1.program.parse(process.argv);
|
package/cjs/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
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;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wrapTypeArray = exports.wrapStringArray = exports.wrapQuotedString = exports.wrapJSDocString = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const js_string_escape_1 = tslib_1.__importDefault(require("js-string-escape"));
|
|
6
|
+
function wrapJSDocString(s, indent, currentColumn) {
|
|
7
|
+
const arr = (s || '')
|
|
8
|
+
.split(/[ \n\r]/)
|
|
9
|
+
.map((x, i, a) => i < a.length - 1 ? x + ' ' : x);
|
|
10
|
+
return _printLines(arr, {
|
|
11
|
+
indent,
|
|
12
|
+
currentColumn,
|
|
13
|
+
lineStart: '* ',
|
|
14
|
+
lineEnd: ''
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
exports.wrapJSDocString = wrapJSDocString;
|
|
18
|
+
function wrapQuotedString(s, indent, currentColumn) {
|
|
19
|
+
const arr = (0, js_string_escape_1.default)(s || '')
|
|
20
|
+
.split(' ')
|
|
21
|
+
.map((x, i, a) => i < a.length - 1 ? x + ' ' : x);
|
|
22
|
+
return '\'' + _printLines(arr, {
|
|
23
|
+
indent,
|
|
24
|
+
currentColumn,
|
|
25
|
+
lineStart: '\'',
|
|
26
|
+
lineEnd: '\' +',
|
|
27
|
+
}) + '\'';
|
|
28
|
+
}
|
|
29
|
+
exports.wrapQuotedString = wrapQuotedString;
|
|
30
|
+
function wrapStringArray(arr, indent, currentColumn) {
|
|
31
|
+
const ar1 = arr
|
|
32
|
+
.map((x, i, a) => ('\'' + x + '\'') + (i < a.length - 1 ? ', ' : ''));
|
|
33
|
+
return '[' + _printLines(ar1, { indent, currentColumn }) + ']';
|
|
34
|
+
}
|
|
35
|
+
exports.wrapStringArray = wrapStringArray;
|
|
36
|
+
function wrapTypeArray(arr, indent, currentColumn) {
|
|
37
|
+
const ar1 = arr
|
|
38
|
+
.map((x, i, a) => x + (i < a.length - 1 ? ' | ' : ''));
|
|
39
|
+
return _printLines(ar1, { indent, currentColumn });
|
|
40
|
+
}
|
|
41
|
+
exports.wrapTypeArray = wrapTypeArray;
|
|
42
|
+
function _printLines(arr, opts = {}) {
|
|
43
|
+
let s = '';
|
|
44
|
+
let line = '';
|
|
45
|
+
const indent = opts.indent || 0;
|
|
46
|
+
let lineWidth = (opts.lineWidth || 90) - (opts.currentColumn || 0);
|
|
47
|
+
const l = arr.length;
|
|
48
|
+
const printLine = (eof) => {
|
|
49
|
+
s += (s ? '\n' + ' '.repeat(indent || 0) + (opts.lineStart ? opts.lineStart : '') : '') +
|
|
50
|
+
line +
|
|
51
|
+
(!eof ? (opts.lineEnd ? opts.lineEnd : '') : '');
|
|
52
|
+
line = '';
|
|
53
|
+
};
|
|
54
|
+
for (let i = 0; i < l; i++) {
|
|
55
|
+
const x = arr[i];
|
|
56
|
+
if (line && line.length + x.length > lineWidth) {
|
|
57
|
+
lineWidth = (opts.lineWidth || 90) - indent;
|
|
58
|
+
printLine(false);
|
|
59
|
+
}
|
|
60
|
+
line += x;
|
|
61
|
+
}
|
|
62
|
+
printLine(true);
|
|
63
|
+
return s;
|
|
64
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TsFile = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
6
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
7
|
+
const putil_flattentext_1 = tslib_1.__importDefault(require("putil-flattentext"));
|
|
8
|
+
class TsFile {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.imports = {};
|
|
11
|
+
this.exports = {};
|
|
12
|
+
this.header = '';
|
|
13
|
+
this.content = '';
|
|
14
|
+
this.addImport = (filename, imported) => {
|
|
15
|
+
this.imports[filename] = this.imports[filename] || [];
|
|
16
|
+
if (!this.imports[filename].includes(imported))
|
|
17
|
+
this.imports[filename].push(imported);
|
|
18
|
+
};
|
|
19
|
+
this.addExport = (filename, exported) => {
|
|
20
|
+
this.exports[filename] = this.exports[filename] || [];
|
|
21
|
+
if (exported && !this.exports[filename].includes(exported))
|
|
22
|
+
this.exports[filename].push(exported);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
generate() {
|
|
26
|
+
let output = '/* #!oprimp_auto_generated!# !! Do NOT remove this line */\n' +
|
|
27
|
+
(this.header ? (0, putil_flattentext_1.default)(this.header) + '\n\n' : '\n');
|
|
28
|
+
const importStr = Object.keys(this.imports)
|
|
29
|
+
.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
|
|
30
|
+
.map(i => `import { ${this.imports[i].join(', ')} } from '${i}';`)
|
|
31
|
+
.join('\n');
|
|
32
|
+
if (importStr)
|
|
33
|
+
output += (0, putil_flattentext_1.default)(importStr) + '\n';
|
|
34
|
+
output += (0, putil_flattentext_1.default)(this.content);
|
|
35
|
+
const exportStr = Object.keys(this.exports)
|
|
36
|
+
.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
|
|
37
|
+
.map(i => {
|
|
38
|
+
const a = this.exports[i];
|
|
39
|
+
return `export ${a.length ? '{' + a.join(', ') + '}' : '*'} from '${i}';`;
|
|
40
|
+
})
|
|
41
|
+
.join('\n');
|
|
42
|
+
if (exportStr)
|
|
43
|
+
output += (0, putil_flattentext_1.default)(exportStr) + '\n';
|
|
44
|
+
return output;
|
|
45
|
+
}
|
|
46
|
+
async writeFile(ctx, filename) {
|
|
47
|
+
await ctx.writer.writeFile(filename, this.generate());
|
|
48
|
+
ctx.logger.log(' - Written', chalk_1.default.whiteBright('./' + path_1.default.relative(ctx.absoluteDir, filename)));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.TsFile = TsFile;
|
package/esm/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './oprimp/index.js';
|
package/esm/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './oprimp/index.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface ILogger {
|
|
2
|
+
log(message: any, ...optionalParams: any[]): any;
|
|
3
|
+
error(message: any, ...optionalParams: any[]): any;
|
|
4
|
+
warn(message: any, ...optionalParams: any[]): any;
|
|
5
|
+
debug?(message: any, ...optionalParams: any[]): any;
|
|
6
|
+
verbose?(message: any, ...optionalParams: any[]): any;
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { OpraDocument } from '@opra/common';
|
|
2
|
+
import { TsFile } from '../utils/ts-file.js';
|
|
3
|
+
import { IFileWriter } from './file-writer.interface.js';
|
|
4
|
+
import { ILogger } from './logger.interface.js';
|
|
5
|
+
export interface ServiceGenerationContext {
|
|
6
|
+
serviceUrl: string;
|
|
7
|
+
document: OpraDocument;
|
|
8
|
+
cwd: string;
|
|
9
|
+
logger: ILogger;
|
|
10
|
+
relativeDir: string;
|
|
11
|
+
absoluteDir: string;
|
|
12
|
+
fileHeader: string;
|
|
13
|
+
writer: IFileWriter;
|
|
14
|
+
indexTs: TsFile;
|
|
15
|
+
builtins: any;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function deleteFiles(dirname: string): void;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
export function deleteFiles(dirname) {
|
|
4
|
+
const files = fs.readdirSync(dirname);
|
|
5
|
+
for (const f of files) {
|
|
6
|
+
const filename = path.join(dirname, f);
|
|
7
|
+
if (fs.statSync(filename).isDirectory()) {
|
|
8
|
+
deleteFiles(filename);
|
|
9
|
+
if (!fs.readdirSync(filename).length)
|
|
10
|
+
fs.rmdirSync(filename);
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
if (path.extname(f) === '.ts') {
|
|
14
|
+
const contents = fs.readFileSync(filename, 'utf-8');
|
|
15
|
+
if (contents.includes('#!oprimp_auto_generated!#')) {
|
|
16
|
+
fs.unlinkSync(filename);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { OpraDocument } from '@opra/common';
|
|
2
|
+
import { IFileWriter } from '../interfaces/file-writer.interface.js';
|
|
3
|
+
import { ILogger } from '../interfaces/logger.interface.js';
|
|
4
|
+
export interface ServiceGenerateConfig {
|
|
5
|
+
serviceUrl: string;
|
|
6
|
+
document: OpraDocument;
|
|
7
|
+
outDir: string;
|
|
8
|
+
cwd?: string;
|
|
9
|
+
logger?: ILogger;
|
|
10
|
+
writer?: IFileWriter;
|
|
11
|
+
fileHeader?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function generateService(config: ServiceGenerateConfig): Promise<void>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import * as process from 'process';
|
|
4
|
+
import { TsFile } from '../utils/ts-file.js';
|
|
5
|
+
import { FileWriter } from './file-writer.js';
|
|
6
|
+
import { generateTypes } from './generate-types.js';
|
|
7
|
+
const builtinsMap = {
|
|
8
|
+
base64Binary: 'Buffer',
|
|
9
|
+
dateString: 'string',
|
|
10
|
+
guid: 'string',
|
|
11
|
+
integer: 'number'
|
|
12
|
+
};
|
|
13
|
+
export async function generateService(config) {
|
|
14
|
+
const cwd = config.cwd || process.cwd();
|
|
15
|
+
const logger = config.logger || console;
|
|
16
|
+
const ctx = {
|
|
17
|
+
serviceUrl: config.serviceUrl,
|
|
18
|
+
document: config.document,
|
|
19
|
+
logger,
|
|
20
|
+
cwd,
|
|
21
|
+
relativeDir: config.outDir,
|
|
22
|
+
absoluteDir: path.resolve(cwd, config.outDir),
|
|
23
|
+
fileHeader: config.fileHeader || '',
|
|
24
|
+
writer: config.writer || new FileWriter(),
|
|
25
|
+
indexTs: new TsFile(),
|
|
26
|
+
builtins: {},
|
|
27
|
+
};
|
|
28
|
+
fs.mkdirSync(ctx.absoluteDir, { recursive: true });
|
|
29
|
+
await generateTypes(ctx);
|
|
30
|
+
const builtinsTs = new TsFile();
|
|
31
|
+
builtinsTs.header = ctx.fileHeader;
|
|
32
|
+
builtinsTs.content = Object.keys(ctx.builtins)
|
|
33
|
+
.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
|
|
34
|
+
.map(s => `export type ${s} = ${builtinsMap[s] || 'any'};`)
|
|
35
|
+
.join('\n');
|
|
36
|
+
if (builtinsTs.content) {
|
|
37
|
+
await builtinsTs.writeFile(ctx, path.join(ctx.absoluteDir, 'builtins.ts'));
|
|
38
|
+
ctx.indexTs.addExport('./builtins.js');
|
|
39
|
+
}
|
|
40
|
+
await ctx.indexTs.writeFile(ctx, path.join(ctx.absoluteDir, 'index.ts'));
|
|
41
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { ComplexType, joinPath, UnionType } from '@opra/common';
|
|
5
|
+
import { wrapJSDocString } from '../utils/string-utils.js';
|
|
6
|
+
import { TsFile } from '../utils/ts-file.js';
|
|
7
|
+
export async function generateTypes(ctx) {
|
|
8
|
+
const targetDir = path.join(ctx.absoluteDir, 'types');
|
|
9
|
+
ctx.logger.log(chalk.yellow('Generating types'));
|
|
10
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
11
|
+
const typeNames = Array.from(ctx.document.types.keys()).sort();
|
|
12
|
+
for (const typeName of typeNames) {
|
|
13
|
+
const dataType = ctx.document.getDataType(typeName);
|
|
14
|
+
if (dataType.isBuiltin)
|
|
15
|
+
continue;
|
|
16
|
+
const tsFile = new TsFile();
|
|
17
|
+
tsFile.header = ctx.fileHeader;
|
|
18
|
+
tsFile.content = `\n/**\n * ${wrapJSDocString(dataType.description || dataType.name)}
|
|
19
|
+
* @type ${dataType.name}
|
|
20
|
+
* @kind ${dataType.kind}
|
|
21
|
+
* @url ${joinPath(ctx.serviceUrl, '$metadata/types/' + dataType.name)}
|
|
22
|
+
*/\n`;
|
|
23
|
+
if (dataType instanceof ComplexType) {
|
|
24
|
+
await generateComplexType(ctx, dataType, tsFile);
|
|
25
|
+
await tsFile.writeFile(ctx, path.join(targetDir, dataType.name + '.ts'));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async function generateComplexType(ctx, dataType, tsFile) {
|
|
30
|
+
const filename = `./types/${dataType.name}`;
|
|
31
|
+
ctx.indexTs.addExport(`${filename}.js`);
|
|
32
|
+
tsFile.header = ctx.fileHeader;
|
|
33
|
+
tsFile.content = `
|
|
34
|
+
export class ${dataType.name} {
|
|
35
|
+
constructor(init?: Partial<I${dataType.name}>) {
|
|
36
|
+
if (init)
|
|
37
|
+
Object.assign(this, init);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ${dataType.name} extends I${dataType.name} {
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface I${dataType.name}`;
|
|
45
|
+
if (dataType.extends) {
|
|
46
|
+
tsFile.content += ' extends ' +
|
|
47
|
+
dataType.extends.map(ex => {
|
|
48
|
+
tsFile.addImport('./' + ex.type + '.js', ex.type);
|
|
49
|
+
let s = '';
|
|
50
|
+
if (ex.omit)
|
|
51
|
+
s += 'Omit<';
|
|
52
|
+
if (ex.pick)
|
|
53
|
+
s += 'Pick<';
|
|
54
|
+
s += ex.type;
|
|
55
|
+
if (ex.pick)
|
|
56
|
+
s += ex.pick.map(x => `'${x}`).join(' | ') + '>';
|
|
57
|
+
if (ex.omit)
|
|
58
|
+
s += ex.omit.map(x => `'${x}`).join(' | ') + '>';
|
|
59
|
+
return s;
|
|
60
|
+
}).join(', ');
|
|
61
|
+
}
|
|
62
|
+
const getTypeName = (dt) => {
|
|
63
|
+
if (dt.isBuiltin) {
|
|
64
|
+
if (dt.name === 'any' || dt.name === 'string' || dt.name === 'number' ||
|
|
65
|
+
dt.name === 'boolean' || dt.name === 'object')
|
|
66
|
+
return dt.name;
|
|
67
|
+
if (dt.name === 'date')
|
|
68
|
+
return 'Date';
|
|
69
|
+
tsFile.addImport('../builtins.js', dt.name);
|
|
70
|
+
ctx.builtins[dt.name] = true;
|
|
71
|
+
return dt.name;
|
|
72
|
+
}
|
|
73
|
+
tsFile.addImport('./' + dt.name + '.js', dt.name);
|
|
74
|
+
return dt.name;
|
|
75
|
+
};
|
|
76
|
+
tsFile.content += ' {\n\t';
|
|
77
|
+
for (const f of dataType.ownFields.values()) {
|
|
78
|
+
const fieldType = ctx.document.getDataType(f.type);
|
|
79
|
+
// Print JSDoc
|
|
80
|
+
tsFile.content += `/**\n * ${f.description || f.name}\n`;
|
|
81
|
+
if (f.default)
|
|
82
|
+
tsFile.content += ` * @default ` + f.default + '\n';
|
|
83
|
+
if (f.format)
|
|
84
|
+
tsFile.content += ` * @format ` + f.format + '\n';
|
|
85
|
+
if (f.exclusive)
|
|
86
|
+
tsFile.content += ` * @exclusive\n`;
|
|
87
|
+
if (f.deprecated)
|
|
88
|
+
tsFile.content += ` * @deprecated ` + (typeof f.deprecated === 'string' ? f.deprecated : '') + '\n';
|
|
89
|
+
tsFile.content += ` */\n`;
|
|
90
|
+
// Print field name
|
|
91
|
+
tsFile.content += `${f.name}${f.required ? '' : '?'}: `;
|
|
92
|
+
if (f.fixed)
|
|
93
|
+
tsFile.content += `${f.fixed}`;
|
|
94
|
+
else {
|
|
95
|
+
if (fieldType instanceof UnionType) {
|
|
96
|
+
const s = fieldType.types.map(t => getTypeName(t)).join(' | ');
|
|
97
|
+
tsFile.content += `(${s})`;
|
|
98
|
+
}
|
|
99
|
+
else
|
|
100
|
+
tsFile.content += `${getTypeName(fieldType)}`;
|
|
101
|
+
tsFile.content += `${f.isArray ? '[]' : ''};\n\n`;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (dataType.additionalFields)
|
|
105
|
+
tsFile.content += '[key: string]: any;\n';
|
|
106
|
+
tsFile.content += '\b\b}\n';
|
|
107
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './generate-service.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './generate-service.js';
|
package/esm/oprimp.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/esm/oprimp.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { program } from 'commander';
|
|
3
|
+
import * as console from 'console';
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import * as process from 'process';
|
|
7
|
+
import { OpraHttpClient } from '@opra/node-client';
|
|
8
|
+
import { deleteFiles } from './oprimp/delete-files.js';
|
|
9
|
+
import { generateService } from './oprimp/generate-service.js';
|
|
10
|
+
import { getCallerFile } from './utils/get-caller-file.util.js';
|
|
11
|
+
const pkgJson = JSON.parse(fs.readFileSync(path.resolve(path.dirname(getCallerFile()), '../package.json'), 'utf-8'));
|
|
12
|
+
program
|
|
13
|
+
.version(pkgJson.version)
|
|
14
|
+
.argument('<serviceUrl>', 'OPRA service url')
|
|
15
|
+
.argument('<outDir>', 'Output directory')
|
|
16
|
+
.option('--no-color', 'Disables colors in logs messages')
|
|
17
|
+
.action(async (serviceUrl, outDir, options) => {
|
|
18
|
+
if (!options.color)
|
|
19
|
+
chalk.level = 0;
|
|
20
|
+
console.log(chalk.yellow('Fetching service metadata from'), chalk.whiteBright(serviceUrl));
|
|
21
|
+
const client = await OpraHttpClient.create(serviceUrl);
|
|
22
|
+
const metadata = client.metadata;
|
|
23
|
+
console.log(chalk.yellow('Retrieved service info:'), chalk.whiteBright(metadata.info.title), '-', chalk.whiteBright(metadata.info.version));
|
|
24
|
+
console.log(chalk.yellow('Removing old files..'));
|
|
25
|
+
deleteFiles(outDir);
|
|
26
|
+
await generateService({
|
|
27
|
+
serviceUrl,
|
|
28
|
+
document: metadata,
|
|
29
|
+
outDir,
|
|
30
|
+
fileHeader: '/*\n' +
|
|
31
|
+
' * Generated by OPRA Service Generator \n' +
|
|
32
|
+
' * Version: ' + pkgJson.version + '\n' +
|
|
33
|
+
' * ' + new Date() + '\n' +
|
|
34
|
+
' */'
|
|
35
|
+
});
|
|
36
|
+
console.log(chalk.greenBright('Completed'));
|
|
37
|
+
});
|
|
38
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getCallerFile(position?: number): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const PATH_PATTERN = /^(?:file:\/\/)?(.+)$/;
|
|
2
|
+
export function getCallerFile(position = 1) {
|
|
3
|
+
if (position >= Error.stackTraceLimit) {
|
|
4
|
+
throw new TypeError('getCallerFile(position) requires position be less then Error.stackTraceLimit but position was: `' +
|
|
5
|
+
position + '` and Error.stackTraceLimit was: `' + Error.stackTraceLimit + '`');
|
|
6
|
+
}
|
|
7
|
+
const oldPrepareStackTrace = Error.prepareStackTrace;
|
|
8
|
+
Error.prepareStackTrace = (_, stack) => stack;
|
|
9
|
+
const stack = new Error().stack;
|
|
10
|
+
Error.prepareStackTrace = oldPrepareStackTrace;
|
|
11
|
+
if (stack !== null && typeof stack === 'object') {
|
|
12
|
+
// stack[0] holds this file
|
|
13
|
+
// stack[1] holds where this function was called
|
|
14
|
+
const s = stack[position] ?
|
|
15
|
+
stack[position].getFileName() : undefined;
|
|
16
|
+
const m = s ? PATH_PATTERN.exec(s) : undefined;
|
|
17
|
+
return m ? m[1] : '';
|
|
18
|
+
}
|
|
19
|
+
return '';
|
|
20
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function wrapJSDocString(s: string, indent?: number, currentColumn?: number): string;
|
|
2
|
+
export declare function wrapQuotedString(s: string, indent?: number, currentColumn?: number): string;
|
|
3
|
+
export declare function wrapStringArray(arr: string[], indent?: number, currentColumn?: number): string;
|
|
4
|
+
export declare function wrapTypeArray(arr: string[], indent?: number, currentColumn?: number): string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import jsStringEscape from 'js-string-escape';
|
|
2
|
+
export function wrapJSDocString(s, indent, currentColumn) {
|
|
3
|
+
const arr = (s || '')
|
|
4
|
+
.split(/[ \n\r]/)
|
|
5
|
+
.map((x, i, a) => i < a.length - 1 ? x + ' ' : x);
|
|
6
|
+
return _printLines(arr, {
|
|
7
|
+
indent,
|
|
8
|
+
currentColumn,
|
|
9
|
+
lineStart: '* ',
|
|
10
|
+
lineEnd: ''
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export function wrapQuotedString(s, indent, currentColumn) {
|
|
14
|
+
const arr = jsStringEscape(s || '')
|
|
15
|
+
.split(' ')
|
|
16
|
+
.map((x, i, a) => i < a.length - 1 ? x + ' ' : x);
|
|
17
|
+
return '\'' + _printLines(arr, {
|
|
18
|
+
indent,
|
|
19
|
+
currentColumn,
|
|
20
|
+
lineStart: '\'',
|
|
21
|
+
lineEnd: '\' +',
|
|
22
|
+
}) + '\'';
|
|
23
|
+
}
|
|
24
|
+
export function wrapStringArray(arr, indent, currentColumn) {
|
|
25
|
+
const ar1 = arr
|
|
26
|
+
.map((x, i, a) => ('\'' + x + '\'') + (i < a.length - 1 ? ', ' : ''));
|
|
27
|
+
return '[' + _printLines(ar1, { indent, currentColumn }) + ']';
|
|
28
|
+
}
|
|
29
|
+
export function wrapTypeArray(arr, indent, currentColumn) {
|
|
30
|
+
const ar1 = arr
|
|
31
|
+
.map((x, i, a) => x + (i < a.length - 1 ? ' | ' : ''));
|
|
32
|
+
return _printLines(ar1, { indent, currentColumn });
|
|
33
|
+
}
|
|
34
|
+
function _printLines(arr, opts = {}) {
|
|
35
|
+
let s = '';
|
|
36
|
+
let line = '';
|
|
37
|
+
const indent = opts.indent || 0;
|
|
38
|
+
let lineWidth = (opts.lineWidth || 90) - (opts.currentColumn || 0);
|
|
39
|
+
const l = arr.length;
|
|
40
|
+
const printLine = (eof) => {
|
|
41
|
+
s += (s ? '\n' + ' '.repeat(indent || 0) + (opts.lineStart ? opts.lineStart : '') : '') +
|
|
42
|
+
line +
|
|
43
|
+
(!eof ? (opts.lineEnd ? opts.lineEnd : '') : '');
|
|
44
|
+
line = '';
|
|
45
|
+
};
|
|
46
|
+
for (let i = 0; i < l; i++) {
|
|
47
|
+
const x = arr[i];
|
|
48
|
+
if (line && line.length + x.length > lineWidth) {
|
|
49
|
+
lineWidth = (opts.lineWidth || 90) - indent;
|
|
50
|
+
printLine(false);
|
|
51
|
+
}
|
|
52
|
+
line += x;
|
|
53
|
+
}
|
|
54
|
+
printLine(true);
|
|
55
|
+
return s;
|
|
56
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ServiceGenerationContext } from '../interfaces/service-generation-context.interface.js';
|
|
2
|
+
export declare class TsFile {
|
|
3
|
+
imports: Record<string, string[]>;
|
|
4
|
+
exports: Record<string, string[]>;
|
|
5
|
+
header: string;
|
|
6
|
+
content: string;
|
|
7
|
+
addImport: (filename: string, imported: string) => void;
|
|
8
|
+
addExport: (filename: string, exported?: string) => void;
|
|
9
|
+
generate(): string;
|
|
10
|
+
writeFile(ctx: ServiceGenerationContext, filename: string): Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import flattenText from 'putil-flattentext';
|
|
4
|
+
export class TsFile {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.imports = {};
|
|
7
|
+
this.exports = {};
|
|
8
|
+
this.header = '';
|
|
9
|
+
this.content = '';
|
|
10
|
+
this.addImport = (filename, imported) => {
|
|
11
|
+
this.imports[filename] = this.imports[filename] || [];
|
|
12
|
+
if (!this.imports[filename].includes(imported))
|
|
13
|
+
this.imports[filename].push(imported);
|
|
14
|
+
};
|
|
15
|
+
this.addExport = (filename, exported) => {
|
|
16
|
+
this.exports[filename] = this.exports[filename] || [];
|
|
17
|
+
if (exported && !this.exports[filename].includes(exported))
|
|
18
|
+
this.exports[filename].push(exported);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
generate() {
|
|
22
|
+
let output = '/* #!oprimp_auto_generated!# !! Do NOT remove this line */\n' +
|
|
23
|
+
(this.header ? flattenText(this.header) + '\n\n' : '\n');
|
|
24
|
+
const importStr = Object.keys(this.imports)
|
|
25
|
+
.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
|
|
26
|
+
.map(i => `import { ${this.imports[i].join(', ')} } from '${i}';`)
|
|
27
|
+
.join('\n');
|
|
28
|
+
if (importStr)
|
|
29
|
+
output += flattenText(importStr) + '\n';
|
|
30
|
+
output += flattenText(this.content);
|
|
31
|
+
const exportStr = Object.keys(this.exports)
|
|
32
|
+
.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
|
|
33
|
+
.map(i => {
|
|
34
|
+
const a = this.exports[i];
|
|
35
|
+
return `export ${a.length ? '{' + a.join(', ') + '}' : '*'} from '${i}';`;
|
|
36
|
+
})
|
|
37
|
+
.join('\n');
|
|
38
|
+
if (exportStr)
|
|
39
|
+
output += flattenText(exportStr) + '\n';
|
|
40
|
+
return output;
|
|
41
|
+
}
|
|
42
|
+
async writeFile(ctx, filename) {
|
|
43
|
+
await ctx.writer.writeFile(filename, this.generate());
|
|
44
|
+
ctx.logger.log(' - Written', chalk.whiteBright('./' + path.relative(ctx.absoluteDir, filename)));
|
|
45
|
+
}
|
|
46
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opra/cli",
|
|
3
|
+
"version": "0.12.0",
|
|
4
|
+
"description": "Opra CLI tools",
|
|
5
|
+
"author": "Panates",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/panates/opra.git",
|
|
10
|
+
"directory": "packages/client"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"compile": "tsc",
|
|
14
|
+
"prebuild": "npm run check && npm run lint && npm run clean",
|
|
15
|
+
"build": "npm run build:cjs && npm run build:esm",
|
|
16
|
+
"build:cjs": "tsc -b tsconfig-build-cjs.json",
|
|
17
|
+
"build:esm": "tsc -b tsconfig-build-esm.json",
|
|
18
|
+
"postbuild": "npm run copy:files && npm run copy:bin",
|
|
19
|
+
"copy:files": "cp README.md package.json ../../LICENSE ../../build/cli && cp ../../package.cjs.json ../../build/cli/cjs/package.json",
|
|
20
|
+
"copy:bin": "cp -R bin ../../build/cli/bin",
|
|
21
|
+
"lint": "eslint . --max-warnings=0",
|
|
22
|
+
"check": "madge --circular src/**",
|
|
23
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules npx jest",
|
|
24
|
+
"cover": "NODE_OPTIONS=--experimental-vm-modules npx jest --collect-coverage",
|
|
25
|
+
"clean": "npm run clean:src && npm run clean:dist && npm run clean:cover",
|
|
26
|
+
"clean:src": "ts-cleanup -s src --all",
|
|
27
|
+
"clean:dist": "rimraf ../../build/cli ../../build/client",
|
|
28
|
+
"clean:cover": "rimraf ../../coverage/client"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@opra/node-client": "^0.12.0",
|
|
32
|
+
"chalk": "^5.2.0",
|
|
33
|
+
"commander": "^10.0.0",
|
|
34
|
+
"putil-flattentext": "^2.1.1",
|
|
35
|
+
"js-string-escape": "^1.0.1"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
},
|
|
39
|
+
"type": "module",
|
|
40
|
+
"types": "esm/index.d.ts",
|
|
41
|
+
"exports": {
|
|
42
|
+
".": {
|
|
43
|
+
"require": "./cjs/index.js",
|
|
44
|
+
"default": "./esm/index.js"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"bin": {
|
|
48
|
+
"oprimp": "bin/oprimp.mjs"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=18.0",
|
|
52
|
+
"npm": ">=7.0.0"
|
|
53
|
+
},
|
|
54
|
+
"files": [
|
|
55
|
+
"bin/",
|
|
56
|
+
"cjs/",
|
|
57
|
+
"esm/",
|
|
58
|
+
"LICENSE",
|
|
59
|
+
"README.md"
|
|
60
|
+
],
|
|
61
|
+
"keywords": [
|
|
62
|
+
"opra",
|
|
63
|
+
"cli",
|
|
64
|
+
"tool",
|
|
65
|
+
"oprimp"
|
|
66
|
+
]
|
|
67
|
+
}
|