@map-colonies/openapi-helpers 3.1.0 → 4.0.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/README.md +118 -8
- package/dist/cli/entrypoint.d.mts +2 -0
- package/dist/cli/entrypoint.mjs +83 -0
- package/dist/cli/entrypoint.mjs.map +1 -0
- package/dist/common/constants.d.ts +2 -0
- package/dist/common/constants.js +8 -0
- package/dist/common/constants.js.map +1 -0
- package/dist/generator/generateErrors.d.mts +5 -1
- package/dist/generator/generateErrors.mjs +74 -72
- package/dist/generator/generateErrors.mjs.map +1 -1
- package/dist/generator/generateTypes.d.mts +8 -1
- package/dist/generator/generateTypes.mjs +14 -26
- package/dist/generator/generateTypes.mjs.map +1 -1
- package/dist/generator/index.d.mts +2 -0
- package/dist/generator/index.mjs +3 -0
- package/dist/generator/index.mjs.map +1 -0
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -9,22 +9,132 @@ Run the following commands:
|
|
|
9
9
|
npm install --save-dev @map-colonies/openapi-helpers supertest prettier openapi-typescript @types/express
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
## types-generator
|
|
13
|
-
The package contains a script that wraps the `openapi-typescript` package and generates types for the OpenAPI schema. The script also formats the generated types using `prettier`.
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
## CLI Usage
|
|
14
|
+
|
|
15
|
+
The package provides a unified CLI for generating TypeScript types and error classes from OpenAPI specifications. All code generation is now performed using the `generate` command, which supports subcommands for types and errors.
|
|
16
|
+
|
|
17
|
+
#### CLI Arguments Reference
|
|
18
|
+
|
|
19
|
+
**Positional Arguments:**
|
|
20
|
+
For both `generate types` and `generate errors` commands, the positional arguments are:
|
|
21
|
+
|
|
22
|
+
- `<openapi-file>`: Path to the OpenAPI YAML or JSON file to use as the source schema.
|
|
23
|
+
- `<output-file>`: Path to the file where the generated code will be written.
|
|
24
|
+
|
|
25
|
+
These arguments are required and must be provided in the order shown.
|
|
26
|
+
|
|
27
|
+
**Optional Arguments:**
|
|
28
|
+
|
|
29
|
+
For `generate types`:
|
|
30
|
+
- `-f, --format`: Format the generated types using Prettier
|
|
31
|
+
- `-t, --add-typed-request-handler`: Add the TypedRequestHandler type to the generated types
|
|
32
|
+
|
|
33
|
+
For `generate errors`:
|
|
34
|
+
- `-f, --format`: Format the generated code using Prettier
|
|
35
|
+
- `-e, --errors-output <all|map|classes>`: Specify what to generate (default: all)
|
|
36
|
+
- `all`: generate both error classes and error code mapping
|
|
37
|
+
- `map`: generate only the error code mapping
|
|
38
|
+
- `classes`: generate only the error classes
|
|
39
|
+
|
|
40
|
+
### Generate Types
|
|
41
|
+
|
|
42
|
+
Generate TypeScript types from an OpenAPI schema:
|
|
16
43
|
```bash
|
|
17
|
-
npx @map-colonies/openapi-helpers <
|
|
44
|
+
npx @map-colonies/openapi-helpers generate types <openapi-file> <output-file> [options]
|
|
18
45
|
```
|
|
19
46
|
|
|
20
47
|
For example:
|
|
21
48
|
```bash
|
|
22
|
-
npx @map-colonies/openapi-helpers ./openapi3.yaml ./src/openapi.d.ts --format --add-typed-request-handler
|
|
49
|
+
npx @map-colonies/openapi-helpers generate types ./openapi3.yaml ./src/openapi.d.ts --format --add-typed-request-handler
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Options:
|
|
53
|
+
- `-f, --format` - Format the generated types using `prettier`.
|
|
54
|
+
- `-t, --add-typed-request-handler` - Add the `TypedRequestHandler` type to the generated types.
|
|
55
|
+
|
|
56
|
+
### Generate Errors
|
|
57
|
+
|
|
58
|
+
Generate error classes and error code mappings from an OpenAPI schema:
|
|
59
|
+
```bash
|
|
60
|
+
npx @map-colonies/openapi-helpers generate errors <openapi-file> <output-file> [options]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
For example:
|
|
64
|
+
```bash
|
|
65
|
+
npx @map-colonies/openapi-helpers generate errors ./openapi3.yaml ./src/errors.ts --format
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
Options:
|
|
70
|
+
- `-f, --format` - Format the generated code using `prettier`.
|
|
71
|
+
- `-e, --errors-output <all|map|classes>` - Specify what to generate:
|
|
72
|
+
- `all` (default): generate both error classes and error code mapping
|
|
73
|
+
- `map`: generate only the error code mapping
|
|
74
|
+
- `classes`: generate only the error classes
|
|
75
|
+
|
|
76
|
+
### Help and Examples
|
|
77
|
+
|
|
78
|
+
To see all available commands and options:
|
|
79
|
+
```bash
|
|
80
|
+
npx @map-colonies/openapi-helpers --help
|
|
81
|
+
npx @map-colonies/openapi-helpers generate --help
|
|
82
|
+
npx @map-colonies/openapi-helpers generate types --help
|
|
83
|
+
npx @map-colonies/openapi-helpers generate errors --help
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
#### Example: Run all generations
|
|
88
|
+
|
|
89
|
+
You can run both types and errors generation in sequence:
|
|
90
|
+
```bash
|
|
91
|
+
npx @map-colonies/openapi-helpers generate types ./openapi3.yaml ./src/openapi.d.ts --format --add-typed-request-handler
|
|
92
|
+
npx @map-colonies/openapi-helpers generate errors ./openapi3.yaml ./src/errors.ts --format --errors-output all
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
## Programmatic Support
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
> [!NOTE]
|
|
101
|
+
> **Programmatic usage of the CLI (importing and using the generators directly) is only supported in ECMAScript modules (ESM).** CommonJS is not supported for direct imports.
|
|
102
|
+
|
|
103
|
+
The code generators (`generateTypes.mts` and `generateErrors.mts`) now support functional programming patterns. You can inject custom transformation logic or AST manipulation by providing functional arguments, making the generators more flexible and composable for advanced use cases.
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
### API Usage
|
|
107
|
+
|
|
108
|
+
You can import and use the generators directly in your own scripts for full functional programming flexibility:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { generateTypes, generateErrors } from '@map-colonies/openapi-helpers/generators';
|
|
112
|
+
|
|
113
|
+
// Generate types
|
|
114
|
+
await generateTypes(
|
|
115
|
+
'openapi3.yaml',
|
|
116
|
+
'src/openapi.d.ts',
|
|
117
|
+
{
|
|
118
|
+
shouldFormat: true,
|
|
119
|
+
addTypedRequestHandler: true,
|
|
120
|
+
// inject?: string,
|
|
121
|
+
// transform?: (schemaObject, metadata) => ...
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// Generate errors
|
|
126
|
+
await generateErrors(
|
|
127
|
+
'openapi3.yaml',
|
|
128
|
+
'src/errors.ts',
|
|
129
|
+
{
|
|
130
|
+
shouldFormat: true,
|
|
131
|
+
includeMapping: true,
|
|
132
|
+
includeErrorClasses: true
|
|
133
|
+
}
|
|
134
|
+
);
|
|
23
135
|
```
|
|
24
136
|
|
|
25
|
-
|
|
26
|
-
- `--format` - format the generated types using `prettier`.
|
|
27
|
-
- `--add-typed-request-handler` - add the `TypedRequestHandler` type to the generated types.
|
|
137
|
+
You can pass custom `inject` or `transform` functions to `generateTypes` for advanced AST/code manipulation, enabling highly composable and functional workflows.
|
|
28
138
|
|
|
29
139
|
## TypedRequestHandler
|
|
30
140
|
The package contains a wrapper for the `express` types package that provides autocomplete for all the request handlers to the API based on the OpenAPI schema. The TypedRequestHandler is initialized with the types generated by `openapi-typescript`, and is configured based on operation name or method and path.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
3
|
+
import { program } from '@commander-js/extra-typings';
|
|
4
|
+
import { generateTypes } from '../generator/generateTypes.mjs';
|
|
5
|
+
import { generateErrors } from '../generator/generateErrors.mjs';
|
|
6
|
+
import ora from 'ora';
|
|
7
|
+
import { PACKAGE_VERSION } from '../common/constants.js';
|
|
8
|
+
const errorOutput = ['all', 'map', 'classes'];
|
|
9
|
+
function isErrorsOutput(value) {
|
|
10
|
+
return errorOutput.includes(value);
|
|
11
|
+
}
|
|
12
|
+
const SECOND = 1000;
|
|
13
|
+
program.name('openapi-helpers').description('Generate TypeScript types and error classes from OpenAPI specifications').version(PACKAGE_VERSION);
|
|
14
|
+
const command = program.command('generate').description('Generate code artifacts (types, error classes) from OpenAPI specifications');
|
|
15
|
+
command
|
|
16
|
+
.command('types')
|
|
17
|
+
.description('Generate TypeScript types from OpenAPI spec')
|
|
18
|
+
.argument('<openapiPath>', 'Path to the OpenAPI specification file')
|
|
19
|
+
.argument('<destinationPath>', 'Path where the generated types will be saved')
|
|
20
|
+
.option('-f, --format', 'Format the generated code using Prettier')
|
|
21
|
+
.option('-t, --add-typed-request-handler', 'Add typed request handler types to the generated output')
|
|
22
|
+
.action(async (openapiPath, destinationPath, options) => {
|
|
23
|
+
try {
|
|
24
|
+
const spinner = ora('Generating types').start();
|
|
25
|
+
await generateTypes(openapiPath, destinationPath, { shouldFormat: options.format, addTypedRequestHandler: options.addTypedRequestHandler });
|
|
26
|
+
await sleep(SECOND);
|
|
27
|
+
spinner.stop();
|
|
28
|
+
console.log('Types generated successfully');
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error('Error generating types:', error);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
command
|
|
36
|
+
.command('errors')
|
|
37
|
+
.description('Generate error classes from OpenAPI spec')
|
|
38
|
+
.argument('<openapiPath>', 'Path to the OpenAPI specification file')
|
|
39
|
+
.argument('<destinationPath>', 'Path where the generated error classes will be saved')
|
|
40
|
+
.option('-f, --format', 'Format the generated code using Prettier')
|
|
41
|
+
.option('-e, --errors-output <all|map|classes>', 'Specify the errors output type', 'all')
|
|
42
|
+
.action(async (openapiPath, destinationPath, options) => {
|
|
43
|
+
try {
|
|
44
|
+
if (!isErrorsOutput(options.errorsOutput)) {
|
|
45
|
+
console.error(`Invalid errors output type: ${options.errorsOutput}`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
const includeMapping = options.errorsOutput === 'map' || options.errorsOutput === 'all';
|
|
49
|
+
const includeErrorClasses = options.errorsOutput === 'classes' || options.errorsOutput === 'all';
|
|
50
|
+
const spinner = ora('Generating errors').start();
|
|
51
|
+
await generateErrors(openapiPath, destinationPath, {
|
|
52
|
+
shouldFormat: options.format,
|
|
53
|
+
includeMapping,
|
|
54
|
+
includeErrorClasses,
|
|
55
|
+
});
|
|
56
|
+
await sleep(SECOND);
|
|
57
|
+
spinner.stop();
|
|
58
|
+
console.log('Errors generated successfully');
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
console.error('Error generating errors:', error);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
// Add examples to the help
|
|
66
|
+
program.addHelpText('after', `
|
|
67
|
+
Examples:
|
|
68
|
+
$ openapi-helpers generate types api.yaml types.ts
|
|
69
|
+
$ openapi-helpers generate types api.yaml types.ts --format
|
|
70
|
+
$ openapi-helpers generate types api.yaml types.ts --add-typed-request-handler
|
|
71
|
+
$ openapi-helpers generate types api.yaml types.ts --add-typed-request-handler --format
|
|
72
|
+
$ openapi-helpers generate errors api.yaml errors.ts
|
|
73
|
+
$ openapi-helpers generate errors api.yaml errors.ts --format
|
|
74
|
+
$ openapi-helpers generate errors api.yaml errors.ts --no-mapping
|
|
75
|
+
$ openapi-helpers generate errors api.yaml errors.ts --no-error-classes
|
|
76
|
+
$ openapi-helpers generate errors api.yaml errors.ts --no-mapping --no-error-classes
|
|
77
|
+
$ openapi-helpers --help
|
|
78
|
+
$ openapi-helpers generate --help
|
|
79
|
+
$ openapi-helpers generate types --help
|
|
80
|
+
$ openapi-helpers generate errors --help
|
|
81
|
+
`);
|
|
82
|
+
program.parse();
|
|
83
|
+
//# sourceMappingURL=entrypoint.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entrypoint.mjs","sourceRoot":"","sources":["../../src/cli/entrypoint.mts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PACKAGE_VERSION = void 0;
|
|
4
|
+
const read_pkg_1 = require("@map-colonies/read-pkg");
|
|
5
|
+
const packageJson = (0, read_pkg_1.readPackageJsonSync)();
|
|
6
|
+
const PACKAGE_VERSION = packageJson.version ?? 'unknown';
|
|
7
|
+
exports.PACKAGE_VERSION = PACKAGE_VERSION;
|
|
8
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/common/constants.ts"],"names":[],"mappings":";;;AAAA,qDAA6D;AAE7D,MAAM,WAAW,GAAG,IAAA,8BAAmB,GAAE,CAAC;AAC1C,MAAM,eAAe,GAAG,WAAW,CAAC,OAAO,IAAI,SAAS,CAAC;AAChD,0CAAe"}
|
|
@@ -1,58 +1,11 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
|
-
import { parseArgs } from 'node:util';
|
|
3
2
|
import path from 'node:path';
|
|
4
3
|
import { dereference } from '@apidevtools/json-schema-ref-parser';
|
|
5
4
|
import { format, resolveConfig } from 'prettier';
|
|
6
5
|
import * as changeCase from 'change-case';
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
options: {
|
|
11
|
-
format: { type: 'boolean', alias: 'f' },
|
|
12
|
-
},
|
|
13
|
-
allowPositionals: true,
|
|
14
|
-
});
|
|
15
|
-
const [openapiPath, destinationPath] = positionals;
|
|
16
|
-
if (openapiPath === undefined || destinationPath === undefined) {
|
|
17
|
-
console.error('Usage: generateErrors <openapiPath> <destinationPath>');
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|
|
20
|
-
const openapi = await dereference(openapiPath);
|
|
21
|
-
if (openapi.paths === undefined) {
|
|
22
|
-
console.error('No paths found in the OpenAPI document.');
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
25
|
-
const errorCodes = new Set();
|
|
26
|
-
function extractCodeFromSchema(schema) {
|
|
27
|
-
// Handle direct code property
|
|
28
|
-
if (schema.type === 'object' && schema.properties?.code) {
|
|
29
|
-
const codeProperty = schema.properties.code;
|
|
30
|
-
// Handle enum values
|
|
31
|
-
if (codeProperty.enum) {
|
|
32
|
-
codeProperty.enum.map(String).forEach((code) => {
|
|
33
|
-
errorCodes.add(code);
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
// Handle allOf combinations
|
|
38
|
-
if (schema.allOf) {
|
|
39
|
-
for (const subSchema of schema.allOf) {
|
|
40
|
-
extractCodeFromSchema(subSchema);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
// Handle oneOf combinations
|
|
44
|
-
if (schema.oneOf) {
|
|
45
|
-
for (const subSchema of schema.oneOf) {
|
|
46
|
-
extractCodeFromSchema(subSchema);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
// Handle anyOf combinations
|
|
50
|
-
if (schema.anyOf) {
|
|
51
|
-
for (const subSchema of schema.anyOf) {
|
|
52
|
-
extractCodeFromSchema(subSchema);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
6
|
+
const ESLINT_DISABLE = '/* eslint-disable */\n';
|
|
7
|
+
const FILE_HEADER = `${ESLINT_DISABLE}// This file was auto-generated. Do not edit manually.
|
|
8
|
+
// To update, run the error generation script again.\n\n`;
|
|
56
9
|
function createError(code) {
|
|
57
10
|
let className = changeCase.pascalCase(code);
|
|
58
11
|
if (!className.endsWith('Error')) {
|
|
@@ -71,32 +24,81 @@ function createError(code) {
|
|
|
71
24
|
}
|
|
72
25
|
};\n`;
|
|
73
26
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
27
|
+
function buildErrorMapping(errorCodes) {
|
|
28
|
+
return Array.from(errorCodes)
|
|
29
|
+
.map((code) => `'${code}': '${code}'`)
|
|
30
|
+
.join(', ');
|
|
31
|
+
}
|
|
32
|
+
export async function generateErrors(openapiPath, destinationPath, options) {
|
|
33
|
+
const openapi = await dereference(openapiPath);
|
|
34
|
+
if (openapi.paths === undefined) {
|
|
35
|
+
console.error('No paths found in the OpenAPI document.');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
const errorCodes = new Set();
|
|
39
|
+
function extractCodeFromSchema(schema) {
|
|
40
|
+
// Handle direct code property
|
|
41
|
+
if (schema.type === 'object' && schema.properties?.code) {
|
|
42
|
+
const codeProperty = schema.properties.code;
|
|
43
|
+
// Handle enum values
|
|
44
|
+
if (codeProperty.enum) {
|
|
45
|
+
codeProperty.enum.map(String).forEach((code) => {
|
|
46
|
+
errorCodes.add(code);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
78
49
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
50
|
+
// Handle allOf combinations
|
|
51
|
+
if (schema.allOf) {
|
|
52
|
+
for (const subSchema of schema.allOf) {
|
|
53
|
+
extractCodeFromSchema(subSchema);
|
|
82
54
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
55
|
+
}
|
|
56
|
+
// Handle oneOf combinations
|
|
57
|
+
if (schema.oneOf) {
|
|
58
|
+
for (const subSchema of schema.oneOf) {
|
|
59
|
+
extractCodeFromSchema(subSchema);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Handle anyOf combinations
|
|
63
|
+
if (schema.anyOf) {
|
|
64
|
+
for (const subSchema of schema.anyOf) {
|
|
65
|
+
extractCodeFromSchema(subSchema);
|
|
86
66
|
}
|
|
87
67
|
}
|
|
88
68
|
}
|
|
69
|
+
for (const [, methods] of Object.entries(openapi.paths)) {
|
|
70
|
+
for (const [key, operation] of Object.entries(methods)) {
|
|
71
|
+
if (['servers', 'parameters'].includes(key)) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
for (const [statusCode, response] of Object.entries(operation.responses ?? {})) {
|
|
75
|
+
if (statusCode.startsWith('2') || statusCode.startsWith('3')) {
|
|
76
|
+
continue; // Skip successful and redirection responses
|
|
77
|
+
}
|
|
78
|
+
const schema = response.content?.['application/json']?.schema;
|
|
79
|
+
if (schema) {
|
|
80
|
+
extractCodeFromSchema(schema);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (errorCodes.size === 0) {
|
|
86
|
+
console.warn('No error codes found in the OpenAPI document.');
|
|
87
|
+
process.exit(0);
|
|
88
|
+
}
|
|
89
|
+
let errorFile = FILE_HEADER;
|
|
90
|
+
if (options.includeErrorClasses === true) {
|
|
91
|
+
errorFile += errorCodes.values().map(createError).toArray().join('\n');
|
|
92
|
+
}
|
|
93
|
+
if (options.includeMapping === true) {
|
|
94
|
+
errorFile += ` export const API_ERRORS_MAP = { ${buildErrorMapping(errorCodes)} } as const;\n`;
|
|
95
|
+
}
|
|
96
|
+
if (options.shouldFormat === true) {
|
|
97
|
+
const prettierOptions = await resolveConfig('./src/index.ts');
|
|
98
|
+
errorFile = await format(errorFile, { ...prettierOptions, parser: 'typescript' });
|
|
99
|
+
}
|
|
100
|
+
const directory = path.dirname(destinationPath);
|
|
101
|
+
await fs.mkdir(directory, { recursive: true });
|
|
102
|
+
await fs.writeFile(destinationPath, errorFile);
|
|
89
103
|
}
|
|
90
|
-
if (errorCodes.size === 0) {
|
|
91
|
-
console.warn('No error codes found in the OpenAPI document.');
|
|
92
|
-
process.exit(0);
|
|
93
|
-
}
|
|
94
|
-
let errorFile = errorCodes.values().map(createError).toArray().join('\n');
|
|
95
|
-
if (shouldFormat === true) {
|
|
96
|
-
const prettierOptions = await resolveConfig('./src/index.ts');
|
|
97
|
-
errorFile = await format(errorFile, { ...prettierOptions, parser: 'typescript' });
|
|
98
|
-
}
|
|
99
|
-
const directory = path.dirname(destinationPath);
|
|
100
|
-
await fs.mkdir(directory, { recursive: true });
|
|
101
|
-
await fs.writeFile(destinationPath, errorFile);
|
|
102
104
|
//# sourceMappingURL=generateErrors.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateErrors.mjs","sourceRoot":"","sources":["../../src/generator/generateErrors.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,
|
|
1
|
+
{"version":3,"file":"generateErrors.mjs","sourceRoot":"","sources":["../../src/generator/generateErrors.mts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
import { SchemaObject, TransformNodeOptions, TransformObject } from 'openapi-typescript';
|
|
3
|
+
import { TypeNode } from 'typescript';
|
|
4
|
+
export declare function generateTypes(openapiPath: string, destinationPath: string, options: {
|
|
5
|
+
shouldFormat?: boolean;
|
|
6
|
+
addTypedRequestHandler?: boolean;
|
|
7
|
+
inject?: string;
|
|
8
|
+
transform?: (schemaObject: SchemaObject, metadata: TransformNodeOptions) => TypeNode | TransformObject | undefined;
|
|
9
|
+
}): Promise<void>;
|
|
@@ -1,35 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
|
-
import { parseArgs } from 'node:util';
|
|
4
3
|
import { format, resolveConfig } from 'prettier';
|
|
5
4
|
import openapiTS, { astToString } from 'openapi-typescript';
|
|
6
|
-
const ARGS_SLICE = 2;
|
|
7
|
-
const { values: { format: shouldFormat, 'add-typed-request-handler': addTypedRequestHandler }, positionals, } = parseArgs({
|
|
8
|
-
args: process.argv.slice(ARGS_SLICE),
|
|
9
|
-
options: {
|
|
10
|
-
format: { type: 'boolean', alias: 'f' },
|
|
11
|
-
'add-typed-request-handler': { type: 'boolean', alias: 't' },
|
|
12
|
-
},
|
|
13
|
-
allowPositionals: true,
|
|
14
|
-
});
|
|
15
|
-
const [openapiPath, destinationPath] = positionals;
|
|
16
|
-
if (openapiPath === undefined || destinationPath === undefined) {
|
|
17
|
-
console.error('Usage: generateTypes <openapiPath> <destinationPath>');
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|
|
20
5
|
const ESLINT_DISABLE = '/* eslint-disable */\n';
|
|
6
|
+
const FILE_HEADER = `${ESLINT_DISABLE}// This file was auto-generated. Do not edit manually.
|
|
7
|
+
// To update, run the error generation script again.\n\n`;
|
|
21
8
|
const typedRequestHandlerImport = "import type { TypedRequestHandlers as ImportedTypedRequestHandlers } from '@map-colonies/openapi-helpers/typedRequestHandler';\n";
|
|
22
9
|
const exportTypedRequestHandlers = 'export type TypedRequestHandlers = ImportedTypedRequestHandlers<paths, operations>;\n';
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
10
|
+
export async function generateTypes(openapiPath, destinationPath, options) {
|
|
11
|
+
const ast = await openapiTS(await fs.readFile(openapiPath, 'utf-8'), { exportType: true, inject: options.inject, transform: options.transform });
|
|
12
|
+
let content = astToString(ast);
|
|
13
|
+
if (options.addTypedRequestHandler === true) {
|
|
14
|
+
content = typedRequestHandlerImport + content + exportTypedRequestHandlers;
|
|
15
|
+
}
|
|
16
|
+
content = FILE_HEADER + content;
|
|
17
|
+
if (options.shouldFormat === true) {
|
|
18
|
+
const prettierOptions = await resolveConfig('./src/index.ts');
|
|
19
|
+
content = await format(content, { ...prettierOptions, parser: 'typescript' });
|
|
20
|
+
}
|
|
21
|
+
await fs.writeFile(destinationPath, content);
|
|
32
22
|
}
|
|
33
|
-
await fs.writeFile(destinationPath, content);
|
|
34
|
-
console.log('Types generated successfully');
|
|
35
23
|
//# sourceMappingURL=generateTypes.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateTypes.mjs","sourceRoot":"","sources":["../../src/generator/generateTypes.mts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"generateTypes.mjs","sourceRoot":"","sources":["../../src/generator/generateTypes.mts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,SAAS,EAAE,EAAE,WAAW,EAAuD,MAAM,oBAAoB,CAAC;AAGjH,MAAM,cAAc,GAAG,wBAAwB,CAAC;AAChD,MAAM,WAAW,GAAG,GAAG,cAAc;yDACoB,CAAC;AAE1D,MAAM,yBAAyB,GAC7B,kIAAkI,CAAC;AACrI,MAAM,0BAA0B,GAAG,uFAAuF,CAAC;AAE3H,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,WAAmB,EACnB,eAAuB,EACvB,OAKC;IAED,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAEjJ,IAAI,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAE/B,IAAI,OAAO,CAAC,sBAAsB,KAAK,IAAI,EAAE,CAAC;QAC5C,OAAO,GAAG,yBAAyB,GAAG,OAAO,GAAG,0BAA0B,CAAC;IAC7E,CAAC;IAED,OAAO,GAAG,WAAW,GAAG,OAAO,CAAC;IAEhC,IAAI,OAAO,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;QAClC,MAAM,eAAe,GAAG,MAAM,aAAa,CAAC,gBAAgB,CAAC,CAAC;QAE9D,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,EAAE,EAAE,GAAG,eAAe,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,EAAE,CAAC,SAAS,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../src/generator/index.mts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@map-colonies/openapi-helpers",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "A package that provides utilities for working with openapi files",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./requestSender": {
|
|
@@ -10,9 +10,13 @@
|
|
|
10
10
|
"./typedRequestHandler": {
|
|
11
11
|
"default": "./dist/typedRequestHandler/typedRequestHandler.js",
|
|
12
12
|
"types": "./dist/typedRequestHandler/typedRequestHandler.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./generators": {
|
|
15
|
+
"import": "./dist/generator/index.mjs",
|
|
16
|
+
"types": "./dist/generator/index.d.mts"
|
|
13
17
|
}
|
|
14
18
|
},
|
|
15
|
-
"bin": "./dist/
|
|
19
|
+
"bin": "./dist/cli/entrypoint.mjs",
|
|
16
20
|
"scripts": {
|
|
17
21
|
"format": "prettier --check .",
|
|
18
22
|
"format:fix": "prettier --write .",
|
|
@@ -26,6 +30,7 @@
|
|
|
26
30
|
"generate:test:types": "npm run build && node dist/generator/generateTypes.mjs tests/openapi3.yaml tests/types.d.ts",
|
|
27
31
|
"clean": "rimraf dist",
|
|
28
32
|
"prepublish": "npm run build",
|
|
33
|
+
"prepack": "npm run build",
|
|
29
34
|
"prepare": "husky",
|
|
30
35
|
"docs": "typedoc"
|
|
31
36
|
},
|
|
@@ -50,8 +55,12 @@
|
|
|
50
55
|
"homepage": "https://github.com/MapColonies/openapi-helpers#readme",
|
|
51
56
|
"dependencies": {
|
|
52
57
|
"@apidevtools/json-schema-ref-parser": "^14.1.1",
|
|
58
|
+
"@commander-js/extra-typings": "^14.0.0",
|
|
59
|
+
"@map-colonies/read-pkg": "^1.0.0",
|
|
53
60
|
"change-case": "^5.4.4",
|
|
61
|
+
"commander": "^14.0.0",
|
|
54
62
|
"oas-normalize": "^14.1.2",
|
|
63
|
+
"ora": "^8.2.0",
|
|
55
64
|
"ts-essentials": "^10.1.1",
|
|
56
65
|
"yaml": "^2.8.0"
|
|
57
66
|
},
|