@opra/cli 1.26.2 → 1.26.4

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/README.md CHANGED
@@ -1,3 +1,126 @@
1
- # @opra/node-client
1
+ # @opra/cli
2
2
 
3
- OPRA NodeJS Client
3
+ [![NPM Version][npm-image]][npm-url]
4
+ [![NPM Downloads][downloads-image]][downloads-url]
5
+ [![CI Tests][ci-test-image]][ci-test-url]
6
+ [![Test Coverage][coveralls-image]][coveralls-url]
7
+
8
+ `@opra/cli` is a command-line tool for OPRA (Open Protocol for Restfull APIs). It currently features `oprimp`, a
9
+ TypeScript code generator that creates client-side models and API services from an OPRA service.
10
+
11
+ ## Installation
12
+
13
+ You can install `@opra/cli` globally or as a development dependency in your project.
14
+
15
+ ### Global Installation
16
+
17
+ ```bash
18
+ npm install -g @opra/cli
19
+ ```
20
+
21
+ ### Local Installation
22
+
23
+ ```bash
24
+ npm install --save-dev @opra/cli
25
+ ```
26
+
27
+ ## CLI Usage: oprimp
28
+
29
+ The `oprimp` tool generates TypeScript code from an OPRA service URL.
30
+
31
+ ```bash
32
+ oprimp <serviceUrl> <outDir> [options]
33
+ ```
34
+
35
+ ### Arguments
36
+
37
+ - `serviceUrl`: The URL of the OPRA service (e.g., `http://localhost:3000/opra`).
38
+ - `outDir`: The output directory where the generated files will be saved.
39
+
40
+ ### Options
41
+
42
+ - `--ext`: Adds `.js` extension to imports. This is useful for ESM projects.
43
+ - `--refns`: Exports referenced API documentation with namespaces.
44
+ - `--no-color`: Disables colors in log messages.
45
+ - `-v, --version`: Displays the version number.
46
+ - `-h, --help`: Displays help information.
47
+
48
+ ### Examples
49
+
50
+ #### Basic Usage
51
+
52
+ Generate TypeScript code from a local OPRA service:
53
+
54
+ ```bash
55
+ npx oprimp http://localhost:3000/opra ./src/generated
56
+ ```
57
+
58
+ #### ESM Projects
59
+
60
+ If your project uses ESM and requires file extensions in imports:
61
+
62
+ ```bash
63
+ npx oprimp http://localhost:3000/opra ./src/generated --ext
64
+ ```
65
+
66
+ ## Programmatic Usage
67
+
68
+ You can also use the `TsGenerator` class programmatically in your scripts.
69
+
70
+ ```typescript
71
+ import { TsGenerator } from '@opra/cli';
72
+
73
+ async function generate() {
74
+ const generator = new TsGenerator({
75
+ serviceUrl: 'http://localhost:3000/opra',
76
+ outDir: './src/generated',
77
+ importExt: true, // Optional: add .js extensions
78
+ fileHeader: '/* Generated by OPRA */', // Optional: add a header to files
79
+ });
80
+
81
+ await generator.generate();
82
+ console.log('Generation completed!');
83
+ }
84
+
85
+ generate().catch(console.error);
86
+ ```
87
+
88
+ ### TsGenerator Options
89
+
90
+ | Option | Type | Description |
91
+ |-----------------------|-----------|----------------------------------------------------------------|
92
+ | `serviceUrl` | `string` | **Required**. The URL of the OPRA service. |
93
+ | `outDir` | `string` | **Required**. The output directory for the generated files. |
94
+ | `cwd` | `string` | The current working directory. Defaults to `process.cwd()`. |
95
+ | `logger` | `ILogger` | Logger instance for outputting information. |
96
+ | `fileHeader` | `string` | Optional header text to add to the top of each generated file. |
97
+ | `importExt` | `boolean` | Whether to add `.js` extension to imports. |
98
+ | `referenceNamespaces` | `boolean` | Whether to export references with namespaces. |
99
+
100
+ ## Support
101
+
102
+ You can report bugs and discuss features on the [GitHub issues](https://github.com/panates/opra/issues) page.
103
+
104
+ ## Node Compatibility
105
+
106
+ - node >= 20.x
107
+
108
+ ## License
109
+
110
+ Available under [MIT](LICENSE) license.
111
+
112
+ [npm-image]: https://img.shields.io/npm/v/@opra/cli
113
+
114
+ [npm-url]: https://npmjs.org/package/@opra/cli
115
+
116
+ [downloads-image]: https://img.shields.io/npm/dm/@opra/cli.svg
117
+
118
+ [downloads-url]: https://npmjs.org/package/@opra/cli
119
+
120
+ [ci-test-image]: https://github.com/panates/opra/actions/workflows/test.yml/badge.svg
121
+
122
+ [ci-test-url]: https://github.com/panates/opra/actions/workflows/test.yml
123
+
124
+ [coveralls-image]: https://coveralls.io/repos/github/panates/opra/badge.svg?branch=main
125
+
126
+ [coveralls-url]: https://coveralls.io/github/panates/opra?branch=main
package/code-block.d.ts CHANGED
@@ -1,5 +1,15 @@
1
+ /**
2
+ * CodeBlock
3
+ *
4
+ * A class representing a block of code, which can be composed of multiple segments.
5
+ */
1
6
  export declare class CodeBlock {
2
7
  [index: string]: any;
8
+ /**
9
+ * Concatenates all segments of the code block into a single string.
10
+ *
11
+ * @returns The full code as a string.
12
+ */
3
13
  toString(): string;
4
14
  [Symbol.toStringTag](): string;
5
15
  }
package/code-block.js CHANGED
@@ -1,4 +1,14 @@
1
+ /**
2
+ * CodeBlock
3
+ *
4
+ * A class representing a block of code, which can be composed of multiple segments.
5
+ */
1
6
  export class CodeBlock {
7
+ /**
8
+ * Concatenates all segments of the code block into a single string.
9
+ *
10
+ * @returns The full code as a string.
11
+ */
2
12
  toString() {
3
13
  // if (this.content) return this.content;
4
14
  let out = '';
package/file-writer.d.ts CHANGED
@@ -1,4 +1,15 @@
1
1
  import type { IFileWriter } from './interfaces/file-writer.interface.js';
2
+ /**
3
+ * FileWriter
4
+ *
5
+ * Implementation of IFileWriter that uses node:fs to write files.
6
+ */
2
7
  export declare class FileWriter implements IFileWriter {
8
+ /**
9
+ * Writes contents to a file.
10
+ *
11
+ * @param filename - The path to the file to write.
12
+ * @param contents - The string content to write to the file.
13
+ */
3
14
  writeFile(filename: string, contents: string): void;
4
15
  }
package/file-writer.js CHANGED
@@ -1,5 +1,16 @@
1
1
  import fs from 'node:fs';
2
+ /**
3
+ * FileWriter
4
+ *
5
+ * Implementation of IFileWriter that uses node:fs to write files.
6
+ */
2
7
  export class FileWriter {
8
+ /**
9
+ * Writes contents to a file.
10
+ *
11
+ * @param filename - The path to the file to write.
12
+ * @param contents - The string content to write to the file.
13
+ */
3
14
  writeFile(filename, contents) {
4
15
  fs.writeFileSync(filename, contents, 'utf-8');
5
16
  }
@@ -1,3 +1,15 @@
1
+ /**
2
+ * IFileWriter
3
+ *
4
+ * Interface for file writing operations.
5
+ */
1
6
  export interface IFileWriter {
7
+ /**
8
+ * Writes contents to a file.
9
+ *
10
+ * @param filename - The path to the file to write.
11
+ * @param contents - The string content to write to the file.
12
+ * @returns A promise that resolves when the file is written, or void if synchronous.
13
+ */
2
14
  writeFile(filename: string, contents: string): Promise<void> | void;
3
15
  }
@@ -1,7 +1,42 @@
1
+ /**
2
+ * ILogger
3
+ *
4
+ * Interface for logging operations within the CLI.
5
+ */
1
6
  export interface ILogger {
7
+ /**
8
+ * Logs a general message.
9
+ *
10
+ * @param message - The message to log.
11
+ * @param optionalParams - Additional parameters for the log message.
12
+ */
2
13
  log(message: any, ...optionalParams: any[]): any;
14
+ /**
15
+ * Logs an error message.
16
+ *
17
+ * @param message - The error message to log.
18
+ * @param optionalParams - Additional parameters for the error message.
19
+ */
3
20
  error(message: any, ...optionalParams: any[]): any;
21
+ /**
22
+ * Logs a warning message.
23
+ *
24
+ * @param message - The warning message to log.
25
+ * @param optionalParams - Additional parameters for the warning message.
26
+ */
4
27
  warn(message: any, ...optionalParams: any[]): any;
28
+ /**
29
+ * Logs a debug message.
30
+ *
31
+ * @param message - The debug message to log.
32
+ * @param optionalParams - Additional parameters for the debug message.
33
+ */
5
34
  debug?(message: any, ...optionalParams: any[]): any;
35
+ /**
36
+ * Logs a verbose message.
37
+ *
38
+ * @param message - The verbose message to log.
39
+ * @param optionalParams - Additional parameters for the verbose message.
40
+ */
6
41
  verbose?(message: any, ...optionalParams: any[]): any;
7
42
  }
package/oprimp-cli.js CHANGED
@@ -15,7 +15,7 @@ program
15
15
  .argument('<serviceUrl>', 'OPRA service url')
16
16
  .argument('<outDir>', 'Output directory')
17
17
  .option('--ext', 'Adds js extension to imports')
18
- .option('--refns', 'Exports references with namespaces')
18
+ .option('--refns', 'Exports referenced API documentation with namespaces')
19
19
  .option('--no-color', 'Disables colors in logs messages')
20
20
  .action(async (serviceUrl, outDir, options) => {
21
21
  if (!options.color)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opra/cli",
3
- "version": "1.26.2",
3
+ "version": "1.26.4",
4
4
  "description": "Opra CLI tools",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
@@ -15,8 +15,8 @@
15
15
  "tslib": "^2.8.1"
16
16
  },
17
17
  "peerDependencies": {
18
- "@opra/client": "^1.26.2",
19
- "@opra/common": "^1.26.2"
18
+ "@opra/client": "^1.26.4",
19
+ "@opra/common": "^1.26.4"
20
20
  },
21
21
  "type": "module",
22
22
  "module": "./index.js",
@@ -1,2 +1,7 @@
1
1
  import type { TsGenerator } from '../ts-generator.js';
2
+ /**
3
+ * Cleans the output directory.
4
+ *
5
+ * @param dirname - The directory to clean.
6
+ */
2
7
  export declare function cleanDirectory(this: TsGenerator, dirname: string): void;
@@ -1,6 +1,11 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import colors from 'ansi-colors';
4
+ /**
5
+ * Cleans the output directory.
6
+ *
7
+ * @param dirname - The directory to clean.
8
+ */
4
9
  export function cleanDirectory(dirname) {
5
10
  const rootDir = dirname;
6
11
  const _cleanDirectory = (targetDir) => {
@@ -14,37 +14,89 @@ export type generateDataTypeResult = {
14
14
  kind: 'embedded';
15
15
  code: string;
16
16
  };
17
+ /**
18
+ * Generates TypeScript code for a given data type.
19
+ *
20
+ * @param dataType - The data type to generate code for.
21
+ * @param intent - The generation intent (root, extends, typeDef).
22
+ * @param currentFile - The current file being generated.
23
+ * @returns A promise that resolves to the generation result.
24
+ * @throws {@link TypeError} If currentFile is not provided for embedded generation.
25
+ */
17
26
  export declare function generateDataType(this: TsGenerator, dataType: DataType, intent: Intent, currentFile?: TsFile): Promise<generateDataTypeResult>;
18
27
  /**
28
+ * Generates the TypeScript type definition for a data type.
19
29
  *
30
+ * @param currentFile - The current file being generated.
31
+ * @param dataType - The data type to generate code for.
32
+ * @param codeBlock - The code block to add the generated code to.
33
+ * @param intent - The generation intent.
34
+ * @throws {@link TypeError} If name is required but not provided.
20
35
  */
21
36
  export declare function _generateTypeCode(this: TsGenerator, currentFile: TsFile, dataType: DataType, codeBlock: CodeBlock, intent?: Intent): Promise<void>;
22
37
  /**
38
+ * Generates code for an array type.
23
39
  *
40
+ * @param currentFile - The current file being generated.
41
+ * @param dataType - The array data type.
42
+ * @param codeBlock - The code block to add the generated code to.
43
+ * @param intent - The generation intent.
44
+ * @throws {@link TypeError} If array type is attempted to be extended.
24
45
  */
25
46
  export declare function _generateArrayTypeCode(this: TsGenerator, currentFile: TsFile, dataType: ArrayType, codeBlock: CodeBlock, intent?: Intent): Promise<void>;
26
47
  /**
48
+ * Generates code for a complex type.
27
49
  *
50
+ * @param currentFile - The current file being generated.
51
+ * @param dataType - The complex data type.
52
+ * @param codeBlock - The code block to add the generated code to.
53
+ * @param intent - The generation intent.
28
54
  */
29
55
  export declare function _generateComplexTypeCode(this: TsGenerator, currentFile: TsFile, dataType: ComplexType, codeBlock: CodeBlock, intent?: Intent): Promise<void>;
30
56
  /**
57
+ * Generates code for an enum type.
31
58
  *
59
+ * @param currentFile - The current file being generated.
60
+ * @param dataType - The enum data type.
61
+ * @param codeBlock - The code block to add the generated code to.
62
+ * @param intent - The generation intent.
32
63
  */
33
64
  export declare function _generateEnumTypeCode(this: TsGenerator, currentFile: TsFile, dataType: EnumType, codeBlock: CodeBlock, intent?: Intent): Promise<void>;
34
65
  /**
66
+ * Generates code for a mapped type.
35
67
  *
68
+ * @param currentFile - The current file being generated.
69
+ * @param dataType - The mapped data type.
70
+ * @param codeBlock - The code block to add the generated code to.
71
+ * @param intent - The generation intent.
36
72
  */
37
73
  export declare function _generateMappedTypeCode(this: TsGenerator, currentFile: TsFile, dataType: MappedType, codeBlock: CodeBlock, intent?: Intent): Promise<void>;
38
74
  /**
75
+ * Generates code for a mixin type.
39
76
  *
77
+ * @param currentFile - The current file being generated.
78
+ * @param dataType - The mixin data type.
79
+ * @param codeBlock - The code block to add the generated code to.
80
+ * @param intent - The generation intent.
40
81
  */
41
82
  export declare function _generateMixinTypeCode(this: TsGenerator, currentFile: TsFile, dataType: MixinType, codeBlock: CodeBlock, intent?: Intent): Promise<void>;
42
83
  /**
84
+ * Generates code for a simple type.
43
85
  *
86
+ * @param currentFile - The current file being generated.
87
+ * @param dataType - The simple data type.
88
+ * @param codeBlock - The code block to add the generated code to.
89
+ * @param intent - The generation intent.
44
90
  */
45
91
  export declare function _generateSimpleTypeCode(this: TsGenerator, currentFile: TsFile, dataType: SimpleType, codeBlock: CodeBlock, intent?: Intent): Promise<void>;
46
92
  /**
93
+ * Generates code for a union type.
47
94
  *
95
+ * @param currentFile - The current file being generated.
96
+ * @param dataType - The union data type.
97
+ * @param codeBlock - The code block to add the generated code to.
98
+ * @param intent - The generation intent.
99
+ * @throws {@link TypeError} If union type is attempted to be extended.
48
100
  */
49
101
  export declare function _generateUnionTypeCode(this: TsGenerator, currentFile: TsFile, dataType: UnionType, codeBlock: CodeBlock, intent?: Intent): Promise<void>;
50
102
  export {};
@@ -12,6 +12,15 @@ const internalTypeNames = [
12
12
  'string',
13
13
  'object',
14
14
  ];
15
+ /**
16
+ * Generates TypeScript code for a given data type.
17
+ *
18
+ * @param dataType - The data type to generate code for.
19
+ * @param intent - The generation intent (root, extends, typeDef).
20
+ * @param currentFile - The current file being generated.
21
+ * @returns A promise that resolves to the generation result.
22
+ * @throws {@link TypeError} If currentFile is not provided for embedded generation.
23
+ */
15
24
  export async function generateDataType(dataType, intent, currentFile) {
16
25
  const doc = dataType.node.getDocument();
17
26
  if (doc.id !== this._document?.id) {
@@ -24,7 +33,7 @@ export async function generateDataType(dataType, intent, currentFile) {
24
33
  if (dataType instanceof ComplexType &&
25
34
  dataType.ctor === OperationResult) {
26
35
  if (currentFile)
27
- currentFile.addImport('@opra/common', [typeName]);
36
+ currentFile.addImport('@opra/common', [typeName], true);
28
37
  return { kind: 'internal', typeName: dataType.name };
29
38
  }
30
39
  if (internalTypeNames.includes(typeName))
@@ -32,7 +41,7 @@ export async function generateDataType(dataType, intent, currentFile) {
32
41
  let file = this._filesMap.get(dataType);
33
42
  if (file) {
34
43
  if (currentFile)
35
- currentFile.addImport(file.filename, [typeName]);
44
+ currentFile.addImport(file.filename, [typeName], true);
36
45
  return { kind: 'named', file, typeName: dataType.name };
37
46
  }
38
47
  if (dataType instanceof SimpleType)
@@ -45,7 +54,7 @@ export async function generateDataType(dataType, intent, currentFile) {
45
54
  this._filesMap.set(dataType, file);
46
55
  if (file.exportTypes.includes(typeName)) {
47
56
  if (currentFile)
48
- currentFile.addImport(file.filename, [typeName]);
57
+ currentFile.addImport(file.filename, [typeName], true);
49
58
  return { kind: 'named', file, typeName: dataType.name };
50
59
  }
51
60
  file.exportTypes.push(typeName);
@@ -62,7 +71,7 @@ export async function generateDataType(dataType, intent, currentFile) {
62
71
  codeBlock.type_end = '\n\n';
63
72
  typesIndexTs.addExport(file.filename);
64
73
  if (currentFile)
65
- currentFile.addImport(file.filename, [typeName]);
74
+ currentFile.addImport(file.filename, [typeName], true);
66
75
  return { kind: 'named', file, typeName };
67
76
  }
68
77
  if (!currentFile)
@@ -77,7 +86,13 @@ export async function generateDataType(dataType, intent, currentFile) {
77
86
  }
78
87
  }
79
88
  /**
89
+ * Generates the TypeScript type definition for a data type.
80
90
  *
91
+ * @param currentFile - The current file being generated.
92
+ * @param dataType - The data type to generate code for.
93
+ * @param codeBlock - The code block to add the generated code to.
94
+ * @param intent - The generation intent.
95
+ * @throws {@link TypeError} If name is required but not provided.
81
96
  */
82
97
  export async function _generateTypeCode(currentFile, dataType, codeBlock, intent) {
83
98
  if (intent === 'root' && !dataType.name) {
@@ -110,7 +125,13 @@ export async function _generateTypeCode(currentFile, dataType, codeBlock, intent
110
125
  throw new TypeError(`${dataType.kind} data types can not be directly exported`);
111
126
  }
112
127
  /**
128
+ * Generates code for an array type.
113
129
  *
130
+ * @param currentFile - The current file being generated.
131
+ * @param dataType - The array data type.
132
+ * @param codeBlock - The code block to add the generated code to.
133
+ * @param intent - The generation intent.
134
+ * @throws {@link TypeError} If array type is attempted to be extended.
114
135
  */
115
136
  export async function _generateArrayTypeCode(currentFile, dataType, codeBlock, intent) {
116
137
  if (intent === 'extends')
@@ -129,7 +150,12 @@ export async function _generateArrayTypeCode(currentFile, dataType, codeBlock, i
129
150
  intent === 'root' ? `type ${dataType.name} = ${out}[]` : `${out}[]`;
130
151
  }
131
152
  /**
153
+ * Generates code for a complex type.
132
154
  *
155
+ * @param currentFile - The current file being generated.
156
+ * @param dataType - The complex data type.
157
+ * @param codeBlock - The code block to add the generated code to.
158
+ * @param intent - The generation intent.
133
159
  */
134
160
  export async function _generateComplexTypeCode(currentFile, dataType, codeBlock, intent) {
135
161
  let out = intent === 'root' ? `interface ${dataType.name} ` : '';
@@ -214,7 +240,12 @@ export async function _generateComplexTypeCode(currentFile, dataType, codeBlock,
214
240
  codeBlock.typeDef = out + '\b}';
215
241
  }
216
242
  /**
243
+ * Generates code for an enum type.
217
244
  *
245
+ * @param currentFile - The current file being generated.
246
+ * @param dataType - The enum data type.
247
+ * @param codeBlock - The code block to add the generated code to.
248
+ * @param intent - The generation intent.
218
249
  */
219
250
  export async function _generateEnumTypeCode(currentFile, dataType, codeBlock, intent) {
220
251
  if (intent === 'root') {
@@ -244,7 +275,12 @@ export async function _generateEnumTypeCode(currentFile, dataType, codeBlock, in
244
275
  ')';
245
276
  }
246
277
  /**
278
+ * Generates code for a mapped type.
247
279
  *
280
+ * @param currentFile - The current file being generated.
281
+ * @param dataType - The mapped data type.
282
+ * @param codeBlock - The code block to add the generated code to.
283
+ * @param intent - The generation intent.
248
284
  */
249
285
  export async function _generateMappedTypeCode(currentFile, dataType, codeBlock, intent) {
250
286
  let out = intent === 'root' ? `type ${dataType.name} = ` : '';
@@ -304,7 +340,12 @@ export async function _generateMappedTypeCode(currentFile, dataType, codeBlock,
304
340
  codeBlock.typeDef = out;
305
341
  }
306
342
  /**
343
+ * Generates code for a mixin type.
307
344
  *
345
+ * @param currentFile - The current file being generated.
346
+ * @param dataType - The mixin data type.
347
+ * @param codeBlock - The code block to add the generated code to.
348
+ * @param intent - The generation intent.
308
349
  */
309
350
  export async function _generateMixinTypeCode(currentFile, dataType, codeBlock, intent) {
310
351
  const outArray = [];
@@ -328,7 +369,12 @@ export async function _generateMixinTypeCode(currentFile, dataType, codeBlock, i
328
369
  codeBlock.typeDef = outArray.join(' & ');
329
370
  }
330
371
  /**
372
+ * Generates code for a simple type.
331
373
  *
374
+ * @param currentFile - The current file being generated.
375
+ * @param dataType - The simple data type.
376
+ * @param codeBlock - The code block to add the generated code to.
377
+ * @param intent - The generation intent.
332
378
  */
333
379
  export async function _generateSimpleTypeCode(currentFile, dataType, codeBlock, intent) {
334
380
  if (intent !== 'typeDef' && dataType.properties) {
@@ -343,7 +389,13 @@ export async function _generateSimpleTypeCode(currentFile, dataType, codeBlock,
343
389
  codeBlock.typeDef = intent === 'root' ? out + ';' : out;
344
390
  }
345
391
  /**
392
+ * Generates code for a union type.
346
393
  *
394
+ * @param currentFile - The current file being generated.
395
+ * @param dataType - The union data type.
396
+ * @param codeBlock - The code block to add the generated code to.
397
+ * @param intent - The generation intent.
398
+ * @throws {@link TypeError} If union type is attempted to be extended.
347
399
  */
348
400
  export async function _generateUnionTypeCode(currentFile, dataType, codeBlock, intent) {
349
401
  if (intent === 'extends')
@@ -1,5 +1,12 @@
1
1
  import { ApiDocument } from '@opra/common';
2
2
  import type { TsGenerator } from '../ts-generator.js';
3
+ /**
4
+ * Generates the document and its references.
5
+ *
6
+ * @param document - The document to generate.
7
+ * @param options - Generation options.
8
+ * @returns An object containing the generated document and its generator.
9
+ */
3
10
  export declare function generateDocument(this: TsGenerator, document?: string | ApiDocument, options?: {
4
11
  typesOnly?: boolean;
5
12
  }): Promise<{
@@ -3,6 +3,13 @@ import { OpraHttpClient } from '@opra/client';
3
3
  import { BUILTIN, HttpApi } from '@opra/common';
4
4
  import colors from 'ansi-colors';
5
5
  import { pascalCase } from 'putil-varhelpers';
6
+ /**
7
+ * Generates the document and its references.
8
+ *
9
+ * @param document - The document to generate.
10
+ * @param options - Generation options.
11
+ * @returns An object containing the generated document and its generator.
12
+ */
6
13
  export async function generateDocument(document, options) {
7
14
  if (!document || typeof document === 'string') {
8
15
  if (document) {
@@ -1,3 +1,9 @@
1
1
  import { HttpApi } from '@opra/common';
2
2
  import type { TsGenerator } from '../ts-generator.js';
3
+ /**
4
+ * Generates TypeScript code for an HTTP API.
5
+ *
6
+ * @param api - The HTTP API to generate code for.
7
+ * @returns A promise that resolves to the generated TsFile.
8
+ */
3
9
  export declare function generateHttpApi(this: TsGenerator, api: HttpApi): Promise<import("../ts-file.js").TsFile>;
@@ -3,6 +3,12 @@ import { camelCase, pascalCase } from 'putil-varhelpers';
3
3
  import { CodeBlock } from '../../code-block.js';
4
4
  import { httpControllerNodeScript } from '../http-controller-node.js';
5
5
  import { wrapJSDocString } from '../utils/string-utils.js';
6
+ /**
7
+ * Generates TypeScript code for an HTTP API.
8
+ *
9
+ * @param api - The HTTP API to generate code for.
10
+ * @returns A promise that resolves to the generated TsFile.
11
+ */
6
12
  export async function generateHttpApi(api) {
7
13
  let file = this._filesMap.get(api);
8
14
  if (file)
@@ -1,3 +1,9 @@
1
1
  import { HttpController } from '@opra/common';
2
2
  import type { TsGenerator } from '../ts-generator.js';
3
+ /**
4
+ * Generates TypeScript code for an HTTP controller.
5
+ *
6
+ * @param controller - The HTTP controller to generate code for.
7
+ * @returns A promise that resolves to the generated TsFile.
8
+ */
3
9
  export declare function generateHttpController(this: TsGenerator, controller: HttpController): Promise<import("../ts-file.js").TsFile>;
@@ -5,21 +5,26 @@ import { camelCase, pascalCase } from 'putil-varhelpers';
5
5
  import { CodeBlock } from '../../code-block.js';
6
6
  import { locateNamedType } from '../utils/locate-named-type.js';
7
7
  import { wrapJSDocString } from '../utils/string-utils.js';
8
+ /**
9
+ * Generates TypeScript code for an HTTP controller.
10
+ *
11
+ * @param controller - The HTTP controller to generate code for.
12
+ * @returns A promise that resolves to the generated TsFile.
13
+ */
8
14
  export async function generateHttpController(controller) {
9
15
  let file = this._filesMap.get(controller);
10
16
  if (file)
11
17
  return file;
18
+ /**
19
+ * Generates parameter documentation for JSDoc.
20
+ *
21
+ * @param name - The name of the parameter.
22
+ * @param type - The data type of the parameter.
23
+ * @param options - Additional options for the parameter.
24
+ * @returns A promise that resolves to the documentation string.
25
+ */
12
26
  const generateParamDoc = async (name, type, options) => {
13
- let typeDef;
14
- if (type) {
15
- const xt = await this.generateDataType(type, 'typeDef', file);
16
- typeDef = xt.kind === 'embedded' ? 'object' : xt.typeName;
17
- }
18
- else
19
- typeDef = 'any';
20
- if (options?.isArray)
21
- typeDef += '[]';
22
- let out = `\n * @param {${typeDef}} ` + (options?.required ? name : `[${name}]`);
27
+ let out = `\n * @param - ` + (options?.required ? name : `[${name}]`);
23
28
  if (options?.description)
24
29
  out += ` - ${wrapJSDocString(options?.description)}`;
25
30
  if (type instanceof ComplexType && type.embedded) {
@@ -49,8 +54,7 @@ export async function generateHttpController(controller) {
49
54
  classBlock.properties = '';
50
55
  const classConstBlock = (classBlock.classConstBlock = new CodeBlock());
51
56
  classConstBlock.head = `\n/**
52
- * @param {OpraHttpClient} client - OpraHttpClient instance to operate
53
- * @constructor
57
+ * @param client - OpraHttpClient instance to operate
54
58
  */
55
59
  constructor(client: OpraHttpClient) {`;
56
60
  classConstBlock.body = `\n\tsuper(client);`;
@@ -69,7 +73,7 @@ constructor(client: OpraHttpClient) {`;
69
73
  classConstBlock.body += `\nthis.${property} = new ${childClassName}(client);`;
70
74
  }
71
75
  }
72
- /** Process operations */
76
+ /* Process operations */
73
77
  const mergedControllerParams = [...controller.parameters];
74
78
  let _base = controller;
75
79
  while (_base.owner instanceof HttpController) {
@@ -107,7 +111,7 @@ constructor(client: OpraHttpClient) {`;
107
111
  * @apiUrl ${path.posix.join(this.serviceUrl, operation.getFullUrl())}
108
112
  */\n`;
109
113
  operationBlock.head = `${operation.name}(`;
110
- /** Process operation parameters */
114
+ /* Process operation parameters */
111
115
  const pathParams = [];
112
116
  const queryParams = [];
113
117
  const headerParams = [];
@@ -129,7 +133,7 @@ constructor(client: OpraHttpClient) {`;
129
133
  queryParams.push(...Object.values(queryParamsMap));
130
134
  headerParams.push(...Object.values(headerParamsMap));
131
135
  }
132
- /** Process path parameters and add as function arguments */
136
+ /* Process path parameters and add as function arguments */
133
137
  let argIndex = 0;
134
138
  for (const prm of pathParams) {
135
139
  let typeDef;
@@ -145,11 +149,11 @@ constructor(client: OpraHttpClient) {`;
145
149
  operationBlock.head += ', ';
146
150
  operationBlock.head += `${prm.name}: ${typeDef}`;
147
151
  operationBlock.doc.parameters +=
148
- `\n * @param {${typeDef}} ` +
152
+ `\n * @param ` +
149
153
  (prm.required ? prm.name : `[${prm.name}]`) +
150
154
  (prm.description ? ' - ' + wrapJSDocString(prm.description || '') : '');
151
155
  }
152
- /** Process requestBody and add as function argument ($body) */
156
+ /* Process requestBody and add as function argument ($body) */
153
157
  let hasBody = false;
154
158
  if (operation.requestBody?.content.length) {
155
159
  if (argIndex++ > 0)
@@ -157,12 +161,11 @@ constructor(client: OpraHttpClient) {`;
157
161
  let typeArr = [];
158
162
  for (const content of operation.requestBody.content) {
159
163
  if (content.type) {
160
- /** Generate JSDoc for parameter */
164
+ /* Generate JSDoc for parameter */
161
165
  operationBlock.doc.parameters += await generateParamDoc('$body', content.type, {
162
166
  required: operation.requestBody.required,
163
167
  description: content.description || content.type.description,
164
168
  });
165
- /** */
166
169
  const xt = await this.generateDataType(content.type, 'typeDef', file);
167
170
  let typeDef = xt.kind === 'embedded' ? xt.code : xt.typeName;
168
171
  if (typeDef === 'any') {
@@ -171,11 +174,11 @@ constructor(client: OpraHttpClient) {`;
171
174
  }
172
175
  if (xt.kind === 'named') {
173
176
  if (operation.requestBody.partial) {
174
- file.addImport('ts-gems', ['PartialDTO']);
177
+ file.addImport('ts-gems', ['PartialDTO'], true);
175
178
  typeDef = `PartialDTO<${typeDef}>`;
176
179
  }
177
180
  else {
178
- file.addImport('ts-gems', ['DTO']);
181
+ file.addImport('ts-gems', ['DTO'], true);
179
182
  typeDef = `DTO<${typeDef}>`;
180
183
  }
181
184
  }
@@ -201,7 +204,7 @@ constructor(client: OpraHttpClient) {`;
201
204
  // operationBlock.doc.parameters += `\n * @param {${typeDef}} $body - Http body` + bodyFields;
202
205
  hasBody = true;
203
206
  }
204
- /** process query params */
207
+ /* process query params */
205
208
  const isQueryRequired = queryParams.find(p => p.required);
206
209
  const isHeadersRequired = queryParams.find(p => p.required);
207
210
  if (queryParams.length) {
@@ -212,7 +215,7 @@ constructor(client: OpraHttpClient) {`;
212
215
  (isHeadersRequired || isQueryRequired ? '' : '?') +
213
216
  ': {\n\t';
214
217
  operationBlock.doc.parameters +=
215
- '\n * @param {object} $params - Available parameters for the operation';
218
+ '\n * @param $params - Available parameters for the operation';
216
219
  let hasAdditionalFields = false;
217
220
  for (const prm of queryParams) {
218
221
  if (typeof prm.name !== 'string') {
@@ -236,7 +239,7 @@ constructor(client: OpraHttpClient) {`;
236
239
  }
237
240
  operationBlock.head += '\b}\b';
238
241
  }
239
- /** process header params */
242
+ /* process header params */
240
243
  if (headerParams.length) {
241
244
  // eslint-disable-next-line no-useless-assignment
242
245
  if (argIndex++ > 0)
@@ -275,11 +278,11 @@ constructor(client: OpraHttpClient) {`;
275
278
  if (isArray)
276
279
  typeDef = typeDef.substring(0, typeDef.length - 2);
277
280
  if (resp.partial) {
278
- file.addImport('ts-gems', ['PartialDTO']);
281
+ file.addImport('ts-gems', ['PartialDTO'], true);
279
282
  typeDef = `PartialDTO<${typeDef}>`;
280
283
  }
281
284
  else {
282
- file.addImport('ts-gems', ['DTO']);
285
+ file.addImport('ts-gems', ['DTO'], true);
283
286
  typeDef = `DTO<${typeDef}>`;
284
287
  }
285
288
  if (isArray)
@@ -292,7 +295,7 @@ constructor(client: OpraHttpClient) {`;
292
295
  typeIs.is(String(resp.contentType), [MimeTypes.opra_response_json]) &&
293
296
  !(resp.type instanceof ComplexType &&
294
297
  resp.type.base?.ctor === OperationResult)) {
295
- file.addImport('@opra/common', ['OperationResult']);
298
+ file.addImport('@opra/common', ['OperationResult'], true);
296
299
  typeDef = typeDef ? `OperationResult<${typeDef}>` : 'OperationResult';
297
300
  }
298
301
  typeDef = typeDef || 'undefined';
@@ -1,21 +1,56 @@
1
1
  import { CodeBlock } from '../code-block.js';
2
+ /**
3
+ * TsFile
4
+ *
5
+ * A class representing a TypeScript source file, managing its imports, exports, and content.
6
+ */
2
7
  export declare class TsFile {
3
8
  readonly filename: string;
9
+ /** The directory name of the file. */
4
10
  readonly dirname: string;
11
+ /** Map of imports where key is the module path. */
5
12
  imports: Record<string, {
6
13
  items: string[];
7
14
  typeImport?: boolean;
8
15
  }>;
16
+ /** Map of exports where key is the module path. */
9
17
  exportFiles: Record<string, {
10
18
  filename: string;
11
19
  items: string[];
12
20
  namespace?: string;
13
21
  }>;
22
+ /** List of types to be exported. */
14
23
  exportTypes: string[];
24
+ /** The code block representing the file's content. */
15
25
  code: CodeBlock;
26
+ /**
27
+ * Initializes a new TsFile instance.
28
+ *
29
+ * @param filename - The name of the file.
30
+ */
16
31
  constructor(filename: string);
32
+ /**
33
+ * Adds an import statement to the file.
34
+ *
35
+ * @param filename - The module to import from.
36
+ * @param items - The specific items to import.
37
+ * @param typeImport - Whether it is a type-only import.
38
+ */
17
39
  addImport(filename: string, items?: string[], typeImport?: boolean): void;
40
+ /**
41
+ * Adds an export statement to the file.
42
+ *
43
+ * @param filename - The module to export from.
44
+ * @param types - The specific items to export.
45
+ * @param namespace - The namespace to export as.
46
+ */
18
47
  addExport(filename: string, types?: string[], namespace?: string): void;
48
+ /**
49
+ * Generates the file content as a string.
50
+ *
51
+ * @param options - Generation options.
52
+ * @returns The generated TypeScript code.
53
+ */
19
54
  generate(options?: {
20
55
  importExt?: boolean;
21
56
  }): string;
@@ -1,13 +1,28 @@
1
1
  import path from 'node:path';
2
2
  import flattenText from 'putil-flattentext';
3
3
  import { CodeBlock } from '../code-block.js';
4
+ /**
5
+ * TsFile
6
+ *
7
+ * A class representing a TypeScript source file, managing its imports, exports, and content.
8
+ */
4
9
  export class TsFile {
5
10
  filename;
11
+ /** The directory name of the file. */
6
12
  dirname;
13
+ /** Map of imports where key is the module path. */
7
14
  imports = {};
15
+ /** Map of exports where key is the module path. */
8
16
  exportFiles = {};
17
+ /** List of types to be exported. */
9
18
  exportTypes = [];
19
+ /** The code block representing the file's content. */
10
20
  code = new CodeBlock();
21
+ /**
22
+ * Initializes a new TsFile instance.
23
+ *
24
+ * @param filename - The name of the file.
25
+ */
11
26
  constructor(filename) {
12
27
  this.filename = filename;
13
28
  this.dirname = path.dirname(filename);
@@ -15,6 +30,13 @@ export class TsFile {
15
30
  this.code.imports = '';
16
31
  this.code.exports = '';
17
32
  }
33
+ /**
34
+ * Adds an import statement to the file.
35
+ *
36
+ * @param filename - The module to import from.
37
+ * @param items - The specific items to import.
38
+ * @param typeImport - Whether it is a type-only import.
39
+ */
18
40
  addImport(filename, items, typeImport) {
19
41
  if (isLocalFile(filename)) {
20
42
  filename = path.relative(this.dirname, path.resolve(this.dirname, filename));
@@ -36,6 +58,13 @@ export class TsFile {
36
58
  imp.items.push(x);
37
59
  });
38
60
  }
61
+ /**
62
+ * Adds an export statement to the file.
63
+ *
64
+ * @param filename - The module to export from.
65
+ * @param types - The specific items to export.
66
+ * @param namespace - The namespace to export as.
67
+ */
39
68
  addExport(filename, types, namespace) {
40
69
  if (isLocalFile(filename)) {
41
70
  filename = path.relative(this.dirname, path.resolve(this.dirname, filename));
@@ -57,6 +86,12 @@ export class TsFile {
57
86
  this.exportFiles[filename].items.push(x);
58
87
  });
59
88
  }
89
+ /**
90
+ * Generates the file content as a string.
91
+ *
92
+ * @param options - Generation options.
93
+ * @returns The generated TypeScript code.
94
+ */
60
95
  generate(options) {
61
96
  this.code.imports = Object.keys(this.imports)
62
97
  .sort((a, b) => {
@@ -9,22 +9,37 @@ import { generateHttpApi } from './generators/generate-http-api.js';
9
9
  import { generateHttpController } from './generators/generate-http-controller.js';
10
10
  import { TsFile } from './ts-file.js';
11
11
  /**
12
- * @namespace TsGenerator
12
+ * TsGenerator
13
+ *
14
+ * A class responsible for generating TypeScript code from an OPRA API document.
13
15
  */
14
16
  export declare namespace TsGenerator {
17
+ /**
18
+ * Configuration options for TsGenerator.
19
+ */
15
20
  interface Options {
21
+ /** The URL of the OPRA service. */
16
22
  serviceUrl: string;
23
+ /** The output directory for the generated files. */
17
24
  outDir: string;
25
+ /** The current working directory. Defaults to process.cwd(). */
18
26
  cwd?: string;
27
+ /** Logger instance for outputting information. */
19
28
  logger?: ILogger;
29
+ /** File writer instance. Defaults to FileWriter. */
20
30
  writer?: IFileWriter;
31
+ /** Optional header to add to each generated file. */
21
32
  fileHeader?: string;
33
+ /** Whether to add .js extension to imports. */
22
34
  importExt?: boolean;
35
+ /** Whether to export references with namespaces. */
23
36
  referenceNamespaces?: boolean;
24
37
  }
25
38
  }
26
39
  /**
27
- * @class TsGenerator
40
+ * TsGenerator
41
+ *
42
+ * Main class for managing the TypeScript code generation process.
28
43
  */
29
44
  export declare class TsGenerator extends EventEmitter {
30
45
  protected cleanDirectory: typeof cleanDirectory;
@@ -63,12 +78,32 @@ export declare class TsGenerator extends EventEmitter {
63
78
  };
64
79
  fileHeader: string;
65
80
  /**
81
+ * Initializes a new TsGenerator instance.
66
82
  *
67
- * @constructor
83
+ * @param init - Configuration options.
68
84
  */
69
85
  constructor(init: TsGenerator.Options);
86
+ /**
87
+ * Starts the code generation process.
88
+ *
89
+ * @throws {@link Error} If generation fails.
90
+ */
70
91
  generate(): Promise<void>;
92
+ /**
93
+ * Retrieves a file from the internal cache by its path.
94
+ *
95
+ * @param filePath - The path of the file to retrieve.
96
+ * @returns The TsFile instance or undefined if not found.
97
+ */
71
98
  protected getFile(filePath: string): TsFile;
99
+ /**
100
+ * Adds a new file to the generator or returns an existing one.
101
+ *
102
+ * @param filePath - The path of the file to add.
103
+ * @param returnExists - Whether to return the file if it already exists instead of throwing.
104
+ * @returns The newly created or existing TsFile instance.
105
+ * @throws {@link Error} If the file already exists and returnExists is false.
106
+ */
72
107
  protected addFile(filePath: string, returnExists?: boolean): TsFile;
73
108
  protected extend(): TsGenerator;
74
109
  }
@@ -11,7 +11,9 @@ import { generateHttpApi } from './generators/generate-http-api.js';
11
11
  import { generateHttpController } from './generators/generate-http-controller.js';
12
12
  import { TsFile } from './ts-file.js';
13
13
  /**
14
- * @class TsGenerator
14
+ * TsGenerator
15
+ *
16
+ * Main class for managing the TypeScript code generation process.
15
17
  */
16
18
  export class TsGenerator extends EventEmitter {
17
19
  _files = {};
@@ -26,8 +28,9 @@ export class TsGenerator extends EventEmitter {
26
28
  options;
27
29
  fileHeader;
28
30
  /**
31
+ * Initializes a new TsGenerator instance.
29
32
  *
30
- * @constructor
33
+ * @param init - Configuration options.
31
34
  */
32
35
  constructor(init) {
33
36
  super();
@@ -49,6 +52,11 @@ export class TsGenerator extends EventEmitter {
49
52
  this.on('warn', (message, ...args) => init.logger?.warn?.(message, ...args));
50
53
  this.on('verbose', (message, ...args) => init.logger?.verbose?.(message, ...args));
51
54
  }
55
+ /**
56
+ * Starts the code generation process.
57
+ *
58
+ * @throws {@link Error} If generation fails.
59
+ */
52
60
  async generate() {
53
61
  if (this._started)
54
62
  return;
@@ -77,9 +85,23 @@ export class TsGenerator extends EventEmitter {
77
85
  this.emit('finish');
78
86
  }
79
87
  }
88
+ /**
89
+ * Retrieves a file from the internal cache by its path.
90
+ *
91
+ * @param filePath - The path of the file to retrieve.
92
+ * @returns The TsFile instance or undefined if not found.
93
+ */
80
94
  getFile(filePath) {
81
95
  return this._files[filePath];
82
96
  }
97
+ /**
98
+ * Adds a new file to the generator or returns an existing one.
99
+ *
100
+ * @param filePath - The path of the file to add.
101
+ * @param returnExists - Whether to return the file if it already exists instead of throwing.
102
+ * @returns The newly created or existing TsFile instance.
103
+ * @throws {@link Error} If the file already exists and returnExists is false.
104
+ */
83
105
  addFile(filePath, returnExists) {
84
106
  if (!(filePath.startsWith('.') || filePath.startsWith('/')))
85
107
  filePath = './' + filePath;
@@ -1,2 +1,8 @@
1
1
  import { DataType } from '@opra/common';
2
+ /**
3
+ * Locates the first named type in the inheritance chain of a data type.
4
+ *
5
+ * @param type - The data type to start from.
6
+ * @returns The found named DataType instance, or undefined if not found.
7
+ */
2
8
  export declare function locateNamedType(type?: DataType): DataType | undefined;
@@ -1,4 +1,10 @@
1
1
  import { ComplexType, EnumType, MappedType, SimpleType, } from '@opra/common';
2
+ /**
3
+ * Locates the first named type in the inheritance chain of a data type.
4
+ *
5
+ * @param type - The data type to start from.
6
+ * @returns The found named DataType instance, or undefined if not found.
7
+ */
2
8
  export function locateNamedType(type) {
3
9
  if (!type)
4
10
  return;
@@ -1,4 +1,36 @@
1
+ /**
2
+ * Wraps a string for use in JSDoc comments.
3
+ *
4
+ * @param s - The string to wrap.
5
+ * @param indent - The number of spaces for indentation.
6
+ * @param currentColumn - The current column position.
7
+ * @returns The wrapped JSDoc string.
8
+ */
1
9
  export declare function wrapJSDocString(s: string, indent?: number, currentColumn?: number): string;
10
+ /**
11
+ * Wraps a string as a quoted string, potentially splitting it across multiple lines.
12
+ *
13
+ * @param s - The string to wrap.
14
+ * @param indent - The number of spaces for indentation.
15
+ * @param currentColumn - The current column position.
16
+ * @returns The wrapped quoted string.
17
+ */
2
18
  export declare function wrapQuotedString(s: string, indent?: number, currentColumn?: number): string;
19
+ /**
20
+ * Wraps an array of strings as a TypeScript array representation.
21
+ *
22
+ * @param arr - The array of strings.
23
+ * @param indent - The number of spaces for indentation.
24
+ * @param currentColumn - The current column position.
25
+ * @returns The string representation of the array.
26
+ */
3
27
  export declare function wrapStringArray(arr: string[], indent?: number, currentColumn?: number): string;
28
+ /**
29
+ * Wraps an array of types as a TypeScript union type representation.
30
+ *
31
+ * @param arr - The array of type names.
32
+ * @param indent - The number of spaces for indentation.
33
+ * @param currentColumn - The current column position.
34
+ * @returns The string representation of the union type.
35
+ */
4
36
  export declare function wrapTypeArray(arr: string[], indent?: number, currentColumn?: number): string;
@@ -1,4 +1,12 @@
1
1
  import jsStringEscape from 'js-string-escape';
2
+ /**
3
+ * Wraps a string for use in JSDoc comments.
4
+ *
5
+ * @param s - The string to wrap.
6
+ * @param indent - The number of spaces for indentation.
7
+ * @param currentColumn - The current column position.
8
+ * @returns The wrapped JSDoc string.
9
+ */
2
10
  export function wrapJSDocString(s, indent, currentColumn) {
3
11
  const arr = (s || '')
4
12
  .split(/[ \n\r]/)
@@ -10,6 +18,14 @@ export function wrapJSDocString(s, indent, currentColumn) {
10
18
  lineEnd: '',
11
19
  });
12
20
  }
21
+ /**
22
+ * Wraps a string as a quoted string, potentially splitting it across multiple lines.
23
+ *
24
+ * @param s - The string to wrap.
25
+ * @param indent - The number of spaces for indentation.
26
+ * @param currentColumn - The current column position.
27
+ * @returns The wrapped quoted string.
28
+ */
13
29
  export function wrapQuotedString(s, indent, currentColumn) {
14
30
  const arr = jsStringEscape(s || '')
15
31
  .split(' ')
@@ -23,10 +39,26 @@ export function wrapQuotedString(s, indent, currentColumn) {
23
39
  }) +
24
40
  "'");
25
41
  }
42
+ /**
43
+ * Wraps an array of strings as a TypeScript array representation.
44
+ *
45
+ * @param arr - The array of strings.
46
+ * @param indent - The number of spaces for indentation.
47
+ * @param currentColumn - The current column position.
48
+ * @returns The string representation of the array.
49
+ */
26
50
  export function wrapStringArray(arr, indent, currentColumn) {
27
51
  const ar1 = arr.map((x, i, a) => "'" + x + "'" + (i < a.length - 1 ? ', ' : ''));
28
52
  return '[' + _printLines(ar1, { indent, currentColumn }) + ']';
29
53
  }
54
+ /**
55
+ * Wraps an array of types as a TypeScript union type representation.
56
+ *
57
+ * @param arr - The array of type names.
58
+ * @param indent - The number of spaces for indentation.
59
+ * @param currentColumn - The current column position.
60
+ * @returns The string representation of the union type.
61
+ */
30
62
  export function wrapTypeArray(arr, indent, currentColumn) {
31
63
  const ar1 = arr.map((x, i, a) => x + (i < a.length - 1 ? ' | ' : ''));
32
64
  return _printLines(ar1, { indent, currentColumn });