react-query-lightbase-codegen 2.5.11 → 3.1.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/dist/cli.d.ts +2 -0
- package/dist/cli.js +180 -0
- package/dist/generateHooks.js +14 -57
- package/dist/generateHooks.js.map +1 -1
- package/dist/generator/clientGenerator.d.ts +0 -10
- package/dist/generator/clientGenerator.js +50 -82
- package/dist/generator/reactQueryGenerator.js +7 -44
- package/dist/generator/schemaGenerator.js +40 -51
- package/dist/utils.d.ts +35 -1
- package/dist/utils.js +118 -14
- package/dist/utils.js.map +1 -1
- package/package.json +4 -1
- package/src/cli.ts +212 -0
- package/src/generator/clientGenerator.ts +60 -110
- package/src/generator/reactQueryGenerator.ts +15 -50
- package/src/generator/schemaGenerator.ts +51 -62
- package/src/utils.ts +144 -15
|
@@ -1,9 +1,27 @@
|
|
|
1
1
|
import type { OpenAPIV3 } from "openapi-types";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getContentSchema,
|
|
4
|
+
getTypeFromSchema,
|
|
5
|
+
pascalCase,
|
|
6
|
+
sanitizePropertyName,
|
|
7
|
+
sanitizeTypeName,
|
|
8
|
+
} from "../utils";
|
|
3
9
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Formats a parameter as a TypeScript property string with optional JSDoc.
|
|
12
|
+
*/
|
|
13
|
+
function formatParamProperty(param: OpenAPIV3.ParameterObject, forceRequired = false): string {
|
|
14
|
+
const safeName = sanitizePropertyName(param.name);
|
|
15
|
+
const isDeprecated = param.deprecated;
|
|
16
|
+
const hasDescription = param.description;
|
|
17
|
+
const isOptional = forceRequired ? false : !param.required;
|
|
18
|
+
|
|
19
|
+
const desc =
|
|
20
|
+
hasDescription || isDeprecated
|
|
21
|
+
? `/**${hasDescription ? `\n * ${param.description}` : ""}${isDeprecated ? "\n * @deprecated" : ""}\n */\n`
|
|
22
|
+
: "";
|
|
23
|
+
|
|
24
|
+
return `${desc}${safeName}${isOptional ? "?" : ""}: ${getTypeFromSchema(param.schema)}`;
|
|
7
25
|
}
|
|
8
26
|
|
|
9
27
|
function generateTypeDefinition(
|
|
@@ -26,18 +44,16 @@ function generateTypeDefinition(
|
|
|
26
44
|
* Generates TypeScript interface definitions from OpenAPI schemas
|
|
27
45
|
*/
|
|
28
46
|
export function generateTypeDefinitions(spec: OpenAPIV3.Document): string {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
generatedTypes: new Set(),
|
|
32
|
-
};
|
|
47
|
+
const schemas = (spec.components?.schemas as { [key: string]: OpenAPIV3.SchemaObject }) || {};
|
|
48
|
+
const generatedTypes = new Set<string>();
|
|
33
49
|
|
|
34
50
|
let output = "/* Generated TypeScript Definitions */\n\n";
|
|
35
51
|
|
|
36
52
|
// Generate types for all schema definitions
|
|
37
|
-
for (const [name, schema] of Object.entries(
|
|
38
|
-
if (
|
|
53
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
54
|
+
if (generatedTypes.has(name)) continue;
|
|
39
55
|
output += generateTypeDefinition(name, schema);
|
|
40
|
-
|
|
56
|
+
generatedTypes.add(name);
|
|
41
57
|
}
|
|
42
58
|
|
|
43
59
|
// Generate request/response types
|
|
@@ -54,34 +70,36 @@ export function generateTypeDefinitions(spec: OpenAPIV3.Document): string {
|
|
|
54
70
|
// Generate request body type
|
|
55
71
|
if (requestBody) {
|
|
56
72
|
const content = (requestBody as OpenAPIV3.RequestBodyObject).content;
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
content["application/json"] ??
|
|
60
|
-
content["multipart/form-data"] ??
|
|
61
|
-
content["application/octet-stream"] ??
|
|
62
|
-
content["application/json;charset=UTF-8"];
|
|
63
|
-
if (jsonContent?.schema) {
|
|
73
|
+
const requestSchema = getContentSchema(content);
|
|
74
|
+
if (requestSchema) {
|
|
64
75
|
const typeName = `${operationId}Request`;
|
|
65
|
-
output += generateTypeDefinition(typeName,
|
|
76
|
+
output += generateTypeDefinition(typeName, requestSchema as OpenAPIV3.SchemaObject);
|
|
66
77
|
}
|
|
67
78
|
}
|
|
68
79
|
|
|
69
80
|
// Generate response types
|
|
81
|
+
const errorTypes: string[] = [];
|
|
70
82
|
if (responses) {
|
|
71
83
|
for (const [code, response] of Object.entries(responses)) {
|
|
72
84
|
const responseObj = response as OpenAPIV3.ResponseObject;
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
responseObj.content?.["application/json"] ??
|
|
76
|
-
responseObj.content?.["application/octet-stream"] ??
|
|
77
|
-
responseObj.content?.["application/json;charset=UTF-8"];
|
|
78
|
-
if (content?.schema) {
|
|
85
|
+
const responseSchema = getContentSchema(responseObj.content);
|
|
86
|
+
if (responseSchema) {
|
|
79
87
|
const typeName = `${operationId}Response${code}`;
|
|
80
|
-
output += generateTypeDefinition(typeName,
|
|
88
|
+
output += generateTypeDefinition(typeName, responseSchema as OpenAPIV3.SchemaObject);
|
|
89
|
+
|
|
90
|
+
// Track non-2xx responses for error union type
|
|
91
|
+
if (!code.startsWith("2")) {
|
|
92
|
+
errorTypes.push(typeName);
|
|
93
|
+
}
|
|
81
94
|
}
|
|
82
95
|
}
|
|
83
96
|
}
|
|
84
97
|
|
|
98
|
+
// Generate error union type if there are error responses
|
|
99
|
+
if (errorTypes.length > 0) {
|
|
100
|
+
output += `export type ${pascalCase(operationId)}Error = ${errorTypes.join(" | ")};\n\n`;
|
|
101
|
+
}
|
|
102
|
+
|
|
85
103
|
// Build data type parts
|
|
86
104
|
const dataProps: string[] = [];
|
|
87
105
|
|
|
@@ -91,43 +109,14 @@ export function generateTypeDefinitions(spec: OpenAPIV3.Document): string {
|
|
|
91
109
|
[]) as OpenAPIV3.ParameterObject[];
|
|
92
110
|
const headerParams = (parameters?.filter((p) => "in" in p && p.in === "header") ||
|
|
93
111
|
[]) as OpenAPIV3.ParameterObject[];
|
|
112
|
+
const cookieParams = (parameters?.filter((p) => "in" in p && p.in === "cookie") ||
|
|
113
|
+
[]) as OpenAPIV3.ParameterObject[];
|
|
94
114
|
|
|
95
|
-
// Add path and
|
|
96
|
-
urlParams.forEach((p) =>
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const desc =
|
|
101
|
-
hasDescription || isDeprecated
|
|
102
|
-
? `/**${hasDescription ? `\n* ${p.description}` : ""}${isDeprecated ? "\n* @deprecated" : ""}
|
|
103
|
-
*/\n`
|
|
104
|
-
: "";
|
|
105
|
-
dataProps.push(`${desc}${safeName}: ${getTypeFromSchema(p.schema)}`);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
queryParams.forEach((p) => {
|
|
109
|
-
const safeName = sanitizePropertyName(p.name);
|
|
110
|
-
const isDeprecated = "deprecated" in p && p.deprecated;
|
|
111
|
-
const hasDescription = "description" in p && p.description;
|
|
112
|
-
const desc =
|
|
113
|
-
hasDescription || isDeprecated
|
|
114
|
-
? `\n/**${hasDescription ? `\n* ${p.description}` : ""}${isDeprecated ? "\n* @deprecated" : ""}
|
|
115
|
-
*/\n`
|
|
116
|
-
: "";
|
|
117
|
-
dataProps.push(`${desc}${safeName}${p.required ? "" : "?"}: ${getTypeFromSchema(p.schema)}`);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
headerParams.forEach((p) => {
|
|
121
|
-
const safeName = sanitizePropertyName(p.name);
|
|
122
|
-
const isDeprecated = "deprecated" in p && p.deprecated;
|
|
123
|
-
const hasDescription = "description" in p && p.description;
|
|
124
|
-
const desc =
|
|
125
|
-
hasDescription || isDeprecated
|
|
126
|
-
? `\n/**${hasDescription ? `\n* ${p.description}` : ""}${isDeprecated ? "\n* @deprecated" : ""}
|
|
127
|
-
*/\n`
|
|
128
|
-
: "";
|
|
129
|
-
dataProps.push(`${desc}${safeName}${p.required ? "" : "?"}: ${getTypeFromSchema(p.schema)}`);
|
|
130
|
-
});
|
|
115
|
+
// Add path, query, header, and cookie parameters
|
|
116
|
+
urlParams.forEach((p) => dataProps.push(formatParamProperty(p, true))); // Path params always required
|
|
117
|
+
queryParams.forEach((p) => dataProps.push(formatParamProperty(p)));
|
|
118
|
+
headerParams.forEach((p) => dataProps.push(formatParamProperty(p)));
|
|
119
|
+
cookieParams.forEach((p) => dataProps.push(formatParamProperty(p)));
|
|
131
120
|
|
|
132
121
|
// Add request body type if it exists
|
|
133
122
|
const hasData = (parameters && parameters.length > 0) || requestBody;
|
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,118 @@
|
|
|
1
1
|
import type { OpenAPIV3 } from "openapi-types";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Supported content types in order of preference
|
|
5
|
+
*/
|
|
6
|
+
export const CONTENT_TYPES = [
|
|
7
|
+
"application/ld+json",
|
|
8
|
+
"application/json",
|
|
9
|
+
"text/plain",
|
|
10
|
+
"multipart/form-data",
|
|
11
|
+
"application/octet-stream",
|
|
12
|
+
"application/json;charset=UTF-8",
|
|
13
|
+
] as const;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Resolves a schema reference to its actual schema object.
|
|
17
|
+
* If the schema is already a concrete schema (not a $ref), returns it as-is.
|
|
18
|
+
*/
|
|
19
|
+
export function resolveSchema(
|
|
20
|
+
schema: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject | undefined,
|
|
21
|
+
spec: OpenAPIV3.Document
|
|
22
|
+
): OpenAPIV3.SchemaObject | undefined {
|
|
23
|
+
if (!schema) return undefined;
|
|
24
|
+
if ("$ref" in schema) {
|
|
25
|
+
const index = schema.$ref.split("/").pop();
|
|
26
|
+
return spec.components?.schemas?.[index as string] as OpenAPIV3.SchemaObject;
|
|
27
|
+
}
|
|
28
|
+
return schema;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Extracts the schema from a content object, checking supported content types in order.
|
|
33
|
+
*/
|
|
34
|
+
export function getContentSchema(
|
|
35
|
+
content: { [media: string]: OpenAPIV3.MediaTypeObject } | undefined
|
|
36
|
+
): OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject | undefined {
|
|
37
|
+
if (!content) return undefined;
|
|
38
|
+
for (const type of CONTENT_TYPES) {
|
|
39
|
+
if (content[type]?.schema) {
|
|
40
|
+
return content[type].schema;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Operation info extracted from OpenAPI paths
|
|
48
|
+
*/
|
|
49
|
+
export interface OperationInfo {
|
|
50
|
+
method: string;
|
|
51
|
+
path: string;
|
|
52
|
+
operationId: string;
|
|
53
|
+
summary?: string;
|
|
54
|
+
description?: string;
|
|
55
|
+
deprecated?: boolean;
|
|
56
|
+
parameters: OpenAPIV3.ParameterObject[];
|
|
57
|
+
requestBody?: OpenAPIV3.RequestBodyObject;
|
|
58
|
+
responses: OpenAPIV3.ResponsesObject;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const HTTP_METHODS = ["get", "post", "put", "delete", "patch"] as const;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Collects all operations from an OpenAPI spec, resolving parameter and request body references.
|
|
65
|
+
*/
|
|
66
|
+
export function collectOperations(spec: OpenAPIV3.Document): OperationInfo[] {
|
|
67
|
+
const operations: OperationInfo[] = [];
|
|
68
|
+
|
|
69
|
+
const resolveParameters = (
|
|
70
|
+
parameters: (OpenAPIV3.ParameterObject | OpenAPIV3.ReferenceObject)[]
|
|
71
|
+
): OpenAPIV3.ParameterObject[] => {
|
|
72
|
+
return parameters.map((p) => {
|
|
73
|
+
if ("$ref" in p) {
|
|
74
|
+
const index = p.$ref.split("/").pop();
|
|
75
|
+
return spec.components?.parameters?.[index as string] as OpenAPIV3.ParameterObject;
|
|
76
|
+
}
|
|
77
|
+
return p;
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const resolveRequestBody = (
|
|
82
|
+
requestBody: OpenAPIV3.RequestBodyObject | OpenAPIV3.ReferenceObject | undefined
|
|
83
|
+
): OpenAPIV3.RequestBodyObject | undefined => {
|
|
84
|
+
if (!requestBody) return undefined;
|
|
85
|
+
if ("$ref" in requestBody) {
|
|
86
|
+
const index = requestBody.$ref.split("/").pop();
|
|
87
|
+
return spec.components?.requestBodies?.[index as string] as OpenAPIV3.RequestBodyObject;
|
|
88
|
+
}
|
|
89
|
+
return requestBody;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
Object.entries(spec.paths || {}).forEach(([path, pathItem]) => {
|
|
93
|
+
if (!pathItem) return;
|
|
94
|
+
|
|
95
|
+
HTTP_METHODS.forEach((method) => {
|
|
96
|
+
const operation = pathItem[method] as OpenAPIV3.OperationObject | undefined;
|
|
97
|
+
if (!operation) return;
|
|
98
|
+
|
|
99
|
+
operations.push({
|
|
100
|
+
method,
|
|
101
|
+
path,
|
|
102
|
+
operationId: sanitizeTypeName(operation.operationId || path.replace(/\W+/g, "_")),
|
|
103
|
+
summary: operation.summary,
|
|
104
|
+
description: operation.description,
|
|
105
|
+
deprecated: operation.deprecated,
|
|
106
|
+
parameters: resolveParameters([...(pathItem.parameters || []), ...(operation.parameters || [])]),
|
|
107
|
+
requestBody: resolveRequestBody(operation.requestBody),
|
|
108
|
+
responses: operation.responses,
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return operations;
|
|
114
|
+
}
|
|
115
|
+
|
|
3
116
|
export function camelCase(str: string): string {
|
|
4
117
|
return str
|
|
5
118
|
.replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase())
|
|
@@ -63,6 +176,7 @@ export function specTitle(spec: OpenAPIV3.Document): string {
|
|
|
63
176
|
* Handles:
|
|
64
177
|
* - References ($ref) by extracting the type name
|
|
65
178
|
* - Nullable types by appending "| null"
|
|
179
|
+
* - Composition types (allOf → intersection, oneOf/anyOf → union)
|
|
66
180
|
* - Enums by creating union types of the values
|
|
67
181
|
* - OneOf schemas as union types
|
|
68
182
|
* - Basic types (string, number, boolean)
|
|
@@ -72,7 +186,7 @@ export function specTitle(spec: OpenAPIV3.Document): string {
|
|
|
72
186
|
* - Objects with additionalProperties as Records
|
|
73
187
|
* - Fallback to "any" for unknown types
|
|
74
188
|
*
|
|
75
|
-
* @param
|
|
189
|
+
* @param schema - The OpenAPI schema/parameter object to convert
|
|
76
190
|
* @returns The TypeScript type as a string
|
|
77
191
|
*/
|
|
78
192
|
export function getTypeFromSchema(
|
|
@@ -88,6 +202,30 @@ export function getTypeFromSchema(
|
|
|
88
202
|
// Add "| null" for nullable types
|
|
89
203
|
const nullable = "nullable" in schema && schema.nullable ? " | null" : "";
|
|
90
204
|
|
|
205
|
+
// Handle allOf (intersection types)
|
|
206
|
+
if ("allOf" in schema && schema.allOf) {
|
|
207
|
+
const types = schema.allOf.map((s) => getTypeFromSchema(s)).filter(Boolean) as string[];
|
|
208
|
+
if (types.length === 0) return "unknown";
|
|
209
|
+
const result = types.length === 1 ? types[0] : `(${types.join(" & ")})`;
|
|
210
|
+
return `${result}${nullable}`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Handle oneOf (union types - exactly one)
|
|
214
|
+
if ("oneOf" in schema && schema.oneOf) {
|
|
215
|
+
const types = schema.oneOf.map((s) => getTypeFromSchema(s)).filter(Boolean) as string[];
|
|
216
|
+
if (types.length === 0) return "unknown";
|
|
217
|
+
const result = types.length === 1 ? types[0] : `(${types.join(" | ")})`;
|
|
218
|
+
return `${result}${nullable}`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Handle anyOf (union types - one or more)
|
|
222
|
+
if ("anyOf" in schema && schema.anyOf) {
|
|
223
|
+
const types = schema.anyOf.map((s) => getTypeFromSchema(s)).filter(Boolean) as string[];
|
|
224
|
+
if (types.length === 0) return "unknown";
|
|
225
|
+
const result = types.length === 1 ? types[0] : `(${types.join(" | ")})`;
|
|
226
|
+
return `${result}${nullable}`;
|
|
227
|
+
}
|
|
228
|
+
|
|
91
229
|
// Handle enums as union types
|
|
92
230
|
if ("enum" in schema && schema.enum) {
|
|
93
231
|
if (Object.values(schema.enum)?.length > 0) {
|
|
@@ -100,14 +238,6 @@ export function getTypeFromSchema(
|
|
|
100
238
|
return schema.enum.map((e) => (typeof e === "string" ? `'${e}'` : e)).join(" | ") + nullable;
|
|
101
239
|
}
|
|
102
240
|
|
|
103
|
-
// Handle oneOf as union types
|
|
104
|
-
if ("oneOf" in schema && schema.oneOf) {
|
|
105
|
-
const unionTypes = schema.oneOf
|
|
106
|
-
.map((subSchema) => getTypeFromSchema(subSchema))
|
|
107
|
-
.filter((type): type is string => type !== undefined);
|
|
108
|
-
return unionTypes.length > 0 ? `${unionTypes.join(" | ")}${nullable}` : `any${nullable}`;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
241
|
// Handle types based on the "type" property
|
|
112
242
|
if ("type" in schema) {
|
|
113
243
|
switch (schema.type) {
|
|
@@ -143,8 +273,7 @@ export function getTypeFromSchema(
|
|
|
143
273
|
const hasDescription = "description" in prop && prop.description;
|
|
144
274
|
const desc =
|
|
145
275
|
hasDescription || isDeprecated
|
|
146
|
-
? `/**${hasDescription ? `\n* ${prop.description}` : ""}${isDeprecated ? "\n* @deprecated" : ""}
|
|
147
|
-
*/\n`
|
|
276
|
+
? `/**${hasDescription ? `\n * ${prop.description}` : ""}${isDeprecated ? "\n * @deprecated" : ""}\n */\n`
|
|
148
277
|
: "";
|
|
149
278
|
return `${desc}${safeName}${isRequired ? "" : "?"}: ${propertyType};`;
|
|
150
279
|
})
|
|
@@ -156,19 +285,19 @@ export function getTypeFromSchema(
|
|
|
156
285
|
if (schema.additionalProperties) {
|
|
157
286
|
const valueType =
|
|
158
287
|
typeof schema.additionalProperties === "boolean"
|
|
159
|
-
? "
|
|
288
|
+
? "unknown"
|
|
160
289
|
: getTypeFromSchema(schema.additionalProperties);
|
|
161
290
|
return `Record<string, ${valueType}>${nullable}`;
|
|
162
291
|
}
|
|
163
292
|
|
|
164
293
|
// Default object type when no properties specified
|
|
165
|
-
return `Record<string,
|
|
294
|
+
return `Record<string, unknown>${nullable}`;
|
|
166
295
|
|
|
167
296
|
default:
|
|
168
|
-
return `
|
|
297
|
+
return `unknown${nullable}`;
|
|
169
298
|
}
|
|
170
299
|
}
|
|
171
300
|
|
|
172
301
|
// Fallback for schemas without a type
|
|
173
|
-
return "
|
|
302
|
+
return "unknown";
|
|
174
303
|
}
|