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/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
- help: {
38
- type: 'boolean',
39
- short: 'h'
40
- }
41
- }
42
- })
43
-
44
-
45
- export const { positionals, values: { project_root, output_file, output_dir, ts_config_path, help } } = _arg
46
-
47
- if (help) {
48
- console.log(`
49
- Usage: tsx cli.js [options] [positionals]
50
-
51
- Options:
52
- -h, --help 输出帮助信息
53
- -r, --project_root <path> 项目根目录
54
- -O, --output_file <path> 输出文件名
55
- -o, --output_dir <path> 输出目录
56
- -t, --ts_config_path <path> tsconfig.json 文件路径
57
- `
58
- )
59
- process.exit(0)
60
- }
61
-
62
-
63
-
64
-
65
-
66
- if (positionals.length == 0) {
67
- console.error('❌ 错误: 必须指定 API 目录');
68
- console.log('💡 用法示例: node cli.js ./api-directory');
69
- process.exit(1)
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()
@@ -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';
@@ -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
- export function gen_type_m({ args = [], typeName }: GenTypeOptions = {}) {
12
- return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
13
- // 只存储元数据,不执行任何逻辑
14
- // Reflect.defineMetadata(
15
- // GEN_TYPE_METADATA_KEY,
16
- // { args, typeName },
17
- // target,
18
- // propertyKey
19
- // );
20
- };
21
- }
22
-
23
-
24
-
25
- export function gen_type_c() {
26
- return function <T>(target: T) {
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
+