elysia-openapi-codegen 0.1.8 → 0.1.9
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 +10 -3
- package/dist/index.js +26 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,12 @@ Generate fully-typed React Query hooks and TypeScript interfaces from OpenAPI sp
|
|
|
11
11
|
- **React Query Integration**: Generates `useQuery` and `useMutation` hooks ready to use
|
|
12
12
|
- **Flexible Arguments**: Supports both flag-based and positional arguments
|
|
13
13
|
|
|
14
|
+
### Using Bunx (Recommended)
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
bunx elysia-openapi-codegen -i https://api.example.com/openapi.json -o ./src/api
|
|
18
|
+
```
|
|
19
|
+
|
|
14
20
|
## Installation
|
|
15
21
|
|
|
16
22
|
### Using Bun (Recommended)
|
|
@@ -50,6 +56,7 @@ elysia-codegen -i <source> -o <output>
|
|
|
50
56
|
```
|
|
51
57
|
|
|
52
58
|
**Arguments:**
|
|
59
|
+
|
|
53
60
|
- `-i, --input <source>` - OpenAPI spec source (URL or file path)
|
|
54
61
|
- `-o, --output <output>` - Output directory for generated files
|
|
55
62
|
- `-h, --help` - Show help message
|
|
@@ -98,13 +105,13 @@ The generator creates a single `generated.ts` file containing all types and hook
|
|
|
98
105
|
All request/response types are automatically generated:
|
|
99
106
|
|
|
100
107
|
```typescript
|
|
101
|
-
import type { User, CreateUserBody, GetUsersResponse } from
|
|
108
|
+
import type { User, CreateUserBody, GetUsersResponse } from "./generated";
|
|
102
109
|
|
|
103
110
|
// Use types in your components
|
|
104
111
|
const user: User = {
|
|
105
112
|
id: 1,
|
|
106
|
-
name:
|
|
107
|
-
email:
|
|
113
|
+
name: "John Doe",
|
|
114
|
+
email: "john@example.com",
|
|
108
115
|
};
|
|
109
116
|
```
|
|
110
117
|
|
package/dist/index.js
CHANGED
|
@@ -77,6 +77,8 @@ function generateGroups(spec) {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
for (const [pathUrl, pathItem] of Object.entries(spec.paths)) {
|
|
80
|
+
if (pathUrl.includes("*"))
|
|
81
|
+
continue;
|
|
80
82
|
const g = getGroup(getPathGroup(pathUrl));
|
|
81
83
|
for (const [method, operation] of Object.entries(pathItem)) {
|
|
82
84
|
if (!["get", "post", "put", "patch", "delete"].includes(method))
|
|
@@ -100,6 +102,8 @@ ${paramProps.join(`
|
|
|
100
102
|
}
|
|
101
103
|
const isMultipart = !op.requestBody?.content?.["application/json"] && !!op.requestBody?.content?.["multipart/form-data"];
|
|
102
104
|
const bodySchema = op.requestBody?.content?.["application/json"]?.schema ?? op.requestBody?.content?.["multipart/form-data"]?.schema;
|
|
105
|
+
const hasBinaryField = (s) => !!s && (s.format === "binary" || Object.values(s.properties ?? {}).some((p) => p.format === "binary"));
|
|
106
|
+
const useFormData = isMultipart || hasBinaryField(bodySchema);
|
|
103
107
|
if (bodySchema) {
|
|
104
108
|
g.types.push(`export type ${capitalize(opId)}Body = ${resolveType(bodySchema)};`);
|
|
105
109
|
}
|
|
@@ -152,27 +156,40 @@ ${fetchStatement}
|
|
|
152
156
|
};`);
|
|
153
157
|
} else {
|
|
154
158
|
const pathParamsList = params.filter((p) => p.in === "path");
|
|
159
|
+
const queryParamsList = params.filter((p) => p.in === "query");
|
|
155
160
|
const hasPathParams = pathParamsList.length > 0;
|
|
161
|
+
const hasQueryParams = queryParamsList.length > 0;
|
|
162
|
+
const hasAnyParams = hasPathParams || hasQueryParams;
|
|
156
163
|
let urlPath = pathUrl;
|
|
157
164
|
for (const p of pathParamsList) {
|
|
158
165
|
urlPath = urlPath.replace(`{${p.name}}`, `\${${p.name}}`);
|
|
159
166
|
}
|
|
167
|
+
const mutQsCode = hasQueryParams ? `
|
|
168
|
+
const _qs = new URLSearchParams();
|
|
169
|
+
${queryParamsList.map((p) => ` if (${p.name} !== undefined) _qs.set('${p.name}', String(${p.name}));`).join(`
|
|
170
|
+
`)}
|
|
171
|
+
const _qstr = _qs.toString();` : "";
|
|
172
|
+
const fetchUrlExpr = hasQueryParams ? `\`\${baseUrl}${urlPath}\${_qstr ? '?' + _qstr : ''}\`` : `\`\${baseUrl}${urlPath}\``;
|
|
173
|
+
const allParamNames = [
|
|
174
|
+
...pathParamsList.map((p) => p.name),
|
|
175
|
+
...queryParamsList.map((p) => p.name)
|
|
176
|
+
];
|
|
160
177
|
let inputType;
|
|
161
178
|
let mutationArg;
|
|
162
|
-
if (hasBody &&
|
|
179
|
+
if (hasBody && hasAnyParams) {
|
|
163
180
|
inputType = `${capitalize(opId)}Params & ${capitalize(opId)}Body`;
|
|
164
|
-
mutationArg = `{ ${
|
|
181
|
+
mutationArg = `{ ${allParamNames.join(", ")}, ...body }`;
|
|
165
182
|
} else if (hasBody) {
|
|
166
183
|
inputType = bodyType;
|
|
167
184
|
mutationArg = "body";
|
|
168
|
-
} else if (
|
|
185
|
+
} else if (hasAnyParams) {
|
|
169
186
|
inputType = paramsType;
|
|
170
|
-
mutationArg = `{ ${
|
|
187
|
+
mutationArg = `{ ${allParamNames.join(", ")} }`;
|
|
171
188
|
} else {
|
|
172
189
|
inputType = "void";
|
|
173
190
|
mutationArg = "";
|
|
174
191
|
}
|
|
175
|
-
const fetchBodyLines = hasBody ?
|
|
192
|
+
const fetchBodyLines = hasBody ? useFormData ? `
|
|
176
193
|
body: Object.entries(body as Record<string, unknown>).reduce((f, [k, v]) => { f.append(k, v as any); return f; }, new FormData()),` : `
|
|
177
194
|
headers: { 'Content-Type': 'application/json' },
|
|
178
195
|
body: JSON.stringify(body),` : "";
|
|
@@ -181,8 +198,8 @@ export const use${capitalize(opId)} = (
|
|
|
181
198
|
options?: UseMutationOptions<${responseType}, Error, ${inputType}>
|
|
182
199
|
) => {
|
|
183
200
|
return useMutation<${responseType}, Error, ${inputType}>({
|
|
184
|
-
mutationFn: async (${mutationArg}) => {
|
|
185
|
-
const res = await fetch(
|
|
201
|
+
mutationFn: async (${mutationArg}) => {${mutQsCode}
|
|
202
|
+
const res = await fetch(${fetchUrlExpr}, {
|
|
186
203
|
method: '${method.toUpperCase()}',${fetchBodyLines}
|
|
187
204
|
});
|
|
188
205
|
if (!res.ok) throw new Error(\`\${res.status}: \${await res.text()}\`);
|
|
@@ -268,7 +285,8 @@ async function main() {
|
|
|
268
285
|
fs.mkdirSync(output, { recursive: true });
|
|
269
286
|
}
|
|
270
287
|
fs.writeFileSync(path.join(output, "base.ts"), `/* eslint-disable */
|
|
271
|
-
|
|
288
|
+
/** Override this before making any API calls if your base URL changes at runtime */
|
|
289
|
+
export let baseUrl = '${baseUrl}';
|
|
272
290
|
`);
|
|
273
291
|
const groupNames = [];
|
|
274
292
|
for (const [groupName, { types, hooks }] of Object.entries(groups)) {
|