elysia-openapi-codegen 0.1.5 → 0.1.6

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 (2) hide show
  1. package/dist/index.js +43 -16
  2. package/package.json +19 -19
package/dist/index.js CHANGED
@@ -77,7 +77,7 @@ function generateTypes(spec) {
77
77
  if (!["get", "post", "put", "patch", "delete"].includes(method))
78
78
  continue;
79
79
  const op = operation;
80
- const opId = op.operationId || `${method}${pathUrl.replace(/[^a-zA-Z0-9]/g, "")}`;
80
+ const opId = toSafeIdentifier(op.operationId || `${method}${pathUrl.replace(/[^a-zA-Z0-9]/g, "")}`);
81
81
  const response = op.responses?.["200"];
82
82
  if (response?.content?.["application/json"]?.schema) {
83
83
  const responseType = resolveType(response.content["application/json"].schema);
@@ -112,7 +112,7 @@ function generateHooks(spec) {
112
112
  if (!["get", "post", "put", "patch", "delete"].includes(method))
113
113
  continue;
114
114
  const op = operation;
115
- const opId = op.operationId || `${method}${pathUrl.replace(/[^a-zA-Z0-9]/g, "")}`;
115
+ const opId = toSafeIdentifier(op.operationId || `${method}${pathUrl.replace(/[^a-zA-Z0-9]/g, "")}`);
116
116
  const hasResponse = !!op.responses?.["200"]?.content?.["application/json"]?.schema;
117
117
  const responseType = hasResponse ? `${capitalize(opId)}Response` : "any";
118
118
  const params = op.parameters || [];
@@ -121,8 +121,18 @@ function generateHooks(spec) {
121
121
  const hasBody = !!op.requestBody?.content?.["application/json"]?.schema;
122
122
  const bodyType = hasBody ? `${capitalize(opId)}Body` : "void";
123
123
  if (method === "get") {
124
- const queryParams = params.map((p) => p.name);
125
- const queryString = queryParams.length > 0 ? `?${queryParams.map((p) => `\${params?.${p} !== undefined ? '${p}=' + params.${p} : ''}`).join("&")}` : "";
124
+ const pathParamsList = params.filter((p) => p.in === "path");
125
+ const queryParamsList = params.filter((p) => p.in === "query");
126
+ let urlPath = pathUrl;
127
+ for (const p of pathParamsList) {
128
+ urlPath = urlPath.replace(`{${p.name}}`, `\${params.${p.name}}`);
129
+ }
130
+ const qsLines = queryParamsList.map((p) => ` if (params?.${p.name} !== undefined) _qs.set('${p.name}', String(params.${p.name}));`).join(`
131
+ `);
132
+ const fetchStatement = queryParamsList.length > 0 ? ` const _qs = new URLSearchParams();
133
+ ${qsLines}
134
+ const _qstr = _qs.toString();
135
+ const res = await fetch(\`\${baseUrl}${urlPath}\${_qstr ? '?' + _qstr : ''}\`);` : ` const res = await fetch(\`\${baseUrl}${urlPath}\`);`;
126
136
  hooks.push(`
127
137
  export const use${capitalize(opId)} = (
128
138
  params${hasParams ? "" : "?"}: ${paramsType},
@@ -131,7 +141,7 @@ export const use${capitalize(opId)} = (
131
141
  return useQuery<${responseType}>({
132
142
  queryKey: ['${opId}', params],
133
143
  queryFn: async () => {
134
- const res = await fetch(\`\${baseUrl}${pathUrl}${queryString}\`);
144
+ ${fetchStatement}
135
145
  if (!res.ok) throw new Error('API Error');
136
146
  return res.json();
137
147
  },
@@ -139,26 +149,40 @@ export const use${capitalize(opId)} = (
139
149
  });
140
150
  };`);
141
151
  } else {
142
- let inputType = "void";
143
- let inputArg = "";
144
- if (hasBody) {
152
+ const pathParamsList = params.filter((p) => p.in === "path");
153
+ const hasPathParams = pathParamsList.length > 0;
154
+ let urlPath = pathUrl;
155
+ for (const p of pathParamsList) {
156
+ urlPath = urlPath.replace(`{${p.name}}`, `\${${p.name}}`);
157
+ }
158
+ let inputType;
159
+ let mutationArg;
160
+ if (hasBody && hasPathParams) {
161
+ inputType = `${capitalize(opId)}Params & ${capitalize(opId)}Body`;
162
+ const pathParamNames = pathParamsList.map((p) => p.name).join(", ");
163
+ mutationArg = `{ ${pathParamNames}, ...body }`;
164
+ } else if (hasBody) {
145
165
  inputType = bodyType;
146
- inputArg = "body";
147
- } else if (hasParams) {
166
+ mutationArg = "body";
167
+ } else if (hasPathParams) {
148
168
  inputType = paramsType;
149
- inputArg = "params";
169
+ const pathParamNames = pathParamsList.map((p) => p.name).join(", ");
170
+ mutationArg = `{ ${pathParamNames} }`;
171
+ } else {
172
+ inputType = "void";
173
+ mutationArg = "";
150
174
  }
151
- const hasInput = inputType !== "void";
175
+ const fetchBodyLine = hasBody ? `
176
+ body: JSON.stringify(body),` : "";
152
177
  hooks.push(`
153
178
  export const use${capitalize(opId)} = (
154
179
  options?: UseMutationOptions<${responseType}, Error, ${inputType}>
155
180
  ) => {
156
181
  return useMutation<${responseType}, Error, ${inputType}>({
157
- mutationFn: async (${hasInput ? inputArg : ""}) => {
158
- const res = await fetch(\`\${baseUrl}${pathUrl}\`, {
182
+ mutationFn: async (${mutationArg}) => {
183
+ const res = await fetch(\`\${baseUrl}${urlPath}\`, {
159
184
  method: '${method.toUpperCase()}',
160
- headers: { 'Content-Type': 'application/json' },
161
- body: JSON.stringify(${hasInput ? inputArg : "{}"}),
185
+ headers: { 'Content-Type': 'application/json' },${fetchBodyLine}
162
186
  });
163
187
  if (!res.ok) throw new Error('API Error');
164
188
  return res.json();
@@ -175,6 +199,9 @@ export const use${capitalize(opId)} = (
175
199
  function capitalize(str) {
176
200
  return str.charAt(0).toUpperCase() + str.slice(1);
177
201
  }
202
+ function toSafeIdentifier(id) {
203
+ return id.replace(/[^a-zA-Z0-9_-]/g, "").replace(/[-_]+(.?)/g, (_, c) => c ? c.toUpperCase() : "");
204
+ }
178
205
  async function parseArgs() {
179
206
  const args = process.argv.slice(2);
180
207
  const config = {
package/package.json CHANGED
@@ -1,25 +1,30 @@
1
1
  {
2
2
  "name": "elysia-openapi-codegen",
3
- "version": "0.1.5",
4
- "description": "Generate React Query hooks and fully typed TypeScript interfaces from Elysia OpenAPI specs.",
5
- "module": "index.ts",
6
- "type": "module",
7
- "bin": {
8
- "elysia-codegen": "./dist/index.js"
9
- },
10
- "files": [
11
- "dist",
12
- "README.md"
13
- ],
3
+ "version": "0.1.6",
14
4
  "author": "Khantamir mkhantamir77@gmail.com",
15
- "license": "MIT",
16
5
  "repository": {
17
6
  "type": "git",
18
7
  "url": "https://github.com/mkhantamir/elysia-openapi-codegen.git"
19
8
  },
9
+ "main": "dist/index.js",
10
+ "module": "index.ts",
11
+ "devDependencies": {
12
+ "@types/bun": "latest"
13
+ },
14
+ "peerDependencies": {
15
+ "typescript": "^6.0.2"
16
+ },
17
+ "bin": {
18
+ "elysia-codegen": "./dist/index.js"
19
+ },
20
20
  "bugs": {
21
21
  "url": "https://github.com/mkhantamir/elysia-openapi-codegen/issues"
22
22
  },
23
+ "description": "Generate React Query hooks and fully typed TypeScript interfaces from Elysia OpenAPI specs.",
24
+ "files": [
25
+ "dist",
26
+ "README.md"
27
+ ],
23
28
  "homepage": "https://github.com/mkhantamir/elysia-openapi-codegen#readme",
24
29
  "keywords": [
25
30
  "elysia",
@@ -29,14 +34,9 @@
29
34
  "react-query",
30
35
  "tanstack-query"
31
36
  ],
32
- "devDependencies": {
33
- "@types/bun": "latest"
34
- },
35
- "peerDependencies": {
36
- "typescript": "^6.0.2"
37
- },
37
+ "license": "MIT",
38
38
  "scripts": {
39
39
  "build": "bun build index.ts --outfile dist/index.js --target node"
40
40
  },
41
- "main": "dist/index.js"
41
+ "type": "module"
42
42
  }