swaggie 1.5.3-beta.1 → 1.5.3-beta.3
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 +24 -5
- package/dist/gen/genOperations.js +6 -3
- package/dist/gen/genTypes.js +23 -1
- package/dist/swagger/typesExtractor.js +77 -3
- package/dist/types.d.ts +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -103,6 +103,7 @@ Sample configuration looks like this:
|
|
|
103
103
|
"preferAny": true,
|
|
104
104
|
"servicePrefix": "",
|
|
105
105
|
"dateFormat": "Date", // "string" | "Date"
|
|
106
|
+
"nullableStrategy": "ignore", // "ignore" | "include" | "nullableAsOptional"
|
|
106
107
|
"queryParamsSerialization": {
|
|
107
108
|
"arrayFormat": "repeat", // "repeat" | "brackets" | "indices"
|
|
108
109
|
"allowDots": true
|
|
@@ -162,6 +163,24 @@ Once you know what your backend expects, you can adjust the configuration file a
|
|
|
162
163
|
}
|
|
163
164
|
```
|
|
164
165
|
|
|
166
|
+
### Nullable Strategy
|
|
167
|
+
|
|
168
|
+
OpenAPI 3.0 allows marking fields as `nullable: true`. Swaggie provides three strategies for translating this into TypeScript, controlled by the `nullableStrategy` option:
|
|
169
|
+
|
|
170
|
+
| Value | Description |
|
|
171
|
+
| ---------------------- | ---------------------------------------------------------------------------------- |
|
|
172
|
+
| `"ignore"` (default) | `nullable: true` is ignored. Types are generated as if `nullable` was not set. |
|
|
173
|
+
| `"include"` | `nullable: true` appends `\| null` to the TypeScript type (e.g. `string \| null`). |
|
|
174
|
+
| `"nullableAsOptional"` | `nullable: true` makes the property optional (`?`) instead of adding `\| null`. |
|
|
175
|
+
|
|
176
|
+
**Examples** for a schema with `tenant: { type: 'string', nullable: true }` (required):
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
// nullableStrategy: "ignore" → tenant: string;
|
|
180
|
+
// nullableStrategy: "include" → tenant: string | null;
|
|
181
|
+
// nullableStrategy: "nullableAsOptional" → tenant?: string;
|
|
182
|
+
```
|
|
183
|
+
|
|
165
184
|
### Code Quality
|
|
166
185
|
|
|
167
186
|
> Please note that it's **recommended** to pipe Swaggie command to some prettifier like `prettier`, `biome` or `dprint` to make the generated code look not only nice, but also persistent.
|
|
@@ -294,14 +313,14 @@ function error(e) {
|
|
|
294
313
|
|
|
295
314
|
| Supported | Not supported |
|
|
296
315
|
| ------------------------------------------------------------------------------ | ----------------------------------------------- |
|
|
297
|
-
| OpenAPI 3
|
|
316
|
+
| OpenAPI 3, OpenAPI 3.1, OpenAPI 3.2 | Swagger, Open API 2.0 |
|
|
298
317
|
| `allOf`, `oneOf`, `anyOf`, `$ref` to schemas | `not` |
|
|
299
|
-
| Spec formats: `JSON`, `YAML` |
|
|
318
|
+
| Spec formats: `JSON`, `YAML` | VERY complex query params |
|
|
300
319
|
| Extensions: `x-position`, `x-name`, `x-enumNames`, `x-enum-varnames` | Multiple response types (only one will be used) |
|
|
301
320
|
| Content types: `JSON`, `text`, `multipart/form-data` | Multiple request types (only one will be used) |
|
|
302
|
-
| Content types: `application/x-www-form-urlencoded`, `application/octet-stream` | References to
|
|
303
|
-
| Different types of enum definitions
|
|
304
|
-
| Paths inheritance, comments (descriptions), nullable
|
|
321
|
+
| Content types: `application/x-www-form-urlencoded`, `application/octet-stream` | References to external spec files |
|
|
322
|
+
| Different types of enum definitions | OpenAPI callbacks |
|
|
323
|
+
| Paths inheritance, comments (descriptions), nullable, `["<TYPE>", null]` | OpenAPI webhooks |
|
|
305
324
|
| Getting documents from remote locations or as path reference (local file) | |
|
|
306
325
|
| Grouping endpoints by tags + handle gracefully duplicate operation ids | |
|
|
307
326
|
|
|
@@ -98,7 +98,7 @@ function prepareClient(
|
|
|
98
98
|
const [respObject, responseContentType] = _utils.getBestResponse.call(void 0, op);
|
|
99
99
|
const returnType = _swagger.getParameterType.call(void 0, respObject, options);
|
|
100
100
|
|
|
101
|
-
const body = getRequestBody(op.requestBody);
|
|
101
|
+
const body = getRequestBody(op.requestBody, options);
|
|
102
102
|
const queryParams = getParams(op.parameters , options, ['query']);
|
|
103
103
|
const params = getParams(op.parameters , options);
|
|
104
104
|
|
|
@@ -278,7 +278,10 @@ function prepareUrl(path) {
|
|
|
278
278
|
);
|
|
279
279
|
} exports.getParamName = getParamName;
|
|
280
280
|
|
|
281
|
-
function getRequestBody(
|
|
281
|
+
function getRequestBody(
|
|
282
|
+
reqBody,
|
|
283
|
+
options
|
|
284
|
+
) {
|
|
282
285
|
if (reqBody && 'content' in reqBody) {
|
|
283
286
|
const [bodyContent, contentType] = _utils.getBestContentType.call(void 0, reqBody);
|
|
284
287
|
const isFormData = contentType === 'form-data';
|
|
@@ -287,7 +290,7 @@ function getRequestBody(reqBody) {
|
|
|
287
290
|
return {
|
|
288
291
|
originalName: _nullishCoalesce(reqBody['x-name'], () => ( 'body')),
|
|
289
292
|
name: getParamName(_nullishCoalesce(reqBody['x-name'], () => ( 'body'))),
|
|
290
|
-
type: isFormData ? 'FormData' : _swagger.getParameterType.call(void 0, bodyContent,
|
|
293
|
+
type: isFormData ? 'FormData' : _swagger.getParameterType.call(void 0, bodyContent, options),
|
|
291
294
|
optional: !reqBody.required,
|
|
292
295
|
original: reqBody,
|
|
293
296
|
contentType,
|
package/dist/gen/genTypes.js
CHANGED
|
@@ -200,7 +200,9 @@ function renderTypeProp(
|
|
|
200
200
|
if ('description' in definition || 'title' in definition) {
|
|
201
201
|
lines.push(_jsDocs.renderComment.call(void 0, _nullishCoalesce(definition.description, () => ( definition.title))));
|
|
202
202
|
}
|
|
203
|
-
|
|
203
|
+
|
|
204
|
+
const isOptional = !required || isNullableAsOptional(definition, options);
|
|
205
|
+
const optionalMark = isOptional ? '?' : '';
|
|
204
206
|
// If prop name is not a valid identifier, we need to wrap it in quotes.
|
|
205
207
|
// We can't use getSafeIdentifier here because it will affect the data model.
|
|
206
208
|
const safePropName = _utils.escapePropName.call(void 0, propName);
|
|
@@ -209,6 +211,26 @@ function renderTypeProp(
|
|
|
209
211
|
return lines.join('\n');
|
|
210
212
|
}
|
|
211
213
|
|
|
214
|
+
/**
|
|
215
|
+
* When nullableAsOptional strategy is set, nullable properties are treated as optional.
|
|
216
|
+
* Supports both OA3.0 (nullable: true) and OA3.1 (type: ["string", "null"]).
|
|
217
|
+
* @returns True if the property should be treated as optional, false otherwise.
|
|
218
|
+
*/
|
|
219
|
+
function isNullableAsOptional(
|
|
220
|
+
definition,
|
|
221
|
+
options
|
|
222
|
+
) {
|
|
223
|
+
if ('$ref' in definition) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return (
|
|
228
|
+
options.nullableStrategy === 'nullableAsOptional' &&
|
|
229
|
+
(definition.nullable === true ||
|
|
230
|
+
(Array.isArray(definition.type) && definition.type.includes('null')))
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
212
234
|
function getMergedCompositeObjects(schema) {
|
|
213
235
|
const { allOf, oneOf, anyOf, ...safeSchema } = schema;
|
|
214
236
|
const composite = allOf || oneOf || anyOf || [];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
|
|
2
2
|
|
|
3
3
|
var _utils = require('../utils');
|
|
4
4
|
|
|
@@ -52,7 +52,15 @@ var _utils = require('../utils');
|
|
|
52
52
|
return 'null';
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
// OpenAPI 3.1 nullable: type is an array containing 'null', e.g. ["string", "null"]
|
|
56
|
+
if (Array.isArray(schema.type)) {
|
|
57
|
+
return getTypeFromOA31ArrayType(schema , options);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// OpenAPI 3.0 nullable: nullable: true
|
|
61
|
+
const isNullable = 'nullable' in schema && schema.nullable === true;
|
|
62
|
+
const strategy = _nullishCoalesce(options.nullableStrategy, () => ( 'ignore'));
|
|
63
|
+
const isNullableSuffix = isNullable && strategy === 'include' ? ' | null' : '';
|
|
56
64
|
const type = getTypeFromSchemaInternal(schema, options);
|
|
57
65
|
|
|
58
66
|
if (isNullableSuffix && type.endsWith('| null')) {
|
|
@@ -61,6 +69,58 @@ var _utils = require('../utils');
|
|
|
61
69
|
return type + isNullableSuffix;
|
|
62
70
|
} exports.getTypeFromSchema = getTypeFromSchema;
|
|
63
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Handles OpenAPI 3.1 schemas where `type` is an array (e.g. `["string", "null"]`).
|
|
74
|
+
* The presence of `"null"` in the array is the OA3.1 way of marking a field as nullable.
|
|
75
|
+
* Respects `nullableStrategy` the same way as OA3.0 `nullable: true`.
|
|
76
|
+
*/
|
|
77
|
+
function getTypeFromOA31ArrayType(
|
|
78
|
+
schema,
|
|
79
|
+
options
|
|
80
|
+
) {
|
|
81
|
+
const unknownType = options.preferAny ? 'any' : 'unknown';
|
|
82
|
+
const types = schema.type ;
|
|
83
|
+
const isNullable = types.includes('null');
|
|
84
|
+
const nonNullTypes = types.filter((t) => t !== 'null');
|
|
85
|
+
|
|
86
|
+
// Build the base type from the non-null types
|
|
87
|
+
let baseType;
|
|
88
|
+
if (nonNullTypes.length === 0) {
|
|
89
|
+
baseType = 'null';
|
|
90
|
+
} else if (nonNullTypes.length === 1) {
|
|
91
|
+
// Synthesize a single-type schema to reuse existing resolution logic
|
|
92
|
+
const singleTypeSchema = { ...schema, type: nonNullTypes[0] } ;
|
|
93
|
+
baseType = getTypeFromSchemaInternal(singleTypeSchema, options);
|
|
94
|
+
} else {
|
|
95
|
+
// Multiple non-null types — resolve each independently and join as a union
|
|
96
|
+
baseType = nonNullTypes
|
|
97
|
+
.map((t) => getTypeFromSchemaInternal({ ...schema, type: t } , options))
|
|
98
|
+
.join(' | ');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!isNullable) {
|
|
102
|
+
return baseType || unknownType;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// All types were 'null' — just return 'null' regardless of strategy
|
|
106
|
+
if (nonNullTypes.length === 0) {
|
|
107
|
+
return 'null';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const strategy = _nullishCoalesce(options.nullableStrategy, () => ( 'ignore'));
|
|
111
|
+
if (strategy === 'include') {
|
|
112
|
+
// We don't want multiple nulls in the type string
|
|
113
|
+
if (baseType.endsWith('| null')) {
|
|
114
|
+
return baseType;
|
|
115
|
+
}
|
|
116
|
+
return `${baseType} | null`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 'ignore' and 'nullableAsOptional' — null is stripped from the type itself
|
|
120
|
+
// (for nullableAsOptional, the optionality is applied at the property level in genTypes.ts)
|
|
121
|
+
return baseType || unknownType;
|
|
122
|
+
}
|
|
123
|
+
|
|
64
124
|
function getTypeFromSchemaInternal(
|
|
65
125
|
schema,
|
|
66
126
|
options
|
|
@@ -102,9 +162,23 @@ function getNestedTypeFromSchema(
|
|
|
102
162
|
schema,
|
|
103
163
|
options
|
|
104
164
|
) {
|
|
105
|
-
|
|
165
|
+
const strategy = _nullishCoalesce(options.nullableStrategy, () => ( 'ignore'));
|
|
166
|
+
|
|
167
|
+
// OA3.0 nullable: true
|
|
168
|
+
const isOA30NullableAndActive =
|
|
169
|
+
'nullable' in schema && schema.nullable === true && strategy === 'include';
|
|
170
|
+
|
|
171
|
+
// OA3.1 nullable: type array containing 'null'
|
|
172
|
+
const isOA31NullableAndActive =
|
|
173
|
+
'type' in schema &&
|
|
174
|
+
Array.isArray(schema.type) &&
|
|
175
|
+
schema.type.includes('null') &&
|
|
176
|
+
strategy === 'include';
|
|
177
|
+
|
|
178
|
+
if (isOA30NullableAndActive || isOA31NullableAndActive || ('enum' in schema && schema.enum)) {
|
|
106
179
|
return `(${getTypeFromSchema(schema, options)})`;
|
|
107
180
|
}
|
|
181
|
+
|
|
108
182
|
return getTypeFromSchema(schema, options);
|
|
109
183
|
}
|
|
110
184
|
|
package/dist/types.d.ts
CHANGED
|
@@ -20,6 +20,13 @@ export interface ClientOptions {
|
|
|
20
20
|
servicePrefix?: string;
|
|
21
21
|
/** How date should be handled. It does not do any special serialization */
|
|
22
22
|
dateFormat?: DateSupport;
|
|
23
|
+
/**
|
|
24
|
+
* Controls how OpenAPI 'nullable' is translated into TypeScript types. Default: 'ignore'.
|
|
25
|
+
* 'include' - 'nullable: true' appends `| null` to the TypeScript type (e.g. `string | null`).
|
|
26
|
+
* 'nullableAsOptional' - 'nullable: true' makes the property optional (`?`) instead of adding `| null`.
|
|
27
|
+
* 'ignore' - 'nullable: true' is ignored.
|
|
28
|
+
*/
|
|
29
|
+
nullableStrategy?: NullableStrategy;
|
|
23
30
|
/** Options for query parameters serialization */
|
|
24
31
|
queryParamsSerialization: QueryParamsSerializationOptions;
|
|
25
32
|
/** Offers ability to adjust the OpenAPI spec before it is processed */
|
|
@@ -42,6 +49,7 @@ export type Template = 'axios' | 'fetch' | 'ng1' | 'ng2' | 'swr-axios' | 'xior'
|
|
|
42
49
|
export type HttpMethod = 'get' | 'put' | 'post' | 'delete' | 'options' | 'head' | 'patch';
|
|
43
50
|
export type DateSupport = 'string' | 'Date';
|
|
44
51
|
export type ArrayFormat = 'indices' | 'repeat' | 'brackets';
|
|
52
|
+
export type NullableStrategy = 'include' | 'nullableAsOptional' | 'ignore';
|
|
45
53
|
/**
|
|
46
54
|
* Local type that represent Operation as understood by Swaggie
|
|
47
55
|
**/
|