gen-api-types 1.0.1 → 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.
package/README.md CHANGED
@@ -65,8 +65,8 @@ npx gen-api-types -o output_dir -O output_file_name ./api_dir1 ./api_dir2
65
65
  Options:
66
66
  -h, --help 输出帮助信息
67
67
  -r, --project_root <path> 项目根目录
68
- -o, --output_file <path> 输出文件,默认index.d.ts
69
- -d, --output_dir <path> 输出目录,默认当前根目录
68
+ -O, --output_file <path> 输出文件名
69
+ -o, --output_dir <path> 输出目录
70
70
  -t, --ts_config_path <path> tsconfig.json 文件路径
71
71
  ```
72
72
 
package/package.json CHANGED
@@ -1,34 +1,35 @@
1
- {
2
- "name": "gen-api-types",
3
- "version": "1.0.1",
4
- "description": "",
5
- "main": "./src/index.ts",
6
- "bin": {
7
- "gen-api-types": "src/cli/index.ts"
8
- },
9
- "scripts": {
10
- "test": "echo \"Error: no test specified\" && exit 1",
11
- "build": "webpack",
12
- "publish:patch": "npm version patch && npm publish"
13
- },
14
- "keywords": [],
15
- "files": [
16
- "src",
17
- "tsconfig.json",
18
- "package.json"
19
- ],
20
- "author": "",
21
- "license": "ISC",
22
- "packageManager": "pnpm@10.7.1",
23
- "dependencies": {
24
- "reflect-metadata": "^0.2.2",
25
- "ts-morph": "^27.0.0",
26
- "tsx": "^4.20.5"
27
- },
28
- "devDependencies": {
29
- "@types/node": "^24.3.1",
30
- "ts-loader": "^9.5.4",
31
- "webpack": "^5.101.0",
32
- "webpack-cli": "^6.0.1"
33
- }
34
- }
1
+ {
2
+ "name": "gen-api-types",
3
+ "version": "1.0.3",
4
+ "description": "",
5
+ "main": "./src/index.ts",
6
+ "bin": {
7
+ "gen-api-types": "src/cli/index.ts"
8
+ },
9
+ "scripts": {
10
+ "test": "vitest",
11
+ "build": "webpack",
12
+ "publish:patch": "npm version patch && npm publish"
13
+ },
14
+ "keywords": [],
15
+ "files": [
16
+ "src",
17
+ "tsconfig.json",
18
+ "package.json"
19
+ ],
20
+ "author": "",
21
+ "license": "ISC",
22
+ "packageManager": "pnpm@10.7.1",
23
+ "dependencies": {
24
+ "reflect-metadata": "^0.2.2",
25
+ "ts-morph": "^27.0.0",
26
+ "tsx": "^4.20.5"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^24.3.1",
30
+ "ts-loader": "^9.5.4",
31
+ "vitest": "^3.2.4",
32
+ "webpack": "^5.101.0",
33
+ "webpack-cli": "^6.0.1"
34
+ }
35
+ }
package/src/argv/index.ts CHANGED
@@ -51,8 +51,8 @@ if (help) {
51
51
  Options:
52
52
  -h, --help 输出帮助信息
53
53
  -r, --project_root <path> 项目根目录
54
- -o, --output_file <path> 输出文件
55
- -d, --output_dir <path> 输出目录
54
+ -O, --output_file <path> 输出文件名
55
+ -o, --output_dir <path> 输出目录
56
56
  -t, --ts_config_path <path> tsconfig.json 文件路径
57
57
  `
58
58
  )
package/src/cli/index.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env tsx
2
2
 
3
3
 
4
- // scripts/generate-api-types.ts
5
4
  import * as path from 'path';
6
5
  import { Decorator, Project } from 'ts-morph';
7
6
  import { pathToFileURL } from 'url';
@@ -9,10 +8,12 @@ import { output_dir, output_file, positionals } from '../argv';
9
8
  import { C_DECO_NAME, M_DECO_NAME } from '../constant';
10
9
  import { GenTypeOptions } from '../decotators';
11
10
  import { TypeTransformer } from '../transformer';
11
+ import { formatResultList } from '../utils';
12
12
 
13
-
13
+ // console.log('positionals', positionals)
14
14
 
15
15
  const sourceFilesGlob = positionals.map(dir => path.normalize(`${dir}/**/*.ts`))
16
+ const out_put_target = path.resolve(output_dir, output_file)
16
17
 
17
18
  // console.log('arg', _arg)
18
19
  // console.log('sourceFilesGlob', sourceFilesGlob)
