@let-aurn/swagger-client 0.4.0 → 0.5.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 +57 -0
- package/dist/core/enums.d.ts +12 -0
- package/dist/core/enums.js +77 -0
- package/dist/core/generate.js +1 -1
- package/dist/core/load-config.js +2 -0
- package/dist/core/read-openapi.d.ts +3 -2
- package/dist/core/read-openapi.js +21 -20
- package/dist/core/schema-to-typescript.js +1 -1
- package/dist/core/types.d.ts +4 -0
- package/dist/generators/endpoints.js +5 -1
- package/dist/generators/models.js +4 -1
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,6 +24,63 @@ npx swagger-client generate --config=swagger-client.json
|
|
|
24
24
|
|
|
25
25
|
`client` can be `angular` or `react`.
|
|
26
26
|
|
|
27
|
+
`enum-style` is optional and accepts `"union"` (the default) or `"enum"`:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"enum-style": "enum"
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Union style emits literal types such as `"PENDING" | "IN_PROGRESS"`.
|
|
36
|
+
Enum style emits exported runtime TypeScript enums in `api-models.ts` and uses
|
|
37
|
+
them in model properties and endpoint request/response types:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
export enum TaskState {
|
|
41
|
+
PENDING = "PENDING",
|
|
42
|
+
IN_PROGRESS = "IN_PROGRESS",
|
|
43
|
+
COMPLETED = "COMPLETED",
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface Task {
|
|
47
|
+
state?: TaskState;
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Standalone enum schemas keep their generated schema names. Inline enums use
|
|
52
|
+
the enclosing model name plus the property path; array items add `Item`, and
|
|
53
|
+
dictionary values add `Value`. Endpoint enums use the endpoint name followed
|
|
54
|
+
by `Path`, `Query`, `Header`, `Body`, or `Response` and the nested property path.
|
|
55
|
+
Declaration and member name collisions receive numeric suffixes. Identical
|
|
56
|
+
inline enums in different locations remain separate declarations; use a shared
|
|
57
|
+
schema with `$ref` to share a single enum.
|
|
58
|
+
|
|
59
|
+
String values retain valid member names such as `PENDING`; invalid identifier
|
|
60
|
+
characters are removed, leading digits receive a `Value` prefix, and reserved
|
|
61
|
+
names are adjusted. Numeric members use names such as `Value_0` and
|
|
62
|
+
`Value_Minus_1`. Every member has an explicit initializer that preserves its
|
|
63
|
+
original JSON value. Duplicate values are emitted once. String, numeric, and
|
|
64
|
+
mixed string/numeric enums are supported; enums containing boolean or structured
|
|
65
|
+
values remain literal unions because TypeScript enums cannot represent them.
|
|
66
|
+
Null values and `nullable: true` become `EnumName | null`. Nullable standalone
|
|
67
|
+
enum schemas emit an `EnumNameValue` enum plus an `EnumName` type alias.
|
|
68
|
+
|
|
69
|
+
When enabling enum style, use enum members instead of assigning raw strings:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { TaskState } from './api-models';
|
|
73
|
+
import type { Task } from './api-models';
|
|
74
|
+
|
|
75
|
+
const task: Task = {
|
|
76
|
+
state: TaskState.PENDING,
|
|
77
|
+
};
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Enums exist at runtime; serialized request values are still the original strings
|
|
81
|
+
or numbers. This option changes generated types only and does not validate or
|
|
82
|
+
convert server responses at runtime.
|
|
83
|
+
|
|
27
84
|
`swaggers-directory` accepts a directory (all JSON files recursively), a file
|
|
28
85
|
glob, or an array of include/exclude glob patterns. Paths are relative to the
|
|
29
86
|
command's working directory; absolute paths also work. Use forward slashes in
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EnumStyle, OpenApiSchema, SchemaModel } from "./types.js";
|
|
2
|
+
export declare function validateEnumStyle(style: EnumStyle | undefined, source: string): void;
|
|
3
|
+
/** Extracts inline enums before endpoint schemas are converted to type strings. */
|
|
4
|
+
export declare class EnumCollector {
|
|
5
|
+
private readonly schemas;
|
|
6
|
+
private readonly usedNames;
|
|
7
|
+
private readonly style;
|
|
8
|
+
constructor(schemas: SchemaModel[], usedNames: Set<string>, style: EnumStyle);
|
|
9
|
+
addModel(name: string, schema: OpenApiSchema): void;
|
|
10
|
+
normalize(schema: OpenApiSchema, context: string): OpenApiSchema;
|
|
11
|
+
}
|
|
12
|
+
export declare function emitEnum(name: string, values: (string | number)[]): string;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { ensureIdentifier, toPascalCase, uniqueName } from "./names.js";
|
|
2
|
+
export function validateEnumStyle(style, source) {
|
|
3
|
+
if (style !== undefined && style !== "union" && style !== "enum") {
|
|
4
|
+
throw new Error(`${source}: "enum-style" must be "union" or "enum".`);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
function enumValues(schema) {
|
|
8
|
+
if (schema.$ref || !schema.enum?.length)
|
|
9
|
+
return undefined;
|
|
10
|
+
const values = schema.enum.filter((value) => value !== null);
|
|
11
|
+
if (!values.length || !values.every((value) => typeof value === "string" || (typeof value === "number" && Number.isFinite(value)))) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
return [...new Set(values)];
|
|
15
|
+
}
|
|
16
|
+
/** Extracts inline enums before endpoint schemas are converted to type strings. */
|
|
17
|
+
export class EnumCollector {
|
|
18
|
+
schemas;
|
|
19
|
+
usedNames;
|
|
20
|
+
style;
|
|
21
|
+
constructor(schemas, usedNames, style) {
|
|
22
|
+
this.schemas = schemas;
|
|
23
|
+
this.usedNames = usedNames;
|
|
24
|
+
this.style = style;
|
|
25
|
+
}
|
|
26
|
+
addModel(name, schema) {
|
|
27
|
+
const values = this.style === "enum" ? enumValues(schema) : undefined;
|
|
28
|
+
if (values && !schema.nullable && !schema.enum.includes(null)) {
|
|
29
|
+
this.schemas.push({ name, schema, enumValues: values });
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
this.schemas.push({ name, schema: this.normalize(schema, values ? `${name}Value` : name) });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
normalize(schema, context) {
|
|
36
|
+
if (this.style === "union")
|
|
37
|
+
return schema;
|
|
38
|
+
const values = enumValues(schema);
|
|
39
|
+
if (values) {
|
|
40
|
+
const name = uniqueName(toPascalCase(context), this.usedNames);
|
|
41
|
+
this.schemas.push({ name, schema: { enum: values }, enumValues: values });
|
|
42
|
+
return {
|
|
43
|
+
$ref: `#/components/schemas/${name}`,
|
|
44
|
+
nullable: schema.nullable === true || schema.enum.includes(null)
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const result = { ...schema };
|
|
48
|
+
if (schema.properties) {
|
|
49
|
+
result.properties = Object.fromEntries(Object.entries(schema.properties).map(([key, child]) => [key, this.normalize(child, `${context}${toPascalCase(key)}`)]));
|
|
50
|
+
}
|
|
51
|
+
if (schema.items)
|
|
52
|
+
result.items = this.normalize(schema.items, `${context}Item`);
|
|
53
|
+
if (typeof schema.additionalProperties === "object") {
|
|
54
|
+
result.additionalProperties = this.normalize(schema.additionalProperties, `${context}Value`);
|
|
55
|
+
}
|
|
56
|
+
for (const kind of ["allOf", "oneOf", "anyOf"]) {
|
|
57
|
+
if (schema[kind]) {
|
|
58
|
+
result[kind] = schema[kind].map((child, index) => this.normalize(child, `${context}${toPascalCase(kind)}${index + 1}`));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export function emitEnum(name, values) {
|
|
65
|
+
const usedNames = new Set();
|
|
66
|
+
const members = values.map((value) => {
|
|
67
|
+
const preferred = typeof value === "number" ? `Value_${String(value).replace(/-/g, "Minus_").replace(/\./g, "_")}` : value;
|
|
68
|
+
const identifier = ensureIdentifier(preferred || "Value", "Value");
|
|
69
|
+
// These names conflict with the enum object's prototype or its JS setter.
|
|
70
|
+
const safeIdentifier = identifier === "prototype" || identifier === "__proto__"
|
|
71
|
+
? `${identifier}Value`
|
|
72
|
+
: identifier;
|
|
73
|
+
const member = uniqueName(safeIdentifier, usedNames);
|
|
74
|
+
return ` ${member} = ${JSON.stringify(value)},`;
|
|
75
|
+
});
|
|
76
|
+
return [`export enum ${name} {`, ...members, "}"].join("\n");
|
|
77
|
+
}
|
package/dist/core/generate.js
CHANGED
|
@@ -13,7 +13,7 @@ import { emitReactClient } from "../generators/react-client.js";
|
|
|
13
13
|
* @throws Error when OpenAPI files cannot be read or generated files cannot be written.
|
|
14
14
|
*/
|
|
15
15
|
export async function generate(config) {
|
|
16
|
-
const project = await readOpenApiProject(config["swaggers-directory"], config["endpoint-name-strategy"], config["endpoint-namespace"], config.serverUrlIndex);
|
|
16
|
+
const project = await readOpenApiProject(config["swaggers-directory"], config["endpoint-name-strategy"], config["endpoint-namespace"], config.serverUrlIndex, config["enum-style"]);
|
|
17
17
|
const modelsPath = config.output["api-models.ts"];
|
|
18
18
|
const endpointsPath = config.output["api-endpoints.ts"];
|
|
19
19
|
const clientPath = config.output["client.ts"];
|
package/dist/core/load-config.js
CHANGED
|
@@ -3,6 +3,7 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { validateEndpointNamespace } from "./endpoint-namespace.js";
|
|
4
4
|
import { validateServerUrlIndex } from "./server-url.js";
|
|
5
5
|
import { resolveSwaggerInput, validateSwaggerInput } from "./swagger-files.js";
|
|
6
|
+
import { validateEnumStyle } from "./enums.js";
|
|
6
7
|
/**
|
|
7
8
|
* Loads and validates a swagger-client configuration file.
|
|
8
9
|
*
|
|
@@ -26,6 +27,7 @@ export async function loadConfig(configPath) {
|
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
29
|
function validateConfig(config, configPath) {
|
|
30
|
+
validateEnumStyle(config["enum-style"], configPath);
|
|
29
31
|
validateServerUrlIndex(config.serverUrlIndex, configPath);
|
|
30
32
|
if (config.client !== "angular" && config.client !== "react") {
|
|
31
33
|
throw new Error(`${configPath}: "client" must be "angular" or "react".`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EndpointNameStrategy, EndpointNamespaceConfig, ProjectModel } from "./types.js";
|
|
1
|
+
import type { EndpointNameStrategy, EnumStyle, EndpointNamespaceConfig, ProjectModel } from "./types.js";
|
|
2
2
|
/**
|
|
3
3
|
* Reads selected OpenAPI JSON files and converts them to the generator project model.
|
|
4
4
|
*
|
|
@@ -6,7 +6,8 @@ import type { EndpointNameStrategy, EndpointNamespaceConfig, ProjectModel } from
|
|
|
6
6
|
* @param endpointNameStrategy The strategy used to name generated endpoint constants.
|
|
7
7
|
* @param endpointNamespace Configurable document field and optional namespace extraction regex.
|
|
8
8
|
* @param serverUrlIndex Optional document server URL index to prefix endpoint paths with.
|
|
9
|
+
* @param enumStyle Emit literal unions (default) or named runtime enums.
|
|
9
10
|
* @returns The normalized schemas and endpoints discovered in the OpenAPI files.
|
|
10
11
|
* @throws Error when a JSON file cannot be read or parsed.
|
|
11
12
|
*/
|
|
12
|
-
export declare function readOpenApiProject(swaggerDirectory: string | string[], endpointNameStrategy?: EndpointNameStrategy, endpointNamespace?: EndpointNamespaceConfig, serverUrlIndex?: number): Promise<ProjectModel>;
|
|
13
|
+
export declare function readOpenApiProject(swaggerDirectory: string | string[], endpointNameStrategy?: EndpointNameStrategy, endpointNamespace?: EndpointNamespaceConfig, serverUrlIndex?: number, enumStyle?: EnumStyle): Promise<ProjectModel>;
|
|
@@ -5,6 +5,7 @@ import { ensureIdentifier, toPascalCase, uniqueName } from "./names.js";
|
|
|
5
5
|
import { extractEndpointNamespace, validateEndpointNamespace } from "./endpoint-namespace.js";
|
|
6
6
|
import { selectServerUrl, validateServerUrlIndex } from "./server-url.js";
|
|
7
7
|
import { discoverSwaggerFiles } from "./swagger-files.js";
|
|
8
|
+
import { EnumCollector, validateEnumStyle } from "./enums.js";
|
|
8
9
|
const HTTP_METHODS = new Set(["get", "post", "put", "patch", "delete", "head", "options"]);
|
|
9
10
|
/**
|
|
10
11
|
* Reads selected OpenAPI JSON files and converts them to the generator project model.
|
|
@@ -13,10 +14,12 @@ const HTTP_METHODS = new Set(["get", "post", "put", "patch", "delete", "head", "
|
|
|
13
14
|
* @param endpointNameStrategy The strategy used to name generated endpoint constants.
|
|
14
15
|
* @param endpointNamespace Configurable document field and optional namespace extraction regex.
|
|
15
16
|
* @param serverUrlIndex Optional document server URL index to prefix endpoint paths with.
|
|
17
|
+
* @param enumStyle Emit literal unions (default) or named runtime enums.
|
|
16
18
|
* @returns The normalized schemas and endpoints discovered in the OpenAPI files.
|
|
17
19
|
* @throws Error when a JSON file cannot be read or parsed.
|
|
18
20
|
*/
|
|
19
|
-
export async function readOpenApiProject(swaggerDirectory, endpointNameStrategy = "operationId", endpointNamespace, serverUrlIndex) {
|
|
21
|
+
export async function readOpenApiProject(swaggerDirectory, endpointNameStrategy = "operationId", endpointNamespace, serverUrlIndex, enumStyle = "union") {
|
|
22
|
+
validateEnumStyle(enumStyle, String(swaggerDirectory));
|
|
20
23
|
validateServerUrlIndex(serverUrlIndex, String(swaggerDirectory));
|
|
21
24
|
if (endpointNameStrategy === "namespaceTagAndOperationId") {
|
|
22
25
|
validateEndpointNamespace(endpointNamespace, String(swaggerDirectory));
|
|
@@ -26,19 +29,20 @@ export async function readOpenApiProject(swaggerDirectory, endpointNameStrategy
|
|
|
26
29
|
const endpoints = [];
|
|
27
30
|
const usedSchemaNames = new Set();
|
|
28
31
|
const usedEndpointNames = new Set();
|
|
32
|
+
const enums = new EnumCollector(schemas, usedSchemaNames, enumStyle);
|
|
29
33
|
for (const file of files) {
|
|
30
34
|
const document = JSON.parse(await readFile(file, "utf8"));
|
|
31
35
|
const sourceFile = relative(sourceDirectory, file);
|
|
32
36
|
const serverUrl = selectServerUrl(document, serverUrlIndex, sourceFile);
|
|
33
|
-
const schemaNameMap = collectSchemas(document,
|
|
37
|
+
const schemaNameMap = collectSchemas(document, enums, usedSchemaNames);
|
|
34
38
|
const namespace = endpointNameStrategy === "namespaceTagAndOperationId"
|
|
35
39
|
? extractEndpointNamespace(document, endpointNamespace, sourceFile)
|
|
36
40
|
: undefined;
|
|
37
|
-
endpoints.push(...collectEndpoints(document, sourceFile, schemaNameMap, usedEndpointNames, endpointNameStrategy, namespace, serverUrl));
|
|
41
|
+
endpoints.push(...collectEndpoints(document, sourceFile, schemaNameMap, usedEndpointNames, endpointNameStrategy, namespace, serverUrl, enums));
|
|
38
42
|
}
|
|
39
43
|
return { schemas, endpoints };
|
|
40
44
|
}
|
|
41
|
-
function collectSchemas(document,
|
|
45
|
+
function collectSchemas(document, enums, usedSchemaNames) {
|
|
42
46
|
const schemaNameMap = new Map();
|
|
43
47
|
const sourceSchemas = document.components?.schemas ?? document.definitions ?? {};
|
|
44
48
|
for (const rawName of Object.keys(sourceSchemas)) {
|
|
@@ -50,14 +54,11 @@ function collectSchemas(document, schemas, usedSchemaNames) {
|
|
|
50
54
|
if (!generatedName) {
|
|
51
55
|
continue;
|
|
52
56
|
}
|
|
53
|
-
|
|
54
|
-
name: generatedName,
|
|
55
|
-
schema: rewriteRefs(schema, schemaNameMap)
|
|
56
|
-
});
|
|
57
|
+
enums.addModel(generatedName, rewriteRefs(schema, schemaNameMap));
|
|
57
58
|
}
|
|
58
59
|
return schemaNameMap;
|
|
59
60
|
}
|
|
60
|
-
function collectEndpoints(document, sourceFile, schemaNameMap, usedEndpointNames, endpointNameStrategy, namespace, serverUrl
|
|
61
|
+
function collectEndpoints(document, sourceFile, schemaNameMap, usedEndpointNames, endpointNameStrategy, namespace, serverUrl, enums) {
|
|
61
62
|
const endpoints = [];
|
|
62
63
|
for (const [path, pathItem] of Object.entries(document.paths ?? {})) {
|
|
63
64
|
if (!isRecord(pathItem)) {
|
|
@@ -76,11 +77,11 @@ function collectEndpoints(document, sourceFile, schemaNameMap, usedEndpointNames
|
|
|
76
77
|
path: serverUrl && path ? `${serverUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}` : serverUrl || path,
|
|
77
78
|
operationId,
|
|
78
79
|
sourceFile,
|
|
79
|
-
pathVariables: variablesFor(parameters, "path", schemaNameMap),
|
|
80
|
-
queryVariables: variablesFor(parameters, "query", schemaNameMap),
|
|
81
|
-
headerVariables: variablesFor(parameters, "header", schemaNameMap),
|
|
82
|
-
bodyType: requestBodyType(operation, schemaNameMap),
|
|
83
|
-
responseType: responseType(operation.responses, schemaNameMap)
|
|
80
|
+
pathVariables: variablesFor(parameters, "path", schemaNameMap, enums, endpointName),
|
|
81
|
+
queryVariables: variablesFor(parameters, "query", schemaNameMap, enums, endpointName),
|
|
82
|
+
headerVariables: variablesFor(parameters, "header", schemaNameMap, enums, endpointName),
|
|
83
|
+
bodyType: requestBodyType(operation, schemaNameMap, enums, endpointName),
|
|
84
|
+
responseType: responseType(operation.responses, schemaNameMap, enums, endpointName)
|
|
84
85
|
});
|
|
85
86
|
}
|
|
86
87
|
}
|
|
@@ -101,26 +102,26 @@ function endpointNameForOperation(operation, operationId, strategy, namespace) {
|
|
|
101
102
|
}
|
|
102
103
|
return `${namespace}_${taggedName}`;
|
|
103
104
|
}
|
|
104
|
-
function variablesFor(parameters, location, schemaNameMap) {
|
|
105
|
+
function variablesFor(parameters, location, schemaNameMap, enums, endpointName) {
|
|
105
106
|
return parameters
|
|
106
107
|
.filter((parameter) => parameter.in === location)
|
|
107
108
|
.map((parameter) => ({
|
|
108
109
|
name: parameter.name,
|
|
109
|
-
type: schemaToType(rewriteRefs(parameter.schema ?? { type: "string" }, schemaNameMap)),
|
|
110
|
+
type: schemaToType(enums.normalize(rewriteRefs(parameter.schema ?? { type: "string" }, schemaNameMap), `${toPascalCase(endpointName)}${toPascalCase(location)}${toPascalCase(parameter.name)}`)),
|
|
110
111
|
required: parameter.required === true || location === "path"
|
|
111
112
|
}));
|
|
112
113
|
}
|
|
113
|
-
function requestBodyType(operation, schemaNameMap) {
|
|
114
|
+
function requestBodyType(operation, schemaNameMap, enums, endpointName) {
|
|
114
115
|
const schema = preferredContentSchema(operation.requestBody?.content);
|
|
115
|
-
return schema ? schemaToType(rewriteRefs(schema, schemaNameMap)) : null;
|
|
116
|
+
return schema ? schemaToType(enums.normalize(rewriteRefs(schema, schemaNameMap), `${toPascalCase(endpointName)}Body`)) : null;
|
|
116
117
|
}
|
|
117
|
-
function responseType(responses, schemaNameMap) {
|
|
118
|
+
function responseType(responses, schemaNameMap, enums, endpointName) {
|
|
118
119
|
const response = responses?.["200"] ?? responses?.["201"] ?? responses?.["204"] ?? firstSuccessResponse(responses);
|
|
119
120
|
const schema = preferredContentSchema(response?.content);
|
|
120
121
|
if (!schema || response === responses?.["204"]) {
|
|
121
122
|
return "void";
|
|
122
123
|
}
|
|
123
|
-
return schemaToType(rewriteRefs(schema, schemaNameMap));
|
|
124
|
+
return schemaToType(enums.normalize(rewriteRefs(schema, schemaNameMap), `${toPascalCase(endpointName)}Response`));
|
|
124
125
|
}
|
|
125
126
|
function firstSuccessResponse(responses) {
|
|
126
127
|
return Object.entries(responses ?? {}).find(([status]) => status.startsWith("2"))?.[1];
|
|
@@ -59,7 +59,7 @@ function schemaToNonNullableType(schema, fallback) {
|
|
|
59
59
|
return (schema.oneOf ?? schema.anyOf ?? []).map((item) => schemaToType(item, fallback)).join(" | ") || fallback;
|
|
60
60
|
}
|
|
61
61
|
if (schema.type === "array") {
|
|
62
|
-
return
|
|
62
|
+
return `Array<${schemaToType(schema.items, "unknown")}>`;
|
|
63
63
|
}
|
|
64
64
|
if (schema.type === "object" || schema.properties) {
|
|
65
65
|
const entries = Object.entries(schema.properties ?? {});
|
package/dist/core/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type ClientKind = "angular" | "react";
|
|
2
|
+
export type EnumStyle = "union" | "enum";
|
|
2
3
|
export type EndpointNameStrategy = "operationId" | "tagAndOperationId" | "namespaceTagAndOperationId";
|
|
3
4
|
export interface EndpointNamespaceConfig {
|
|
4
5
|
/** JSON Pointer to a string in each OpenAPI document. */
|
|
@@ -8,6 +9,8 @@ export interface EndpointNamespaceConfig {
|
|
|
8
9
|
}
|
|
9
10
|
export interface SwaggerClientConfig {
|
|
10
11
|
client: ClientKind;
|
|
12
|
+
/** Emit literal unions by default, or named runtime enums. */
|
|
13
|
+
"enum-style"?: EnumStyle;
|
|
11
14
|
/** Directory, file glob, or include/exclude glob patterns, relative to the working directory. */
|
|
12
15
|
"swaggers-directory": string | string[];
|
|
13
16
|
"endpoint-name-strategy"?: EndpointNameStrategy;
|
|
@@ -27,6 +30,7 @@ export interface ProjectModel {
|
|
|
27
30
|
export interface SchemaModel {
|
|
28
31
|
name: string;
|
|
29
32
|
schema: OpenApiSchema;
|
|
33
|
+
enumValues?: (string | number)[];
|
|
30
34
|
}
|
|
31
35
|
export interface EndpointModel {
|
|
32
36
|
name: string;
|
|
@@ -136,7 +136,11 @@ function propertyKey(name) {
|
|
|
136
136
|
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : quote(name);
|
|
137
137
|
}
|
|
138
138
|
function qualifyModelTypes(type, modelNames) {
|
|
139
|
-
return type.replace(
|
|
139
|
+
return type.replace(/"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\b[A-Z][A-Za-z0-9_]*\b/g, (name, offset) => {
|
|
140
|
+
// Preserve string literals and property keys that happen to match model names.
|
|
141
|
+
if (name.startsWith('"') || name.startsWith("'") || /^\s*\??\s*:/.test(type.slice(offset + name.length))) {
|
|
142
|
+
return name;
|
|
143
|
+
}
|
|
140
144
|
return modelNames.has(name) ? `ApiModels.${name}` : name;
|
|
141
145
|
});
|
|
142
146
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { emitSchemaInterface } from "../core/schema-to-typescript.js";
|
|
2
|
+
import { emitEnum } from "../core/enums.js";
|
|
2
3
|
/**
|
|
3
4
|
* Emits the generated TypeScript API model file.
|
|
4
5
|
*
|
|
@@ -12,7 +13,9 @@ export function emitModels(project) {
|
|
|
12
13
|
""
|
|
13
14
|
];
|
|
14
15
|
for (const schema of project.schemas) {
|
|
15
|
-
lines.push(
|
|
16
|
+
lines.push(schema.enumValues
|
|
17
|
+
? emitEnum(schema.name, schema.enumValues)
|
|
18
|
+
: emitSchemaInterface(schema.name, schema.schema), "");
|
|
16
19
|
}
|
|
17
20
|
return lines.join("\n");
|
|
18
21
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { ClientKind, EndpointNameStrategy, EndpointNamespaceConfig, EndpointModel, ProjectModel, SchemaModel, SwaggerClientConfig, VariableModel } from "./core/types.js";
|
|
1
|
+
export type { ClientKind, EnumStyle, EndpointNameStrategy, EndpointNamespaceConfig, EndpointModel, ProjectModel, SchemaModel, SwaggerClientConfig, VariableModel } from "./core/types.js";
|
|
2
2
|
export { generate } from "./core/generate.js";
|
|
3
3
|
export { loadConfig } from "./core/load-config.js";
|
|
4
4
|
export { readOpenApiProject } from "./core/read-openapi.js";
|