czh-api 1.0.3 → 1.0.5

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.
@@ -18,8 +18,8 @@ import { compile } from 'json-schema-to-typescript';
18
18
  import { Converter } from '@apiture/openapi-down-convert';
19
19
 
20
20
  // 统一的类型转换函数
21
- function convertOpenApiTypeToTypeScript(openApiType: string | undefined): string {
22
- if (!openApiType) return 'any';
21
+ function convertOpenApiTypeToTypeScript(openApiType: string | undefined): string {
22
+ if (!openApiType) return 'any';
23
23
 
24
24
  switch (openApiType) {
25
25
  case 'integer':
@@ -32,91 +32,306 @@ function convertOpenApiTypeToTypeScript(openApiType: string | undefined): string
32
32
  return 'boolean';
33
33
  case 'string':
34
34
  return 'string';
35
- case 'number':
36
- return 'number';
37
- default:
38
- return 'any';
39
- }
40
- }
35
+ case 'number':
36
+ return 'number';
37
+ default:
38
+ return openApiType;
39
+ }
40
+ }
41
41
 
42
42
  // 改进类型定义的辅助函数
43
- function improveTypeDefinitions(typesContent: string, endpoints: any[]): string {
43
+ function isValidTypeIdentifier(typeName: string | undefined): boolean {
44
+ if (!typeName) return false;
45
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(typeName);
46
+ }
47
+
48
+ function escapeRegex(value: string): string {
49
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
50
+ }
51
+
52
+ function resolveInterfaceTargetType(typeName: string | undefined): string | null {
53
+ if (typeof typeName !== 'string' || typeName.length === 0) return null;
54
+ if (isValidTypeIdentifier(typeName)) return typeName;
55
+
56
+ const arrayMatch = typeName.match(/^([A-Za-z_$][A-Za-z0-9_$]*)\[\]$/);
57
+ if (arrayMatch) {
58
+ return arrayMatch[1];
59
+ }
60
+
61
+ return null;
62
+ }
63
+
64
+ function findInterfaceBlockRange(content: string, typeName: string): { start: number; end: number } | null {
65
+ const startRegex = new RegExp(`export interface ${escapeRegex(typeName)}\\s*\\{`);
66
+ const match = startRegex.exec(content);
67
+ if (!match) return null;
68
+
69
+ const start = match.index;
70
+ const openBraceIndex = start + match[0].lastIndexOf('{');
71
+ let depth = 0;
72
+
73
+ for (let i = openBraceIndex; i < content.length; i++) {
74
+ const ch = content[i];
75
+ if (ch === '{') {
76
+ depth++;
77
+ } else if (ch === '}') {
78
+ depth--;
79
+ if (depth === 0) {
80
+ return { start, end: i + 1 };
81
+ }
82
+ }
83
+ }
84
+
85
+ return null;
86
+ }
87
+
88
+ const TS_BUILTIN_TYPE_NAMES = new Set([
89
+ 'string',
90
+ 'number',
91
+ 'boolean',
92
+ 'null',
93
+ 'undefined',
94
+ 'void',
95
+ 'any',
96
+ 'unknown',
97
+ 'never',
98
+ 'object',
99
+ 'Record',
100
+ 'Array',
101
+ 'Date',
102
+ 'Promise',
103
+ 'true',
104
+ 'false',
105
+ ]);
106
+
107
+ function extractTypeIdentifiers(typeExpr: string | undefined): string[] {
108
+ if (!typeExpr) return [];
109
+ const matches = typeExpr.match(/[A-Za-z_$][A-Za-z0-9_$]*/g) || [];
110
+ return [...new Set(matches.filter(name => !TS_BUILTIN_TYPE_NAMES.has(name)))];
111
+ }
112
+
113
+ function getSchemaNameFromRef(ref: string | undefined): string {
114
+ if (!ref) return '';
115
+ return ref.split('/').pop() || '';
116
+ }
117
+
118
+ function resolveSchemaTypeFromModuleSchemas(
119
+ schema: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject | undefined,
120
+ moduleSchemas: { [typeName: string]: OpenAPIV3.SchemaObject },
121
+ visitedRefs: Set<string> = new Set()
122
+ ): string {
123
+ if (!schema) return 'any';
124
+
125
+ if ((schema as OpenAPIV3.ReferenceObject).$ref) {
126
+ const refName = getSchemaNameFromRef((schema as OpenAPIV3.ReferenceObject).$ref);
127
+ return refName || 'any';
128
+ }
129
+
130
+ const schemaObj = schema as OpenAPIV3.SchemaObject;
131
+
132
+ if (schemaObj.anyOf && Array.isArray(schemaObj.anyOf) && schemaObj.anyOf.length > 0) {
133
+ const types = schemaObj.anyOf
134
+ .map(item => resolveSchemaTypeFromModuleSchemas(item as OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject, moduleSchemas, visitedRefs))
135
+ .filter(Boolean);
136
+ if (types.length > 0) {
137
+ return [...new Set(types)].join(' | ');
138
+ }
139
+ }
140
+
141
+ if (schemaObj.oneOf && Array.isArray(schemaObj.oneOf) && schemaObj.oneOf.length > 0) {
142
+ const types = schemaObj.oneOf
143
+ .map(item => resolveSchemaTypeFromModuleSchemas(item as OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject, moduleSchemas, visitedRefs))
144
+ .filter(Boolean);
145
+ if (types.length > 0) {
146
+ return [...new Set(types)].join(' | ');
147
+ }
148
+ }
149
+
150
+ if (schemaObj.type === 'array') {
151
+ const itemType = resolveSchemaTypeFromModuleSchemas(
152
+ schemaObj.items as OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject | undefined,
153
+ moduleSchemas,
154
+ visitedRefs
155
+ );
156
+ return `${itemType || 'any'}[]`;
157
+ }
158
+
159
+ if (schemaObj.type === 'object' && schemaObj.additionalProperties) {
160
+ if (schemaObj.additionalProperties === true) {
161
+ return 'Record<string, any>';
162
+ }
163
+ const valueType = resolveSchemaTypeFromModuleSchemas(
164
+ schemaObj.additionalProperties as OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject,
165
+ moduleSchemas,
166
+ visitedRefs
167
+ );
168
+ return `Record<string, ${valueType || 'any'}>`;
169
+ }
170
+
171
+ if (schemaObj.type === 'object' && schemaObj.properties && Object.keys(schemaObj.properties).length > 0) {
172
+ const requiredSet = new Set(schemaObj.required || []);
173
+ const inlineFields = Object.entries(schemaObj.properties).map(([key, value]) => {
174
+ const fieldType = resolveSchemaTypeFromModuleSchemas(
175
+ value as OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject,
176
+ moduleSchemas,
177
+ visitedRefs
178
+ );
179
+ const optional = requiredSet.has(key) ? '' : '?';
180
+ return `${key}${optional}: ${fieldType || 'any'}`;
181
+ });
182
+ return `{ ${inlineFields.join('; ')} }`;
183
+ }
184
+
185
+ const baseType = convertOpenApiTypeToTypeScript(schemaObj.type);
186
+ if (!baseType) return 'any';
187
+ return schemaObj.nullable ? `${baseType} | null` : baseType;
188
+ }
189
+
190
+ function collectSchemaProperties(
191
+ schema: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject | undefined,
192
+ moduleSchemas: { [typeName: string]: OpenAPIV3.SchemaObject },
193
+ visitedRefs: Set<string> = new Set()
194
+ ): { properties: Record<string, OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject>; required: Set<string> } {
195
+ const collected: { properties: Record<string, OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject>; required: Set<string> } = {
196
+ properties: {},
197
+ required: new Set<string>(),
198
+ };
199
+ if (!schema) return collected;
200
+
201
+ if ((schema as OpenAPIV3.ReferenceObject).$ref) {
202
+ const ref = (schema as OpenAPIV3.ReferenceObject).$ref;
203
+ if (!visitedRefs.has(ref)) {
204
+ visitedRefs.add(ref);
205
+ const refName = getSchemaNameFromRef(ref);
206
+ const refSchema = moduleSchemas[refName];
207
+ if (refSchema) {
208
+ const nested = collectSchemaProperties(refSchema, moduleSchemas, visitedRefs);
209
+ Object.assign(collected.properties, nested.properties);
210
+ nested.required.forEach(field => collected.required.add(field));
211
+ }
212
+ }
213
+ return collected;
214
+ }
215
+
216
+ const schemaObj = schema as OpenAPIV3.SchemaObject;
217
+
218
+ if (schemaObj.allOf && Array.isArray(schemaObj.allOf)) {
219
+ schemaObj.allOf.forEach(item => {
220
+ const nested = collectSchemaProperties(item as OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject, moduleSchemas, visitedRefs);
221
+ Object.assign(collected.properties, nested.properties);
222
+ nested.required.forEach(field => collected.required.add(field));
223
+ });
224
+ }
225
+
226
+ if (schemaObj.properties) {
227
+ Object.entries(schemaObj.properties).forEach(([name, value]) => {
228
+ collected.properties[name] = value as OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject;
229
+ });
230
+ }
231
+
232
+ (schemaObj.required || []).forEach(field => collected.required.add(field));
233
+ return collected;
234
+ }
235
+
236
+ function buildInterfaceDefinitionFromModuleSchemas(
237
+ typeName: string,
238
+ moduleSchemas: { [typeName: string]: OpenAPIV3.SchemaObject }
239
+ ): string | null {
240
+ const schema = moduleSchemas[typeName];
241
+ if (!schema) return null;
242
+
243
+ const { properties, required } = collectSchemaProperties(schema, moduleSchemas);
244
+ const entries = Object.entries(properties);
245
+ if (entries.length === 0) return null;
246
+
247
+ const lines = entries.map(([propName, propSchema]) => {
248
+ const optional = required.has(propName) ? '' : '?';
249
+ const propType = resolveSchemaTypeFromModuleSchemas(propSchema, moduleSchemas) || 'any';
250
+ const propDescription = (propSchema as OpenAPIV3.SchemaObject).description || '';
251
+ const comment = propDescription ? ` // ${propDescription}` : '';
252
+ return ` ${propName}${optional}: ${propType};${comment}`;
253
+ });
254
+
255
+ return `export interface ${typeName} {\n${lines.join('\n')}\n}`;
256
+ }
257
+
258
+ function improveTypeDefinitions(typesContent: string, endpoints: any[]): string {
44
259
  let improvedContent = typesContent;
45
260
 
46
261
  // 收集所有需要改进的类型定义
47
262
  const typeImprovements: { [typeName: string]: string } = {};
263
+
264
+ // 辅助函数:将 jsdoc params 转换为接口字段
265
+ function buildFieldsFromJsdocParams(jsdocParams: any[]): string {
266
+ return jsdocParams
267
+ .filter((param: any) => param.name && param.type)
268
+ .map((param: any) => {
269
+ const optional = !param.required ? '?' : '';
270
+ let type = param.type;
271
+
272
+ // 处理联合类型,确保正确的 TypeScript 语法
273
+ if (type && type.includes(' | ')) {
274
+ if (type.includes('null')) {
275
+ type = type.replace(/\s*\|\s*null/g, ' | null');
276
+ }
277
+ } else {
278
+ type = convertOpenApiTypeToTypeScript(type);
279
+ }
280
+
281
+ const comment = param.description ? ` // ${param.description}` : '';
282
+ return ` ${param.name}${optional}: ${type};${comment}`;
283
+ })
284
+ .join('\n');
285
+ }
48
286
 
49
287
  endpoints.forEach(endpoint => {
50
- // 改进 params 类型
51
- if (endpoint.requestParamsTypeName && endpoint.jsdocParams) {
52
- const paramsFields = endpoint.jsdocParams
53
- .filter((param: any) => param.name && param.type)
54
- .map((param: any) => {
55
- const optional = !param.required ? '?' : '';
56
- let type = param.type;
57
-
58
- // 处理联合类型,确保正确的 TypeScript 语法
59
- if (type && type.includes(' | ')) {
60
- // 如果包含 null,确保格式正确
61
- if (type.includes('null')) {
62
- type = type.replace(/\s*\|\s*null/g, ' | null');
63
- }
64
- } else {
65
- // 处理基本类型转换
66
- type = convertOpenApiTypeToTypeScript(type);
67
- }
68
-
69
- const comment = param.description ? ` // ${param.description}` : '';
70
- return ` ${param.name}${optional}: ${type};${comment}`;
71
- })
72
- .join('\n');
73
-
74
- if (paramsFields) {
75
- typeImprovements[endpoint.requestParamsTypeName] = `export interface ${endpoint.requestParamsTypeName} {
76
- ${paramsFields}
77
- }`;
78
- }
79
- }
288
+ // 改进 params 类型 - 使用 paramsJsdocParams(仅包含 path/query 参数)
289
+ const requestParamsTypeName = endpoint.requestParamsTypeName;
290
+ if (requestParamsTypeName && isValidTypeIdentifier(requestParamsTypeName) && endpoint.paramsJsdocParams && endpoint.paramsJsdocParams.length > 0) {
291
+ const paramsFields = buildFieldsFromJsdocParams(endpoint.paramsJsdocParams);
292
+
293
+ if (paramsFields) {
294
+ typeImprovements[requestParamsTypeName] = `export interface ${requestParamsTypeName} {
295
+ ${paramsFields}
296
+ }`;
297
+ }
298
+ }
80
299
 
81
- // 改进 requestBody 类型
82
- if (endpoint.requestBodyTypeName && endpoint.jsdocParams) {
83
- const dataFields = endpoint.jsdocParams
84
- .filter((param: any) => param.name && param.type)
85
- .map((param: any) => {
86
- const optional = !param.required ? '?' : '';
87
- let type = param.type;
88
-
89
- // 处理联合类型,确保正确的 TypeScript 语法
90
- if (type && type.includes(' | ')) {
91
- // 如果包含 null,确保格式正确
92
- if (type.includes('null')) {
93
- type = type.replace(/\s*\|\s*null/g, ' | null');
94
- }
95
- } else {
96
- // 处理基本类型转换
97
- type = convertOpenApiTypeToTypeScript(type);
98
- }
99
-
100
- const comment = param.description ? ` // ${param.description}` : '';
101
- return ` ${param.name}${optional}: ${type};${comment}`;
102
- })
103
- .join('\n');
104
-
105
- if (dataFields) {
106
- typeImprovements[endpoint.requestBodyTypeName] = `export interface ${endpoint.requestBodyTypeName} {
107
- ${dataFields}
108
- }`;
109
- }
110
- }
300
+ // 改进 requestBody 类型 - 使用 dataJsdocParams(仅包含 requestBody 参数)
301
+ const requestBodyTypeName = endpoint.requestBodyTypeName;
302
+ if (requestBodyTypeName && isValidTypeIdentifier(requestBodyTypeName) && endpoint.dataJsdocParams && endpoint.dataJsdocParams.length > 0) {
303
+ const dataFields = buildFieldsFromJsdocParams(endpoint.dataJsdocParams);
304
+
305
+ if (dataFields) {
306
+ typeImprovements[requestBodyTypeName] = `export interface ${requestBodyTypeName} {
307
+ ${dataFields}
308
+ }`;
309
+ }
310
+ }
311
+
312
+ // 改进 response 类型 - 使用 responseJsdocParams(包含响应字段信息)
313
+ const responseTargetType = resolveInterfaceTargetType(endpoint.responseTypeName);
314
+ if (responseTargetType && endpoint.responseTypeName !== 'void' && endpoint.responseJsdocParams && endpoint.responseJsdocParams.length > 0) {
315
+ const responseFields = buildFieldsFromJsdocParams(endpoint.responseJsdocParams);
316
+
317
+ if (responseFields) {
318
+ typeImprovements[responseTargetType] = `export interface ${responseTargetType} {
319
+ ${responseFields}
320
+ }`;
321
+ }
322
+ }
111
323
  });
112
324
 
113
325
  // 替换现有的类型定义
114
326
  Object.entries(typeImprovements).forEach(([typeName, newDefinition]) => {
115
327
  // 查找并替换现有的接口定义
116
- const interfaceRegex = new RegExp(`export interface ${typeName}\\s*\\{[^}]*\\}`, 'g');
117
- if (interfaceRegex.test(improvedContent)) {
118
- improvedContent = improvedContent.replace(interfaceRegex, newDefinition);
119
- } else {
328
+ const blockRange = findInterfaceBlockRange(improvedContent, typeName);
329
+ if (blockRange) {
330
+ improvedContent =
331
+ improvedContent.slice(0, blockRange.start) +
332
+ newDefinition +
333
+ improvedContent.slice(blockRange.end);
334
+ } else {
120
335
  // 如果没找到接口定义,添加到末尾
121
336
  improvedContent += '\n\n' + newDefinition;
122
337
  }
@@ -126,7 +341,7 @@ ${dataFields}
126
341
  }
127
342
 
128
343
  // 移除无用的类型别名
129
- function removeUselessTypeAliases(typesContent: string): string {
344
+ function removeUselessTypeAliases(typesContent: string): string {
130
345
  let cleanedContent = typesContent;
131
346
 
132
347
  // 收集要删除的类型别名及其对应的基础类型
@@ -193,10 +408,42 @@ function removeUselessTypeAliases(typesContent: string): string {
193
408
  // 移除孤立的类型注释(没有对应类型定义的注释)
194
409
  cleanedContent = cleanedContent.replace(/\/\*\*\n \* [^\n]*\n \*\/\n(?!export)/g, '');
195
410
 
196
- return cleanedContent;
197
- }
198
-
199
- interface IConfig {
411
+ return cleanedContent;
412
+ }
413
+
414
+ function normalizeRef(refValue: string): string {
415
+ const match = refValue.match(/^#\/components\/schemas\/([^/]+)$/);
416
+ if (match) {
417
+ return `#/definitions/${match[1]}`;
418
+ }
419
+ return refValue;
420
+ }
421
+
422
+ function normalizeSchemaRefs<T>(value: T): T {
423
+ if (Array.isArray(value)) {
424
+ return value.map(item => normalizeSchemaRefs(item)) as T;
425
+ }
426
+
427
+ if (value && typeof value === 'object') {
428
+ const obj = value as Record<string, unknown>;
429
+ const normalized: Record<string, unknown> = {};
430
+
431
+ for (const key in obj) {
432
+ const currentValue = obj[key];
433
+ if (key === '$ref' && typeof currentValue === 'string') {
434
+ normalized[key] = normalizeRef(currentValue);
435
+ } else {
436
+ normalized[key] = normalizeSchemaRefs(currentValue);
437
+ }
438
+ }
439
+
440
+ return normalized as T;
441
+ }
442
+
443
+ return value;
444
+ }
445
+
446
+ interface IConfig {
200
447
  url: string;
201
448
  outputDir: string;
202
449
  httpClientPath: string;
@@ -317,13 +564,14 @@ export const handleBuild = async () => {
317
564
  if (Object.keys(module.schemas).length > 0) {
318
565
  console.log(chalk.blue(`正在为模块 "${moduleName}" 生成类型文件,包含 ${Object.keys(module.schemas).length} 个 schema`));
319
566
  try {
320
- const schemasWithTitles: { [key: string]: OpenAPIV3.SchemaObject } = {};
321
- for (const schemaName in module.schemas) {
322
- schemasWithTitles[schemaName] = {
323
- ...module.schemas[schemaName],
324
- title: schemaName,
325
- };
326
- }
567
+ const normalizedModuleSchemas = normalizeSchemaRefs(module.schemas);
568
+ const schemasWithTitles: { [key: string]: OpenAPIV3.SchemaObject } = {};
569
+ for (const schemaName in normalizedModuleSchemas) {
570
+ schemasWithTitles[schemaName] = {
571
+ ...normalizedModuleSchemas[schemaName],
572
+ title: schemaName,
573
+ };
574
+ }
327
575
 
328
576
  const rootSchemaForCompiler = {
329
577
  title: 'schemas',
@@ -374,9 +622,10 @@ export const handleBuild = async () => {
374
622
 
375
623
  // 尝试逐个验证 schema,过滤掉有问题的
376
624
  const validSchemas: { [key: string]: OpenAPIV3.SchemaObject } = {};
377
- const invalidSchemas: string[] = [];
625
+ const invalidSchemas: string[] = [];
626
+ const normalizedModuleSchemas = normalizeSchemaRefs(module.schemas);
378
627
 
379
- for (const schemaName in module.schemas) {
628
+ for (const schemaName in normalizedModuleSchemas) {
380
629
  try {
381
630
  // 尝试单独编译每个 schema
382
631
  const testSchema = {
@@ -384,12 +633,12 @@ export const handleBuild = async () => {
384
633
  type: 'object',
385
634
  properties: {},
386
635
  additionalProperties: false,
387
- definitions: { [schemaName]: module.schemas[schemaName] },
388
- components: { schemas: { [schemaName]: module.schemas[schemaName] } }
636
+ definitions: { [schemaName]: normalizedModuleSchemas[schemaName] },
637
+ components: { schemas: { [schemaName]: normalizedModuleSchemas[schemaName] } }
389
638
  };
390
639
  await compile(testSchema as any, 'test', { unreachableDefinitions: true });
391
640
  validSchemas[schemaName] = {
392
- ...module.schemas[schemaName],
641
+ ...normalizedModuleSchemas[schemaName],
393
642
  title: schemaName
394
643
  };
395
644
  } catch (schemaError) {
@@ -463,81 +712,100 @@ export const handleBuild = async () => {
463
712
  console.log(chalk.blue(`为模块 "${moduleName}" 生成基于 JSDoc 的类型定义...`));
464
713
 
465
714
  // 创建类型定义映射
466
- const typeDefinitions: { [typeName: string]: string } = {};
715
+ const typeDefinitions: { [typeName: string]: string } = {};
716
+ const nestedReferencedTypes = new Set<string>();
467
717
 
468
718
  // 从 endpoints 中提取类型定义
469
719
  module.endpoints.forEach(endpoint => {
470
- // 处理 params 类型
471
- if (endpoint.requestParamsTypeName && endpoint.jsdocParams) {
472
- const paramsFields = endpoint.jsdocParams
473
- .filter(param => param.name && param.type)
474
- .map(param => {
475
- const optional = !param.required ? '?' : '';
476
- let type = param.type;
477
-
478
- // 处理联合类型
479
- if (type && type.includes(' | ')) {
480
- // 如果包含 null,确保格式正确
481
- if (type.includes('null')) {
482
- type = type.replace(/\s*\|\s*null/g, ' | null');
483
- }
484
- } else {
485
- // 处理基本类型转换
486
- type = convertOpenApiTypeToTypeScript(type);
487
- }
488
-
489
- const comment = param.description ? ` // ${param.description}` : '';
490
- return ` ${param.name}${optional}: ${type};${comment}`;
491
- })
492
- .join('\n');
493
-
494
- if (paramsFields) {
495
- typeDefinitions[endpoint.requestParamsTypeName] = `export interface ${endpoint.requestParamsTypeName} {
496
- ${paramsFields}
497
- }`;
498
- }
499
- }
720
+ // 辅助函数:将 jsdoc params 转换为字段
721
+ const buildFields = (params: any[]) => params
722
+ .filter((param: any) => param.name && param.type)
723
+ .map((param: any) => {
724
+ const optional = !param.required ? '?' : '';
725
+ let type = param.type;
726
+
727
+ if (type && type.includes(' | ')) {
728
+ if (type.includes('null')) {
729
+ type = type.replace(/\s*\|\s*null/g, ' | null');
730
+ }
731
+ } else {
732
+ type = convertOpenApiTypeToTypeScript(type);
733
+ }
734
+
735
+ extractTypeIdentifiers(type).forEach(typeName => {
736
+ if (isValidTypeIdentifier(typeName)) {
737
+ nestedReferencedTypes.add(typeName);
738
+ }
739
+ });
740
+
741
+ const comment = param.description ? ` // ${param.description}` : '';
742
+ return ` ${param.name}${optional}: ${type};${comment}`;
743
+ })
744
+ .join('\n');
745
+
746
+ // 处理 params 类型 - 使用 paramsJsdocParams
747
+ const requestParamsTypeName = endpoint.requestParamsTypeName;
748
+ if (requestParamsTypeName && isValidTypeIdentifier(requestParamsTypeName) && endpoint.paramsJsdocParams && endpoint.paramsJsdocParams.length > 0) {
749
+ const paramsFields = buildFields(endpoint.paramsJsdocParams);
750
+
751
+ if (paramsFields) {
752
+ typeDefinitions[requestParamsTypeName] = `export interface ${requestParamsTypeName} {
753
+ ${paramsFields}
754
+ }`;
755
+ }
756
+ }
500
757
 
501
- // 处理 data 类型 (requestBody)
502
- if (endpoint.requestBodyTypeName && endpoint.jsdocParams) {
503
- const dataFields = endpoint.jsdocParams
504
- .filter(param => param.name && param.type)
505
- .map(param => {
506
- const optional = !param.required ? '?' : '';
507
- let type = param.type;
508
-
509
- // 处理联合类型
510
- if (type && type.includes(' | ')) {
511
- // 如果包含 null,确保格式正确
512
- if (type.includes('null')) {
513
- type = type.replace(/\s*\|\s*null/g, ' | null');
514
- }
515
- } else {
516
- // 处理基本类型转换
517
- type = convertOpenApiTypeToTypeScript(type);
518
- }
519
-
520
- const comment = param.description ? ` // ${param.description}` : '';
521
- return ` ${param.name}${optional}: ${type};${comment}`;
522
- })
523
- .join('\n');
524
-
525
- if (dataFields) {
526
- typeDefinitions[endpoint.requestBodyTypeName] = `export interface ${endpoint.requestBodyTypeName} {
527
- ${dataFields}
528
- }`;
529
- }
530
- }
758
+ // 处理 data 类型 (requestBody) - 使用 dataJsdocParams
759
+ const requestBodyTypeName = endpoint.requestBodyTypeName;
760
+ if (requestBodyTypeName && isValidTypeIdentifier(requestBodyTypeName) && endpoint.dataJsdocParams && endpoint.dataJsdocParams.length > 0) {
761
+ const dataFields = buildFields(endpoint.dataJsdocParams);
762
+
763
+ if (dataFields) {
764
+ typeDefinitions[requestBodyTypeName] = `export interface ${requestBodyTypeName} {
765
+ ${dataFields}
766
+ }`;
767
+ }
768
+ }
769
+
770
+ // 处理 response 类型 - 使用 responseJsdocParams
771
+ const responseTargetType = resolveInterfaceTargetType(endpoint.responseTypeName);
772
+ if (responseTargetType && endpoint.responseTypeName !== 'void' && endpoint.responseJsdocParams && endpoint.responseJsdocParams.length > 0) {
773
+ const responseFields = buildFields(endpoint.responseJsdocParams);
774
+
775
+ if (responseFields) {
776
+ typeDefinitions[responseTargetType] = `export interface ${responseTargetType} {
777
+ ${responseFields}
778
+ }`;
779
+ }
780
+ }
531
781
  });
532
782
 
533
783
  // 为没有定义的类型添加基本定义
534
- allReferencedTypes.forEach(typeName => {
535
- if (!typeDefinitions[typeName]) {
536
- typeDefinitions[typeName] = `export interface ${typeName} {
537
- [key: string]: any;
538
- }`;
539
- }
540
- });
784
+ const fallbackReferencedTypes = [
785
+ ...new Set([
786
+ ...allReferencedTypes,
787
+ ...Object.keys(module.schemas || {}),
788
+ ...nestedReferencedTypes,
789
+ ])
790
+ ];
791
+ fallbackReferencedTypes.forEach(typeName => {
792
+ if (!isValidTypeIdentifier(typeName)) {
793
+ return;
794
+ }
795
+ if (!typeDefinitions[typeName]) {
796
+ const schemaBasedDefinition = buildInterfaceDefinitionFromModuleSchemas(
797
+ typeName,
798
+ module.schemas as { [typeName: string]: OpenAPIV3.SchemaObject }
799
+ );
800
+ if (schemaBasedDefinition) {
801
+ typeDefinitions[typeName] = schemaBasedDefinition;
802
+ return;
803
+ }
804
+ typeDefinitions[typeName] = `export interface ${typeName} {
805
+ [key: string]: any;
806
+ }`;
807
+ }
808
+ });
541
809
 
542
810
  const basicTypesContent = `/* eslint-disable */
543
811
  /**
@@ -611,4 +879,4 @@ ${Object.values(typeDefinitions).join('\n\n')}
611
879
  } catch (error) {
612
880
  console.error(chalk.red('An error occurred during build process:'), error);
613
881
  }
614
- };
882
+ };