swaggie 1.0.0 → 1.0.2-test.1

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 CHANGED
@@ -10,15 +10,27 @@
10
10
  ![npm downloads](https://img.shields.io/npm/dw/swaggie.svg)
11
11
  ![npm bundle size](https://img.shields.io/bundlephobia/minzip/swaggie.svg)
12
12
  ![npm install size](https://packagephobia.now.sh/badge?p=swaggie)
13
+ ![Hackage Dependencies](https://img.shields.io/hackage-deps/v/swaggie)
13
14
 
14
- <!-- ![Dependencies](https://img.shields.io/david/yhnavein/swaggie.svg) -->
15
-
16
- Generate ES6 or Typescript code from an OpenAPI 3.0 spec, so that accessing REST API resources from the client code is less error-prone, static-typed and just easier to use long-term.
15
+ Generate Typescript code from an OpenAPI 3.0 document, so that accessing REST API resources from the client code is less error-prone, static-typed and just easier to use long-term.
17
16
 
18
17
  You can take a look at the [Examples section](#example) down below.
19
18
 
20
19
  Project is based and inspired by [OpenApi Client](https://github.com/mikestead/openapi-client).
21
20
 
21
+ ### Features
22
+
23
+ - Generate TypeScript code from OpenAPI 3.0 spec
24
+ - Supports `fetch`, `axios`, `xior`, `SWR + axios`, `Angular 1`, `Angular 2+` templates. It's flexible.
25
+ - Possible to create your own template that works with your existing codebase
26
+ - It's a dev tool that generates code, so it's not a runtime dependency
27
+ - Support for `allOf`, `oneOf`, `anyOf` and `$ref` in schemas
28
+ - Support for different types of enums
29
+ - Support for different content types
30
+ - JSDoc comments for generated code
31
+ - Small library size and very small and tree-shakable output that is all placed in one file
32
+ - OpenAPI 3.1 is partially supported (mostly enums, more to come)
33
+
22
34
  ## Install
23
35
 
24
36
  In your project
@@ -31,7 +43,7 @@ Or globally to run CLI from anywhere
31
43
 
32
44
  ## OpenAPI versions
33
45
 
34
- Swaggie from version 1.0 supports OpenAPI 3.0 (and some features of 3.1). Swagger or OpenAPI v2 documents are not supported anymore, but you have few options how to deal with it:
46
+ Swaggie from version 1.0 supports OpenAPI 3.0 (and some features of 3.1). Swagger or OpenAPI 2.0 documents are not supported anymore, but you have few options how to deal with it:
35
47
 
36
48
  - **(preferred)** From your backend server generate OpenAPI 3.0 spec instead of version 2 (samples are updated to use OpenAPI 3.0)
37
49
  - Convert your OpenAPI 2.0 spec to 3.0 using [swagger2openapi](https://www.npmjs.com/package/swagger2openapi) tool (or something similar)
@@ -65,7 +77,7 @@ Options:
65
77
  Sample CLI usage using Swagger's Pet Store:
66
78
 
67
79
  ```bash
68
- swaggie -s https://petstore3.swagger.io/api/v3/openapi.json -o ./client/petstore/
80
+ swaggie -s https://petstore3.swagger.io/api/v3/openapi.json -o ./client/petstore.ts
69
81
  ```
70
82
 
71
83
  `swaggie` outputs TypeScript that is somehow formatted, but it's far from perfect. You can adjust the generated code by prettifying output using your preferred beautify tool using your repo's styling guidelines. For example involving `prettier` looks like this:
@@ -104,17 +116,18 @@ Sample configuration looks like this:
104
116
  The following templates are bundled with Swaggie:
105
117
 
106
118
  ```
107
- axios Default template. Recommended for React / Vue / similar frameworks. Uses axios
108
- xior Lightweight and modern alternative to axios. Uses [xior](https://github.com/suhaotian/xior#intro)
109
- swr-axios Template that embraces SRW for GET requests and as a fallback uses axios.
110
- fetch Template similar to axios, but with fetch API instead. Recommended for React / Vue / similar frameworks
111
- ng1 Template for Angular 1 (this is for the old one)
112
- ng2 Template for Angular 2+ (uses HttpClient, InjectionTokens, etc)
119
+ axios Default template. Recommended for React / Vue / similar frameworks. Uses axios
120
+ xior Lightweight and modern alternative to axios. Uses [xior](https://github.com/suhaotian/xior#intro)
121
+ swr-axios Template that embraces SRW for GET requests and as a fallback uses axios.
122
+ tanstack-query Template that uses TanStack Query for GET requests and as a fallback uses xior.
123
+ fetch Template similar to axios, but with fetch API instead. Recommended for React / Vue / similar frameworks
124
+ ng1 Template for Angular 1 (this is for the old one)
125
+ ng2 Template for Angular 2+ (uses HttpClient, InjectionTokens, etc)
113
126
  ```
114
127
 
115
128
  If you want to use your own template, you can use the path to your template for the `-t` parameter:
116
129
 
117
- ```
130
+ ```bash
118
131
  swaggie -s https://petstore3.swagger.io/api/v3/openapi.json -o ./client/petstore --template ./my-swaggie-template/
119
132
  ```
120
133
 
@@ -159,13 +172,13 @@ Once you know what your backend expects, you can adjust the configuration file a
159
172
 
160
173
  [prettier](https://prettier.io/) - the most popular one
161
174
 
162
- ```sh
175
+ ```bash
163
176
  prettier ./FILE_PATH.ts --write
164
177
  ```
165
178
 
166
179
  [biome](https://biomejs.dev) - the super fast one
167
180
 
168
- ```sh
181
+ ```bash
169
182
  biome check ./FILE_PATH.ts --apply-unsafe
170
183
  ```
171
184
 
@@ -235,44 +248,6 @@ You might wonder how to set up server to fully utilize Swaggie's features. For t
235
248
 
236
249
  Server is not necessary to use Swaggie. Swaggie cares only about the JSON/yaml file with the Open API spec, but for your development purpose you might want to have a server that can serve this file automatically from the actual endpoints.
237
250
 
238
- ## Competitors
239
-
240
- If you are familiar with the client-code generators for the Swagger / OpenAPI standards then you might wonder why `swaggie` is better than existing tools. I compiled a quick comparison with other tools below:
241
-
242
- ### Swaggie
243
-
244
- - Fast and small ![swaggie size](https://packagephobia.now.sh/badge?p=swaggie)
245
- - Lightweight and easy to start
246
- - Easy to contribute to, custom templates
247
- - Flexible, suits well in the existing apps
248
- - Generates REST clients and all models
249
- - Supports different templates (like `axios`, `fetch`, `xior`, `swr-axios`, `ng1`, `ng2`)
250
- - Written in TypeScript
251
- - Generates only one file with everything you need inside
252
-
253
- ### [NSwag](https://github.com/RicoSuter/NSwag)
254
-
255
- - Slow and big ![nswag size](https://packagephobia.now.sh/badge?p=nswag)
256
- - Complicated templates, not easy to contribute to
257
- - Enforces usage of other tools and architecture
258
- - Generates more boilerplate code
259
- - Written in .NET, require .NET to execute, although published to npm as well
260
- - Many more features (but mostly for .NET apps), client generation is just a part of it
261
-
262
- ### [Hey API](https://heyapi.vercel.app)
263
-
264
- - Fast and small ![nswag size](https://packagephobia.now.sh/badge?p=%40hey-api%2Fopenapi-ts)
265
- - No flexibility, other clients are discouraged from use
266
- - Generates a lot of code and multiple files
267
- - Written in TypeScript
268
-
269
- ### [Kiota](https://learn.microsoft.com/en-us/openapi/kiota/)
270
-
271
- - A lot of boilerplate code and many files
272
- - Written in .NET, requires .NET to execute, published to NuGet
273
- - Not flexible at all - you need to use their architecture in your code
274
- - Looks like an enterprise solution with many configuration options
275
-
276
251
  ## Using Swaggie programmatically
277
252
 
278
253
  ```javascript
@@ -297,7 +272,7 @@ function error(e) {
297
272
 
298
273
  | Supported | Not supported |
299
274
  | ------------------------------------------------------------------------------ | ------------------------------------------ |
300
- | OpenAPI 3 | Swagger 2 |
275
+ | OpenAPI 3 | Swagger, Open API 2.0 |
301
276
  | `allOf`, `oneOf`, `anyOf`, `$ref` to schemas | `not` |
302
277
  | Spec formats: `JSON`, `YAML` | Very complex query params |
303
278
  | Extensions: `x-position`, `x-name`, `x-enumNames`, `x-enum-varnames` | Multiple response types (one will be used) |
@@ -307,3 +282,10 @@ function error(e) {
307
282
  | Paths inheritance, comments (descriptions) | |
308
283
  | Getting documents from remote locations or as path reference (local file) | |
309
284
  | Grouping endpoints by tags + handle gracefully duplicate operation ids | |
285
+
286
+ ## Used by
287
+
288
+ <div style="display: flex; gap: 1rem;">
289
+ <a href="https://www.britishcouncil.org"><img alt="British Council" src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/e1/BritishCouncil.png/320px-BritishCouncil.png" style="height: 50px;" /></a>
290
+ <a href="https://kpmg.com/"><img alt="KPMG" src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/31/KPMG.svg/320px-KPMG.svg.png" style="height: 50px;" /></a>
291
+ </div>
package/dist/types.d.ts CHANGED
@@ -29,7 +29,7 @@ export interface FullAppOptions extends ClientOptions {
29
29
  /** Path to the configuration file that contains actual config to be used */
30
30
  config?: string;
31
31
  }
32
- export type Template = 'axios' | 'fetch' | 'ng1' | 'ng2' | 'swr-axios' | 'xior';
32
+ export type Template = 'axios' | 'fetch' | 'ng1' | 'ng2' | 'swr-axios' | 'xior' | 'tanstack-query';
33
33
  export type HttpMethod = 'get' | 'put' | 'post' | 'delete' | 'options' | 'head' | 'patch';
34
34
  export type DateSupport = 'string' | 'Date';
35
35
  export type ArrayFormat = 'indices' | 'repeat' | 'brackets';
@@ -1,4 +1,3 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _createStarExport(obj) { Object.keys(obj) .filter((key) => key !== "default" && key !== "__esModule") .forEach((key) => { if (exports.hasOwnProperty(key)) { return; } Object.defineProperty(exports, key, {enumerable: true, configurable: true, get: () => obj[key]}); }); }var _utils = require('./utils'); _createStarExport(_utils);
2
2
  var _documentLoader = require('./documentLoader'); _createStarExport(_documentLoader);
3
- var _testutils = require('./test.utils'); _createStarExport(_testutils);
4
3
  var _templateManager = require('./templateManager'); _createStarExport(_templateManager);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swaggie",
3
- "version": "1.0.0",
3
+ "version": "1.0.2-test.1",
4
4
  "description": "Generate TypeScript REST client code from an OpenAPI spec",
5
5
  "author": {
6
6
  "name": "Piotr Dabrowski",
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "scripts": {
27
27
  "build": "sucrase ./src -d ./dist --transforms typescript,imports && npm run rm-tests && npm run types",
28
- "rm-tests": "find dist/ -name '*.spec.js' -type f -delete",
28
+ "rm-tests": "find dist/ \\( -name '*.spec.js' -o -name 'types.js' \\) -type f -delete",
29
29
  "types": "tsc src/types.ts --outDir dist/ --declaration --emitDeclarationOnly && cp test/index.d.ts ./dist/",
30
30
  "test": "mocha",
31
31
  "coverage": "npx nyc -r text -r json-summary mocha"
@@ -51,21 +51,21 @@
51
51
  ],
52
52
  "dependencies": {
53
53
  "case": "^1.6.3",
54
- "commander": "^12.1.0",
55
- "eta": "^3.4.0",
54
+ "commander": "^13.1.0",
55
+ "eta": "^3.5.0",
56
56
  "js-yaml": "^4.1.0",
57
57
  "nanocolors": "^0.2.0",
58
- "undici": "^6.19.2"
58
+ "undici": "^7.3.0"
59
59
  },
60
60
  "devDependencies": {
61
- "@types/chai": "4.3.16",
61
+ "@types/chai": "5.0.1",
62
62
  "@types/js-yaml": "4.0.9",
63
- "@types/mocha": "10.0.7",
64
- "@types/node": "20.14.9",
65
- "chai": "4.4.1",
66
- "mocha": "10.6.0",
63
+ "@types/mocha": "10.0.10",
64
+ "@types/node": "22.13.1",
65
+ "chai": "5.1.2",
66
+ "mocha": "11.1.0",
67
67
  "openapi-types": "^12.1.3",
68
68
  "sucrase": "3.35.0",
69
- "typescript": "5.5.3"
69
+ "typescript": "5.7.3"
70
70
  }
71
71
  }
File without changes
@@ -0,0 +1,25 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ //----------------------
4
+ // <auto-generated>
5
+ // Generated using Swaggie (https://github.com/yhnavein/swaggie)
6
+ // Please avoid doing any manual changes in this file
7
+ // </auto-generated>
8
+ //----------------------
9
+ // ReSharper disable InconsistentNaming
10
+ // deno-lint-ignore-file
11
+
12
+ import xior, { type XiorResponse, type XiorRequestConfig, encodeParams } from "xior";
13
+ import { QueryClient, type UndefinedInitialDataOptions, useQuery } from '@tanstack/react-query';
14
+
15
+ export const queryClient = new QueryClient();
16
+
17
+ export const http = xior.create({
18
+ baseURL: '<%= it.baseUrl || '' %>',
19
+ paramsSerializer: (params) =>
20
+ encodeParams(params, true, null, {
21
+ allowDots: <%= it.allowDots %>,
22
+ arrayFormat: '<%= it.arrayFormat %>',
23
+ }),
24
+ });
25
+
@@ -0,0 +1,22 @@
1
+ export const <%= it.camelCaseName %>Client = {
2
+ <% it.operations.forEach((operation) => { %>
3
+ <%~ include('operation.ejs', operation); %>
4
+
5
+ <% }); %>
6
+ };
7
+
8
+
9
+ <% var getOperations = it.operations.filter((o) => o.method === 'GET');
10
+ if(getOperations.length > 0) { %>
11
+ <% getOperations.forEach((operation) => {
12
+ var opName = operation.name;
13
+ if(opName.toLowerCase().startsWith("get")) {
14
+ opName = opName.substring(3);
15
+ }
16
+ opName[0] = opName[0].toUpperCase();
17
+ var customName = "use" + it.clientName + opName;
18
+ var queryOperation = Object.assign({ rqOpName: customName, opKey: it.clientName + opName, clientName: it.camelCaseName }, operation); %>
19
+ <%~ include('queryOperation.ejs', queryOperation); %>
20
+
21
+ <% }); %>
22
+ <% } %>
@@ -0,0 +1,44 @@
1
+ /**
2
+ <% it.parameters.forEach((parameter) => { %>
3
+ * @param <%= parameter.name %> <%= parameter.optional ? '(optional)' : '' %> <%= parameter.name !== parameter.originalName ? `(API name: ${parameter.originalName})` : '' %>
4
+
5
+ <% }); -%>
6
+ */
7
+ <%= it.name %>(<% it.parameters.forEach((parameter) => { %>
8
+ <%= parameter.name %>: <%~ parameter.type %> <%= parameter.optional ? '| null | undefined' : '' %>,
9
+ <% }); %>
10
+ $config?: XiorRequestConfig
11
+ ): Promise<XiorResponse<<%~ it.returnType %>>> {
12
+ const url = `<%= it.url %>`;
13
+
14
+ return http.request<<%~ it.returnType %>>({
15
+ url: url,
16
+ method: '<%= it.method %>',
17
+ <% if(it.body) { %>
18
+ <% if(it.body.contentType === 'urlencoded') { %>
19
+ data: new URLSearchParams(<%= it.body.name %> as any),
20
+ <% } else { %>
21
+ data: <%= it.body.name %>,
22
+ <% } %>
23
+ <% } %>
24
+ <% if(it.query && it.query.length > 0) { %>
25
+ params: {
26
+ <% it.query.forEach((parameter) => { %>
27
+ '<%= parameter.originalName %>': <%= parameter.name %>,
28
+ <% }); %>
29
+ },
30
+ <% } %>
31
+ <% if(it.headers && it.headers.length > 0) { %>
32
+ headers: {
33
+ <% it.headers.forEach((parameter) => { %>
34
+ <% if (parameter.value) { %>
35
+ '<%= parameter.originalName %>': '<%= parameter.value %>',
36
+ <% } else { %>
37
+ '<%= parameter.originalName %>': <%= parameter.name %>,
38
+ <% } %>
39
+ <% }); %>
40
+ },
41
+ <% } %>
42
+ ...$config,
43
+ });
44
+ },
@@ -0,0 +1,21 @@
1
+ /**
2
+ <% it.parameters.forEach((parameter) => { %>
3
+ * @param <%= parameter.name %> <%= parameter.optional ? '(optional)' : '' %> <%= parameter.name !== parameter.originalName ? `(API name: ${parameter.originalName})` : '' %>
4
+
5
+ <% }); -%>
6
+ * @param $config (optional) Additional configuration for TanStack Query
7
+ * @param $httpConfig (optional) Additional configuration for xior request (actually executes the request)
8
+ */
9
+ export function <%= it.rqOpName %>(<% it.parameters.forEach((parameter) => { %>
10
+ <%= parameter.name %>: <%~ parameter.type %> <%= parameter.optional ? ' | null | undefined' : '' %>,
11
+ <% }); %>
12
+ $config?: UndefinedInitialDataOptions<<%~ it.returnType %>, Error, <%~ it.returnType %>>,
13
+ $httpConfig?: XiorRequestConfig
14
+ ) {
15
+ return useQuery({
16
+ queryKey: ['<%= it.clientName %>', '<%= it.opKey %>', <% it.query.forEach((parameter) => { %><%= parameter.name %>, <% }); %>],
17
+ queryFn: () => <%= it.clientName %>Client.<%= it.name %>(<% it.parameters.forEach((parameter) => { %>
18
+ <%= parameter.name %>, <% }); %>$httpConfig),
19
+ ...$config
20
+ });
21
+ }
@@ -1,64 +0,0 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }var _nodefs = require('node:fs'); var _nodefs2 = _interopRequireDefault(_nodefs);
2
- var _nodepath = require('node:path'); var _nodepath2 = _interopRequireDefault(_nodepath);
3
-
4
-
5
-
6
-
7
-
8
- /**
9
- * Returns a valid OpenAPI 3.0 document with the minimal required fields.
10
- * And it allows to easily override any of the fields.
11
- */
12
- function getDocument(document = {}) {
13
- return {
14
- openapi: '3.0.0',
15
- paths: {},
16
- info: {
17
- title: 'Test',
18
- version: '1.0.0',
19
- },
20
- components: {},
21
-
22
- ...document,
23
- };
24
- } exports.getDocument = getDocument;
25
-
26
- /**
27
- * Returns a valid ClientOptions object with the minimal required fields.
28
- * And it allows to easily override any of the fields.
29
- */
30
- function getClientOptions(opts = {}) {
31
- return {
32
- src: 'http://example.com/swagger.json',
33
- out: 'output.ts',
34
- template: 'xior',
35
- queryParamsSerialization: {
36
- allowDots: true,
37
- arrayFormat: 'repeat',
38
- },
39
- ...opts,
40
- };
41
- } exports.getClientOptions = getClientOptions;
42
-
43
- /**
44
- * Utility that will set up a mock response for a given URL
45
- * @param mockAgent Agent that will be used to intercept the request
46
- * @param url Full URL to intercept
47
- * @param responseFileName Filename that contains the response. It will be loaded from the test folder
48
- */
49
- function mockRequest(mockAgent, url, responseFileName) {
50
- const urlObject = new URL(url);
51
- const mockPool = mockAgent.get(urlObject.origin);
52
-
53
- const response = _nodefs2.default.readFileSync(_nodepath2.default.join(__dirname, '..', '..', 'test', responseFileName), {
54
- encoding: 'utf-8',
55
- });
56
-
57
- // Set up the mock response
58
- mockPool
59
- .intercept({
60
- path: urlObject.pathname,
61
- method: 'GET',
62
- })
63
- .reply(200, response);
64
- } exports.mockRequest = mockRequest;