swagger2api-v3 1.1.9 → 1.1.11
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 +5 -3
- package/dist/.swagger2api.schema.json +6 -2
- package/dist/cli/index.js +22 -21
- package/dist/config/validator.js +94 -9
- package/dist/core/generator.d.ts +83 -11
- package/dist/core/generator.js +295 -94
- package/dist/core/parser.d.ts +20 -16
- package/dist/core/parser.js +72 -103
- package/dist/core/query-parameter-generator.d.ts +26 -0
- package/dist/core/query-parameter-generator.js +114 -0
- package/dist/core/type-generator.d.ts +73 -0
- package/dist/core/type-generator.js +215 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +26 -21
- package/dist/types/index.d.ts +31 -6
- package/dist/utils/comment.d.ts +4 -1
- package/dist/utils/comment.js +6 -2
- package/dist/utils/file.d.ts +10 -3
- package/dist/utils/file.js +49 -19
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/logger.d.ts +115 -0
- package/dist/utils/logger.js +261 -0
- package/dist/utils/naming.js +3 -2
- package/dist/utils/schema.d.ts +79 -0
- package/dist/utils/schema.js +536 -0
- package/dist/utils/type.d.ts +7 -25
- package/dist/utils/type.js +29 -241
- package/package.json +21 -19
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isValidIdentifier = isValidIdentifier;
|
|
4
|
+
exports.formatTsPropertyName = formatTsPropertyName;
|
|
5
|
+
exports.escapeJSDocText = escapeJSDocText;
|
|
6
|
+
exports.stripNullFromUnion = stripNullFromUnion;
|
|
7
|
+
exports.isObjectSchema = isObjectSchema;
|
|
8
|
+
exports.isGenericResponseSchema = isGenericResponseSchema;
|
|
9
|
+
exports.getVisibleSchemaProperties = getVisibleSchemaProperties;
|
|
10
|
+
exports.schemaNeedsUsageVariant = schemaNeedsUsageVariant;
|
|
11
|
+
exports.renderObjectMembers = renderObjectMembers;
|
|
12
|
+
exports.swaggerTypeToTsType = swaggerTypeToTsType;
|
|
13
|
+
exports.getRefName = getRefName;
|
|
14
|
+
const naming_1 = require("./naming");
|
|
15
|
+
/**
|
|
16
|
+
* 判断字符串是否为合法 TypeScript 标识符
|
|
17
|
+
* @param name 属性名
|
|
18
|
+
* @returns 是否为合法标识符
|
|
19
|
+
*/
|
|
20
|
+
function isValidIdentifier(name) {
|
|
21
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 格式化 TypeScript 属性名
|
|
25
|
+
* @param name 属性名
|
|
26
|
+
* @returns 可用于类型声明的属性名
|
|
27
|
+
*/
|
|
28
|
+
function formatTsPropertyName(name) {
|
|
29
|
+
return isValidIdentifier(name) ? name : JSON.stringify(name);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 转义 JSDoc 文本
|
|
33
|
+
* @param text 原始文本
|
|
34
|
+
* @returns 可安全写入单行 JSDoc 的文本
|
|
35
|
+
*/
|
|
36
|
+
function escapeJSDocText(text) {
|
|
37
|
+
return text.replace(/\*\//g, '*\\/').replace(/\s+/g, ' ').trim();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 移除联合类型中的顶层 null 类型
|
|
41
|
+
* @param typeStr 类型字符串
|
|
42
|
+
* @returns 移除 null 后的类型字符串
|
|
43
|
+
*/
|
|
44
|
+
function stripNullFromUnion(typeStr) {
|
|
45
|
+
if (!typeStr)
|
|
46
|
+
return 'any';
|
|
47
|
+
const normalized = Array.from(new Set(splitTopLevelUnion(typeStr).filter((part) => part && part !== 'null')));
|
|
48
|
+
return normalized.length > 0 ? normalized.join(' | ') : 'any';
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 判断 Schema 是否应按对象处理
|
|
52
|
+
* @param schema OpenAPI Schema
|
|
53
|
+
* @returns 是否为对象 Schema
|
|
54
|
+
*/
|
|
55
|
+
function isObjectSchema(schema) {
|
|
56
|
+
return (schema.type === 'object' ||
|
|
57
|
+
(schema.type === undefined &&
|
|
58
|
+
(schema.properties !== undefined ||
|
|
59
|
+
schema.additionalProperties !== undefined)));
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 判断 Schema 是否为项目约定的泛型响应容器
|
|
63
|
+
* @param schema OpenAPI Schema
|
|
64
|
+
* @returns 是否为泛型响应容器
|
|
65
|
+
*/
|
|
66
|
+
function isGenericResponseSchema(schema) {
|
|
67
|
+
if (!isObjectSchema(schema) || !schema.properties?.data)
|
|
68
|
+
return false;
|
|
69
|
+
const hasCommonField = ['code', 'message', 'success', 'status'].some((field) => schema.properties?.[field] !== undefined);
|
|
70
|
+
const dataSchema = schema.properties.data;
|
|
71
|
+
const hasGenericData = isObjectSchema(dataSchema) &&
|
|
72
|
+
!dataSchema.properties &&
|
|
73
|
+
dataSchema.additionalProperties === undefined;
|
|
74
|
+
return hasCommonField && hasGenericData;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 获取当前使用场景可见的对象属性
|
|
78
|
+
* @param schema OpenAPI Schema
|
|
79
|
+
* @param usage Schema 使用场景
|
|
80
|
+
* @returns 可见属性数组
|
|
81
|
+
*/
|
|
82
|
+
function getVisibleSchemaProperties(schema, usage = 'neutral') {
|
|
83
|
+
return Object.entries(schema.properties || {}).filter(([, property]) => {
|
|
84
|
+
if (usage === 'request' && property.readOnly)
|
|
85
|
+
return false;
|
|
86
|
+
if (usage === 'response' && property.writeOnly)
|
|
87
|
+
return false;
|
|
88
|
+
return true;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 判断 Schema 在指定场景下是否需要独立类型
|
|
93
|
+
* @param schema OpenAPI Schema
|
|
94
|
+
* @param schemas 全部组件 Schema
|
|
95
|
+
* @param usage Schema 使用场景
|
|
96
|
+
* @returns 是否需要独立类型
|
|
97
|
+
*/
|
|
98
|
+
function schemaNeedsUsageVariant(schema, schemas, usage) {
|
|
99
|
+
return detectUsageVariant(schema, schemas, usage, new Set());
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 渲染对象类型成员
|
|
103
|
+
* @param schema OpenAPI Schema
|
|
104
|
+
* @param schemas 全部组件 Schema
|
|
105
|
+
* @param usage Schema 使用场景
|
|
106
|
+
* @param overrides 属性类型覆盖
|
|
107
|
+
* @returns 对象成员代码
|
|
108
|
+
*/
|
|
109
|
+
function renderObjectMembers(schema, schemas = {}, usage = 'neutral', overrides = {}) {
|
|
110
|
+
const properties = getVisibleSchemaProperties(schema, usage);
|
|
111
|
+
const propertyTypes = [];
|
|
112
|
+
let hasOptionalProperty = false;
|
|
113
|
+
const members = properties.map(([key, property]) => {
|
|
114
|
+
const optional = schema.required?.includes(key) ? '' : '?';
|
|
115
|
+
let propertyType = overrides[key] || swaggerTypeToTsType(property, schemas, usage);
|
|
116
|
+
if (optional) {
|
|
117
|
+
propertyType = stripNullFromUnion(propertyType);
|
|
118
|
+
hasOptionalProperty = true;
|
|
119
|
+
}
|
|
120
|
+
propertyTypes.push(propertyType);
|
|
121
|
+
const comment = property.description
|
|
122
|
+
? ` /** ${escapeJSDocText(property.description)} */\n`
|
|
123
|
+
: '';
|
|
124
|
+
const readonly = property.readOnly ? 'readonly ' : '';
|
|
125
|
+
return `${comment} ${readonly}${formatTsPropertyName(key)}${optional}: ${propertyType};`;
|
|
126
|
+
});
|
|
127
|
+
const indexMember = createAdditionalPropertiesMember(schema, schemas, usage, propertyTypes, hasOptionalProperty);
|
|
128
|
+
if (indexMember)
|
|
129
|
+
members.push(indexMember);
|
|
130
|
+
return members.join('\n');
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* 将 OpenAPI Schema 转换为 TypeScript 类型
|
|
134
|
+
* @param schema OpenAPI Schema
|
|
135
|
+
* @param schemas 全部组件 Schema
|
|
136
|
+
* @param usage Schema 使用场景
|
|
137
|
+
* @returns TypeScript 类型字符串
|
|
138
|
+
*/
|
|
139
|
+
function swaggerTypeToTsType(schema, schemas = {}, usage = 'neutral') {
|
|
140
|
+
if (!schema)
|
|
141
|
+
return 'any';
|
|
142
|
+
let baseType;
|
|
143
|
+
if (schema.enum) {
|
|
144
|
+
baseType = createEnumUnion(schema.enum);
|
|
145
|
+
}
|
|
146
|
+
else if (schema.allOf) {
|
|
147
|
+
baseType = createAllOfType(schema, schemas, usage);
|
|
148
|
+
}
|
|
149
|
+
else if (schema.oneOf || schema.anyOf) {
|
|
150
|
+
baseType = createComposedUnionType(schema, schemas, usage);
|
|
151
|
+
}
|
|
152
|
+
else if (schema.$ref) {
|
|
153
|
+
baseType = createReferenceType(schema.$ref, schemas, usage);
|
|
154
|
+
}
|
|
155
|
+
else if (Array.isArray(schema.type)) {
|
|
156
|
+
baseType = createTypeArrayType(schema, schemas, usage);
|
|
157
|
+
}
|
|
158
|
+
else if (schema.type === 'array') {
|
|
159
|
+
const itemType = swaggerTypeToTsType(schema.items, schemas, usage);
|
|
160
|
+
baseType = `${parenthesizeArrayItem(itemType)}[]`;
|
|
161
|
+
}
|
|
162
|
+
else if (isObjectSchema(schema)) {
|
|
163
|
+
baseType = createObjectType(schema, schemas, usage);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
baseType = createPrimitiveType(schema);
|
|
167
|
+
}
|
|
168
|
+
return schema.nullable === true ? addNullableType(baseType) : baseType;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* 创建 OpenAPI 3.1 类型数组对应的联合类型
|
|
172
|
+
* @param schema OpenAPI Schema
|
|
173
|
+
* @param schemas 全部组件 Schema
|
|
174
|
+
* @param usage Schema 使用场景
|
|
175
|
+
* @returns TypeScript 联合类型
|
|
176
|
+
*/
|
|
177
|
+
function createTypeArrayType(schema, schemas, usage) {
|
|
178
|
+
const schemaTypes = Array.isArray(schema.type) ? schema.type : [];
|
|
179
|
+
const types = schemaTypes.map((type) => swaggerTypeToTsType({ ...schema, type, nullable: false }, schemas, usage));
|
|
180
|
+
return createUniqueUnion(types);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* 从 $ref 字符串中提取并解码引用名称
|
|
184
|
+
* @param ref OpenAPI $ref 字符串
|
|
185
|
+
* @returns 引用名称
|
|
186
|
+
*/
|
|
187
|
+
function getRefName(ref) {
|
|
188
|
+
const segment = ref.split('/').pop() || '';
|
|
189
|
+
try {
|
|
190
|
+
return decodeURIComponent(segment).replace(/~1/g, '/').replace(/~0/g, '~');
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
return segment.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* 递归检测指定场景下的访问属性差异
|
|
198
|
+
* @param schema OpenAPI Schema
|
|
199
|
+
* @param schemas 全部组件 Schema
|
|
200
|
+
* @param usage Schema 使用场景
|
|
201
|
+
* @param seenRefs 已访问引用
|
|
202
|
+
* @returns 是否存在差异
|
|
203
|
+
*/
|
|
204
|
+
function detectUsageVariant(schema, schemas, usage, seenRefs) {
|
|
205
|
+
if (schema.$ref) {
|
|
206
|
+
const refName = getRefName(schema.$ref);
|
|
207
|
+
if (!refName || seenRefs.has(refName))
|
|
208
|
+
return false;
|
|
209
|
+
const referencedSchema = schemas[refName];
|
|
210
|
+
if (!referencedSchema)
|
|
211
|
+
return false;
|
|
212
|
+
seenRefs.add(refName);
|
|
213
|
+
return detectUsageVariant(referencedSchema, schemas, usage, seenRefs);
|
|
214
|
+
}
|
|
215
|
+
for (const property of Object.values(schema.properties || {})) {
|
|
216
|
+
if (usage === 'request' && property.readOnly)
|
|
217
|
+
return true;
|
|
218
|
+
if (usage === 'response' && property.writeOnly)
|
|
219
|
+
return true;
|
|
220
|
+
if (detectUsageVariant(property, schemas, usage, new Set(seenRefs))) {
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const nestedSchemas = [
|
|
225
|
+
schema.items,
|
|
226
|
+
typeof schema.additionalProperties === 'object'
|
|
227
|
+
? schema.additionalProperties
|
|
228
|
+
: undefined,
|
|
229
|
+
...(schema.allOf || []),
|
|
230
|
+
...(schema.oneOf || []),
|
|
231
|
+
...(schema.anyOf || [])
|
|
232
|
+
].filter((item) => !!item);
|
|
233
|
+
return nestedSchemas.some((item) => detectUsageVariant(item, schemas, usage, new Set(seenRefs)));
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* 创建枚举字面量联合类型
|
|
237
|
+
* @param values 枚举值
|
|
238
|
+
* @returns 联合类型
|
|
239
|
+
*/
|
|
240
|
+
function createEnumUnion(values) {
|
|
241
|
+
if (values.length === 0)
|
|
242
|
+
return 'never';
|
|
243
|
+
return createUniqueUnion(values.map((value) => toLiteralType(value)));
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* 创建 allOf 类型
|
|
247
|
+
* @param schema OpenAPI Schema
|
|
248
|
+
* @param schemas 全部组件 Schema
|
|
249
|
+
* @param usage Schema 使用场景
|
|
250
|
+
* @returns 交叉类型或泛型响应类型
|
|
251
|
+
*/
|
|
252
|
+
function createAllOfType(schema, schemas, usage) {
|
|
253
|
+
const genericType = createGenericResponseType(schema, schemas, usage);
|
|
254
|
+
const types = genericType
|
|
255
|
+
? [genericType]
|
|
256
|
+
: (schema.allOf || [])
|
|
257
|
+
.map((item) => swaggerTypeToTsType(item, schemas, usage))
|
|
258
|
+
.filter((type) => type !== 'any');
|
|
259
|
+
const ownSchema = { ...schema, allOf: undefined, nullable: false };
|
|
260
|
+
if (hasOwnTypeConstraints(ownSchema)) {
|
|
261
|
+
types.push(swaggerTypeToTsType(ownSchema, schemas, usage));
|
|
262
|
+
}
|
|
263
|
+
const uniqueTypes = Array.from(new Set(types));
|
|
264
|
+
return uniqueTypes.length > 0
|
|
265
|
+
? uniqueTypes.map(parenthesizeIntersectionItem).join(' & ')
|
|
266
|
+
: 'any';
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* 创建项目约定的泛型响应类型
|
|
270
|
+
* @param schema OpenAPI Schema
|
|
271
|
+
* @param schemas 全部组件 Schema
|
|
272
|
+
* @param usage Schema 使用场景
|
|
273
|
+
* @returns 泛型响应类型,无法匹配时返回空值
|
|
274
|
+
*/
|
|
275
|
+
function createGenericResponseType(schema, schemas, usage) {
|
|
276
|
+
if (schema.allOf?.length !== 2)
|
|
277
|
+
return undefined;
|
|
278
|
+
const refSchema = schema.allOf.find((item) => item.$ref);
|
|
279
|
+
const objectSchema = schema.allOf.find((item) => !item.$ref);
|
|
280
|
+
if (!refSchema?.$ref || !objectSchema?.properties)
|
|
281
|
+
return undefined;
|
|
282
|
+
const propertyEntries = Object.entries(objectSchema.properties);
|
|
283
|
+
if (propertyEntries.length !== 1 || propertyEntries[0][0] !== 'data') {
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
const refName = getRefName(refSchema.$ref);
|
|
287
|
+
const referencedSchema = schemas[refName];
|
|
288
|
+
if (!referencedSchema || !isGenericResponseSchema(referencedSchema)) {
|
|
289
|
+
return undefined;
|
|
290
|
+
}
|
|
291
|
+
const containerType = createReferenceType(refSchema.$ref, schemas, usage);
|
|
292
|
+
const dataType = swaggerTypeToTsType(propertyEntries[0][1], schemas, usage);
|
|
293
|
+
return `${containerType}<${dataType}>`;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* 判断 Schema 是否包含 allOf 之外的类型约束
|
|
297
|
+
* @param schema OpenAPI Schema
|
|
298
|
+
* @returns 是否包含类型约束
|
|
299
|
+
*/
|
|
300
|
+
function hasOwnTypeConstraints(schema) {
|
|
301
|
+
const hasStructuralConstraint = !!(schema.$ref ||
|
|
302
|
+
schema.enum ||
|
|
303
|
+
schema.properties ||
|
|
304
|
+
schema.additionalProperties !== undefined ||
|
|
305
|
+
schema.items ||
|
|
306
|
+
schema.oneOf ||
|
|
307
|
+
schema.anyOf);
|
|
308
|
+
return (hasStructuralConstraint ||
|
|
309
|
+
(schema.type !== undefined && schema.type !== 'object'));
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* 创建 oneOf 或 anyOf 组合类型
|
|
313
|
+
* @param schema OpenAPI Schema
|
|
314
|
+
* @param schemas 全部组件 Schema
|
|
315
|
+
* @param usage Schema 使用场景
|
|
316
|
+
* @returns 联合类型及其同级约束
|
|
317
|
+
*/
|
|
318
|
+
function createComposedUnionType(schema, schemas, usage) {
|
|
319
|
+
const unionType = createUniqueUnion((schema.oneOf || schema.anyOf || []).map((item) => {
|
|
320
|
+
const itemType = swaggerTypeToTsType(item, schemas, usage);
|
|
321
|
+
return createDiscriminatedUnionMember(schema, item, itemType);
|
|
322
|
+
}));
|
|
323
|
+
const ownSchema = {
|
|
324
|
+
...schema,
|
|
325
|
+
oneOf: undefined,
|
|
326
|
+
anyOf: undefined,
|
|
327
|
+
nullable: false
|
|
328
|
+
};
|
|
329
|
+
if (!hasOwnTypeConstraints(ownSchema))
|
|
330
|
+
return unionType;
|
|
331
|
+
const ownType = swaggerTypeToTsType(ownSchema, schemas, usage);
|
|
332
|
+
return `${parenthesizeIntersectionItem(unionType)} & ${ownType}`;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* 为联合成员添加 discriminator 字面量属性
|
|
336
|
+
* @param schema 联合 Schema
|
|
337
|
+
* @param item 联合成员 Schema
|
|
338
|
+
* @param itemType 联合成员类型
|
|
339
|
+
* @returns 可判别的联合成员类型
|
|
340
|
+
*/
|
|
341
|
+
function createDiscriminatedUnionMember(schema, item, itemType) {
|
|
342
|
+
const discriminator = schema.discriminator;
|
|
343
|
+
if (!discriminator || !item.$ref)
|
|
344
|
+
return itemType;
|
|
345
|
+
const refName = getRefName(item.$ref);
|
|
346
|
+
const mappedEntry = Object.entries(discriminator.mapping || {}).find(([, mappedRef]) => mappedRef === item.$ref || getRefName(mappedRef) === refName);
|
|
347
|
+
const discriminatorValue = mappedEntry?.[0] || refName;
|
|
348
|
+
if (!discriminatorValue)
|
|
349
|
+
return itemType;
|
|
350
|
+
const propertyName = formatTsPropertyName(discriminator.propertyName);
|
|
351
|
+
return `${parenthesizeIntersectionItem(itemType)} & { ${propertyName}: ${JSON.stringify(discriminatorValue)} }`;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* 创建引用类型名称
|
|
355
|
+
* @param ref OpenAPI $ref
|
|
356
|
+
* @param schemas 全部组件 Schema
|
|
357
|
+
* @param usage Schema 使用场景
|
|
358
|
+
* @returns TypeScript 类型名称
|
|
359
|
+
*/
|
|
360
|
+
function createReferenceType(ref, schemas, usage) {
|
|
361
|
+
const refName = getRefName(ref);
|
|
362
|
+
const baseName = (0, naming_1.sanitizeTypeName)(refName || 'any');
|
|
363
|
+
const referencedSchema = schemas[refName];
|
|
364
|
+
if (usage !== 'neutral' &&
|
|
365
|
+
referencedSchema &&
|
|
366
|
+
schemaNeedsUsageVariant(referencedSchema, schemas, usage)) {
|
|
367
|
+
return `${baseName}${usage === 'request' ? 'Input' : 'Output'}`;
|
|
368
|
+
}
|
|
369
|
+
return baseName;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* 创建对象类型
|
|
373
|
+
* @param schema OpenAPI Schema
|
|
374
|
+
* @param schemas 全部组件 Schema
|
|
375
|
+
* @param usage Schema 使用场景
|
|
376
|
+
* @returns TypeScript 对象类型
|
|
377
|
+
*/
|
|
378
|
+
function createObjectType(schema, schemas, usage) {
|
|
379
|
+
const properties = getVisibleSchemaProperties(schema, usage);
|
|
380
|
+
if (properties.length === 0) {
|
|
381
|
+
if (schema.additionalProperties &&
|
|
382
|
+
typeof schema.additionalProperties === 'object') {
|
|
383
|
+
return `Record<string, ${swaggerTypeToTsType(schema.additionalProperties, schemas, usage)}>`;
|
|
384
|
+
}
|
|
385
|
+
if (schema.additionalProperties === false) {
|
|
386
|
+
return 'Record<string, never>';
|
|
387
|
+
}
|
|
388
|
+
return 'Record<string, any>';
|
|
389
|
+
}
|
|
390
|
+
return `{\n${renderObjectMembers(schema, schemas, usage)}\n}`;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* 创建 additionalProperties 索引签名
|
|
394
|
+
* @param schema OpenAPI Schema
|
|
395
|
+
* @param schemas 全部组件 Schema
|
|
396
|
+
* @param usage Schema 使用场景
|
|
397
|
+
* @param propertyTypes 已知属性类型
|
|
398
|
+
* @param hasOptionalProperty 是否存在可选属性
|
|
399
|
+
* @returns 索引签名
|
|
400
|
+
*/
|
|
401
|
+
function createAdditionalPropertiesMember(schema, schemas, usage, propertyTypes, hasOptionalProperty) {
|
|
402
|
+
if (schema.additionalProperties === undefined ||
|
|
403
|
+
schema.additionalProperties === false) {
|
|
404
|
+
return undefined;
|
|
405
|
+
}
|
|
406
|
+
const additionalType = schema.additionalProperties === true
|
|
407
|
+
? 'any'
|
|
408
|
+
: swaggerTypeToTsType(schema.additionalProperties, schemas, usage);
|
|
409
|
+
if (additionalType === 'any')
|
|
410
|
+
return ' [key: string]: any;';
|
|
411
|
+
const indexTypes = [additionalType, ...propertyTypes];
|
|
412
|
+
if (hasOptionalProperty)
|
|
413
|
+
indexTypes.push('undefined');
|
|
414
|
+
return ` [key: string]: ${createUniqueUnion(indexTypes)};`;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* 创建基础类型
|
|
418
|
+
* @param schema OpenAPI Schema
|
|
419
|
+
* @returns TypeScript 基础类型
|
|
420
|
+
*/
|
|
421
|
+
function createPrimitiveType(schema) {
|
|
422
|
+
switch (schema.type) {
|
|
423
|
+
case 'integer':
|
|
424
|
+
case 'number':
|
|
425
|
+
return 'number';
|
|
426
|
+
case 'string':
|
|
427
|
+
return schema.format === 'binary' ? 'Blob' : 'string';
|
|
428
|
+
case 'boolean':
|
|
429
|
+
return 'boolean';
|
|
430
|
+
case 'null':
|
|
431
|
+
return 'null';
|
|
432
|
+
default:
|
|
433
|
+
return 'any';
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* 将枚举值转换为 TypeScript 字面量类型
|
|
438
|
+
* @param value 枚举值
|
|
439
|
+
* @returns 字面量类型字符串
|
|
440
|
+
*/
|
|
441
|
+
function toLiteralType(value) {
|
|
442
|
+
if (value === null)
|
|
443
|
+
return 'null';
|
|
444
|
+
if (typeof value === 'string')
|
|
445
|
+
return JSON.stringify(value);
|
|
446
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
447
|
+
return String(value);
|
|
448
|
+
}
|
|
449
|
+
return 'any';
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* 创建去重后的联合类型
|
|
453
|
+
* @param types 类型数组
|
|
454
|
+
* @returns 联合类型
|
|
455
|
+
*/
|
|
456
|
+
function createUniqueUnion(types) {
|
|
457
|
+
if (types.includes('any'))
|
|
458
|
+
return 'any';
|
|
459
|
+
const uniqueTypes = Array.from(new Set(types.filter(Boolean)));
|
|
460
|
+
return uniqueTypes.length > 0 ? uniqueTypes.join(' | ') : 'any';
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* 为类型添加 null 联合成员
|
|
464
|
+
* @param type TypeScript 类型
|
|
465
|
+
* @returns 可空类型
|
|
466
|
+
*/
|
|
467
|
+
function addNullableType(type) {
|
|
468
|
+
if (type === 'any')
|
|
469
|
+
return 'any';
|
|
470
|
+
if (splitTopLevelUnion(type).includes('null'))
|
|
471
|
+
return type;
|
|
472
|
+
return `${type} | null`;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* 拆分顶层联合类型
|
|
476
|
+
* @param typeStr 类型字符串
|
|
477
|
+
* @returns 联合类型成员
|
|
478
|
+
*/
|
|
479
|
+
function splitTopLevelUnion(typeStr) {
|
|
480
|
+
const parts = [];
|
|
481
|
+
let current = '';
|
|
482
|
+
let parenDepth = 0;
|
|
483
|
+
let angleDepth = 0;
|
|
484
|
+
let braceDepth = 0;
|
|
485
|
+
let bracketDepth = 0;
|
|
486
|
+
for (const char of typeStr) {
|
|
487
|
+
if (char === '(')
|
|
488
|
+
parenDepth++;
|
|
489
|
+
else if (char === ')')
|
|
490
|
+
parenDepth = Math.max(0, parenDepth - 1);
|
|
491
|
+
else if (char === '<')
|
|
492
|
+
angleDepth++;
|
|
493
|
+
else if (char === '>')
|
|
494
|
+
angleDepth = Math.max(0, angleDepth - 1);
|
|
495
|
+
else if (char === '{')
|
|
496
|
+
braceDepth++;
|
|
497
|
+
else if (char === '}')
|
|
498
|
+
braceDepth = Math.max(0, braceDepth - 1);
|
|
499
|
+
else if (char === '[')
|
|
500
|
+
bracketDepth++;
|
|
501
|
+
else if (char === ']')
|
|
502
|
+
bracketDepth = Math.max(0, bracketDepth - 1);
|
|
503
|
+
if (char === '|' &&
|
|
504
|
+
parenDepth === 0 &&
|
|
505
|
+
angleDepth === 0 &&
|
|
506
|
+
braceDepth === 0 &&
|
|
507
|
+
bracketDepth === 0) {
|
|
508
|
+
parts.push(current.trim());
|
|
509
|
+
current = '';
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
current += char;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
if (current.trim())
|
|
516
|
+
parts.push(current.trim());
|
|
517
|
+
return parts;
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* 必要时为数组元素类型添加括号
|
|
521
|
+
* @param type TypeScript 类型
|
|
522
|
+
* @returns 数组元素类型
|
|
523
|
+
*/
|
|
524
|
+
function parenthesizeArrayItem(type) {
|
|
525
|
+
return splitTopLevelUnion(type).length > 1 || type.includes(' & ')
|
|
526
|
+
? `(${type})`
|
|
527
|
+
: type;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* 必要时为交叉类型成员添加括号
|
|
531
|
+
* @param type TypeScript 类型
|
|
532
|
+
* @returns 交叉类型成员
|
|
533
|
+
*/
|
|
534
|
+
function parenthesizeIntersectionItem(type) {
|
|
535
|
+
return splitTopLevelUnion(type).length > 1 ? `(${type})` : type;
|
|
536
|
+
}
|
package/dist/utils/type.d.ts
CHANGED
|
@@ -1,40 +1,22 @@
|
|
|
1
|
-
import { SwaggerParameter,
|
|
1
|
+
import { SwaggerParameter, SwaggerResponses, SwaggerSchema } from '../types';
|
|
2
|
+
export * from './schema';
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
-
* @param typeStr 类型字符串
|
|
5
|
-
* @returns 移除 null 后的类型字符串
|
|
6
|
-
*/
|
|
7
|
-
export declare function stripNullFromUnion(typeStr: string): string;
|
|
8
|
-
/**
|
|
9
|
-
* 将 OpenAPI schema 转换为 TypeScript 类型
|
|
10
|
-
* @param schema OpenAPI schema
|
|
11
|
-
* @param schemas 可选的 schemas 上下文,用于查找被引用的类型定义
|
|
12
|
-
* @returns TypeScript类型字符串
|
|
13
|
-
*/
|
|
14
|
-
export declare function swaggerTypeToTsType(schema: SwaggerSchema | undefined | null, schemas?: Record<string, SwaggerSchema>): string;
|
|
15
|
-
/**
|
|
16
|
-
* 从 $ref 字符串中提取引用名称
|
|
17
|
-
* @param ref OpenAPI $ref 字符串
|
|
18
|
-
* @returns 引用名称
|
|
19
|
-
*/
|
|
20
|
-
export declare function getRefName(ref: string): string;
|
|
21
|
-
/**
|
|
22
|
-
* 将 OpenAPI 参数转换为 schema 对象
|
|
4
|
+
* 将 OpenAPI 参数转换为 Schema 对象
|
|
23
5
|
* @param parameter OpenAPI 参数
|
|
24
|
-
* @returns 参数对应的
|
|
6
|
+
* @returns 参数对应的 Schema
|
|
25
7
|
*/
|
|
26
8
|
export declare function swaggerParameterToSchema(parameter: SwaggerParameter): SwaggerSchema;
|
|
27
9
|
/**
|
|
28
10
|
* 获取响应类型
|
|
29
11
|
* @param responses OpenAPI 响应对象
|
|
30
12
|
* @param schemas OpenAPI components.schemas
|
|
31
|
-
* @returns TypeScript类型字符串
|
|
13
|
+
* @returns TypeScript 类型字符串
|
|
32
14
|
*/
|
|
33
15
|
export declare function getResponseType(responses: SwaggerResponses, schemas?: Record<string, SwaggerSchema>): string;
|
|
34
16
|
/**
|
|
35
|
-
* 从 OpenAPI content 对象中获取最合适的
|
|
17
|
+
* 从 OpenAPI content 对象中获取最合适的 Schema
|
|
36
18
|
* @param content OpenAPI content 对象
|
|
37
|
-
* @returns 匹配到的
|
|
19
|
+
* @returns 匹配到的 Schema
|
|
38
20
|
*/
|
|
39
21
|
export declare function getSchemaFromContent(content: Record<string, {
|
|
40
22
|
schema?: SwaggerSchema;
|