@@ -49,6 +50,7 @@ function getApiMethodsInfo() {
49
50
  const project = new Project({});
50
51
 
51
52
  project.addSourceFilesAtPaths(sourceFilesGlob);
53
+ // console.log('project.getSourceFiles().length', project.getSourceFiles().length)
52
54
  // debugger
53
55
  // 3. 遍历所有源文件
54
56
  for (const sourceFile of project.getSourceFiles()) {
@@ -84,11 +86,10 @@ function getApiMethodsInfo() {
84
86
  return apiMethodsInfo
85
87
  }
86
88
 
87
- type ExcuteApiMethodsResult = {
88
- successList: { data: any, typeName: string, fullMethodName: string }[],
89
- errorList: { fullMethodName: string, error: any }[]
89
+ type ExecuteApiMethodResult = {
90
+ data?: any, typeName: string, fullMethodName: string, error?: any
90
91
  }
91
- async function excuteApiMethods(apiMethodsInfo: ApiMethodInfo[]): Promise<ExcuteApiMethodsResult> {
92
+ async function executeApiMethods(apiMethodsInfo: ApiMethodInfo[]): Promise<ExecuteApiMethodResult[]> {
92
93
  const apiModuleMap = new Map<string, any>();
93
94
  const taskList = apiMethodsInfo.map(async (apiMethodInfo) => {
94
95
  const { className, methodName, fullMethodName, modulePath, args, typeName } = apiMethodInfo
@@ -103,69 +104,89 @@ async function excuteApiMethods(apiMethodsInfo: ApiMethodInfo[]): Promise<Excute
103
104
 
104
105
  try {
105
106
  apiModule = await import(pathToFileURL(modulePath).href)
106
- console.log('apiMethod', apiModule)
107
+ // console.log('apiMethod', apiModule)
107
108
  if (apiModule) apiModuleMap.set(modulePath, apiModule)
108
109
  } catch (error) {
109
- console.log('import modulePath error', error)
110
+ console.log(`import ${modulePath} error \r\n`, error)
111
+ return { error: `module error`, fullMethodName, typeName }
110
112
  }
111
-
112
113
  }
114
+
115
+
113
116
  const apiMethod = apiModule?.[className]?.[methodName]
114
117
  if (apiMethod && typeof apiMethod === 'function') {
115
118
  try {
116
- console.log(`🔍 Calling ${fullMethodName} with args:`, args);
119
+ // console.log(`🔍 Calling ${fullMethodName} with args:`, args);
117
120
  const result = apiMethod.apply(apiModule, args)
118
121
  if (result instanceof Promise) {
119
122
  const data = await result
120
123
  // console.log(`${fullMethodName} result`)
121
124
  return { data, typeName, fullMethodName }
122
125
  }
123
- return { error: 'not Promise method', fullMethodName }
126
+ return { error: 'not Promise method', fullMethodName, typeName }
124
127
  } catch (error) {
125
128
  console.error(`❌ ${fullMethodName} execute error:`, error)
126
- return { error, fullMethodName }
129
+ return { error, fullMethodName, typeName }
127
130
  }
128
131
  } else {
129
- console.error(`❌ 无法获取 ${fullMethodName} 可调用方法 `)
132
+ console.error(`❌ 无法获取 ${fullMethodName} 方法, 或不是可调用方法 `)
130
133
  return {
131
- error: `method error`, fullMethodName
134
+ error: `method error`, fullMethodName, typeName
132
135
  }
133
136
  }
134
137
  })
135
138
 
136
- const resultList = await Promise.all(taskList)
137
- return {
138
- successList: resultList.filter(item => !item.error) as ExcuteApiMethodsResult['successList'],
139
- errorList: resultList.filter(item => item.error) as ExcuteApiMethodsResult['errorList'],
140
- }
139
+ return Promise.all(taskList)
140
+
141
141
  }
142
142
 
143
- function createDeclarationFile(successList: ExcuteApiMethodsResult['successList']) {
144
- const out_put_target = path.resolve(output_dir, output_file)
145
- // console.log('out_put_target', out_put_target)
143
+
144
+ function createDeclarationFile(successList: ExecuteApiMethodResult[]) {
146
145
  const ttf = new TypeTransformer({ filePath: out_put_target })
147
- const tasks = successList.map(item => ttf.transform(item.data, item.typeName))
146
+ const tasks = successList.map(async ({ data, typeName, fullMethodName }) => {
147
+ try {
148
+ await ttf.transform(data, typeName)
149
+ return { fullMethodName }
150
+ } catch (error) {
151
+ console.error('transform error', error)
152
+ return { fullMethodName, error }
153
+ }
154
+ })
148
155
  return Promise.all(tasks)
149
156
  }
150
157
 
158
+
159
+
160
+
151
161
  async function main() {
152
162
  try {
153
163
  console.log('🚀 开始生成API类型...');
154
164
  const apiMethodsInfo = getApiMethodsInfo();
155
- const { successList, errorList } = await excuteApiMethods(apiMethodsInfo);
156
-
157
- console.group('请求结果:')
158
- console.table({
159
- "✔️ successList": successList.map(item => item.fullMethodName).join(' '),
160
- "❌ errorList": errorList.map(item => item.fullMethodName).join(' ')
161
- })
162
- console.groupEnd()
163
- await createDeclarationFile(successList)
164
-
165
- console.log('✅ API 类型生成完成');
166
-
165
+ if (apiMethodsInfo.length == 0) return console.warn('⚠️ 未找到需要转换的API,请检查api_dir 和 get_type装饰器标注是否正确!')
166
+ const executeList = await executeApiMethods(apiMethodsInfo);
167
+
168
+ const { successList: executeSuccessList, errorList: executeErrorList } = formatResultList(executeList)
169
+ if (executeErrorList.length) {
170
+ console.group('请求结果:')
171
+ console.table({
172
+ "✔️ executeSuccessList": executeSuccessList.map(item => item.fullMethodName).join(' '),
173
+ "❌ executeErrorList": executeErrorList.map(item => item.fullMethodName).join(' ')
174
+ })
175
+ console.groupEnd()
176
+ }
177
+ const transformList = await createDeclarationFile(executeSuccessList)
178
+ const { successList: transformSuccessList, errorList: transformErrorList } = formatResultList(transformList)
167
179
 
180
+ if (transformErrorList.length) {
181
+ console.group('转换结果:')
182
+ console.table({
183
+ "✔️ transformSuccessList": transformSuccessList.map(item => item.fullMethodName).join(' '),
184
+ "❌ transformErrorList": transformErrorList.map(item => item.fullMethodName).join(' ')
185
+ })
186
+ console.groupEnd()
168
187
 
188
+ }
189
+ if (transformSuccessList.length) console.log('✅ API 类型生成完成:', out_put_target);
169
190
  } catch (error) {
170
191
  console.error('❌ 出错了', error)
171
192
  }
@@ -14,8 +14,10 @@ function inferType(value: any): string {
14
14
  return "number";
15
15
  } else if (typeof value === "boolean") {
16
16
  return "boolean";
17
- } else if (!value) {
17
+ } else if (value === null) {
18
18
  return "null";
19
+ } else if (!value) {
20
+ return "undefined";
19
21
  } else {
20
22
  return "any";
21
23
  }
@@ -25,9 +27,11 @@ function inferType(value: any): string {
25
27
  export class TypeTransformer {
26
28
  project: Project;
27
29
  sourceFile: SourceFile;
28
- constructor({ projectOptions = {}, filePath = "types.d.ts" } = {}) {
30
+ isExported: boolean
31
+ constructor({ projectOptions = {}, filePath = "types.d.ts", isExported = false } = {}) {
29
32
  this.project = new Project(projectOptions);
30
33
  this.sourceFile = this.createSourceFile(filePath);
34
+ this.isExported = isExported
31
35
  }
32
36
 
33
37
  createSourceFile(filePath: string) {
@@ -45,14 +49,14 @@ export class TypeTransformer {
45
49
  // 4. 添加类型别名
46
50
  this.sourceFile.addTypeAlias({
47
51
  name: typeName,
48
- // isExported: true,
52
+ isExported: this.isExported,
49
53
  type: inferType(val)
50
54
  });
55
+ // if (typeName == 'Res_Weather') throw Error('res_weather test error')
51
56
  }
52
- transform(res: any, typeName: string, isAsync = true) {
53
-
57
+ async transform(res: any, typeName: string, isAsync = true) {
54
58
  this.generateTypeFromObject(res, typeName)
55
- return this.sourceFile.save();
59
+ await this.sourceFile.save();
56
60
  }
57
61
 
58
62
  }
@@ -1,12 +1,8 @@
1
-
2
- export function testHello() {
3
- if (Math.random() > 0.5) throw new Error('test error')
4
-
5
- return { hello: 'hello' };
1
+ type ResultInfoBase = { error?: any }
2
+ export function formatResultList<T extends ResultInfoBase>(list: T[]) {
3
+ return list.reduce((acc, item) => {
4
+ if (item.error) acc.errorList.push(item)
5
+ else acc.successList.push(item)
6
+ return acc
7
+ }, { successList: [] as Omit<T, 'error'>[], errorList: [] as T[] })
6
8
  }
7
-
8
- let val = Array(10).fill(testHello())
9
- console.log('val', val)
10
-
11
-
12
- console.log('999', 123)