swgto-ts 3.0.2 → 3.1.0
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 +22 -0
- package/dist/{chunk-GTJIYNPK.js → chunk-PAFR76SO.js} +76 -40
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -81,6 +81,7 @@ export interface SwaggerTsConfig {
|
|
|
81
81
|
fileNaming?: 'module' | 'path'
|
|
82
82
|
flattenQueryParam?: boolean
|
|
83
83
|
mergeParams?: boolean
|
|
84
|
+
flattenOnGet?: boolean
|
|
84
85
|
apiDocs?: ApiDocsConfig
|
|
85
86
|
}
|
|
86
87
|
```
|
|
@@ -102,6 +103,7 @@ export interface SwaggerTsConfig {
|
|
|
102
103
|
| `cleanOutput` | `boolean` | `false` | 生成前是否清空输出目录 |
|
|
103
104
|
| `flattenQueryParam` | `boolean` | `false` | 当 query 参数只有一个且为 `$ref` 引用类型时,直接用引用类型代替 `{ query: RefType }` |
|
|
104
105
|
| `mergeParams` | `boolean` | `false` | 合并参数层级:`true` 时展平 `path/query/body` 嵌套,直接使用 `params: { field1, field2 }` 代替 `params: { path: {...}, query: {...} }` |
|
|
106
|
+
| `flattenOnGet` | `boolean` | `false` | GET 请求专用展平:将所有 body/query/path 中的 `$ref` 参数展开为顶层交叉类型,彻底消除嵌套。详见 [GET 参数展平](#get-参数展平) |
|
|
105
107
|
| `apiDocs` | `ApiDocsConfig` | 见说明 | 自动生成 API 文档(HTML/Markdown),详细配置见 [API 文档生成](#api-文档生成) |
|
|
106
108
|
|
|
107
109
|
### 函数名生成规则
|
|
@@ -148,6 +150,26 @@ getUser({ path: { id: '1' }, query: { name: 'foo' } })
|
|
|
148
150
|
getUser({ id: '1', name: 'foo' })
|
|
149
151
|
```
|
|
150
152
|
|
|
153
|
+
### GET 参数展平
|
|
154
|
+
|
|
155
|
+
`flattenOnGet: true` 专门解决 GET 请求中 body 和 query 参数嵌套的问题。当 GET 请求同时包含 body 参数(如 `userDTO`)和 query 参数(如 `pageQuery`)时,会将所有 `$ref` 类型的参数展开为顶层交叉类型,实现 `{...userDTO, ...pageQuery}` 的效果。
|
|
156
|
+
|
|
157
|
+
**背景**:部分后端定义 GET 请求时,参数在 body 和 query 中分散定义,但实际入参是合并平铺的。仅靠 `mergeParams` 会保留 body 内部的属性名作为嵌套字段。
|
|
158
|
+
|
|
159
|
+
**示例**:一个 GET 请求的 body 中有 `adminUserVO: AdminUserVO`,query 中有 `pageRequest: SessionPageRequestDTO`
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
// flattenOnGet: false(默认),body 属性名保留为嵌套字段
|
|
163
|
+
get_ai_sessions({ adminUserVO: {...}, pageRequest: {...} })
|
|
164
|
+
// 类型:{ adminUserVO: AdminUserVO; pageRequest: SessionPageRequestDTO; }
|
|
165
|
+
|
|
166
|
+
// flattenOnGet: true,$ref 参数全部展开到顶层
|
|
167
|
+
get_ai_sessions({ ...adminUserVOFields, ...pageRequestFields })
|
|
168
|
+
// 类型:AdminUserVO & SessionPageRequestDTO
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
> **注意**:`flattenOnGet` 仅对 GET 请求生效,且通常与 `mergeParams` 和 `flattenQueryParam` 一起使用效果最佳。该选项会同时影响生成的类型签名和函数体实现。
|
|
172
|
+
|
|
151
173
|
### API 文档生成
|
|
152
174
|
|
|
153
175
|
配置 `apiDocs` 可在生成代码的同时,自动输出一份人类可读的 API 文档:
|
|
@@ -52,6 +52,7 @@ async function loadConfig(cwd) {
|
|
|
52
52
|
fileNaming: rawConfig.fileNaming ?? "path",
|
|
53
53
|
flattenQueryParam: rawConfig.flattenQueryParam ?? false,
|
|
54
54
|
mergeParams: rawConfig.mergeParams ?? false,
|
|
55
|
+
flattenOnGet: rawConfig.flattenOnGet ?? false,
|
|
55
56
|
apiDocs: {
|
|
56
57
|
enable: rawConfig.apiDocs?.enable ?? false,
|
|
57
58
|
output: rawConfig.apiDocs?.output ?? defaultOutput,
|
|
@@ -259,28 +260,60 @@ function parsePaths(document, docUrl, config) {
|
|
|
259
260
|
const pathFields = pathParams.map((param) => `${param.name}: ${schemaToTs(param.schema)};`);
|
|
260
261
|
const queryFields = queryParams.map((param) => `${param.name}${param.required ? "" : "?"}: ${schemaToTs(param.schema)};`);
|
|
261
262
|
const requestTypeParts = [];
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
263
|
+
const isFlattenOnGet = config.flattenOnGet && method === "get";
|
|
264
|
+
if (isFlattenOnGet) {
|
|
265
|
+
const flatFields = [];
|
|
266
|
+
if (requestBodySchema) {
|
|
267
|
+
if (!requestBodySchema.$ref && requestBodySchema.properties) {
|
|
268
|
+
for (const [name, schema] of Object.entries(requestBodySchema.properties)) {
|
|
269
|
+
if (schema.$ref) {
|
|
270
|
+
requestTypeParts.push(schemaToTs(schema));
|
|
271
|
+
} else {
|
|
272
|
+
const required = requestBodySchema.required?.includes(name) ?? false;
|
|
273
|
+
flatFields.push(`${name}${required ? "" : "?"}: ${schemaToTs(schema)}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
requestTypeParts.push(bodyType);
|
|
278
|
+
}
|
|
270
279
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
if (config.mergeParams) {
|
|
275
|
-
requestTypeParts.push(schemaToTs(queryParams[0].schema));
|
|
280
|
+
for (const param of queryParams) {
|
|
281
|
+
if (param.schema?.$ref) {
|
|
282
|
+
requestTypeParts.push(schemaToTs(param.schema));
|
|
276
283
|
} else {
|
|
277
|
-
|
|
284
|
+
flatFields.push(`${param.name}${param.required ? "" : "?"}: ${schemaToTs(param.schema)}`);
|
|
278
285
|
}
|
|
279
|
-
}
|
|
286
|
+
}
|
|
287
|
+
for (const field of pathFields) {
|
|
288
|
+
flatFields.push(field);
|
|
289
|
+
}
|
|
290
|
+
if (flatFields.length) {
|
|
291
|
+
requestTypeParts.push(`{ ${flatFields.join(" ")} }`);
|
|
292
|
+
}
|
|
293
|
+
} else {
|
|
294
|
+
if (bodyType) {
|
|
295
|
+
requestTypeParts.push(bodyType);
|
|
296
|
+
}
|
|
297
|
+
if (pathFields.length) {
|
|
280
298
|
if (config.mergeParams) {
|
|
281
|
-
requestTypeParts.push(`{ ${
|
|
299
|
+
requestTypeParts.push(`{ ${pathFields.join(" ")} }`);
|
|
300
|
+
} else {
|
|
301
|
+
requestTypeParts.push(`{ path: { ${pathFields.join(" ")} } }`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (queryFields.length) {
|
|
305
|
+
if (config.flattenQueryParam && queryParams.length === 1 && queryParams[0].schema?.$ref) {
|
|
306
|
+
if (config.mergeParams) {
|
|
307
|
+
requestTypeParts.push(schemaToTs(queryParams[0].schema));
|
|
308
|
+
} else {
|
|
309
|
+
requestTypeParts.push(`{ query: ${schemaToTs(queryParams[0].schema)} }`);
|
|
310
|
+
}
|
|
282
311
|
} else {
|
|
283
|
-
|
|
312
|
+
if (config.mergeParams) {
|
|
313
|
+
requestTypeParts.push(`{ ${queryFields.join(" ")} }`);
|
|
314
|
+
} else {
|
|
315
|
+
requestTypeParts.push(`{ query: { ${queryFields.join(" ")} } }`);
|
|
316
|
+
}
|
|
284
317
|
}
|
|
285
318
|
}
|
|
286
319
|
}
|
|
@@ -369,18 +402,19 @@ function buildFunctionDoc(operation, typeImportPath, requestParamType) {
|
|
|
369
402
|
}
|
|
370
403
|
return ["/**", ...lines.map((line) => ` * ${line}`), " */"].join("\n");
|
|
371
404
|
}
|
|
372
|
-
function renderFunction(operation, useGenericUrl, mergeParams) {
|
|
373
|
-
const
|
|
405
|
+
function renderFunction(operation, useGenericUrl, mergeParams, flattenOnGet = false) {
|
|
406
|
+
const effectiveMerge = mergeParams || flattenOnGet && operation.method === "get";
|
|
407
|
+
const queryLine = operation.queryParams.length ? ` params: ${effectiveMerge ? "params" : "params?.query"},
|
|
374
408
|
` : "";
|
|
375
409
|
const bodyOnly = Boolean(operation.requestBodySchema) && !operation.queryParams.length && !operation.pathParams.length;
|
|
376
410
|
const hasBodySchema = Boolean(operation.requestBodySchema);
|
|
377
|
-
const needsCleanData =
|
|
411
|
+
const needsCleanData = effectiveMerge && hasBodySchema && operation.pathParams.length > 0;
|
|
378
412
|
const destructureLine = needsCleanData ? ` const { ${operation.pathParams.map((p) => p.name).join(", ")}, ...requestData } = params;
|
|
379
413
|
` : "";
|
|
380
|
-
const dataValue = needsCleanData ? "requestData" :
|
|
414
|
+
const dataValue = needsCleanData ? "requestData" : effectiveMerge ? "params" : bodyOnly ? "params" : "params?.body";
|
|
381
415
|
const bodyLine = hasBodySchema ? ` data: ${dataValue},
|
|
382
416
|
` : "";
|
|
383
|
-
const pathArg =
|
|
417
|
+
const pathArg = effectiveMerge ? "params" : "params?.path";
|
|
384
418
|
const urlValue = useGenericUrl && operation.pathParams.length ? `buildUrl(${JSON.stringify(operation.requestPath)}, ${pathArg})` : operation.pathParams.length ? `buildUrl(${pathArg})` : JSON.stringify(operation.requestPath);
|
|
385
419
|
const pathLine = ` url: ${urlValue},
|
|
386
420
|
`;
|
|
@@ -391,19 +425,20 @@ ${queryLine}${bodyLine} ...config,
|
|
|
391
425
|
});
|
|
392
426
|
}`;
|
|
393
427
|
}
|
|
394
|
-
function generateJsRequestFile(operation, httpClientPath, typeImportPath, mergeParams = false) {
|
|
428
|
+
function generateJsRequestFile(operation, httpClientPath, typeImportPath, mergeParams = false, flattenOnGet = false) {
|
|
429
|
+
const effectiveMerge = mergeParams || flattenOnGet && operation.method === "get";
|
|
395
430
|
const requestParamType = operation.requestTypeExpression ?? "void";
|
|
396
|
-
const queryLine = operation.queryParams.length ? ` params: ${
|
|
431
|
+
const queryLine = operation.queryParams.length ? ` params: ${effectiveMerge ? "params" : "params?.query"},
|
|
397
432
|
` : "";
|
|
398
433
|
const bodyOnly = Boolean(operation.requestBodySchema) && !operation.queryParams.length && !operation.pathParams.length;
|
|
399
434
|
const hasBodySchema = Boolean(operation.requestBodySchema);
|
|
400
|
-
const needsCleanData =
|
|
435
|
+
const needsCleanData = effectiveMerge && hasBodySchema && operation.pathParams.length > 0;
|
|
401
436
|
const destructureLine = needsCleanData ? ` const { ${operation.pathParams.map((p) => p.name).join(", ")}, ...requestData } = params;
|
|
402
437
|
` : "";
|
|
403
|
-
const dataValue = needsCleanData ? "requestData" :
|
|
438
|
+
const dataValue = needsCleanData ? "requestData" : effectiveMerge ? "params" : bodyOnly ? "params" : "params?.body";
|
|
404
439
|
const bodyLine = hasBodySchema ? ` data: ${dataValue},
|
|
405
440
|
` : "";
|
|
406
|
-
const pathArg =
|
|
441
|
+
const pathArg = effectiveMerge ? "params" : "params?.path";
|
|
407
442
|
const pathLine = operation.pathParams.length ? ` url: buildUrl(${pathArg}),
|
|
408
443
|
` : ` url: ${JSON.stringify(operation.requestPath)},
|
|
409
444
|
`;
|
|
@@ -426,7 +461,7 @@ ${queryLine}${bodyLine} ...config,
|
|
|
426
461
|
}
|
|
427
462
|
`;
|
|
428
463
|
}
|
|
429
|
-
function generateJsModuleFile(operations, httpClientPath, typeImportPath, mergeParams = false) {
|
|
464
|
+
function generateJsModuleFile(operations, httpClientPath, typeImportPath, mergeParams = false, flattenOnGet = false) {
|
|
430
465
|
const needsBuildUrl = operations.some((op) => op.pathParams.length > 0);
|
|
431
466
|
const buildUrlHelper = needsBuildUrl ? `
|
|
432
467
|
function buildUrl(url, path) {
|
|
@@ -437,7 +472,7 @@ function buildUrl(url, path) {
|
|
|
437
472
|
const doc = buildFunctionDoc(op, typeImportPath, op.requestTypeExpression ?? "void");
|
|
438
473
|
return `${doc}
|
|
439
474
|
|
|
440
|
-
${renderFunction(op, true, mergeParams)}`;
|
|
475
|
+
${renderFunction(op, true, mergeParams, flattenOnGet)}`;
|
|
441
476
|
}).join("\n\n");
|
|
442
477
|
return `/* eslint-disable */
|
|
443
478
|
// Auto-generated by swgto.
|
|
@@ -461,20 +496,21 @@ function buildFunctionDoc2(operation) {
|
|
|
461
496
|
}
|
|
462
497
|
return ["/**", ...lines.map((line) => ` * ${line}`), " */", ""].join("\n");
|
|
463
498
|
}
|
|
464
|
-
function renderFunction2(operation, useGenericUrl, mergeParams) {
|
|
499
|
+
function renderFunction2(operation, useGenericUrl, mergeParams, flattenOnGet = false) {
|
|
500
|
+
const effectiveMerge = mergeParams || flattenOnGet && operation.method === "get";
|
|
465
501
|
const requestArg = operation.requestTypeExpression ? `params: ${operation.requestTypeExpression}, config?: RequestConfig` : "params?: void, config?: RequestConfig";
|
|
466
502
|
const defaultResponseType = operation.responseTypeName ?? "unknown";
|
|
467
503
|
const bodyOnly = Boolean(operation.requestBodySchema) && !operation.queryParams.length && !operation.pathParams.length;
|
|
468
504
|
const hasBodySchema = Boolean(operation.requestBodySchema);
|
|
469
|
-
const needsCleanData =
|
|
505
|
+
const needsCleanData = effectiveMerge && hasBodySchema && operation.pathParams.length > 0;
|
|
470
506
|
const destructureLine = needsCleanData ? ` const { ${operation.pathParams.map((p) => p.name).join(", ")}, ...requestData } = params;
|
|
471
507
|
` : "";
|
|
472
|
-
const dataValue = needsCleanData ? "requestData" :
|
|
508
|
+
const dataValue = needsCleanData ? "requestData" : effectiveMerge ? "params" : bodyOnly ? "params" : "params?.body";
|
|
473
509
|
const bodyLine = hasBodySchema ? ` data: ${dataValue},
|
|
474
510
|
` : "";
|
|
475
|
-
const queryLine = operation.queryParams.length ? ` params: ${
|
|
511
|
+
const queryLine = operation.queryParams.length ? ` params: ${effectiveMerge ? "params" : "params?.query"},
|
|
476
512
|
` : "";
|
|
477
|
-
const pathArg =
|
|
513
|
+
const pathArg = effectiveMerge ? "params" : "params?.path";
|
|
478
514
|
const urlValue = useGenericUrl && operation.pathParams.length ? `buildUrl(${JSON.stringify(operation.requestPath)}, ${pathArg})` : operation.pathParams.length ? `buildUrl(${pathArg})` : JSON.stringify(operation.requestPath);
|
|
479
515
|
const pathLine = ` url: ${urlValue},
|
|
480
516
|
`;
|
|
@@ -485,7 +521,7 @@ ${queryLine}${bodyLine} ...config,
|
|
|
485
521
|
});
|
|
486
522
|
}`;
|
|
487
523
|
}
|
|
488
|
-
function generateTsRequestFile(operation, httpClientPath, typeImportPath, mergeParams = false) {
|
|
524
|
+
function generateTsRequestFile(operation, httpClientPath, typeImportPath, mergeParams = false, flattenOnGet = false) {
|
|
489
525
|
const importTypes = [...operation.requestImportTypes, operation.responseTypeName, "RequestConfig"].filter(
|
|
490
526
|
(value, index, array) => Boolean(value) && array.indexOf(value) === index
|
|
491
527
|
);
|
|
@@ -501,10 +537,10 @@ function buildUrl(path?: Record<string, any>): string {
|
|
|
501
537
|
import request from ${JSON.stringify(httpClientPath)};
|
|
502
538
|
${importLine}
|
|
503
539
|
${buildUrlHelper}
|
|
504
|
-
${renderFunction2(operation, false, mergeParams)}
|
|
540
|
+
${renderFunction2(operation, false, mergeParams, flattenOnGet)}
|
|
505
541
|
`;
|
|
506
542
|
}
|
|
507
|
-
function generateTsModuleFile(operations, httpClientPath, typeImportPath, mergeParams = false) {
|
|
543
|
+
function generateTsModuleFile(operations, httpClientPath, typeImportPath, mergeParams = false, flattenOnGet = false) {
|
|
508
544
|
const importTypeSet = /* @__PURE__ */ new Set();
|
|
509
545
|
for (const op of operations) {
|
|
510
546
|
for (const t of op.requestImportTypes) importTypeSet.add(t);
|
|
@@ -519,7 +555,7 @@ function buildUrl(url: string, path?: Record<string, unknown>): string {
|
|
|
519
555
|
return url.replace(/\\{([^}]+)\\}/g, (_, key) => String(path?.[key] ?? ''));
|
|
520
556
|
}
|
|
521
557
|
` : "";
|
|
522
|
-
const functions = operations.map((op) => renderFunction2(op, true, mergeParams)).join("\n\n");
|
|
558
|
+
const functions = operations.map((op) => renderFunction2(op, true, mergeParams, flattenOnGet)).join("\n\n");
|
|
523
559
|
return `/* eslint-disable */
|
|
524
560
|
// Auto-generated by swgto.
|
|
525
561
|
import request from ${JSON.stringify(httpClientPath)};
|
|
@@ -743,7 +779,7 @@ async function generateFromConfig(cwd = process.cwd()) {
|
|
|
743
779
|
for (const [controllerName, controllerOperations] of Object.entries(controllerMap)) {
|
|
744
780
|
const relativeFile = path4.join(config.outputDir, moduleName, `${controllerName}.${config.outputType}`);
|
|
745
781
|
const absoluteFile = path4.join(cwd, relativeFile);
|
|
746
|
-
const content = config.outputType === "ts" ? generateTsModuleFile(controllerOperations, config.httpClientPath, getRootImportPath(config.typeName), config.mergeParams) : generateJsModuleFile(controllerOperations, config.httpClientPath, getRootImportPath(config.typeName), config.mergeParams);
|
|
782
|
+
const content = config.outputType === "ts" ? generateTsModuleFile(controllerOperations, config.httpClientPath, getRootImportPath(config.typeName), config.mergeParams, config.flattenOnGet) : generateJsModuleFile(controllerOperations, config.httpClientPath, getRootImportPath(config.typeName), config.mergeParams, config.flattenOnGet);
|
|
747
783
|
for (const op of controllerOperations) {
|
|
748
784
|
op.fileBaseName = controllerName;
|
|
749
785
|
}
|
|
@@ -754,7 +790,7 @@ async function generateFromConfig(cwd = process.cwd()) {
|
|
|
754
790
|
for (const operation of moduleOperations) {
|
|
755
791
|
const relativeFile = path4.join(config.outputDir, moduleName, `${operation.fileBaseName}.${config.outputType}`);
|
|
756
792
|
const absoluteFile = path4.join(cwd, relativeFile);
|
|
757
|
-
const content = config.outputType === "ts" ? generateTsRequestFile(operation, config.httpClientPath, getRootImportPath(config.typeName), config.mergeParams) : generateJsRequestFile(operation, config.httpClientPath, getRootImportPath(config.typeName), config.mergeParams);
|
|
793
|
+
const content = config.outputType === "ts" ? generateTsRequestFile(operation, config.httpClientPath, getRootImportPath(config.typeName), config.mergeParams, config.flattenOnGet) : generateJsRequestFile(operation, config.httpClientPath, getRootImportPath(config.typeName), config.mergeParams, config.flattenOnGet);
|
|
758
794
|
await writeTextFile(absoluteFile, content);
|
|
759
795
|
files.push(relativeFile);
|
|
760
796
|
}
|
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED