elysia-openapi-codegen 0.1.7 → 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.
Files changed (3) hide show
  1. package/README.md +10 -3
  2. package/dist/index.js +26 -8
  3. package/package.json +3 -2
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 './generated';
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: 'John Doe',
107
- email: 'john@example.com'
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 && hasPathParams) {
179
+ if (hasBody && hasAnyParams) {
163
180
  inputType = `${capitalize(opId)}Params & ${capitalize(opId)}Body`;
164
- mutationArg = `{ ${pathParamsList.map((p) => p.name).join(", ")}, ...body }`;
181
+ mutationArg = `{ ${allParamNames.join(", ")}, ...body }`;
165
182
  } else if (hasBody) {
166
183
  inputType = bodyType;
167
184
  mutationArg = "body";
168
- } else if (hasPathParams) {
185
+ } else if (hasAnyParams) {
169
186
  inputType = paramsType;
170
- mutationArg = `{ ${pathParamsList.map((p) => p.name).join(", ")} }`;
187
+ mutationArg = `{ ${allParamNames.join(", ")} }`;
171
188
  } else {
172
189
  inputType = "void";
173
190
  mutationArg = "";
174
191
  }
175
- const fetchBodyLines = hasBody ? isMultipart ? `
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(\`\${baseUrl}${urlPath}\`, {
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
- export const baseUrl = '${baseUrl}';
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)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elysia-openapi-codegen",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "author": "Khantamir mkhantamir77@gmail.com",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,7 +36,8 @@
36
36
  ],
37
37
  "license": "MIT",
38
38
  "scripts": {
39
- "build": "bun build index.ts --outfile dist/index.js --target node"
39
+ "build": "bun build index.ts --outfile dist/index.js --target node",
40
+ "publish": "bun run build && npm publish --access public"
40
41
  },
41
42
  "type": "module"
42
43
  }