create-swagger-client 0.1.3 → 0.1.5

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/LICENSE +21 -0
  2. package/index.mjs +71 -61
  3. package/package.json +1 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cuong Nguyen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/index.mjs CHANGED
@@ -20,67 +20,76 @@ var isUrl = (str) => {
20
20
  }
21
21
  };
22
22
  async function generate() {
23
- if (!source)
24
- return;
23
+ if (!source) return;
25
24
  if (isUrl(source) === false) {
26
25
  source = resolve(process.cwd(), source);
27
26
  }
28
27
  const spinner = ora(`Generating API client from ${source}...`).start();
29
28
  const ast = await openapiTS(source);
30
29
  const contents = astToString(ast);
31
- const project = new tsMorph.Project;
32
- const sourceFile = project.createSourceFile(resolve(process.cwd(), outPut), contents, {
33
- overwrite: true
34
- });
30
+ const project = new tsMorph.Project();
31
+ const sourceFile = project.createSourceFile(
32
+ resolve(process.cwd(), outPut),
33
+ contents,
34
+ {
35
+ overwrite: true,
36
+ },
37
+ );
35
38
  sourceFile.addTypeAlias({
36
39
  name: "RestMethod",
37
40
  isExported: true,
38
- type: '"get" | "post" | "put" | "delete" | "patch"'
41
+ type: '"get" | "post" | "put" | "delete" | "patch"',
39
42
  });
40
43
  sourceFile.addTypeAlias({
41
44
  name: "KeyPaths",
42
45
  isExported: true,
43
- type: "keyof paths"
46
+ type: "keyof paths",
44
47
  });
45
48
  sourceFile.addTypeAlias({
46
49
  name: "ExtractPathParams",
47
50
  isExported: true,
48
- typeParameters: ["T extends KeyPaths", "K extends RestMethod"],
49
- type: "paths[T][K] extends { parameters: { path?: infer P } } ? P : never"
51
+ typeParameters: ["K extends KeyPaths", "M extends RestMethod"],
52
+ type: "paths[K][M] extends { parameters: { path?: infer P } } ? P : never",
50
53
  });
51
54
  sourceFile.addTypeAlias({
52
55
  name: "ExtractQueryParams",
53
56
  isExported: true,
54
- typeParameters: ["T extends KeyPaths", "K extends RestMethod"],
55
- type: "paths[T][K] extends { parameters: { query?: infer Q } } ? Q : never"
57
+ typeParameters: ["K extends KeyPaths", "M extends RestMethod"],
58
+ type: "paths[K][M] extends { parameters: { query?: infer Q } } ? Q : never",
56
59
  });
57
60
  sourceFile.addTypeAlias({
58
61
  name: "ExtractHeaderParams",
59
62
  isExported: true,
60
- typeParameters: ["T extends KeyPaths", "K extends RestMethod"],
61
- type: "paths[T][K] extends { parameters: { header?: infer H } } ? H : never"
63
+ typeParameters: ["K extends KeyPaths", "M extends RestMethod"],
64
+ type: "paths[K][M] extends { parameters: { header?: infer H } } ? H : never",
62
65
  });
63
66
  sourceFile.addTypeAlias({
64
67
  name: "ExtractBody",
65
68
  isExported: true,
66
- typeParameters: ["T extends KeyPaths", "K extends RestMethod"],
67
- type: `paths[T][K] extends {
69
+ typeParameters: ["K extends KeyPaths", "M extends RestMethod"],
70
+ type: `paths[K][M] extends {
68
71
  requestBody: { content: { "application/json": infer B } };
69
72
  }
70
73
  ? B
71
- : never`
74
+ : never`,
72
75
  });
73
76
  sourceFile.addTypeAlias({
74
77
  name: "APIResponse",
75
78
  isExported: true,
76
- typeParameters: ["T extends KeyPaths", "K extends RestMethod"],
77
- type: `paths[T][K] extends {
78
- responses:
79
- | { content: { "application/json": infer R } }
80
- | { [code: number]: { content: { "application/json": infer R } } };
81
- }
82
- ? R
83
- : unknown`
79
+ typeParameters: ["K extends KeyPaths", "M extends RestMethod"],
80
+ type: `paths[K][M] extends { responses: infer R }
81
+ ? R extends {
82
+ content: { "application/json": infer C };
83
+ }
84
+ ? C
85
+ : R extends {
86
+ [status: number]: infer S;
87
+ }
88
+ ? S extends { content: { "application/json": infer C } }
89
+ ? C
90
+ : never
91
+ : never
92
+ : never;`,
84
93
  });
85
94
  sourceFile.addTypeAlias({
86
95
  name: "ApiPayload",
@@ -91,7 +100,7 @@ async function generate() {
91
100
  query?: ExtractQueryParams<T, K>;
92
101
  body?: K extends "post" | "put" | "patch" ? ExtractBody<T, K> : never;
93
102
  headers?: ExtractHeaderParams<T, K>;
94
- }`
103
+ }`,
95
104
  });
96
105
  sourceFile.addTypeAlias({
97
106
  name: "ApiClientType",
@@ -101,14 +110,14 @@ async function generate() {
101
110
  path: T,
102
111
  payload?: ApiPayload<T, K>,
103
112
  ) => Promise<APIResponse<T, K>>;
104
- }`
113
+ }`,
105
114
  });
