@jayfong/x-server 1.8.0

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.
Files changed (68) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/README.md +51 -0
  3. package/lib/cli/api_generator.d.ts +44 -0
  4. package/lib/cli/api_generator.js +339 -0
  5. package/lib/cli/build_util.d.ts +9 -0
  6. package/lib/cli/build_util.js +141 -0
  7. package/lib/cli/cli.d.ts +2 -0
  8. package/lib/cli/cli.js +216 -0
  9. package/lib/cli/deploy_util.d.ts +10 -0
  10. package/lib/cli/deploy_util.js +51 -0
  11. package/lib/cli/env_util.d.ts +29 -0
  12. package/lib/cli/env_util.js +139 -0
  13. package/lib/cli/register.d.ts +0 -0
  14. package/lib/cli/register.js +5 -0
  15. package/lib/cli/template_util.d.ts +3 -0
  16. package/lib/cli/template_util.js +18 -0
  17. package/lib/cli/templates/handlers.ts +3 -0
  18. package/lib/cli/templates/hooks.ts +2 -0
  19. package/lib/cli/templates/models.ts +22 -0
  20. package/lib/cli/templates/routes.ts +26 -0
  21. package/lib/cli/templates/tasks.ts +2 -0
  22. package/lib/core/define_bus.d.ts +38 -0
  23. package/lib/core/define_bus.js +15 -0
  24. package/lib/core/define_handler.d.ts +16 -0
  25. package/lib/core/define_handler.js +41 -0
  26. package/lib/core/define_hook.d.ts +2 -0
  27. package/lib/core/define_hook.js +8 -0
  28. package/lib/core/define_server.d.ts +3 -0
  29. package/lib/core/define_server.js +10 -0
  30. package/lib/core/define_task.d.ts +2 -0
  31. package/lib/core/define_task.js +27 -0
  32. package/lib/core/handler.d.ts +10 -0
  33. package/lib/core/handler.js +76 -0
  34. package/lib/core/http_error.d.ts +2 -0
  35. package/lib/core/http_error.js +8 -0
  36. package/lib/core/http_method.d.ts +3 -0
  37. package/lib/core/http_method.js +10 -0
  38. package/lib/core/server.d.ts +16 -0
  39. package/lib/core/server.js +137 -0
  40. package/lib/core/types.d.ts +123 -0
  41. package/lib/core/types.js +2 -0
  42. package/lib/index.d.ts +22 -0
  43. package/lib/index.js +41 -0
  44. package/lib/plugins/base.d.ts +4 -0
  45. package/lib/plugins/base.js +2 -0
  46. package/lib/plugins/cors.d.ts +21 -0
  47. package/lib/plugins/cors.js +39 -0
  48. package/lib/plugins/file_parser.d.ts +13 -0
  49. package/lib/plugins/file_parser.js +19 -0
  50. package/lib/plugins/ws_parser.d.ts +13 -0
  51. package/lib/plugins/ws_parser.js +19 -0
  52. package/lib/plugins/xml_parser.d.ts +19 -0
  53. package/lib/plugins/xml_parser.js +48 -0
  54. package/lib/services/base.d.ts +4 -0
  55. package/lib/services/base.js +2 -0
  56. package/lib/services/cache.d.ts +119 -0
  57. package/lib/services/cache.js +196 -0
  58. package/lib/services/captcha.d.ts +33 -0
  59. package/lib/services/captcha.js +34 -0
  60. package/lib/services/dispose.d.ts +17 -0
  61. package/lib/services/dispose.js +24 -0
  62. package/lib/services/jwt.d.ts +21 -0
  63. package/lib/services/jwt.js +50 -0
  64. package/lib/services/redis.d.ts +10 -0
  65. package/lib/services/redis.js +14 -0
  66. package/lib/x.d.ts +5 -0
  67. package/lib/x.js +11 -0
  68. package/package.json +86 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,154 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
