czh-api 1.0.2 → 1.0.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.
@@ -13,22 +13,83 @@ function addSchemaWithDependencies(name, module, allSchemas) {
13
13
  }
14
14
  const schema = allSchemas[name];
15
15
  if (!schema) {
16
+ console.warn(`警告: Schema "${name}" 未找到,跳过此引用`);
16
17
  return; // Schema not found
17
18
  }
18
19
  module.schemas[name] = schema;
19
- // Recursively add dependencies
20
- JSON.stringify(schema, (key, value) => {
21
- if (key === '$ref' && typeof value === 'string') {
22
- const depName = getSchemaName(value);
23
- addSchemaWithDependencies(depName, module, allSchemas);
20
+ // Recursively add dependencies with error handling
21
+ try {
22
+ JSON.stringify(schema, (key, value) => {
23
+ if (key === '$ref' && typeof value === 'string') {
24
+ const depName = getSchemaName(value);
25
+ if (depName && allSchemas[depName]) {
26
+ addSchemaWithDependencies(depName, module, allSchemas);
27
+ }
28
+ else {
29
+ console.warn(`警告: 依赖 Schema "${depName}" 未找到,跳过此引用`);
30
+ }
31
+ }
32
+ return value;
33
+ });
34
+ }
35
+ catch (error) {
36
+ console.warn(`警告: 处理 Schema "${name}" 的依赖时出错:`, error);
37
+ }
38
+ }
39
+ function toCamelCase(str) {
40
+ return str
41
+ .split('/')
42
+ .filter(p => p)
43
+ .map((part, index) => {
44
+ const cleaned = part.replace(/[^a-zA-Z0-9]/g, '');
45
+ if (index === 0) {
46
+ return cleaned.charAt(0).toLowerCase() + cleaned.slice(1);
24
47
  }
25
- return value;
26
- });
48
+ return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);
49
+ })
50
+ .join('');
27
51
  }
