gen-api-types 1.0.4 → 1.0.8
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/LICENSE +21 -0
- package/README.en.md +145 -36
- package/README.md +146 -109
- package/bin/index.js +21 -33
- package/package.json +11 -5
- package/src/argv/index.ts +75 -70
- package/src/cli/index.ts +196 -196
- package/src/constant/index.ts +3 -3
- package/src/decotators/index.ts +32 -27
- package/src/index.ts +2 -2
- package/src/transformer/index.ts +62 -62
- package/src/utils/index.ts +8 -8
- package/tsconfig.json +115 -115
package/src/argv/index.ts
CHANGED
|
@@ -1,70 +1,75 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import { parseArgs } from "util";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
// 1. 配置
|
|
6
|
-
const PROJECT_ROOT = path.resolve();
|
|
7
|
-
const OUTPUT_FILE = 'index.d.ts';
|
|
8
|
-
const OUTPUT_DIR = PROJECT_ROOT
|
|
9
|
-
const TS_CONFIG_PATH = path.join(PROJECT_ROOT, 'tsconfig.json');
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const _arg = parseArgs({
|
|
14
|
-
allowPositionals: true,
|
|
15
|
-
args: process.argv.slice(2),
|
|
16
|
-
options: {
|
|
17
|
-
output_dir: {
|
|
18
|
-
type: 'string',
|
|
19
|
-
short: 'o',
|
|
20
|
-
default: OUTPUT_DIR,
|
|
21
|
-
},
|
|
22
|
-
output_file: {
|
|
23
|
-
type: 'string',
|
|
24
|
-
short: 'O',
|
|
25
|
-
default: OUTPUT_FILE,
|
|
26
|
-
},
|
|
27
|
-
project_root: {
|
|
28
|
-
type: 'string',
|
|
29
|
-
short: 'r',
|
|
30
|
-
default: PROJECT_ROOT,
|
|
31
|
-
},
|
|
32
|
-
ts_config_path: {
|
|
33
|
-
type: 'string',
|
|
34
|
-
short: 't',
|
|
35
|
-
default: TS_CONFIG_PATH,
|
|
36
|
-
},
|
|
37
|
-
|
|
38
|
-
type: 'boolean',
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { parseArgs } from "util";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
// 1. 配置
|
|
6
|
+
const PROJECT_ROOT = path.resolve();
|
|
7
|
+
const OUTPUT_FILE = 'index.d.ts';
|
|
8
|
+
const OUTPUT_DIR = PROJECT_ROOT
|
|
9
|
+
const TS_CONFIG_PATH = path.join(PROJECT_ROOT, 'tsconfig.json');
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const _arg = parseArgs({
|
|
14
|
+
allowPositionals: true,
|
|
15
|
+
args: process.argv.slice(2),
|
|
16
|
+
options: {
|
|
17
|
+
output_dir: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
short: 'o',
|
|
20
|
+
default: OUTPUT_DIR,
|
|
21
|
+
},
|
|
22
|
+
output_file: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
short: 'O',
|
|
25
|
+
default: OUTPUT_FILE,
|
|
26
|
+
},
|
|
27
|
+
project_root: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
short: 'r',
|
|
30
|
+
default: PROJECT_ROOT,
|
|
31
|
+
},
|
|
32
|
+
ts_config_path: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
short: 't',
|
|
35
|
+
default: TS_CONFIG_PATH,
|
|
36
|
+
},
|
|
37
|
+
isExported: {
|
|
38
|
+
type: 'boolean',
|
|
39
|
+
default: false,
|
|
40
|
+
},
|
|
41
|
+
help: {
|
|
42
|
+
type: 'boolean',
|
|
43
|
+
short: 'h'
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
export const { positionals, values: { project_root, output_file, output_dir, ts_config_path, isExported, help } } = _arg
|
|
50
|
+
|
|
51
|
+
if (help) {
|
|
52
|
+
console.log(`
|
|
53
|
+
Usage: tsx cli.js [options] [positionals]
|
|
54
|
+
|
|
55
|
+
Options:
|
|
56
|
+
-h, --help 输出帮助信息
|
|
57
|
+
-r, --project_root <path> 项目根目录
|
|
58
|
+
-O, --output_file <path> 输出文件名
|
|
59
|
+
-o, --output_dir <path> 输出目录
|
|
60
|
+
-t, --ts_config_path <path> tsconfig.json 文件路径
|
|
61
|
+
--isExported 生成导出的类型声明
|
|
62
|
+
`
|
|
63
|
+
)
|
|
64
|
+
process.exit(0)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if (positionals.length == 0) {
|
|
72
|
+
console.error('❌ 错误: 必须指定 API 目录');
|
|
73
|
+
console.log('💡 用法示例: node cli.js ./api-directory');
|
|
74
|
+
process.exit(1)
|
|
75
|
+
}
|
package/src/cli/index.ts
CHANGED
|
@@ -1,196 +1,196 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
import { Decorator, Project } from 'ts-morph';
|
|
6
|
-
import { pathToFileURL } from 'url';
|
|
7
|
-
import { output_dir, output_file, positionals } from '../argv';
|
|
8
|
-
import { C_DECO_NAME, M_DECO_NAME } from '../constant';
|
|
9
|
-
import { GenTypeOptions } from '../decotators';
|
|
10
|
-
import { TypeTransformer } from '../transformer';
|
|
11
|
-
import { formatResultList } from '../utils';
|
|
12
|
-
|
|
13
|
-
// console.log('positionals', positionals)
|
|
14
|
-
|
|
15
|
-
const sourceFilesGlob = positionals.map(dir => path.normalize(`${dir}/**/*.ts`))
|
|
16
|
-
const out_put_target = path.resolve(output_dir, output_file)
|
|
17
|
-
|
|
18
|
-
// console.log('arg', _arg)
|
|
19
|
-
// console.log('sourceFilesGlob', sourceFilesGlob)
|
|
20
|
-
// debugger
|
|
21
|
-
|
|
22
|
-
type ApiMethodInfo = {
|
|
23
|
-
className: string,
|
|
24
|
-
methodName: string,
|
|
25
|
-
fullMethodName: string,
|
|
26
|
-
modulePath: string,
|
|
27
|
-
args: any[],
|
|
28
|
-
typeName: string,
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function parserDecoArgs(deco: Decorator): GenTypeOptions {
|
|
32
|
-
const errMsg = `装饰器参数错误`
|
|
33
|
-
try {
|
|
34
|
-
const optionStr = deco.getArguments()[0]?.getText()
|
|
35
|
-
if (!optionStr) return {}
|
|
36
|
-
const option = eval(`(()=>(${optionStr}))()`)
|
|
37
|
-
if (typeof option === 'object') return option
|
|
38
|
-
else throw new Error(errMsg)
|
|
39
|
-
} catch (error) {
|
|
40
|
-
console.warn('parserDecoArgs error', error)
|
|
41
|
-
return {}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
function getApiMethodsInfo() {
|
|
46
|
-
console.log('sourceFilesGlob', sourceFilesGlob)
|
|
47
|
-
const apiMethodsInfo: ApiMethodInfo[] = []
|
|
48
|
-
// 2. 使用ts-morph创建项目,便于解析源码
|
|
49
|
-
// const project = new Project({ tsConfigFilePath: ts_config_path });
|
|
50
|
-
const project = new Project({});
|
|
51
|
-
|
|
52
|
-
project.addSourceFilesAtPaths(sourceFilesGlob);
|
|
53
|
-
// console.log('project.getSourceFiles().length', project.getSourceFiles().length)
|
|
54
|
-
// debugger
|
|
55
|
-
// 3. 遍历所有源文件
|
|
56
|
-
for (const sourceFile of project.getSourceFiles()) {
|
|
57
|
-
|
|
58
|
-
const classes = sourceFile.getClasses();
|
|
59
|
-
for (const classDeclaration of classes) {
|
|
60
|
-
const c_deco = classDeclaration.getDecorator(C_DECO_NAME)
|
|
61
|
-
if (!c_deco) continue
|
|
62
|
-
const methods = classDeclaration.getMethods();
|
|
63
|
-
for (const method of methods) {
|
|
64
|
-
|
|
65
|
-
const className = classDeclaration.getName()!;
|
|
66
|
-
const methodName = method.getName();
|
|
67
|
-
const fullMethodName = `${className}.${methodName}`;
|
|
68
|
-
|
|
69
|
-
if (!method.isStatic()) {
|
|
70
|
-
console.warn(`⚠️ ${fullMethodName} is not static method,only static method can be transformed`)
|
|
71
|
-
continue
|
|
72
|
-
}
|
|
73
|
-
// 4. 检查方法是否被我们的装饰器标记
|
|
74
|
-
const m_deco = method.getDecorator(M_DECO_NAME)
|
|
75
|
-
if (!m_deco) continue
|
|
76
|
-
|
|
77
|
-
const { args = [], typeName = `Response_${className}_${methodName}` } = parserDecoArgs(m_deco)
|
|
78
|
-
apiMethodsInfo.push({
|
|
79
|
-
className, methodName, fullMethodName, modulePath: sourceFile.getFilePath(),
|
|
80
|
-
typeName, args
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return apiMethodsInfo
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
type ExecuteApiMethodResult = {
|
|
90
|
-
data?: any, typeName: string, fullMethodName: string, error?: any
|
|
91
|
-
}
|
|
92
|
-
async function executeApiMethods(apiMethodsInfo: ApiMethodInfo[]): Promise<ExecuteApiMethodResult[]> {
|
|
93
|
-
const apiModuleMap = new Map<string, any>();
|
|
94
|
-
const taskList = apiMethodsInfo.map(async (apiMethodInfo) => {
|
|
95
|
-
const { className, methodName, fullMethodName, modulePath, args, typeName } = apiMethodInfo
|
|
96
|
-
console.log(`📋 处理 ${fullMethodName} ...`);
|
|
97
|
-
let apiModule = null
|
|
98
|
-
if (apiModuleMap.has(modulePath)) apiModule = apiModuleMap.get(modulePath)
|
|
99
|
-
else {
|
|
100
|
-
// apiModule = await import(modulePath)
|
|
101
|
-
// debugger
|
|
102
|
-
// console.log('modulePath', modulePath)
|
|
103
|
-
// console.log('pathToFileURL(modulePath).href', pathToFileURL(modulePath).href)
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
apiModule = await import(pathToFileURL(modulePath).href)
|
|
107
|
-
// console.log('apiMethod', apiModule)
|
|
108
|
-
if (apiModule) apiModuleMap.set(modulePath, apiModule)
|
|
109
|
-
} catch (error) {
|
|
110
|
-
console.log(`import ${modulePath} error \r\n`, error)
|
|
111
|
-
return { error: `module error`, fullMethodName, typeName }
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const apiMethod = apiModule?.[className]?.[methodName]
|
|
117
|
-
if (apiMethod && typeof apiMethod === 'function') {
|
|
118
|
-
try {
|
|
119
|
-
// console.log(`🔍 Calling ${fullMethodName} with args:`, args);
|
|
120
|
-
const result = apiMethod.apply(apiModule, args)
|
|
121
|
-
if (result instanceof Promise) {
|
|
122
|
-
const data = await result
|
|
123
|
-
// console.log(`${fullMethodName} result`)
|
|
124
|
-
return { data, typeName, fullMethodName }
|
|
125
|
-
}
|
|
126
|
-
return { error: 'not Promise method', fullMethodName, typeName }
|
|
127
|
-
} catch (error) {
|
|
128
|
-
console.error(`❌ ${fullMethodName} execute error:`, error)
|
|
129
|
-
return { error, fullMethodName, typeName }
|
|
130
|
-
}
|
|
131
|
-
} else {
|
|
132
|
-
console.error(`❌ 无法获取 ${fullMethodName} 方法, 或不是可调用方法 `)
|
|
133
|
-
return {
|
|
134
|
-
error: `method error`, fullMethodName, typeName
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
return Promise.all(taskList)
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
function createDeclarationFile(successList: ExecuteApiMethodResult[]) {
|
|
145
|
-
const ttf = new TypeTransformer({ filePath: out_put_target })
|
|
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
|
-
})
|
|
155
|
-
return Promise.all(tasks)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
async function main() {
|
|
162
|
-
try {
|
|
163
|
-
console.log('🚀 开始生成API类型...');
|
|
164
|
-
const apiMethodsInfo = getApiMethodsInfo();
|
|
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)
|
|
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()
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
if (transformSuccessList.length) console.log('✅ API 类型生成完成:', out_put_target);
|
|
190
|
-
} catch (error) {
|
|
191
|
-
console.error('❌ 出错了', error)
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
main()
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { Decorator, Project } from 'ts-morph';
|
|
6
|
+
import { pathToFileURL } from 'url';
|
|
7
|
+
import { isExported, output_dir, output_file, positionals } from '../argv';
|
|
8
|
+
import { C_DECO_NAME, M_DECO_NAME } from '../constant';
|
|
9
|
+
import { GenTypeOptions } from '../decotators';
|
|
10
|
+
import { TypeTransformer } from '../transformer';
|
|
11
|
+
import { formatResultList } from '../utils';
|
|
12
|
+
|
|
13
|
+
// console.log('positionals', positionals)
|
|
14
|
+
|
|
15
|
+
const sourceFilesGlob = positionals.map(dir => path.normalize(`${dir}/**/*.ts`))
|
|
16
|
+
const out_put_target = path.resolve(output_dir, output_file)
|
|
17
|
+
|
|
18
|
+
// console.log('arg', _arg)
|
|
19
|
+
// console.log('sourceFilesGlob', sourceFilesGlob)
|
|
20
|
+
// debugger
|
|
21
|
+
|
|
22
|
+
type ApiMethodInfo = {
|
|
23
|
+
className: string,
|
|
24
|
+
methodName: string,
|
|
25
|
+
fullMethodName: string,
|
|
26
|
+
modulePath: string,
|
|
27
|
+
args: any[],
|
|
28
|
+
typeName: string,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function parserDecoArgs(deco: Decorator): GenTypeOptions {
|
|
32
|
+
const errMsg = `装饰器参数错误`
|
|
33
|
+
try {
|
|
34
|
+
const optionStr = deco.getArguments()[0]?.getText()
|
|
35
|
+
if (!optionStr) return {}
|
|
36
|
+
const option = eval(`(()=>(${optionStr}))()`)
|
|
37
|
+
if (typeof option === 'object') return option
|
|
38
|
+
else throw new Error(errMsg)
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.warn('parserDecoArgs error', error)
|
|
41
|
+
return {}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
function getApiMethodsInfo() {
|
|
46
|
+
console.log('sourceFilesGlob', sourceFilesGlob)
|
|
47
|
+
const apiMethodsInfo: ApiMethodInfo[] = []
|
|
48
|
+
// 2. 使用ts-morph创建项目,便于解析源码
|
|
49
|
+
// const project = new Project({ tsConfigFilePath: ts_config_path });
|
|
50
|
+
const project = new Project({});
|
|
51
|
+
|
|
52
|
+
project.addSourceFilesAtPaths(sourceFilesGlob);
|
|
53
|
+
// console.log('project.getSourceFiles().length', project.getSourceFiles().length)
|
|
54
|
+
// debugger
|
|
55
|
+
// 3. 遍历所有源文件
|
|
56
|
+
for (const sourceFile of project.getSourceFiles()) {
|
|
57
|
+
|
|
58
|
+
const classes = sourceFile.getClasses();
|
|
59
|
+
for (const classDeclaration of classes) {
|
|
60
|
+
const c_deco = classDeclaration.getDecorator(C_DECO_NAME)
|
|
61
|
+
if (!c_deco) continue
|
|
62
|
+
const methods = classDeclaration.getMethods();
|
|
63
|
+
for (const method of methods) {
|
|
64
|
+
|
|
65
|
+
const className = classDeclaration.getName()!;
|
|
66
|
+
const methodName = method.getName();
|
|
67
|
+
const fullMethodName = `${className}.${methodName}`;
|
|
68
|
+
|
|
69
|
+
if (!method.isStatic()) {
|
|
70
|
+
console.warn(`⚠️ ${fullMethodName} is not static method,only static method can be transformed`)
|
|
71
|
+
continue
|
|
72
|
+
}
|
|
73
|
+
// 4. 检查方法是否被我们的装饰器标记
|
|
74
|
+
const m_deco = method.getDecorator(M_DECO_NAME)
|
|
75
|
+
if (!m_deco) continue
|
|
76
|
+
|
|
77
|
+
const { args = [], typeName = `Response_${className}_${methodName}` } = parserDecoArgs(m_deco)
|
|
78
|
+
apiMethodsInfo.push({
|
|
79
|
+
className, methodName, fullMethodName, modulePath: sourceFile.getFilePath(),
|
|
80
|
+
typeName, args
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return apiMethodsInfo
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
type ExecuteApiMethodResult = {
|
|
90
|
+
data?: any, typeName: string, fullMethodName: string, error?: any
|
|
91
|
+
}
|
|
92
|
+
async function executeApiMethods(apiMethodsInfo: ApiMethodInfo[]): Promise<ExecuteApiMethodResult[]> {
|
|
93
|
+
const apiModuleMap = new Map<string, any>();
|
|
94
|
+
const taskList = apiMethodsInfo.map(async (apiMethodInfo) => {
|
|
95
|
+
const { className, methodName, fullMethodName, modulePath, args, typeName } = apiMethodInfo
|
|
96
|
+
console.log(`📋 处理 ${fullMethodName} ...`);
|
|
97
|
+
let apiModule = null
|
|
98
|
+
if (apiModuleMap.has(modulePath)) apiModule = apiModuleMap.get(modulePath)
|
|
99
|
+
else {
|
|
100
|
+
// apiModule = await import(modulePath)
|
|
101
|
+
// debugger
|
|
102
|
+
// console.log('modulePath', modulePath)
|
|
103
|
+
// console.log('pathToFileURL(modulePath).href', pathToFileURL(modulePath).href)
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
apiModule = await import(pathToFileURL(modulePath).href)
|
|
107
|
+
// console.log('apiMethod', apiModule)
|
|
108
|
+
if (apiModule) apiModuleMap.set(modulePath, apiModule)
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.log(`import ${modulePath} error \r\n`, error)
|
|
111
|
+
return { error: `module error`, fullMethodName, typeName }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
const apiMethod = apiModule?.[className]?.[methodName]
|
|
117
|
+
if (apiMethod && typeof apiMethod === 'function') {
|
|
118
|
+
try {
|
|
119
|
+
// console.log(`🔍 Calling ${fullMethodName} with args:`, args);
|
|
120
|
+
const result = apiMethod.apply(apiModule, args)
|
|
121
|
+
if (result instanceof Promise) {
|
|
122
|
+
const data = await result
|
|
123
|
+
// console.log(`${fullMethodName} result`)
|
|
124
|
+
return { data, typeName, fullMethodName }
|
|
125
|
+
}
|
|
126
|
+
return { error: 'not Promise method', fullMethodName, typeName }
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error(`❌ ${fullMethodName} execute error:`, error)
|
|
129
|
+
return { error, fullMethodName, typeName }
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
console.error(`❌ 无法获取 ${fullMethodName} 方法, 或不是可调用方法 `)
|
|
133
|
+
return {
|
|
134
|
+
error: `method error`, fullMethodName, typeName
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
return Promise.all(taskList)
|
|
140
|
+
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
function createDeclarationFile(successList: ExecuteApiMethodResult[]) {
|
|
145
|
+
const ttf = new TypeTransformer({ filePath: out_put_target, isExported })
|
|
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
|
+
})
|
|
155
|
+
return Promise.all(tasks)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
async function main() {
|
|
162
|
+
try {
|
|
163
|
+
console.log('🚀 开始生成API类型...');
|
|
164
|
+
const apiMethodsInfo = getApiMethodsInfo();
|
|
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)
|
|
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()
|
|
187
|
+
|
|
188
|
+
}
|
|
189
|
+
if (transformSuccessList.length) console.log('✅ API 类型生成完成:', out_put_target);
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error('❌ 出错了', error)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
main()
|
package/src/constant/index.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export const GEN_TYPE_METADATA_KEY = Symbol('gen:type:metadata');
|
|
2
|
-
export const M_DECO_NAME = 'gen_type_m';
|
|
3
|
-
export const C_DECO_NAME = 'gen_type_c';
|
|
1
|
+
export const GEN_TYPE_METADATA_KEY = Symbol('gen:type:metadata');
|
|
2
|
+
export const M_DECO_NAME = 'gen_type_m';
|
|
3
|
+
export const C_DECO_NAME = 'gen_type_c';
|
package/src/decotators/index.ts
CHANGED
|
@@ -1,28 +1,33 @@
|
|
|
1
|
-
// import 'reflect-metadata';
|
|
2
|
-
|
|
3
|
-
// 定义一个唯一的metadata key
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export interface GenTypeOptions {
|
|
7
|
-
args?: any[];
|
|
8
|
-
typeName?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
1
|
+
// import 'reflect-metadata';
|
|
2
|
+
|
|
3
|
+
// 定义一个唯一的metadata key
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export interface GenTypeOptions {
|
|
7
|
+
args?: any[];
|
|
8
|
+
typeName?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 标记方法
|
|
12
|
+
*/
|
|
13
|
+
export function gen_type_m({ args = [], typeName }: GenTypeOptions = {}) {
|
|
14
|
+
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
|
15
|
+
// 只存储元数据,不执行任何逻辑
|
|
16
|
+
// Reflect.defineMetadata(
|
|
17
|
+
// GEN_TYPE_METADATA_KEY,
|
|
18
|
+
// { args, typeName },
|
|
19
|
+
// target,
|
|
20
|
+
// propertyKey
|
|
21
|
+
// );
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 标记类
|
|
28
|
+
* @returns
|
|
29
|
+
*/
|
|
30
|
+
export function gen_type_c() {
|
|
31
|
+
return function <T>(target: T) {
|
|
32
|
+
};
|
|
28
33
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './decotators';
|
|
2
|
-
|
|
1
|
+
export * from './decotators';
|
|
2
|
+
|