+
5
+ ## [1.8.0](https://github.com/jfWorks/x-server/compare/v1.7.2...v1.8.0) (2022-04-18)
6
+
7
+
8
+ ### Features
9
+
10
+ * 更改包名为 @jayfong/x-server ([1be9998](https://github.com/jfWorks/x-server/commit/1be9998070cc0960aeef2d333356f66a22bb5766))
11
+ * 移除 cwd 选项 ([12cfa42](https://github.com/jfWorks/x-server/commit/12cfa4222ec8d69d73f2b38a68ea6aa975dbe575))
12
+ * npc ([5d314ea](https://github.com/jfWorks/x-server/commit/5d314ea341a72ae5b09db979d5d2b63ffadf0670))
13
+ * plugins ([1df0cd1](https://github.com/jfWorks/x-server/commit/1df0cd10ccf170d9e7180abf9d27416304856894))
14
+ * service 通过 x 提供 ([a159154](https://github.com/jfWorks/x-server/commit/a1591541e865e755d4d6dbdeb405d5d08b334a4a))
15
+ * templates ([deeb0d6](https://github.com/jfWorks/x-server/commit/deeb0d643bdf04d04533d20bd1d5a068b178347a))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * test ([994845b](https://github.com/jfWorks/x-server/commit/994845b0e2ac84ce050494de27b705c1e45fb343))
21
+
22
+ ### [1.7.2](https://github.com/jfWorks/x-server/compare/v1.7.1...v1.7.2) (2022-04-16)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * npx -> tnpx ([2ef5650](https://github.com/jfWorks/x-server/commit/2ef5650679dbc1ceea5a44e8348c31aa06f5ae5b))
28
+
29
+ ### [1.7.1](https://github.com/jfWorks/x-server/compare/v1.7.0...v1.7.1) (2022-04-16)
30
+
31
+ ## [1.7.0](https://github.com/jfWorks/x-server/compare/v1.6.0...v1.7.0) (2021-11-08)
32
+
33
+
34
+ ### Features
35
+
36
+ * 升级 vtils 使用 dedent, ms ([251591b](https://github.com/jfWorks/x-server/commit/251591ba47b9a9d619e038adcb66dfb52fd80264))
37
+
38
+ ## [1.6.0](https://github.com/jfWorks/x-server/compare/v1.5.7...v1.6.0) (2021-11-07)
39
+
40
+
41
+ ### Features
42
+
43
+ * 支持 npc ([53069e6](https://github.com/jfWorks/x-server/commit/53069e6ba77d2ad8852cc2d5d7ede73a9d05c1da))
44
+
45
+ ### [1.5.7](https://github.com/jfWorks/x-server/compare/v1.5.6...v1.5.7) (2021-11-07)
46
+
47
+ ### [1.5.6](https://github.com/jfWorks/x-server/compare/v1.5.5...v1.5.6) (2021-11-07)
48
+
49
+
50
+ ### Bug Fixes
51
+
52
+ * task ([f36571a](https://github.com/jfWorks/x-server/commit/f36571a51d32fff19d62c233cc31fd250d90281a))
53
+
54
+ ### [1.5.5](https://github.com/jfWorks/x-server/compare/v1.5.4...v1.5.5) (2021-11-07)
55
+
56
+
57
+ ### Bug Fixes
58
+
59
+ * env ([5917b3d](https://github.com/jfWorks/x-server/commit/5917b3dddaeb750c692c7ac8a1af6fdb65eab942))
60
+
61
+ ### [1.5.4](https://github.com/jfWorks/x-server/compare/v1.5.3...v1.5.4) (2021-11-07)
62
+
63
+
64
+ ### Bug Fixes
65
+
66
+ * 兼容 prisma ([41c3417](https://github.com/jfWorks/x-server/commit/41c34175d2af074c44e76422d9ba2b421be556c9))
67
+
68
+ ### [1.5.3](https://github.com/jfWorks/x-server/compare/v1.5.2...v1.5.3) (2021-11-07)
69
+
70
+
71
+ ### Bug Fixes
72
+
73
+ * 兼容 prisma ([668bfd1](https://github.com/jfWorks/x-server/commit/668bfd1d7efd91b1a3c607d7050379d43fccdd22))
74
+
75
+ ### [1.5.2](https://github.com/jfWorks/x-server/compare/v1.5.1...v1.5.2) (2021-11-07)
76
+
77
+ ### [1.5.1](https://github.com/jfWorks/x-server/compare/v1.5.0...v1.5.1) (2021-11-07)
78
+
79
+
80
+ ### Bug Fixes
81
+
82
+ * env ([f79cd48](https://github.com/jfWorks/x-server/commit/f79cd48a3ee9101605d9ac80dbaac5f8ca7f94df))
83
+
84
+ ## [1.5.0](https://github.com/jfWorks/x-server/compare/v1.4.0...v1.5.0) (2021-11-07)
85
+
86
+
87
+ ### Features
88
+
89
+ * 支持 DATABASE_ACTION_URL ([e21884e](https://github.com/jfWorks/x-server/commit/e21884eeca8e1c451f8536d93663e1b663ee5303))
90
+
91
+ ## [1.4.0](https://github.com/jfWorks/x-server/compare/v1.3.1...v1.4.0) (2021-11-06)
92
+
93
+
94
+ ### Features
95
+
96
+ * 进一步简化部署流程 ([a2062d8](https://github.com/jfWorks/x-server/commit/a2062d8d07df2a256d470ec10b633183fa5e44c1))
97
+ * build 优化 ([4d988bb](https://github.com/jfWorks/x-server/commit/4d988bb953f68a44916d6bb89c9d05ee5801abb9))
98
+
99
+ ### [1.3.1](https://github.com/jfWorks/x-server/compare/v1.3.0...v1.3.1) (2021-11-06)
100
+
101
+
102
+ ### Bug Fixes
103
+
104
+ * **cli/build:** 将 svg-captcha 加入 external ([d2b9b8d](https://github.com/jfWorks/x-server/commit/d2b9b8d7e4818d7eb3eb5249b6b24c344a9f027b))
105
+ * ws ([6cdb442](https://github.com/jfWorks/x-server/commit/6cdb4421415b4e607a6e00c5469a955114aca112))
106
+
107
+ ## [1.3.0](https://github.com/jfWorks/x-server/compare/v1.1.3...v1.3.0) (2021-11-06)
108
+
109
+
110
+ ### Features
111
+
112
+ * deploy ([b0808ce](https://github.com/jfWorks/x-server/commit/b0808cec8d5eb35fe80897e13db719151f6935b3))
113
+ * **EnvUtil:** 支持多行 ([fb99204](https://github.com/jfWorks/x-server/commit/fb9920478db090fce3eb57340b71e8c9b5c9af96))
114
+
115
+
116
+ ### Bug Fixes
117
+
118
+ * deploy ([0a02537](https://github.com/jfWorks/x-server/commit/0a02537d4e1198249f6e0d9c1fc6e40b7c24684c))
119
+
120
+ ## [1.2.0](https://github.com/jfWorks/x-server/compare/v1.1.3...v1.2.0) (2021-11-05)
121
+
122
+
123
+ ### Features
124
+
125
+ * deploy ([b0808ce](https://github.com/jfWorks/x-server/commit/b0808cec8d5eb35fe80897e13db719151f6935b3))
126
+ * **EnvUtil:** 支持多行 ([fb99204](https://github.com/jfWorks/x-server/commit/fb9920478db090fce3eb57340b71e8c9b5c9af96))
127
+
128
+ ### [1.1.3](https://github.com/jfWorks/x-server/compare/v1.1.2...v1.1.3) (2021-11-05)
129
+
130
+
131
+ ### Bug Fixes
132
+
133
+ * **EnvUtil:** 解析注释正则更新 ([5e9cfbf](https://github.com/jfWorks/x-server/commit/5e9cfbff44a8695f648e7b7ef25226b1e6c4b350))
134
+
135
+ ### [1.1.2](https://github.com/jfWorks/x-server/compare/v1.1.1...v1.1.2) (2021-11-05)
136
+
137
+ ### [1.1.1](https://github.com/jfWorks/x-server/compare/v1.1.0...v1.1.1) (2021-11-05)
138
+
139
+ ## [1.1.0](https://github.com/jfWorks/x-server/compare/v1.0.1...v1.1.0) (2021-11-05)
140
+
141
+
142
+ ### Features
143
+
144
+ * **cli:** 新增 run ([c9d8de0](https://github.com/jfWorks/x-server/commit/c9d8de01de8373541f6e4e628fe43067a58d0b8b))
145
+ * **cli:** prisma 支持通过 mode 传入环境 ([5547b7b](https://github.com/jfWorks/x-server/commit/5547b7b66205b25e545a94db88665c513cc8fbd0))
146
+ * **cli:** prisma 支持增强 ([2be1aa1](https://github.com/jfWorks/x-server/commit/2be1aa1394d6aac5c2f4c9a7ca24a3b89a4c0811))
147
+ * **run:** 不传 script 时选择 npm scripts ([474baf0](https://github.com/jfWorks/x-server/commit/474baf0f30b039600922679850f494dadbea4963))
148
+
149
+
150
+ ### Bug Fixes
151
+
152
+ * **cli:** 简化模式配置 ([a4e9e41](https://github.com/jfWorks/x-server/commit/a4e9e413b8e5209489a3944ae0bb7930c42a999c))
153
+
154
+ ### 1.0.1 (2021-11-05)
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # x-server
2
+
3
+ X 服务端。
4
+
5
+ ## 技术栈
6
+
7
+ - [fastify v3](https://github.com/fastify/fastify)
8
+ - [prisma v3](https://github.com/prisma/prisma)
9
+
10
+ ## 特性
11
+
12
+ - 通过 `cache` 支持 redis 缓存
13
+ - 通过 `jwt` 支持用户认证
14
+ - 通过 `captcha` 支持验证码
15
+ - 通过 `defineHandler` 支持 POST、GET、POST、FILE、WS、XML 请求
16
+ - 通过 `defineTask` 支持定时任务
17
+
18
+ ## 命令
19
+
20
+ ### xs dev
21
+
22
+ 开始开发
23
+
24
+ - `-i aaa -i bbb`: 额外需要生成的索引文件
25
+ - `-npc xxx@bbbb`: npc 地址及密钥,格式:地址@密钥
26
+
27
+ ### xs build
28
+
29
+ 构建
30
+
31
+ - `-e aaa -e bbb`: 不应该被打的包
32
+
33
+ ### xs deploy
34
+
35
+ 部署
36
+
37
+ ### xs api
38
+
39
+ 生成 API
40
+
41
+ ### xs prisma
42
+
43
+ prisma 代理,主要为了注入环境变量
44
+
45
+ - `-p false`: 是否生产模式
46
+
47
+ ### xs run
48
+
49
+ 执行 package.json 里的脚本
50
+
51
+ - `-p false`: 是否生产模式
@@ -0,0 +1,44 @@
1
+ import * as ts from 'ts-morph';
2
+ import createDebug from 'debug';
3
+ import type { JSONSchema4 } from 'json-schema';
4
+ import type { XHandler } from '../core/types';
5
+ interface ApiData {
6
+ name: string;
7
+ desc: string;
8
+ type: 'string' | 'number' | 'boolean' | 'object' | 'array' | 'file';
9
+ enum: any[];
10
+ required: boolean;
11
+ children: ApiData[];
12
+ }
13
+ declare type ApiHandles = Array<{
14
+ category: string;
15
+ name: string;
16
+ path: string;
17
+ handlerMethod: XHandler.Method;
18
+ method: string;
19
+ requestData: ApiData;
20
+ responseData: ApiData;
21
+ requestDataJsonSchema: JSONSchema4;
22
+ responseDataJsonSchema: JSONSchema4;
23
+ }>;
24
+ export declare class ApiGenerator {
25
+ debug: createDebug.Debugger;
26
+ cwd: string;
27
+ project: ts.Project;
28
+ getTypeBySymbol(symbol: ts.Symbol): ts.Type;
29
+ getComment(declaration?: ts.Node): {
30
+ existing: boolean;
31
+ description: string;
32
+ tags: Map<string, string>;
33
+ };
34
+ getCommentBySymbol(symbol: ts.Symbol): string;
35
+ typeToApiData(type: ts.Type, _symbol?: ts.Symbol): ApiData;
36
+ apiDataToJsonSchema(apiData: ApiData, jsonSchema?: JSONSchema4): JSONSchema4;
37
+ genYApiData(handles: ApiHandles): {
38
+ name: string;
39
+ desc: string;
40
+ list: any;
41
+ }[];
42
+ generate(): Promise<void>;
43
+ }
44
+ export {};
@@ -0,0 +1,339 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.ApiGenerator = void 0;
30
+ const parseComment = __importStar(require("comment-parser"));
31
+ const ts = __importStar(require("ts-morph"));
32
+ const debug_1 = __importDefault(require("debug"));
33
+ const fs_extra_1 = __importDefault(require("fs-extra"));
34
+ const node_path_1 = __importDefault(require("node:path"));
35
+ const http_method_1 = require("../core/http_method");
36
+ const vtils_1 = require("vtils");
37
+ class ApiGenerator {
38
+ constructor() {
39
+ this.debug = (0, debug_1.default)('api');
40
+ this.cwd = process.cwd();
41
+ }
42
+ getTypeBySymbol(symbol) {
43
+ return symbol.getTypeAtLocation(symbol.getDeclarations()[0] || symbol.getValueDeclarationOrThrow());
44
+ }
45
+ getComment(declaration) {
46
+ const text = (declaration?.getLeadingCommentRanges()[0]?.getText() || '').trim();
47
+ const comment = parseComment.parse(text)[0];
48
+ const description = comment?.description || '';
49
+ const tags = new Map();
50
+ comment?.tags.forEach(tag => {
51
+ tags.set(tag.tag, tag.description);
52
+ });
53
+ return {
54
+ existing: !!comment,
55
+ description: description,
56
+ tags: tags,
57
+ };
58
+ }
59
+ getCommentBySymbol(symbol) {
60
+ return this.getComment(symbol.getDeclarations()[0])?.description || '';
61
+ }
62
+ typeToApiData(type, _symbol) {
63
+ // ws
64
+ if (type.getSymbol()?.getName() === 'SocketStream') {
65
+ return {
66
+ name: 'ws',
67
+ desc: 'ws',
68
+ required: false,
69
+ type: 'object',
70
+ children: [],
71
+ enum: [],
72
+ };
73
+ }
74
+ // XFile
75
+ if (type.getSymbol()?.getName() === 'MultipartFile') {
76
+ const symbol = _symbol || type.getSymbol();
77
+ return {
78
+ name: 'file',
79
+ desc: (symbol && this.getCommentBySymbol(symbol)) || '',
80
+ required: !!symbol && !(symbol.getFlags() & ts.SymbolFlags.Optional),
81
+ type: 'file',
82
+ children: [],
83
+ enum: [],
84
+ };
85
+ }
86
+ let isRequired = true;
87
+ let isUnion = type.isUnion();
88
+ const unionTypes = isUnion
89
+ ? type
90
+ .getUnionTypes()
91
+ .filter(item => !item.isBooleanLiteral() && !item.isNull() && !item.isUndefined())
92
+ : [];
93
+ isUnion = !!unionTypes.length;
94
+ if (isUnion) {
95
+ if (unionTypes.length === 1 && !unionTypes[0].isLiteral()) {
96
+ isUnion = false;
97
+ }
98
+ // 兼容 prisma 生成的类型用 null 表示可选
99
+ isRequired = unionTypes.length === type.getUnionTypes().length;
100
+ // 必须用 getBaseTypeOfLiteralType 获取枚举字面量的原始类型
101
+ type = unionTypes[0].getBaseTypeOfLiteralType();
102
+ }
103
+ const isEnum = type.isEnum();
104
+ const enumData = isEnum
105
+ ? // @ts-ignore
106
+ type.compilerType.types.reduce((res, item) => {
107
+ res[item.getSymbol().getName()] = item.value;
108
+ return res;
109
+ }, {})
110
+ : {};
111
+ const enumKeys = Object.keys(enumData);
112
+ const enumValues = Object.values(enumData);
113
+ const isIntersection = type.isIntersection();
114
+ const intersectionTypes = isIntersection && type.getIntersectionTypes();
115
+ const isArray = type.isArray();
116
+ const isString = isEnum
117
+ ? typeof enumValues[0] === 'string'
118
+ : type.isString() || ['Date'].includes(type.getSymbol()?.getName() || '');
119
+ const isNumber = isEnum
120
+ ? typeof enumValues[0] === 'number'
121
+ : type.isNumber();
122
+ const isBoolean = type.isBoolean() ||
123
+ (type.isUnion() &&
124
+ type.getUnionTypes().some(item => item.isBooleanLiteral()) &&
125
+ type
126
+ .getUnionTypes()
127
+ .every(item => item.isBooleanLiteral() || item.isNull() || item.isUndefined()));
128
+ const isObject = (!isArray && !isString && !isNumber && !isBoolean && type.isObject()) ||
129
+ // 将交集类型视为对象
130
+ isIntersection;
131
+ const symbol = _symbol || type.getSymbol();
132
+ const apiName = symbol?.getName() || '__type';
133
+ const apiDesc = [
134
+ symbol && this.getCommentBySymbol(symbol),
135
+ isEnum &&
136
+ `枚举:${enumKeys
137
+ .map((key, index) => `${key}->${enumValues[index]}`)
138
+ .join('; ')}`,
139
+ ]
140
+ .filter(Boolean)
141
+ .join('\n');
142
+ const apiEnum = isUnion
143
+ ? unionTypes.map(t => t.getLiteralValue())
144
+ : isEnum
145
+ ? enumValues
146
+ : [];
147
+ const apiType = isArray
148
+ ? 'array'
149
+ : isString
150
+ ? 'string'
151
+ : isNumber
152
+ ? 'number'
153
+ : isBoolean
154
+ ? 'boolean'
155
+ : 'object';
156
+ const apiRequired = isRequired === false
157
+ ? false
158
+ : !!symbol && !(symbol.getFlags() & ts.SymbolFlags.Optional);
159
+ const apiChildren = isArray
160
+ ? [this.typeToApiData(type.getArrayElementTypeOrThrow())]
161
+ : isObject
162
+ ? (0, vtils_1.ii)(() => {
163
+ const context = type._context;
164
+ const compilerFactory = context.compilerFactory;
165
+ const rawChecker = type.compilerType
166
+ .checker;
167
+ let symbols = [];
168
+ if (intersectionTypes) {
169
+ // https://github.com/microsoft/TypeScript/issues/38184
170
+ symbols = rawChecker
171
+ .getAllPossiblePropertiesOfTypes(intersectionTypes.map(item => item.compilerType))
172
+ // https://github.com/dsherret/ts-morph/blob/a7072fcf6f9babb784b40f0326c80dea4563a4aa/packages/ts-morph/src/compiler/types/Type.ts#L296
173
+ .map((symbol) => compilerFactory.getSymbol(symbol));
174
+ }
175
+ else {
176
+ // symbols = type.getApparentProperties()
177
+ // https://github.com/microsoft/TypeScript/issues/38184
178
+ symbols = rawChecker
179
+ .getAllPossiblePropertiesOfTypes([type.compilerType])
180
+ // https://github.com/dsherret/ts-morph/blob/a7072fcf6f9babb784b40f0326c80dea4563a4aa/packages/ts-morph/src/compiler/types/Type.ts#L296
181
+ .map((symbol) => compilerFactory.getSymbol(symbol));
182
+ }
183
+ return symbols.map(symbol => {
184
+ return this.typeToApiData(!symbol.compilerSymbol.declarations
185
+ ? // 对于复杂对象,没有定义的,通过 type 直接获取(在前面通过 getText 预处理得到)
186
+ compilerFactory.getType(symbol.compilerSymbol.type)
187
+ : this.getTypeBySymbol(symbol), symbol);
188
+ });
189
+ })
190
+ : [];
191
+ return {
192
+ name: apiName,
193
+ desc: apiDesc,
194
+ enum: apiEnum,
195
+ type: apiType,
196
+ required: apiRequired,
197
+ children: apiChildren,
198
+ };
199
+ }
200
+ apiDataToJsonSchema(apiData, jsonSchema = {}) {
201
+ jsonSchema.description = apiData.desc;
202
+ if (apiData.type === 'object') {
203
+ jsonSchema.type = 'object';
204
+ jsonSchema.properties = apiData.children.reduce((res, item) => {
205
+ ;
206
+ res[item.name] = this.apiDataToJsonSchema(item);
207
+ return res;
208
+ }, {});
209
+ jsonSchema.required = apiData.children
210
+ .filter(item => item.required)
211
+ .map(item => item.name);
212
+ }
213
+ else if (apiData.type === 'array') {
214
+ jsonSchema.type = 'array';
215
+ jsonSchema.items = apiData.children.map(item => this.apiDataToJsonSchema(item))[0];
216
+ }
217
+ else {
218
+ jsonSchema.type = apiData.type;
219
+ if (apiData.enum.length) {
220
+ jsonSchema.enum = apiData.enum;
221
+ }
222
+ }
223
+ return jsonSchema;
224
+ }
225
+ genYApiData(handles) {
226
+ const data = {};
227
+ for (const handle of handles) {
228
+ data[handle.category] = data[handle.category] || [];
229
+ data[handle.category].push(handle.handlerMethod === 'FILE'
230
+ ? {
231
+ method: handle.method.toUpperCase(),
232
+ title: handle.name,
233
+ path: handle.path,
234
+ req_body_type: 'form',
235
+ res_body_type: 'json',
236
+ req_body_is_json_schema: false,
237
+ res_body_is_json_schema: true,
238
+ req_params: [],
239
+ req_query: [],
240
+ req_headers: [],
241
+ req_body_form: handle.requestData.children.map(item => ({
242
+ required: item.required ? 1 : 0,
243
+ name: item.name,
244
+ type: item.type === 'file' ? 'file' : 'text',
245
+ })),
246
+ res_body: JSON.stringify(handle.responseDataJsonSchema),
247
+ }
248
+ : {
249
+ method: handle.method.toUpperCase(),
250
+ title: handle.name,
251
+ path: handle.path,
252
+ req_body_type: 'json',
253
+ res_body_type: 'json',
254
+ req_body_is_json_schema: true,
255
+ res_body_is_json_schema: true,
256
+ req_params: [],
257
+ req_query: [],
258
+ req_headers: [],
259
+ req_body_form: [],
260
+ req_body_other: JSON.stringify(handle.requestDataJsonSchema),
261
+ res_body: JSON.stringify(handle.responseDataJsonSchema),
262
+ });
263
+ }
264
+ return Object.keys(data).map(cat => ({
265
+ name: cat,
266
+ desc: cat,
267
+ list: data[cat],
268
+ }));
269
+ }
270
+ async generate() {
271
+ this.debug('启动项目...');
272
+ this.project = new ts.Project({
273
+ tsConfigFilePath: node_path_1.default.join(this.cwd, 'tsconfig.json'),
274
+ });
275
+ this.debug('加载文件...');
276
+ const sourceFile = this.project.getSourceFileOrThrow(node_path_1.default.join(this.cwd, 'src/generated/handlers.ts'));
277
+ this.debug('导出处理器...');
278
+ const handlerGroup = sourceFile.getExportSymbols();
279
+ const handles = [];
280
+ this.debug('生成API文档...');
281
+ for (const handlerList of handlerGroup) {
282
+ const handlerListSourceFile = this.getTypeBySymbol(handlerList)
283
+ .getProperties()[0]
284
+ .getDeclarations()[0]
285
+ .getSourceFile();
286
+ const basePath = `/${(0, vtils_1.snakeCase)(handlerListSourceFile
287
+ .getFilePath()
288
+ .replace('.ts', '')
289
+ .split('/src/handlers/')[1]).replace(/_/g, '/')}/`.replace(/\/{2,}/g, '/');
290
+ for (const handler of handlerListSourceFile
291
+ .getVariableStatements()
292
+ .filter(item => item.isExported())) {
293
+ // 重要:这一步必须,先调一遍 getText 获取看到的对象,后续对于复杂定义才不会报错
294
+ handler.getDeclarations().forEach(exp => {
295
+ exp.getType().getText();
296
+ });
297
+ const handlerExp = handler.getDeclarations()[0];
298
+ const handlerPath = `${basePath}${handlerExp.getName()}`;
299
+ this.debug('生成接口: %s ...', handlerPath);
300
+ const [requestType, responseType, methodType] = handlerExp
301
+ .getType()
302
+ .getTypeArguments();
303
+ const handlerComment = this.getComment(handlerExp.getParent().getParent());
304
+ if (handlerComment.tags.has('private')) {
305
+ this.debug('跳过生成接口: %s', `${handlerPath}`);
306
+ continue;
307
+ }
308
+ const handlerCategory = (0, vtils_1.pascalCase)(basePath) || 'ROOT';
309
+ const handlerName = handlerComment.description || handlerPath;
310
+ const handlerMethod = methodType.getLiteralValueOrThrow();
311
+ const serverMethod = http_method_1.HandlerMethodToHttpMethod[handlerMethod];
312
+ const requestData = this.typeToApiData(requestType);
313
+ const responseData = this.typeToApiData(responseType);
314
+ const requestDataJsonSchema = this.apiDataToJsonSchema(requestData);
315
+ const responseDataJsonSchema = this.apiDataToJsonSchema(responseData);
316
+ handles.push({
317
+ category: handlerCategory,
318
+ name: handlerName,
319
+ path: handlerPath,
320
+ handlerMethod: handlerMethod,
321
+ method: serverMethod,
322
+ requestData: requestData,
323
+ responseData: responseData,
324
+ requestDataJsonSchema: requestDataJsonSchema,
325
+ responseDataJsonSchema: responseDataJsonSchema,
326
+ });
327
+ }
328
+ }
329
+ this.debug('写入文件...');
330
+ await Promise.all([
331
+ fs_extra_1.default.outputJSON(node_path_1.default.join(this.cwd, 'temp/api.json'), handles, {
332
+ spaces: 2,
333
+ }),
334
+ fs_extra_1.default.outputJSON(node_path_1.default.join(this.cwd, 'temp/yapi.json'), this.genYApiData(handles), { spaces: 2 }),
335
+ ]);
336
+ }
337
+ }
338
+ exports.ApiGenerator = ApiGenerator;
339
+ // new ApiGenerator().generate()
@@ -0,0 +1,9 @@
1
+ import { ParsedEnv } from './env_util';
2
+ export interface BuildOptions {
3
+ cwd: string;
4
+ external?: string[];
5
+ inlineEnvs?: ParsedEnv[];
6
+ }
7
+ export declare class BuildUtil {
8
+ static build(options: BuildOptions): Promise<void>;
9
+ }