28
- function getModuleName(path) {
52
+ function getModuleName(path, pathPrefixes = []) {
53
+ // 尝试匹配配置的路径前缀
54
+ for (const prefix of pathPrefixes) {
55
+ if (path.startsWith(prefix.path)) {
56
+ // 获取前缀后的路径部分
57
+ const remainingPath = path.substring(prefix.path.length);
58
+ const parts = remainingPath.split('/').filter(p => p && !p.startsWith('{'));
59
+ // 确定包名
60
+ const packageName = prefix.packageName || toCamelCase(prefix.path);
61
+ // 如果有二级路径,使用二级路径作为子模块
62
+ if (parts.length > 0 && parts[0]) {
63
+ return `${packageName}/${parts[0]}`;
64
+ }
65
+ // 如果没有二级路径,直接使用包名
66
+ return packageName;
67
+ }
68
+ }
69
+ // 如果没有匹配到配置的前缀,使用默认逻辑
29
70
  const parts = path.split('/').filter(p => p && !p.startsWith('{'));
30
71
  return parts[0] || 'default';
31
72
  }
73
+ function convertOpenApiTypeToTypeScript(openApiType) {
74
+ if (!openApiType)
75
+ return 'any';
76
+ switch (openApiType) {
77
+ case 'integer':
78
+ return 'number';
79
+ case 'object':
80
+ return 'any';
81
+ case 'array':
82
+ return 'any[]';
83
+ case 'boolean':
84
+ return 'boolean';
85
+ case 'string':
86
+ return 'string';
87
+ case 'number':
88
+ return 'number';
89
+ default:
90
+ return 'any';
91
+ }
92
+ }
32
93
  function extractJsdocParamsFromSchema(schema, allSchemas) {
33
94
  var _a;
34
95
  const params = [];
@@ -45,11 +106,58 @@ function extractJsdocParamsFromSchema(schema, allSchemas) {
45
106
  if (targetSchema === null || targetSchema === void 0 ? void 0 : targetSchema.properties) {
46
107
  for (const propName in targetSchema.properties) {
47
108
  const prop = targetSchema.properties[propName];
48
- // 调试: 打印出当前处理的属性
49
- // console.log(`正在处理属性: ${propName}`, '值为:', prop);
109
+ let propType = 'any';
110
+ // 处理 anyOf 数组 (FastAPI 常用的联合类型)
111
+ if (prop.anyOf && Array.isArray(prop.anyOf)) {
112
+ const types = prop.anyOf.map(item => {
113
+ if (isReferenceObject(item)) {
114
+ return getSchemaName(item.$ref);
115
+ }
116
+ else {
117
+ const itemSchema = item;
118
+ // 处理 null 类型 (在 OpenAPI 中可能以不同方式表示)
119
+ if (itemSchema.type === 'null' ||
120
+ itemSchema.type === undefined && itemSchema.nullable === true ||
121
+ JSON.stringify(itemSchema) === '{"type":"null"}' ||
122
+ Object.keys(itemSchema).length === 0) {
123
+ return 'null';
124
+ }
125
+ return convertOpenApiTypeToTypeScript(itemSchema.type);
126
+ }
127
+ }).filter(type => type && type !== ''); // 只过滤掉空字符串,保留 any
128
+ if (types.length > 0) {
129
+ propType = types.join(' | ');
130
+ }
131
+ }
132
+ // 处理 oneOf 数组
133
+ else if (prop.oneOf && Array.isArray(prop.oneOf)) {
134
+ const types = prop.oneOf.map(item => {
135
+ if (isReferenceObject(item)) {
136
+ return getSchemaName(item.$ref);
137
+ }
138
+ else {
139
+ const itemSchema = item;
140
+ // 处理 null 类型 (在 OpenAPI 中可能以不同方式表示)
141
+ if (itemSchema.type === 'null' ||
142
+ itemSchema.type === undefined && itemSchema.nullable === true ||
143
+ JSON.stringify(itemSchema) === '{"type":"null"}' ||
144
+ Object.keys(itemSchema).length === 0) {
145
+ return 'null';
146
+ }
147
+ return convertOpenApiTypeToTypeScript(itemSchema.type);
148
+ }
149
+ }).filter(type => type && type !== ''); // 只过滤掉空字符串,保留 any
150
+ if (types.length > 0) {
151
+ propType = types.join(' | ');
152
+ }
153
+ }
154
+ // 处理普通类型
155
+ else if (prop.type) {
156
+ propType = convertOpenApiTypeToTypeScript(prop.type);
157
+ }
50
158
  params.push({
51
159
  name: propName,
52
- type: (prop === null || prop === void 0 ? void 0 : prop.type) || 'any',
160
+ type: propType,
53
161
  description: (prop === null || prop === void 0 ? void 0 : prop.description) || '',
54
162
  required: (_a = targetSchema.required) === null || _a === void 0 ? void 0 : _a.includes(propName)
55
163
  });
@@ -75,10 +183,11 @@ function getModuleNameFromPackage(operation) {
75
183
  * @param api The bundled OpenAPI document.
76
184
  * @param excludePaths Paths to exclude (by prefix).
77
185
  * @param includePaths Paths to include (by prefix). If provided, only these paths will be processed.
186
+ * @param pathPrefixes Path prefix configurations for custom module grouping.
78
187
  * @returns A structured representation of modules and their endpoints.
79
188
  */
80
- const processApi = (api, excludePaths = [], includePaths = []) => {
81
- var _a, _b, _c, _d, _e, _f, _g;
189
+ const processApi = (api, excludePaths = [], includePaths = [], pathPrefixes = []) => {
190
+ var _a, _b, _c, _d, _e, _f;
82
191
  const modules = {};
83
192
  const allSchemas = ((_a = api.components) === null || _a === void 0 ? void 0 : _a.schemas) || api.definitions || {};
84
193
  const moduleFunctionNames = {};
@@ -101,7 +210,7 @@ const processApi = (api, excludePaths = [], includePaths = []) => {
101
210
  if (!operation.tags)
102
211
  continue;
103
212
  const moduleNameFromPackage = getModuleNameFromPackage(operation);
104
- const moduleName = moduleNameFromPackage || getModuleName(path);
213
+ const moduleName = moduleNameFromPackage || getModuleName(path, pathPrefixes);
105
214
  if (!modules[moduleName]) {
106
215
  modules[moduleName] = {
107
216
  name: moduleName,
@@ -178,12 +287,6 @@ const processApi = (api, excludePaths = [], includePaths = []) => {
178
287
  };
179
288
  for (const param of operation.parameters) {
180
289
  if (!isReferenceObject(param)) {
181
- const paramToAdd = {
182
- name: param.name,
183
- description: param.description || '',
184
- required: param.required,
185
- type: ((_c = param.schema) === null || _c === void 0 ? void 0 : _c.type) || 'any'
186
- };
187
290
  // If the parameter's schema is a reference, expand its properties
188
291
  if (param.in === 'query' && param.schema && isReferenceObject(param.schema)) {
189
292
  const schemaName = getSchemaName(param.schema.$ref);
@@ -240,7 +343,7 @@ const processApi = (api, excludePaths = [], includePaths = []) => {
240
343
  // Handle standard Request Body
241
344
  if (operation.requestBody && !isReferenceObject(operation.requestBody) && !isFormData) {
242
345
  const requestBody = operation.requestBody;
243
- const jsonContent = (_d = requestBody.content) === null || _d === void 0 ? void 0 : _d['application/json'];
346
+ const jsonContent = (_c = requestBody.content) === null || _c === void 0 ? void 0 : _c['application/json'];
244
347
  if (jsonContent === null || jsonContent === void 0 ? void 0 : jsonContent.schema) {
245
348
  if (isReferenceObject(jsonContent.schema)) {
246
349
  const name = getSchemaName(jsonContent.schema.$ref);
@@ -276,7 +379,7 @@ const processApi = (api, excludePaths = [], includePaths = []) => {
276
379
  }
277
380
  else if (isFormData && operation.requestBody && !isReferenceObject(operation.requestBody) && !requestBodyTypeName) {
278
381
  // Handle cases where FormData is defined in requestBody but we haven't processed it yet
279
- const formDataContent = (_e = operation.requestBody.content) === null || _e === void 0 ? void 0 : _e['multipart/form-data'];
382
+ const formDataContent = (_d = operation.requestBody.content) === null || _d === void 0 ? void 0 : _d['multipart/form-data'];
280
383
  if (formDataContent === null || formDataContent === void 0 ? void 0 : formDataContent.schema) {
281
384
  const typeName = `${functionName.charAt(0).toUpperCase() + functionName.slice(1)}Data`;
282
385
  requestBodyTypeName = typeName;
@@ -289,7 +392,7 @@ const processApi = (api, excludePaths = [], includePaths = []) => {
289
392
  let responseTypeName = 'void';
290
393
  const successResponse = operation.responses['200'];
291
394
  if (successResponse) {
292
- const mediaType = ((_f = successResponse.content) === null || _f === void 0 ? void 0 : _f['*/*']) || ((_g = successResponse.content) === null || _g === void 0 ? void 0 : _g['application/json']);
395
+ const mediaType = ((_e = successResponse.content) === null || _e === void 0 ? void 0 : _e['*/*']) || ((_f = successResponse.content) === null || _f === void 0 ? void 0 : _f['application/json']);
293
396
  const schema = mediaType === null || mediaType === void 0 ? void 0 : mediaType.schema;
294
397
  if (schema && isReferenceObject(schema)) {
295
398
  const name = getSchemaName(schema.$ref);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "czh-api",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "A CLI tool to generate TypeScript API clients from Swagger/OpenAPI documents.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -19,6 +19,7 @@
19
19
  "author": "Czh",
20
20
  "license": "ISC",
21
21
  "dependencies": {
22
+ "@apiture/openapi-down-convert": "^0.14.2",
22
23
  "@types/pinyin": "^2.10.2",
23
24
  "axios": "^1.10.0",
24
25
  "chalk": "^4.1.2",