106
115
  sourceFile.addTypeAlias({
107
116
  name: "TypePaths",
108
117
  typeParameters: ["T extends RestMethod"],
109
118
  type: `{
110
119
  [K in KeyPaths]: paths[K] extends { [M in T]: unknown } ? K : never;
111
- }[KeyPaths]`
120
+ }[KeyPaths]`,
112
121
  });
113
122
  sourceFile.addClass({
114
123
  name: "RestApiClient",
@@ -121,10 +130,10 @@ async function generate() {
121
130
  name: "option",
122
131
  type: "RequestInit",
123
132
  hasQuestionToken: true,
124
- scope: tsMorph.Scope.Private
125
- }
126
- ]
127
- }
133
+ scope: tsMorph.Scope.Private,
134
+ },
135
+ ],
136
+ },
128
137
  ],
129
138
  methods: [
130
139
  {
@@ -133,7 +142,7 @@ async function generate() {
133
142
  isAsync: true,
134
143
  parameters: [
135
144
  { name: "input", type: "RequestInfo" },
136
- { name: "init", type: "RequestInit", hasQuestionToken: true }
145
+ { name: "init", type: "RequestInit", hasQuestionToken: true },
137
146
  ],
138
147
  statements: `const headers = {
139
148
  "Content-Type": "application/json",
@@ -147,7 +156,7 @@ async function generate() {
147
156
  \`API request failed: \${response.status} \${response.statusText} - \${errorBody}\`,
148
157
  );
149
158
  }
150
- return response.json();`
159
+ return response.json();`,
151
160
  },
152
161
  {
153
162
  name: "request",
@@ -158,8 +167,8 @@ async function generate() {
158
167
  {
159
168
  name: "init",
160
169
  type: "ApiPayload<P, M>",
161
- initializer: "{} as ApiPayload<P, M>"
162
- }
170
+ initializer: "{} as ApiPayload<P, M>",
171
+ },
163
172
  ],
164
173
  returnType: "Promise<APIResponse<P, M>>",
165
174
  statements: `const url = new URL(this.basePath + String(path));
@@ -179,7 +188,7 @@ async function generate() {
179
188
 
180
189
  return this.fetcher(url.toString(), requestInit) as Promise<
181
190
  APIResponse<P, M>
182
- >;`
191
+ >;`,
183
192
  },
184
193
  {
185
194
  name: "get",
@@ -190,11 +199,11 @@ async function generate() {
190
199
  {
191
200
  name: "payload",
192
201
  type: 'ApiPayload<T, "get">',
193
- hasQuestionToken: true
194
- }
202
+ hasQuestionToken: true,
203
+ },
195
204
  ],
196
205
  returnType: 'Promise<APIResponse<T, "get">>',
197
- statements: 'return this.request("get", path, payload);'
206
+ statements: 'return this.request("get", path, payload);',
198
207
  },
199
208
  {
200
209
  name: "post",
@@ -205,11 +214,11 @@ async function generate() {
205
214
  {
206
215
  name: "payload",
207
216
  type: 'ApiPayload<T, "post">',
208
- hasQuestionToken: true
209
- }
217
+ hasQuestionToken: true,
218
+ },
210
219
  ],
211
220
  returnType: 'Promise<APIResponse<T, "post">>',
212
- statements: 'return this.request("post", path, payload);'
221
+ statements: 'return this.request("post", path, payload);',
213
222
  },
214
223
  {
215
224
  name: "put",
@@ -220,11 +229,11 @@ async function generate() {
220
229
  {
221
230
  name: "payload",
222
231
  type: 'ApiPayload<T, "put">',
223
- hasQuestionToken: true
224
- }
232
+ hasQuestionToken: true,
233
+ },
225
234
  ],
226
235
  returnType: 'Promise<APIResponse<T, "put">>',
227
- statements: 'return this.request("put", path, payload);'
236
+ statements: 'return this.request("put", path, payload);',
228
237
  },
