swaggie 1.8.0 → 1.8.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 +14 -4
- package/dist/cli.js +6 -1
- package/dist/gen/genTypes.js +45 -19
- package/dist/index.js +3 -0
- package/dist/swagger/typesExtractor.js +80 -10
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -80,7 +80,8 @@ swaggie -s https://petstore3.swagger.io/api/v3/openapi.json -o ./client/petstore
|
|
|
80
80
|
-t, --template <string> Template to use for code generation (default: "axios")
|
|
81
81
|
-m, --mode <mode> Generation mode: "full" or "schemas" (default: "full")
|
|
82
82
|
-d, --schemaStyle <style> Schema object style: "interface" or "type" (default: "interface")
|
|
83
|
-
--
|
|
83
|
+
--enumStyle <style> Enum style for plain string enums: "union" or "enum" (default: "union")
|
|
84
|
+
--preferAny Use "any" instead of "unknown" for untyped values (default: false)
|
|
84
85
|
--skipDeprecated Exclude deprecated operations from the output (default: false)
|
|
85
86
|
--servicePrefix Prefix for service names — useful when generating multiple APIs
|
|
86
87
|
--allowDots Use dot notation to serialize nested object query params
|
|
@@ -123,6 +124,7 @@ swaggie -c swaggie.config.json
|
|
|
123
124
|
"nullableStrategy": "ignore",
|
|
124
125
|
"generationMode": "full",
|
|
125
126
|
"schemaDeclarationStyle": "interface",
|
|
127
|
+
"enumDeclarationStyle": "union",
|
|
126
128
|
"queryParamsSerialization": {
|
|
127
129
|
"arrayFormat": "repeat",
|
|
128
130
|
"allowDots": true
|
|
@@ -279,6 +281,14 @@ Use `schemaDeclarationStyle` (or CLI `--schemaStyle`) to control object schema o
|
|
|
279
281
|
| `"interface"`| `export interface Tag { ... }` (default) |
|
|
280
282
|
| `"type"` | `export type Tag = { ... };` |
|
|
281
283
|
|
|
284
|
+
### Enum Declaration Style
|
|
285
|
+
|
|
286
|
+
Use `enumDeclarationStyle` (or CLI `--enumStyle`) for plain string enums:
|
|
287
|
+
- `"union"` (default): `export type Status = "active" | "disabled";`
|
|
288
|
+
- `"enum"`: `export enum Status { active = "active", disabled = "disabled" }`
|
|
289
|
+
|
|
290
|
+
Note: this applies only to plain string enums. Non-string enums are still emitted as union types.
|
|
291
|
+
|
|
282
292
|
### Parameter Modifiers
|
|
283
293
|
|
|
284
294
|
Sometimes an API spec marks a parameter as required, but your client handles it in an interceptor and you don't want it cluttering every method signature. Parameter modifiers let you override this globally without touching the spec.
|
|
@@ -360,12 +370,12 @@ Swaggie only needs a JSON or YAML OpenAPI spec file — it does not require a ru
|
|
|
360
370
|
| Supported | Not Supported |
|
|
361
371
|
| ------------------------------------------------------------------------------ | ---------------------------------------------------- |
|
|
362
372
|
| OpenAPI 3.0, 3.1, 3.2 | Swagger / OpenAPI 2.0 |
|
|
363
|
-
| `allOf`, `oneOf`, `anyOf`, `$ref
|
|
373
|
+
| `allOf`, `oneOf`, `anyOf`, `$ref`, external $refs | `not` keyword |
|
|
364
374
|
| Spec formats: JSON, YAML | Very complex query parameter structures |
|
|
365
375
|
| Extensions: `x-position`, `x-name`, `x-enumNames`, `x-enum-varnames` | Multiple response types (only the first is used) |
|
|
366
376
|
| Content types: JSON, plain text, multipart/form-data | Multiple request body types (only the first is used) |
|
|
367
|
-
| Content types: `application/x-www-form-urlencoded`, `application/octet-stream` |
|
|
368
|
-
| Various enum definition styles
|
|
377
|
+
| Content types: `application/x-www-form-urlencoded`, `application/octet-stream` | OpenAPI callbacks and webhooks |
|
|
378
|
+
| Various enum definition styles, support for additionalProperties | |
|
|
369
379
|
| Nullable types, path inheritance, JSDoc descriptions | |
|
|
370
380
|
| Remote URLs and local file paths as spec source | |
|
|
371
381
|
| Grouping by tags, graceful handling of duplicate operation IDs | |
|
package/dist/cli.js
CHANGED
|
@@ -23,6 +23,10 @@ const schemaStyleOption = new (0, _commander.Option)(
|
|
|
23
23
|
'-d, --schemaStyle <style>',
|
|
24
24
|
'Schema object declaration style'
|
|
25
25
|
).choices(['interface', 'type']);
|
|
26
|
+
const enumStyleOption = new (0, _commander.Option)(
|
|
27
|
+
'--enumStyle <style>',
|
|
28
|
+
'Enum declaration style for plain string enums'
|
|
29
|
+
).choices(['union', 'enum']);
|
|
26
30
|
|
|
27
31
|
const program = new (0, _commander.Command)();
|
|
28
32
|
program
|
|
@@ -64,7 +68,8 @@ program
|
|
|
64
68
|
)
|
|
65
69
|
.addOption(arrayFormatOption)
|
|
66
70
|
.addOption(modeOption)
|
|
67
|
-
.addOption(schemaStyleOption)
|
|
71
|
+
.addOption(schemaStyleOption)
|
|
72
|
+
.addOption(enumStyleOption);
|
|
68
73
|
|
|
69
74
|
program.parse(process.argv);
|
|
70
75
|
|
package/dist/gen/genTypes.js
CHANGED
|
@@ -70,7 +70,7 @@ function renderSchema(
|
|
|
70
70
|
return result.join('\n');
|
|
71
71
|
}
|
|
72
72
|
if ('enum' in schema) {
|
|
73
|
-
result.push(renderEnumType(safeName, schema));
|
|
73
|
+
result.push(renderEnumType(safeName, schema, options));
|
|
74
74
|
return result.join('\n');
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -83,7 +83,15 @@ function renderSchema(
|
|
|
83
83
|
if ('allOf' in schema) {
|
|
84
84
|
const types = _swagger.getRefCompositeTypes.call(void 0, schema);
|
|
85
85
|
const mergedSchema = getMergedCompositeObjects(schema);
|
|
86
|
+
const objectType = _swagger.getTypeFromSchema.call(void 0, mergedSchema, options);
|
|
86
87
|
const objectContents = generateObjectTypeContents(mergedSchema, options);
|
|
88
|
+
const hasAdditionalProperties = !!mergedSchema.additionalProperties;
|
|
89
|
+
|
|
90
|
+
if (hasAdditionalProperties) {
|
|
91
|
+
const compositeTypes = [...types, objectType].join(' & ');
|
|
92
|
+
result.push(`export type ${safeName} = ${compositeTypes};`);
|
|
93
|
+
return `${result.join('\n')}\n`;
|
|
94
|
+
}
|
|
87
95
|
|
|
88
96
|
if (useTypeAliases) {
|
|
89
97
|
const compositeTypes = [...types, `{${objectContents ? `\n${objectContents}\n` : ''}}`].join(' & ');
|
|
@@ -95,7 +103,7 @@ function renderSchema(
|
|
|
95
103
|
result.push(`export interface ${safeName} ${extensions}{`);
|
|
96
104
|
result.push(objectContents);
|
|
97
105
|
} else if ('oneOf' in schema || 'anyOf' in schema) {
|
|
98
|
-
const typeDefinition =
|
|
106
|
+
const typeDefinition = _swagger.getTypeFromSchema.call(void 0, schema, options);
|
|
99
107
|
result.push(`export type ${safeName} = ${typeDefinition};`);
|
|
100
108
|
|
|
101
109
|
return `${result.join('\n')}\n`;
|
|
@@ -105,7 +113,15 @@ function renderSchema(
|
|
|
105
113
|
result.push(`export type ${safeName} = ${generateItemsType(schema.items, options)}[];`);
|
|
106
114
|
return result.join('\n');
|
|
107
115
|
} else {
|
|
116
|
+
const objectType = _swagger.getTypeFromSchema.call(void 0, schema, options);
|
|
117
|
+
const hasAdditionalProperties = !!schema.additionalProperties;
|
|
118
|
+
|
|
108
119
|
const objectContents = generateObjectTypeContents(schema, options);
|
|
120
|
+
if (hasAdditionalProperties) {
|
|
121
|
+
result.push(`export type ${safeName} = ${objectType};`);
|
|
122
|
+
return `${result.join('\n')}\n`;
|
|
123
|
+
}
|
|
124
|
+
|
|
109
125
|
if (useTypeAliases) {
|
|
110
126
|
result.push(`export type ${safeName} = {`);
|
|
111
127
|
result.push(objectContents);
|
|
@@ -119,21 +135,6 @@ function renderSchema(
|
|
|
119
135
|
return `${result.join('\n')}\n}\n`;
|
|
120
136
|
}
|
|
121
137
|
|
|
122
|
-
/**
|
|
123
|
-
* Generates the type definition for an `anyOf` or `oneOf` schema.
|
|
124
|
-
* @param schema - The schema object to generate the type definition for.
|
|
125
|
-
* @param options - The options for the generation.
|
|
126
|
-
* @returns The type definition for the `anyOf` or `oneOf` schema.
|
|
127
|
-
*/
|
|
128
|
-
function getTypesFromAnyOrOneOf(schema, options) {
|
|
129
|
-
const composite = schema.allOf || schema.oneOf || schema.anyOf;
|
|
130
|
-
if (!composite) {
|
|
131
|
-
return '';
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return composite.map((s) => _swagger.getTypeFromSchema.call(void 0, s, options)).join(' | ');
|
|
135
|
-
}
|
|
136
|
-
|
|
137
138
|
/**
|
|
138
139
|
* Generates the inline contents of an object type.
|
|
139
140
|
*/
|
|
@@ -182,11 +183,36 @@ function renderExtendedEnumType(name, def) {
|
|
|
182
183
|
/**
|
|
183
184
|
* Render simple enum types (just a union of values)
|
|
184
185
|
*/
|
|
185
|
-
function renderEnumType(name, def) {
|
|
186
|
+
function renderEnumType(name, def, options) {
|
|
187
|
+
if (options.enumDeclarationStyle === 'enum' && shouldRenderStringEnumDeclaration(def)) {
|
|
188
|
+
return renderStringEnumDeclaration(name, def);
|
|
189
|
+
}
|
|
190
|
+
|
|
186
191
|
const values = def.enum.map((v) => (typeof v === 'number' ? v : `"${v}"`)).join(' | ');
|
|
187
192
|
return `export type ${name} = ${values};\n`;
|
|
188
193
|
}
|
|
189
194
|
|
|
195
|
+
function shouldRenderStringEnumDeclaration(def)
|
|
196
|
+
|
|
197
|
+
{
|
|
198
|
+
return (
|
|
199
|
+
def.type === 'string' &&
|
|
200
|
+
Array.isArray(def.enum) &&
|
|
201
|
+
def.enum.every((value) => typeof value === 'string')
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function renderStringEnumDeclaration(name, def) {
|
|
206
|
+
let res = `export enum ${name} {\n`;
|
|
207
|
+
for (let index = 0; index < def.enum.length; index++) {
|
|
208
|
+
const value = def.enum[index];
|
|
209
|
+
const memberName = _nullishCoalesce(_utils.escapePropName.call(void 0, value), () => ( `VALUE_${index}`));
|
|
210
|
+
res += ` ${memberName} = ${JSON.stringify(value)},\n`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return `${res}}\n`;
|
|
214
|
+
}
|
|
215
|
+
|
|
190
216
|
/**
|
|
191
217
|
* OpenApi 3.1 introduced a new way to define enums that we support here.
|
|
192
218
|
*/
|
|
@@ -267,7 +293,7 @@ function getMergedCompositeObjects(schema) {
|
|
|
267
293
|
subSchemas.push(safeSchema);
|
|
268
294
|
}
|
|
269
295
|
|
|
270
|
-
return deepMerge({}, ...subSchemas);
|
|
296
|
+
return deepMerge({}, ...subSchemas) ;
|
|
271
297
|
}
|
|
272
298
|
|
|
273
299
|
function isObject(item) {
|
package/dist/index.js
CHANGED
|
@@ -83,6 +83,7 @@ function readFile(filePath) {
|
|
|
83
83
|
arrayFormat,
|
|
84
84
|
mode,
|
|
85
85
|
schemaStyle,
|
|
86
|
+
enumStyle,
|
|
86
87
|
template,
|
|
87
88
|
queryParamsSerialization = {},
|
|
88
89
|
...rest
|
|
@@ -104,6 +105,8 @@ function readFile(filePath) {
|
|
|
104
105
|
generationMode: _nullishCoalesce(_nullishCoalesce(mode, () => ( rest.generationMode)), () => ( _swagger.APP_DEFAULTS.generationMode)),
|
|
105
106
|
schemaDeclarationStyle:
|
|
106
107
|
_nullishCoalesce(_nullishCoalesce(schemaStyle, () => ( rest.schemaDeclarationStyle)), () => ( _swagger.APP_DEFAULTS.schemaDeclarationStyle)),
|
|
108
|
+
enumDeclarationStyle:
|
|
109
|
+
_nullishCoalesce(_nullishCoalesce(enumStyle, () => ( rest.enumDeclarationStyle)), () => ( _swagger.APP_DEFAULTS.enumDeclarationStyle)),
|
|
107
110
|
queryParamsSerialization: mergedQueryParamsSerialization,
|
|
108
111
|
};
|
|
109
112
|
} exports.prepareAppOptions = prepareAppOptions;
|
|
@@ -192,20 +192,16 @@ function getNestedTypeFromSchema(
|
|
|
192
192
|
*/
|
|
193
193
|
function getTypeFromObject(
|
|
194
194
|
schema,
|
|
195
|
-
options
|
|
195
|
+
options,
|
|
196
|
+
requiredOverride
|
|
196
197
|
) {
|
|
197
198
|
const unknownType = options.preferAny ? 'any' : 'unknown';
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const extraProps = schema.additionalProperties;
|
|
201
|
-
return `{ [key: string]: ${
|
|
202
|
-
extraProps === true ? 'any' : getTypeFromSchemaResolved(extraProps, options)
|
|
203
|
-
} }`;
|
|
204
|
-
}
|
|
199
|
+
let objectWithNamedPropsType = '';
|
|
200
|
+
let objectWithIndexSignatureType = '';
|
|
205
201
|
|
|
206
202
|
if (schema.properties) {
|
|
207
203
|
const props = Object.keys(schema.properties);
|
|
208
|
-
const required = schema.required
|
|
204
|
+
const required = _nullishCoalesce(_nullishCoalesce(requiredOverride, () => ( schema.required)), () => ( []));
|
|
209
205
|
const result = [];
|
|
210
206
|
|
|
211
207
|
for (const prop of props) {
|
|
@@ -217,7 +213,26 @@ function getTypeFromObject(
|
|
|
217
213
|
);
|
|
218
214
|
}
|
|
219
215
|
|
|
220
|
-
|
|
216
|
+
objectWithNamedPropsType = `{ ${result.join('\n')} }`;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (schema.additionalProperties) {
|
|
220
|
+
const extraProps = schema.additionalProperties;
|
|
221
|
+
objectWithIndexSignatureType = `{ [key: string]: ${
|
|
222
|
+
extraProps === true ? 'any' : getTypeFromSchemaResolved(extraProps, options)
|
|
223
|
+
} }`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (objectWithNamedPropsType && objectWithIndexSignatureType) {
|
|
227
|
+
return `${objectWithNamedPropsType} & ${objectWithIndexSignatureType}`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (objectWithNamedPropsType) {
|
|
231
|
+
return objectWithNamedPropsType;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (objectWithIndexSignatureType) {
|
|
235
|
+
return objectWithIndexSignatureType;
|
|
221
236
|
}
|
|
222
237
|
|
|
223
238
|
return unknownType;
|
|
@@ -229,11 +244,64 @@ function getTypeFromObject(
|
|
|
229
244
|
function getTypeFromComposites(schema, options) {
|
|
230
245
|
const composite = schema.allOf || schema.oneOf || schema.anyOf;
|
|
231
246
|
|
|
247
|
+
if (!composite) {
|
|
248
|
+
return options.preferAny ? 'any' : 'unknown';
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const isUnionComposite = !!schema.oneOf || !!schema.anyOf;
|
|
252
|
+
const hasParentObjectShape = !!schema.properties || !!schema.additionalProperties;
|
|
253
|
+
|
|
254
|
+
if (isUnionComposite && hasParentObjectShape) {
|
|
255
|
+
const fallbackType = options.preferAny ? 'any' : 'unknown';
|
|
256
|
+
const parentObjectType = getTypeFromObject(schema, options);
|
|
257
|
+
const parentRequired = schema.required || [];
|
|
258
|
+
const parentPropSet = new Set(Object.keys(schema.properties || {}));
|
|
259
|
+
|
|
260
|
+
return composite
|
|
261
|
+
.map((subSchema) => {
|
|
262
|
+
if (!('$ref' in subSchema) && isRequiredOnlyCompositeBranch(subSchema)) {
|
|
263
|
+
const branchRequired = subSchema.required || [];
|
|
264
|
+
const validRequired = branchRequired.filter((name) => {
|
|
265
|
+
const isKnown = parentPropSet.has(name);
|
|
266
|
+
if (!isKnown) {
|
|
267
|
+
console.warn(
|
|
268
|
+
`Composite required key '${name}' is not present in schema properties and will be ignored.`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
return isKnown;
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
return getTypeFromObject(schema, options, Array.from(new Set([...parentRequired, ...validRequired])));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const subType = getTypeFromSchemaResolved(subSchema, options);
|
|
278
|
+
if (subType === fallbackType) {
|
|
279
|
+
return parentObjectType;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return `${parentObjectType} & ${subType}`;
|
|
283
|
+
})
|
|
284
|
+
.join(' | ');
|
|
285
|
+
}
|
|
286
|
+
|
|
232
287
|
return composite
|
|
233
288
|
.map((s) => getTypeFromSchemaResolved(s, options))
|
|
234
289
|
.join(schema.allOf ? ' & ' : ' | ');
|
|
235
290
|
}
|
|
236
291
|
|
|
292
|
+
function isRequiredOnlyCompositeBranch(schema) {
|
|
293
|
+
return (
|
|
294
|
+
Array.isArray(schema.required) &&
|
|
295
|
+
!schema.type &&
|
|
296
|
+
!schema.properties &&
|
|
297
|
+
!schema.additionalProperties &&
|
|
298
|
+
!schema.allOf &&
|
|
299
|
+
!schema.oneOf &&
|
|
300
|
+
!schema.anyOf &&
|
|
301
|
+
!schema.enum
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
237
305
|
/**
|
|
238
306
|
* Escapes name so it can be used as a valid identifier in the generated code.
|
|
239
307
|
* Component names can contain certain characters that are not allowed in identifiers.
|
|
@@ -264,6 +332,7 @@ function getTypeFromComposites(schema, options) {
|
|
|
264
332
|
nullableStrategy: 'ignore',
|
|
265
333
|
generationMode: 'full',
|
|
266
334
|
schemaDeclarationStyle: 'interface',
|
|
335
|
+
enumDeclarationStyle: 'union',
|
|
267
336
|
queryParamsSerialization: {
|
|
268
337
|
allowDots: true,
|
|
269
338
|
arrayFormat: 'repeat',
|
|
@@ -283,6 +352,7 @@ function getTypeFromComposites(schema, options) {
|
|
|
283
352
|
nullableStrategy: _nullishCoalesce(opts.nullableStrategy, () => ( exports.APP_DEFAULTS.nullableStrategy)),
|
|
284
353
|
generationMode: _nullishCoalesce(opts.generationMode, () => ( exports.APP_DEFAULTS.generationMode)),
|
|
285
354
|
schemaDeclarationStyle: _nullishCoalesce(opts.schemaDeclarationStyle, () => ( exports.APP_DEFAULTS.schemaDeclarationStyle)),
|
|
355
|
+
enumDeclarationStyle: _nullishCoalesce(opts.enumDeclarationStyle, () => ( exports.APP_DEFAULTS.enumDeclarationStyle)),
|
|
286
356
|
queryParamsSerialization: {
|
|
287
357
|
...exports.APP_DEFAULTS.queryParamsSerialization,
|
|
288
358
|
...opts.queryParamsSerialization,
|
package/dist/types.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ export interface ClientOptions {
|
|
|
33
33
|
generationMode?: GenerationMode;
|
|
34
34
|
/** Controls whether object schemas are emitted as interfaces or type aliases */
|
|
35
35
|
schemaDeclarationStyle?: SchemaDeclarationStyle;
|
|
36
|
+
/** Controls whether plain string enums are emitted as unions or TypeScript enums */
|
|
37
|
+
enumDeclarationStyle?: EnumDeclarationStyle;
|
|
36
38
|
/** Offers ability to adjust the OpenAPI spec before it is processed */
|
|
37
39
|
modifiers?: {
|
|
38
40
|
/** Global-level modifiers for parameter with a given name */
|
|
@@ -46,6 +48,7 @@ export interface CliOptions extends FullAppOptions {
|
|
|
46
48
|
arrayFormat?: ArrayFormat;
|
|
47
49
|
mode?: GenerationMode;
|
|
48
50
|
schemaStyle?: SchemaDeclarationStyle;
|
|
51
|
+
enumStyle?: EnumDeclarationStyle;
|
|
49
52
|
}
|
|
50
53
|
export interface FullAppOptions extends ClientOptions {
|
|
51
54
|
/** Path to the configuration file that contains actual config to be used */
|
|
@@ -58,6 +61,7 @@ export type ArrayFormat = 'indices' | 'repeat' | 'brackets';
|
|
|
58
61
|
export type NullableStrategy = 'include' | 'nullableAsOptional' | 'ignore';
|
|
59
62
|
export type GenerationMode = 'full' | 'schemas';
|
|
60
63
|
export type SchemaDeclarationStyle = 'interface' | 'type';
|
|
64
|
+
export type EnumDeclarationStyle = 'union' | 'enum';
|
|
61
65
|
/**
|
|
62
66
|
* Internal options type used throughout the app after `prepareAppOptions` has run.
|
|
63
67
|
* All fields that have defaults are required here so the rest of the codebase never
|
|
@@ -69,6 +73,7 @@ export interface AppOptions extends ClientOptions {
|
|
|
69
73
|
nullableStrategy: NullableStrategy;
|
|
70
74
|
generationMode: GenerationMode;
|
|
71
75
|
schemaDeclarationStyle: SchemaDeclarationStyle;
|
|
76
|
+
enumDeclarationStyle: EnumDeclarationStyle;
|
|
72
77
|
queryParamsSerialization: {
|
|
73
78
|
allowDots: boolean;
|
|
74
79
|
arrayFormat: ArrayFormat;
|