@opra/cli 0.33.13 → 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/ts-generator/index.js +4 -0
- 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 -131
- 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/process-resources.js +0 -126
- 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/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
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { EventEmitter } from 'node:events';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import process from 'node:process';
|
|
6
|
+
import { FileWriter } from '../file-writer.js';
|
|
7
|
+
import { cleanDirectory } from './processors/clean-directory.js';
|
|
8
|
+
import { generateComplexTypeDefinition, generateEnumTypeDefinition, generateMappedTypeDefinition, generateMixinTypeDefinition, generateSimpleTypeDefinition, processDataType, resolveTypeNameOrDef, } from './processors/process-data-types.js';
|
|
9
|
+
import { processDocument } from './processors/process-document.js';
|
|
10
|
+
import { processHttpApi } from './processors/process-http-api.js';
|
|
11
|
+
import { processHttpController } from './processors/process-http-controller.js';
|
|
12
|
+
import { TsFile } from './ts-file.js';
|
|
13
|
+
/**
|
|
14
|
+
* @class TsGenerator
|
|
15
|
+
*/
|
|
16
|
+
export class TsGenerator extends EventEmitter {
|
|
17
|
+
/**
|
|
18
|
+
*
|
|
19
|
+
* @constructor
|
|
20
|
+
*/
|
|
21
|
+
constructor(init) {
|
|
22
|
+
super();
|
|
23
|
+
this._started = false;
|
|
24
|
+
this._files = {};
|
|
25
|
+
this.serviceUrl = init.serviceUrl;
|
|
26
|
+
this.cwd = init.cwd || process.cwd();
|
|
27
|
+
this.outDir = init.outDir ? path.resolve(this.cwd, init.outDir) : this.cwd;
|
|
28
|
+
this.fileHeader = init.fileHeader || '';
|
|
29
|
+
this.writer = init.writer || new FileWriter();
|
|
30
|
+
this.options = { importExt: !!init.importExt };
|
|
31
|
+
this._documentsMap = new Map();
|
|
32
|
+
this._filesMap = new WeakMap();
|
|
33
|
+
this.on('log', (message, ...args) => init.logger?.log?.(message, ...args));
|
|
34
|
+
this.on('error', (message, ...args) => init.logger?.error?.(message, ...args));
|
|
35
|
+
this.on('debug', (message, ...args) => init.logger?.debug?.(message, ...args));
|
|
36
|
+
this.on('warn', (message, ...args) => init.logger?.warn?.(message, ...args));
|
|
37
|
+
this.on('verbose', (message, ...args) => init.logger?.verbose?.(message, ...args));
|
|
38
|
+
}
|
|
39
|
+
async generate() {
|
|
40
|
+
if (this._started)
|
|
41
|
+
return;
|
|
42
|
+
this.emit('start');
|
|
43
|
+
try {
|
|
44
|
+
this._started = true;
|
|
45
|
+
this.emit('log', chalk.cyan('Removing old files..'));
|
|
46
|
+
this.cleanDirectory(this.outDir);
|
|
47
|
+
this._apiPath = '/api';
|
|
48
|
+
await this.processDocument();
|
|
49
|
+
const { importExt } = this.options;
|
|
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
|
+
catch (e) {
|
|
59
|
+
this.emit('error', e);
|
|
60
|
+
throw e;
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
this.emit('finish');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
getFile(filePath) {
|
|
67
|
+
return this._files[filePath];
|
|
68
|
+
}
|
|
69
|
+
addFile(filePath, returnExists) {
|
|
70
|
+
if (!(filePath.startsWith('.') || filePath.startsWith('/')))
|
|
71
|
+
filePath = './' + filePath;
|
|
72
|
+
let file = this.getFile(filePath);
|
|
73
|
+
if (file) {
|
|
74
|
+
if (returnExists)
|
|
75
|
+
return file;
|
|
76
|
+
throw new Error(`File "${filePath}" already exists`);
|
|
77
|
+
}
|
|
78
|
+
file = new TsFile(filePath);
|
|
79
|
+
file.code.header = this.fileHeader + (this._fileHeaderDocInfo ? '\n' + this._fileHeaderDocInfo : '') + '\n\n';
|
|
80
|
+
this._files[file.filename] = file;
|
|
81
|
+
return file;
|
|
82
|
+
}
|
|
83
|
+
extend() {
|
|
84
|
+
const instance = {
|
|
85
|
+
options: { ...this.options },
|
|
86
|
+
};
|
|
87
|
+
Object.setPrototypeOf(instance, this);
|
|
88
|
+
return instance;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
(() => {
|
|
92
|
+
TsGenerator.prototype.cleanDirectory = cleanDirectory;
|
|
93
|
+
TsGenerator.prototype.processDocument = processDocument;
|
|
94
|
+
TsGenerator.prototype.processDataType = processDataType;
|
|
95
|
+
TsGenerator.prototype.processHttpApi = processHttpApi;
|
|
96
|
+
TsGenerator.prototype.processHttpController = processHttpController;
|
|
97
|
+
TsGenerator.prototype.generateEnumTypeDefinition = generateEnumTypeDefinition;
|
|
98
|
+
TsGenerator.prototype.generateComplexTypeDefinition = generateComplexTypeDefinition;
|
|
99
|
+
TsGenerator.prototype.generateSimpleTypeDefinition = generateSimpleTypeDefinition;
|
|
100
|
+
TsGenerator.prototype.generateMappedTypeDefinition = generateMappedTypeDefinition;
|
|
101
|
+
TsGenerator.prototype.generateMixinTypeDefinition = generateMixinTypeDefinition;
|
|
102
|
+
TsGenerator.prototype.resolveTypeNameOrDef = resolveTypeNameOrDef;
|
|
103
|
+
})();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ComplexType, EnumType, MappedType, SimpleType } from '@opra/common';
|
|
2
|
+
export function locateNamedType(type) {
|
|
3
|
+
if (!type)
|
|
4
|
+
return;
|
|
5
|
+
if (type.name)
|
|
6
|
+
return type;
|
|
7
|
+
if (type instanceof SimpleType ||
|
|
8
|
+
type instanceof ComplexType ||
|
|
9
|
+
type instanceof EnumType ||
|
|
10
|
+
type instanceof MappedType)
|
|
11
|
+
return locateNamedType(type.base);
|
|
12
|
+
}
|
|
@@ -2,33 +2,33 @@ import jsStringEscape from 'js-string-escape';
|
|
|
2
2
|
export function wrapJSDocString(s, indent, currentColumn) {
|
|
3
3
|
const arr = (s || '')
|
|
4
4
|
.split(/[ \n\r]/)
|
|
5
|
-
.map((x, i, a) => i < a.length - 1 ? x + ' ' : x);
|
|
5
|
+
.map((x, i, a) => (i < a.length - 1 ? x + ' ' : x));
|
|
6
6
|
return _printLines(arr, {
|
|
7
7
|
indent,
|
|
8
8
|
currentColumn,
|
|
9
9
|
lineStart: '* ',
|
|
10
|
-
lineEnd: ''
|
|
10
|
+
lineEnd: '',
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
13
|
export function wrapQuotedString(s, indent, currentColumn) {
|
|
14
14
|
const arr = jsStringEscape(s || '')
|
|
15
15
|
.split(' ')
|
|
16
|
-
.map((x, i, a) => i < a.length - 1 ? x + ' ' : x);
|
|
17
|
-
return '
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
.map((x, i, a) => (i < a.length - 1 ? x + ' ' : x));
|
|
17
|
+
return ("'" +
|
|
18
|
+
_printLines(arr, {
|
|
19
|
+
indent,
|
|
20
|
+
currentColumn,
|
|
21
|
+
lineStart: "'",
|
|
22
|
+
lineEnd: "' +",
|
|
23
|
+
}) +
|
|
24
|
+
"'");
|
|
23
25
|
}
|
|
24
26
|
export function wrapStringArray(arr, indent, currentColumn) {
|
|
25
|
-
const ar1 = arr
|
|
26
|
-
.map((x, i, a) => ('\'' + x + '\'') + (i < a.length - 1 ? ', ' : ''));
|
|
27
|
+
const ar1 = arr.map((x, i, a) => "'" + x + "'" + (i < a.length - 1 ? ', ' : ''));
|
|
27
28
|
return '[' + _printLines(ar1, { indent, currentColumn }) + ']';
|
|
28
29
|
}
|
|
29
30
|
export function wrapTypeArray(arr, indent, currentColumn) {
|
|
30
|
-
const ar1 = arr
|
|
31
|
-
.map((x, i, a) => x + (i < a.length - 1 ? ' | ' : ''));
|
|
31
|
+
const ar1 = arr.map((x, i, a) => x + (i < a.length - 1 ? ' | ' : ''));
|
|
32
32
|
return _printLines(ar1, { indent, currentColumn });
|
|
33
33
|
}
|
|
34
34
|
function _printLines(arr, opts = {}) {
|
|
@@ -38,9 +38,10 @@ function _printLines(arr, opts = {}) {
|
|
|
38
38
|
let lineWidth = (opts.lineWidth || 90) - (opts.currentColumn || 0);
|
|
39
39
|
const l = arr.length;
|
|
40
40
|
const printLine = (eof) => {
|
|
41
|
-
s +=
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
s +=
|
|
42
|
+
(s ? '\n' + ' '.repeat(indent || 0) + (opts.lineStart ? opts.lineStart : '') : '') +
|
|
43
|
+
line +
|
|
44
|
+
(!eof ? (opts.lineEnd ? opts.lineEnd : '') : '');
|
|
44
45
|
line = '';
|
|
45
46
|
};
|
|
46
47
|
for (let i = 0; i < l; i++) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opra/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-alpha.7",
|
|
4
4
|
"description": "Opra CLI tools",
|
|
5
5
|
"author": "Panates",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
"copy:bin": "cp -R bin ../../build/cli/bin",
|
|
21
21
|
"lint": "eslint . --max-warnings=0",
|
|
22
22
|
"check": "madge --circular src/**",
|
|
23
|
-
"
|
|
24
|
-
"
|
|
23
|
+
"format": "prettier . --write --log-level=warn",
|
|
24
|
+
"test": "jest --passWithNoTests",
|
|
25
|
+
"cover": "jest --passWithNoTests --collect-coverage",
|
|
25
26
|
"clean": "npm run clean:src && npm run clean:test && npm run clean:dist && npm run clean:cover",
|
|
26
27
|
"clean:src": "ts-cleanup -s src --all",
|
|
27
28
|
"clean:test": "ts-cleanup -s test --all",
|
|
@@ -29,9 +30,10 @@
|
|
|
29
30
|
"clean:cover": "rimraf ../../coverage/client"
|
|
30
31
|
},
|
|
31
32
|
"dependencies": {
|
|
32
|
-
"@opra/client": "^0.
|
|
33
|
+
"@opra/client": "^1.0.0-alpha.7",
|
|
34
|
+
"@opra/common": "^1.0.0-alpha.7",
|
|
33
35
|
"chalk": "^5.3.0",
|
|
34
|
-
"commander": "^
|
|
36
|
+
"commander": "^12.0.0",
|
|
35
37
|
"js-string-escape": "^1.0.1",
|
|
36
38
|
"putil-flattentext": "^2.1.1",
|
|
37
39
|
"putil-varhelpers": "^1.6.5"
|
|
@@ -61,4 +63,4 @@
|
|
|
61
63
|
"tool",
|
|
62
64
|
"oprimp"
|
|
63
65
|
]
|
|
64
|
-
}
|
|
66
|
+
}
|
package/types/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './
|
|
1
|
+
export * from './ts-generator/index.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const httpControllerNodeScript = "import { kClient, OpraHttpClient } from '@opra/client';\n\nconst PARAM_PATTERN = /:\\w+/g;\n\nexport class HttpControllerNode {\n readonly [kClient]: OpraHttpClient;\n\n constructor(client: OpraHttpClient) {\n this[kClient] = client;\n }\n \n protected _prepareUrl(url: string, params: Record<string, any>): string {\n return url.replace(PARAM_PATTERN, s => {\n return params[s.substring(1)] || '';\n });\n }\n}\n";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ts-generator.js';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ComplexType, DataType, EnumType, MappedType, MixinType, SimpleType } from '@opra/common';
|
|
2
|
+
import { TsFile } from '../ts-file.js';
|
|
3
|
+
import type { TsGenerator } from '../ts-generator';
|
|
4
|
+
type Intent = 'scope' | 'extends' | 'field';
|
|
5
|
+
export declare function processDataType(this: TsGenerator, dataType: DataType): Promise<TsFile | undefined>;
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
export declare function generateEnumTypeDefinition(this: TsGenerator, dataType: EnumType, intent: Intent): Promise<string>;
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateComplexTypeDefinition(this: TsGenerator, dataType: ComplexType, file: TsFile, intent: Intent): Promise<string>;
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
export declare function generateSimpleTypeDefinition(this: TsGenerator, dataType: SimpleType, intent: Intent): Promise<string>;
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
export declare function generateMixinTypeDefinition(this: TsGenerator, dataType: MixinType, file: TsFile, intent: Intent): Promise<string>;
|
|
22
|
+
/**
|
|
23
|
+
*
|
|
24
|
+
*/
|
|
25
|
+
export declare function generateMappedTypeDefinition(this: TsGenerator, dataType: MappedType, file: TsFile, intent: Intent): Promise<string>;
|
|
26
|
+
/**
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveTypeNameOrDef(this: TsGenerator, dataType: DataType, file: TsFile, intent: Intent): Promise<string>;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ApiDocument } from '@opra/common';
|
|
2
|
+
import type { TsGenerator } from '../ts-generator';
|
|
3
|
+
export declare function processDocument(this: TsGenerator, document?: string | ApiDocument, options?: {
|
|
4
|
+
typesOnly?: boolean;
|
|
5
|
+
}): Promise<{
|
|
6
|
+
document: ApiDocument;
|
|
7
|
+
generator: TsGenerator;
|
|
8
|
+
}>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CodeBlock } from '../code-block.js';
|
|
1
2
|
export declare class TsFile {
|
|
2
3
|
readonly filename: string;
|
|
3
4
|
readonly dirname: string;
|
|
@@ -7,11 +8,10 @@ export declare class TsFile {
|
|
|
7
8
|
}>;
|
|
8
9
|
exportFiles: Record<string, string[]>;
|
|
9
10
|
exportTypes: string[];
|
|
10
|
-
|
|
11
|
-
content: string;
|
|
11
|
+
code: CodeBlock;
|
|
12
12
|
constructor(filename: string);
|
|
13
|
-
addImport
|
|
14
|
-
addExport
|
|
13
|
+
addImport(filename: string, items?: string[], typeImport?: boolean): void;
|
|
14
|
+
addExport(filename: string, types?: string[]): void;
|
|
15
15
|
generate(options?: {
|
|
16
16
|
importExt?: boolean;
|
|
17
17
|
}): string;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { EventEmitter } from 'node:events';
|
|
3
|
+
import { ApiDocument } from '@opra/common';
|
|
4
|
+
import { IFileWriter } from '../interfaces/file-writer.interface.js';
|
|
5
|
+
import { ILogger } from '../interfaces/logger.interface.js';
|
|
6
|
+
import { cleanDirectory } from './processors/clean-directory.js';
|
|
7
|
+
import { generateComplexTypeDefinition, generateEnumTypeDefinition, generateMappedTypeDefinition, generateMixinTypeDefinition, generateSimpleTypeDefinition, processDataType, resolveTypeNameOrDef } from './processors/process-data-types.js';
|
|
8
|
+
import { processDocument } from './processors/process-document.js';
|
|
9
|
+
import { processHttpApi } from './processors/process-http-api.js';
|
|
10
|
+
import { processHttpController } from './processors/process-http-controller.js';
|
|
11
|
+
import { TsFile } from './ts-file.js';
|
|
12
|
+
/**
|
|
13
|
+
* @namespace TsGenerator
|
|
14
|
+
*/
|
|
15
|
+
export declare namespace TsGenerator {
|
|
16
|
+
interface Options {
|
|
17
|
+
serviceUrl: string;
|
|
18
|
+
outDir: string;
|
|
19
|
+
cwd?: string;
|
|
20
|
+
logger?: ILogger;
|
|
21
|
+
writer?: IFileWriter;
|
|
22
|
+
fileHeader?: string;
|
|
23
|
+
importExt?: boolean;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* @class TsGenerator
|
|
28
|
+
*/
|
|
29
|
+
export declare class TsGenerator extends EventEmitter {
|
|
30
|
+
protected cleanDirectory: typeof cleanDirectory;
|
|
31
|
+
protected processDocument: typeof processDocument;
|
|
32
|
+
protected processDataType: typeof processDataType;
|
|
33
|
+
protected processHttpApi: typeof processHttpApi;
|
|
34
|
+
protected processHttpController: typeof processHttpController;
|
|
35
|
+
protected generateEnumTypeDefinition: typeof generateEnumTypeDefinition;
|
|
36
|
+
protected generateComplexTypeDefinition: typeof generateComplexTypeDefinition;
|
|
37
|
+
protected generateSimpleTypeDefinition: typeof generateSimpleTypeDefinition;
|
|
38
|
+
protected generateMappedTypeDefinition: typeof generateMappedTypeDefinition;
|
|
39
|
+
protected generateMixinTypeDefinition: typeof generateMixinTypeDefinition;
|
|
40
|
+
protected resolveTypeNameOrDef: typeof resolveTypeNameOrDef;
|
|
41
|
+
protected _started: boolean;
|
|
42
|
+
protected _document?: ApiDocument;
|
|
43
|
+
protected _documentRoot: string;
|
|
44
|
+
protected _typesRoot: string;
|
|
45
|
+
protected _apiPath: string;
|
|
46
|
+
protected _fileHeaderDocInfo: string;
|
|
47
|
+
protected _files: Record<string, TsFile>;
|
|
48
|
+
protected _documentsMap: Map<string, {
|
|
49
|
+
document: ApiDocument;
|
|
50
|
+
generator: TsGenerator;
|
|
51
|
+
}>;
|
|
52
|
+
protected _filesMap: WeakMap<Object, TsFile>;
|
|
53
|
+
readonly serviceUrl: string;
|
|
54
|
+
readonly outDir: string;
|
|
55
|
+
readonly cwd: string;
|
|
56
|
+
readonly writer: IFileWriter;
|
|
57
|
+
readonly options: {
|
|
58
|
+
importExt: boolean;
|
|
59
|
+
};
|
|
60
|
+
fileHeader: string;
|
|
61
|
+
/**
|
|
62
|
+
*
|
|
63
|
+
* @constructor
|
|
64
|
+
*/
|
|
65
|
+
constructor(init: TsGenerator.Options);
|
|
66
|
+
generate(): Promise<void>;
|
|
67
|
+
protected getFile(filePath: string): TsFile;
|
|
68
|
+
protected addFile(filePath: string, returnExists?: boolean): TsFile;
|
|
69
|
+
protected extend(): TsGenerator;
|
|
70
|
+
}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ApiExporter = void 0;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
6
|
-
const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
|
|
7
|
-
const node_path_1 = tslib_1.__importDefault(require("node:path"));
|
|
8
|
-
const node_process_1 = tslib_1.__importDefault(require("node:process"));
|
|
9
|
-
const client_1 = require("@opra/client");
|
|
10
|
-
const file_writer_js_1 = require("./file-writer.js");
|
|
11
|
-
const process_resources_js_1 = require("./process-resources.js");
|
|
12
|
-
const process_types_js_1 = require("./process-types.js");
|
|
13
|
-
const ts_file_js_1 = require("./ts-file.js");
|
|
14
|
-
class ApiExporter {
|
|
15
|
-
constructor(config) {
|
|
16
|
-
this.files = {};
|
|
17
|
-
this.client = new client_1.OpraHttpClient(config.serviceUrl);
|
|
18
|
-
this.cwd = config.cwd || node_process_1.default.cwd();
|
|
19
|
-
this.outDir = node_path_1.default.resolve(this.cwd, config.outDir);
|
|
20
|
-
this.logger = config.logger || {
|
|
21
|
-
log: () => void 0,
|
|
22
|
-
error: () => void 0,
|
|
23
|
-
debug: () => void 0,
|
|
24
|
-
warn: () => void 0,
|
|
25
|
-
verbose: () => void 0,
|
|
26
|
-
};
|
|
27
|
-
this.fileHeader = config.fileHeader || '';
|
|
28
|
-
this.writer = config.writer || new file_writer_js_1.FileWriter();
|
|
29
|
-
this.serviceClassName = config.name;
|
|
30
|
-
this.importExt = config.importExt;
|
|
31
|
-
// this.nsMap = nsMap || new ResponsiveMap(); // implement references later
|
|
32
|
-
}
|
|
33
|
-
async execute() {
|
|
34
|
-
this.logger.log(chalk_1.default.cyan('Fetching service metadata from'), chalk_1.default.whiteBright(this.client.serviceUrl));
|
|
35
|
-
this.document = await this.client.getMetadata();
|
|
36
|
-
this.logger.log(chalk_1.default.cyan('Retrieved service info:\n'), chalk_1.default.white('Title:'), chalk_1.default.whiteBright(this.document.info.title), '\n', chalk_1.default.white('Version:'), chalk_1.default.whiteBright(this.document.info.version), '\n');
|
|
37
|
-
this.serviceClassName = (this.serviceClassName || this.document.info.title || 'Service1').replace(/[^\w_$]*/g, '');
|
|
38
|
-
this.serviceClassName = this.serviceClassName.charAt(0).toUpperCase() + this.serviceClassName.substring(1);
|
|
39
|
-
this.fileHeader += `/*
|
|
40
|
-
* ${this.document.info.title}
|
|
41
|
-
* Version ${this.document.info.version}
|
|
42
|
-
* ${this.client.serviceUrl}
|
|
43
|
-
*/`;
|
|
44
|
-
this.logger.log(chalk_1.default.cyan('Removing old files..'));
|
|
45
|
-
this.cleanDirectory(this.outDir);
|
|
46
|
-
this.logger.log(chalk_1.default.cyan(`Generating service interface ( ${chalk_1.default.whiteBright(this.serviceClassName)} )`));
|
|
47
|
-
node_fs_1.default.mkdirSync(this.outDir, { recursive: true });
|
|
48
|
-
this.logger.log(chalk_1.default.cyan('Processing types'));
|
|
49
|
-
await this.processTypes();
|
|
50
|
-
this.logger.log(chalk_1.default.cyan('Processing resources'));
|
|
51
|
-
const rootTs = this.addFile('/' + this.serviceClassName + '.ts');
|
|
52
|
-
await this.processResource(this.document.root, this.serviceClassName, rootTs);
|
|
53
|
-
const { importExt } = this;
|
|
54
|
-
// Write files
|
|
55
|
-
for (const file of Object.values(this.files)) {
|
|
56
|
-
const filename = node_path_1.default.join(this.outDir, file.filename);
|
|
57
|
-
const targetDir = node_path_1.default.dirname(filename);
|
|
58
|
-
node_fs_1.default.mkdirSync(targetDir, { recursive: true });
|
|
59
|
-
await this.writer.writeFile(filename, file.generate({ importExt }));
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
getFile(filePath) {
|
|
63
|
-
return this.files[filePath];
|
|
64
|
-
}
|
|
65
|
-
addFile(filePath, returnExists) {
|
|
66
|
-
if (!(filePath.startsWith('.') || filePath.startsWith('/')))
|
|
67
|
-
filePath = './' + filePath;
|
|
68
|
-
let file = this.getFile(filePath);
|
|
69
|
-
if (file) {
|
|
70
|
-
if (returnExists)
|
|
71
|
-
return file;
|
|
72
|
-
throw new Error(`File "${filePath}" already exists`);
|
|
73
|
-
}
|
|
74
|
-
file = new ts_file_js_1.TsFile(filePath);
|
|
75
|
-
file.header = this.fileHeader;
|
|
76
|
-
this.files[file.filename] = file;
|
|
77
|
-
return file;
|
|
78
|
-
}
|
|
79
|
-
cleanDirectory(dirname) {
|
|
80
|
-
if (!node_fs_1.default.existsSync(dirname))
|
|
81
|
-
return;
|
|
82
|
-
const files = node_fs_1.default.readdirSync(dirname);
|
|
83
|
-
for (const f of files) {
|
|
84
|
-
const absolutePath = node_path_1.default.join(dirname, f);
|
|
85
|
-
if (node_fs_1.default.statSync(absolutePath).isDirectory()) {
|
|
86
|
-
this.cleanDirectory(absolutePath);
|
|
87
|
-
if (!node_fs_1.default.readdirSync(absolutePath).length)
|
|
88
|
-
node_fs_1.default.rmdirSync(absolutePath);
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
if (node_path_1.default.extname(f) === '.ts') {
|
|
92
|
-
const contents = node_fs_1.default.readFileSync(absolutePath, 'utf-8');
|
|
93
|
-
if (contents.includes('#!oprimp_auto_generated!#')) {
|
|
94
|
-
node_fs_1.default.unlinkSync(absolutePath);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
static async execute(config) {
|
|
100
|
-
const exporter = new ApiExporter(config);
|
|
101
|
-
await exporter.execute();
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
exports.ApiExporter = ApiExporter;
|
|
105
|
-
(() => {
|
|
106
|
-
ApiExporter.prototype.processResource = process_resources_js_1.processResource;
|
|
107
|
-
ApiExporter.prototype.processTypes = process_types_js_1.processTypes;
|
|
108
|
-
ApiExporter.prototype.generateTypeFile = process_types_js_1.generateTypeFile;
|
|
109
|
-
ApiExporter.prototype.generateComplexTypeDefinition = process_types_js_1.generateComplexTypeDefinition;
|
|
110
|
-
ApiExporter.prototype.generateSimpleTypeDefinition = process_types_js_1.generateSimpleTypeDefinition;
|
|
111
|
-
ApiExporter.prototype.resolveTypeNameOrDef = process_types_js_1.resolveTypeNameOrDef;
|
|
112
|
-
ApiExporter.prototype.generateEnumTypeDefinition = process_types_js_1.generateEnumTypeDefinition;
|
|
113
|
-
ApiExporter.prototype.generateMixinTypeDefinition = process_types_js_1.generateMixinTypeDefinition;
|
|
114
|
-
ApiExporter.prototype.generateMappedTypeDefinition = process_types_js_1.generateMappedTypeDefinition;
|
|
115
|
-
})();
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.processResource = 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
|
-
async function processResource(resource, className, tsFile) {
|
|
9
|
-
tsFile.addImport('@opra/client', ['kClient', 'OpraHttpClient']);
|
|
10
|
-
tsFile.content = `\n
|
|
11
|
-
/**
|
|
12
|
-
* ${(0, string_utils_js_1.wrapJSDocString)(resource.description || '')}
|
|
13
|
-
* @class ${className}
|
|
14
|
-
* @url ${node_path_1.default.posix.join(this.client.serviceUrl, '#resources/' + className)}
|
|
15
|
-
*/
|
|
16
|
-
export class ${className} {
|
|
17
|
-
readonly [kClient]: OpraHttpClient;\n`;
|
|
18
|
-
let constructorBody = `
|
|
19
|
-
constructor(client: OpraHttpClient) {
|
|
20
|
-
this[kClient] = client;\n`;
|
|
21
|
-
if (resource instanceof common_1.Container) {
|
|
22
|
-
for (const child of resource.resources.values()) {
|
|
23
|
-
// Determine class name of child resource
|
|
24
|
-
let childClassName = child.name.charAt(0).toUpperCase() + child.name.substring(1);
|
|
25
|
-
if (child instanceof common_1.Container)
|
|
26
|
-
childClassName += 'Container';
|
|
27
|
-
else
|
|
28
|
-
childClassName += 'Resource';
|
|
29
|
-
// Create TsFile for child resource
|
|
30
|
-
const dir = node_path_1.default.dirname(tsFile.filename);
|
|
31
|
-
const basename = dir === '/'
|
|
32
|
-
? 'root'
|
|
33
|
-
: node_path_1.default.basename(tsFile.filename, node_path_1.default.extname(tsFile.filename));
|
|
34
|
-
const childFile = this.addFile(node_path_1.default.join(dir, basename, child.name + '.ts'));
|
|
35
|
-
await this.processResource(child, childClassName, childFile);
|
|
36
|
-
tsFile.addImport(childFile.filename, [childClassName]);
|
|
37
|
-
tsFile.content += `
|
|
38
|
-
/**
|
|
39
|
-
* ${(0, string_utils_js_1.wrapJSDocString)(child.description || '')}
|
|
40
|
-
* @url ${node_path_1.default.posix.join(this.client.serviceUrl, '#resources/' + child.name)}
|
|
41
|
-
*/
|
|
42
|
-
readonly ${child.name}: ${childClassName};\n`;
|
|
43
|
-
constructorBody += ` this.${child.name} = new ${childClassName}(client);\n`;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
else if (resource instanceof common_1.Collection) {
|
|
47
|
-
tsFile.addImport('@opra/client', ['HttpCollectionNode']);
|
|
48
|
-
const typeName = resource.type.name || '';
|
|
49
|
-
tsFile.addImport(`/types/${typeName}`, [typeName], true);
|
|
50
|
-
constructorBody += ` const node = this[kClient].collection('${resource.getFullPath()}');\n`;
|
|
51
|
-
for (const [operation, endpoint] of resource.operations.entries()) {
|
|
52
|
-
tsFile.content += `
|
|
53
|
-
/**
|
|
54
|
-
* ${(0, string_utils_js_1.wrapJSDocString)(endpoint.description || '')}
|
|
55
|
-
*/
|
|
56
|
-
readonly ${operation}: HttpCollectionNode<${typeName}>['${operation}'];\n`;
|
|
57
|
-
constructorBody += ` this.${operation} = node.${operation}.bind(node);\n`;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
else if (resource instanceof common_1.Singleton) {
|
|
61
|
-
tsFile.addImport('@opra/client', ['HttpSingletonNode']);
|
|
62
|
-
const typeName = resource.type.name || '';
|
|
63
|
-
tsFile.addImport(`/types/${typeName}`, [typeName], true);
|
|
64
|
-
constructorBody += ` const node = this[kClient].singleton('${resource.getFullPath()}');\n`;
|
|
65
|
-
for (const [operation, endpoint] of resource.operations.entries()) {
|
|
66
|
-
tsFile.content += `
|
|
67
|
-
/**
|
|
68
|
-
* ${(0, string_utils_js_1.wrapJSDocString)(endpoint.description || '')}
|
|
69
|
-
*/
|
|
70
|
-
readonly ${operation}: HttpSingletonNode<${typeName}>['${operation}'];\n`;
|
|
71
|
-
constructorBody += ` this.${operation} = node.${operation}.bind(node);\n`;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
else if (resource instanceof common_1.Storage) {
|
|
75
|
-
tsFile.addImport('@opra/client', ['HttpStorageNode']);
|
|
76
|
-
constructorBody += ` const node = this[kClient].storage('${resource.getFullPath()}');\n`;
|
|
77
|
-
for (const [operation, endpoint] of resource.operations.entries()) {
|
|
78
|
-
tsFile.content += `
|
|
79
|
-
/**
|
|
80
|
-
* ${(0, string_utils_js_1.wrapJSDocString)(endpoint.description || '')}
|
|
81
|
-
*/`;
|
|
82
|
-
if (operation === 'post' && endpoint.returnType) {
|
|
83
|
-
const typeName = endpoint.returnType.name || 'any'; // todo
|
|
84
|
-
tsFile.addImport(`/types/${typeName}`, [typeName], true);
|
|
85
|
-
tsFile.content += `\n readonly ${operation}: HttpStorageNode<${typeName}>['${operation}'];\n`;
|
|
86
|
-
}
|
|
87
|
-
else
|
|
88
|
-
tsFile.content += `\n readonly ${operation}: HttpStorageNode['${operation}'];\n`;
|
|
89
|
-
constructorBody += ` this.${operation} = node.${operation}.bind(node);\n`;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
if (resource.actions.size) {
|
|
93
|
-
tsFile.addImport('@opra/client', ['HttpRequestObservable']);
|
|
94
|
-
for (const [action, endpoint] of resource.actions.entries()) {
|
|
95
|
-
let returnTypeDef = endpoint.returnType ?
|
|
96
|
-
await this.resolveTypeNameOrDef({
|
|
97
|
-
file: tsFile,
|
|
98
|
-
dataType: endpoint.returnType,
|
|
99
|
-
intent: 'field'
|
|
100
|
-
})
|
|
101
|
-
: 'any';
|
|
102
|
-
if (returnTypeDef.length > 40)
|
|
103
|
-
returnTypeDef = '\n\t\t' + returnTypeDef + '\n\b\b';
|
|
104
|
-
const actionPath = resource.getFullPath() + '/' + action;
|
|
105
|
-
let params = '';
|
|
106
|
-
for (const prm of endpoint.parameters.values()) {
|
|
107
|
-
const paramTypeDef = await this.resolveTypeNameOrDef({
|
|
108
|
-
file: tsFile,
|
|
109
|
-
dataType: prm.type,
|
|
110
|
-
intent: 'field'
|
|
111
|
-
}) || 'any';
|
|
112
|
-
params += `${prm.name}: ${paramTypeDef}`;
|
|
113
|
-
if (prm.isArray)
|
|
114
|
-
params += '[]';
|
|
115
|
-
params += ';\n';
|
|
116
|
-
}
|
|
117
|
-
params = params ? '\n\t\t\tparams: {\n\t' + params + '\b}\n\b\b' : '';
|
|
118
|
-
tsFile.content += `
|
|
119
|
-
/**
|
|
120
|
-
* ${(0, string_utils_js_1.wrapJSDocString)(endpoint.description || '')}
|
|
121
|
-
*/
|
|
122
|
-
${action}(${params}): HttpRequestObservable<${returnTypeDef}> {\b
|
|
123
|
-
return this[kClient].action('${actionPath}'${params ? ', params' : ''});
|
|
124
|
-
}
|
|
125
|
-
`;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
tsFile.content += constructorBody + ` }\n\n}\n`;
|
|
129
|
-
return tsFile.content;
|
|
130
|
-
}
|
|
131
|
-
exports.processResource = processResource;
|