229
238
  {
230
239
  name: "delete",
@@ -235,11 +244,11 @@ async function generate() {
235
244
  {
236
245
  name: "payload",
237
246
  type: 'ApiPayload<T, "delete">',
238
- hasQuestionToken: true
239
- }
247
+ hasQuestionToken: true,
248
+ },
240
249
  ],
241
250
  returnType: 'Promise<APIResponse<T, "delete">>',
242
- statements: 'return this.request("delete", path, payload);'
251
+ statements: 'return this.request("delete", path, payload);',
243
252
  },
244
253
  {
245
254
  name: "patch",
@@ -250,18 +259,18 @@ async function generate() {
250
259
  {
251
260
  name: "payload",
252
261
  type: 'ApiPayload<T, "patch">',
253
- hasQuestionToken: true
254
- }
262
+ hasQuestionToken: true,
263
+ },
255
264
  ],
256
265
  returnType: 'Promise<APIResponse<T, "patch">>',
257
- statements: 'return this.request("patch", path, payload);'
266
+ statements: 'return this.request("patch", path, payload);',
258
267
  },
259
268
  {
260
269
  name: "buildPathUrl",
261
270
  scope: tsMorph.Scope.Private,
262
271
  parameters: [
263
272
  { name: "basePath", type: "string" },
264
- { name: "pathParams", type: "unknown", hasQuestionToken: true }
273
+ { name: "pathParams", type: "unknown", hasQuestionToken: true },
265
274
  ],
266
275
  returnType: "string",
267
276
  statements: `let pathname = basePath;
@@ -271,27 +280,27 @@ async function generate() {
271
280
  encodeURIComponent(String(params[key])),
272
281
  );
273
282
  }
274
- return pathname;`
283
+ return pathname;`,
275
284
  },
276
285
  {
277
286
  name: "prepareBody",
278
287
  scope: tsMorph.Scope.Private,
279
288
  parameters: [
280
289
  { name: "method", type: "RestMethod" },
281
- { name: "body", type: "unknown", hasQuestionToken: true }
290
+ { name: "body", type: "unknown", hasQuestionToken: true },
282
291
  ],
283
292
  returnType: "string | undefined",
284
293
  statements: `if (body && ["post", "put", "patch"].includes(method)) {
285
294
  return JSON.stringify(body);
286
295
  }
287
- return undefined;`
296
+ return undefined;`,
288
297
  },
289
298
  {
290
299
  name: "appendQueryParams",
291
300
  scope: tsMorph.Scope.Private,
292
301
  parameters: [
293
302
  { name: "url", type: "URL" },
294
- { name: "queryParams", type: "unknown", hasQuestionToken: true }
303
+ { name: "queryParams", type: "unknown", hasQuestionToken: true },
295
304
  ],
296
305
  returnType: "void",
297
306
  statements: `if (queryParams != null) {
@@ -301,15 +310,16 @@ async function generate() {
301
310
  url.searchParams.append(key, String(value));
302
311
  }
303
312
  }
304
- }`
305
- }
306
- ]
313
+ }`,
314
+ },
315
+ ],
307
316
  });
308
317
  await sourceFile.formatText();
309
318
  await project.save();
310
319
  spinner.stopAndPersist({
311
320
  symbol: "✔",
312
- text: `API client generated at ${resolve(process.cwd(), outPut)}`
321
+ text: `API client generated at ${resolve(process.cwd(), outPut)}`,
313
322
  });
314
323
  }
324
+
315
325
  generate();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-swagger-client",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Generate fully type-safe REST API clients from OpenAPI/Swagger specifications",
5
5
  "main": "./index.mjs",
6
6
  "module": "./index.mjs",