befly 3.8.19 → 3.8.20
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 +7 -6
- package/bunfig.toml +1 -1
- package/lib/database.ts +28 -25
- package/lib/dbHelper.ts +3 -3
- package/lib/jwt.ts +90 -99
- package/lib/logger.ts +44 -23
- package/lib/redisHelper.ts +19 -22
- package/loader/loadApis.ts +69 -114
- package/loader/loadHooks.ts +65 -0
- package/loader/loadPlugins.ts +50 -219
- package/main.ts +106 -133
- package/package.json +15 -7
- package/paths.ts +20 -0
- package/plugins/cache.ts +1 -3
- package/plugins/db.ts +8 -11
- package/plugins/logger.ts +5 -3
- package/plugins/redis.ts +10 -14
- package/router/api.ts +60 -106
- package/router/root.ts +15 -12
- package/router/static.ts +54 -58
- package/sync/syncAll.ts +58 -0
- package/sync/syncApi.ts +264 -0
- package/sync/syncDb/apply.ts +194 -0
- package/sync/syncDb/constants.ts +76 -0
- package/sync/syncDb/ddl.ts +194 -0
- package/sync/syncDb/helpers.ts +200 -0
- package/sync/syncDb/index.ts +164 -0
- package/sync/syncDb/schema.ts +201 -0
- package/sync/syncDb/sqlite.ts +50 -0
- package/sync/syncDb/table.ts +321 -0
- package/sync/syncDb/tableCreate.ts +146 -0
- package/sync/syncDb/version.ts +72 -0
- package/sync/syncDb.ts +19 -0
- package/sync/syncDev.ts +206 -0
- package/sync/syncMenu.ts +331 -0
- package/tsconfig.json +2 -4
- package/types/api.d.ts +6 -0
- package/types/befly.d.ts +152 -28
- package/types/context.d.ts +29 -3
- package/types/hook.d.ts +35 -0
- package/types/index.ts +14 -1
- package/types/plugin.d.ts +6 -7
- package/types/sync.d.ts +403 -0
- package/check.ts +0 -378
- package/env.ts +0 -106
- package/lib/middleware.ts +0 -275
- package/types/env.ts +0 -65
- package/types/util.d.ts +0 -45
- package/util.ts +0 -257
package/lib/redisHelper.ts
CHANGED
|
@@ -4,30 +4,27 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { RedisClient } from 'bun';
|
|
7
|
-
import { Env } from '../env.js';
|
|
8
7
|
import { Logger } from '../lib/logger.js';
|
|
9
8
|
import { Database } from './database.js';
|
|
10
9
|
|
|
11
|
-
/**
|
|
12
|
-
* Redis 键前缀
|
|
13
|
-
*/
|
|
14
|
-
const prefix = Env.REDIS_KEY_PREFIX ? `${Env.REDIS_KEY_PREFIX}:` : '';
|
|
15
|
-
|
|
16
10
|
/**
|
|
17
11
|
* Redis 助手类
|
|
18
12
|
*/
|
|
19
13
|
export class RedisHelper {
|
|
20
14
|
private client: RedisClient;
|
|
15
|
+
private prefix: string;
|
|
21
16
|
|
|
22
17
|
/**
|
|
23
18
|
* 构造函数
|
|
19
|
+
* @param prefix - Key 前缀
|
|
24
20
|
*/
|
|
25
|
-
constructor() {
|
|
21
|
+
constructor(prefix: string = '') {
|
|
26
22
|
const client = Database.getRedis();
|
|
27
23
|
if (!client) {
|
|
28
24
|
throw new Error('Redis 客户端未初始化,请先调用 Database.connectRedis()');
|
|
29
25
|
}
|
|
30
26
|
this.client = client;
|
|
27
|
+
this.prefix = prefix ? `${prefix}:` : '';
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
/**
|
|
@@ -40,7 +37,7 @@ export class RedisHelper {
|
|
|
40
37
|
async setObject<T = any>(key: string, obj: T, ttl: number | null = null): Promise<string | null> {
|
|
41
38
|
try {
|
|
42
39
|
const data = JSON.stringify(obj);
|
|
43
|
-
const pkey = `${prefix}${key}`;
|
|
40
|
+
const pkey = `${this.prefix}${key}`;
|
|
44
41
|
|
|
45
42
|
if (ttl) {
|
|
46
43
|
return await this.client.setex(pkey, ttl, data);
|
|
@@ -59,7 +56,7 @@ export class RedisHelper {
|
|
|
59
56
|
*/
|
|
60
57
|
async getObject<T = any>(key: string): Promise<T | null> {
|
|
61
58
|
try {
|
|
62
|
-
const pkey = `${prefix}${key}`;
|
|
59
|
+
const pkey = `${this.prefix}${key}`;
|
|
63
60
|
const data = await this.client.get(pkey);
|
|
64
61
|
return data ? JSON.parse(data) : null;
|
|
65
62
|
} catch (error: any) {
|
|
@@ -74,7 +71,7 @@ export class RedisHelper {
|
|
|
74
71
|
*/
|
|
75
72
|
async delObject(key: string): Promise<void> {
|
|
76
73
|
try {
|
|
77
|
-
const pkey = `${prefix}${key}`;
|
|
74
|
+
const pkey = `${this.prefix}${key}`;
|
|
78
75
|
await this.client.del(pkey);
|
|
79
76
|
} catch (error: any) {
|
|
80
77
|
Logger.error('Redis delObject 错误', error);
|
|
@@ -90,7 +87,7 @@ export class RedisHelper {
|
|
|
90
87
|
*/
|
|
91
88
|
async genTimeID(): Promise<number> {
|
|
92
89
|
const timestamp = Math.floor(Date.now() / 1000); // 秒级时间戳
|
|
93
|
-
const key = `${prefix}time_id_counter:${timestamp}`;
|
|
90
|
+
const key = `${this.prefix}time_id_counter:${timestamp}`;
|
|
94
91
|
|
|
95
92
|
const counter = await this.client.incr(key);
|
|
96
93
|
await this.client.expire(key, 1);
|
|
@@ -118,7 +115,7 @@ export class RedisHelper {
|
|
|
118
115
|
}
|
|
119
116
|
|
|
120
117
|
const timestamp = Math.floor(Date.now() / 1000); // 秒级时间戳
|
|
121
|
-
const key = `${prefix}time_id_counter:${timestamp}`;
|
|
118
|
+
const key = `${this.prefix}time_id_counter:${timestamp}`;
|
|
122
119
|
|
|
123
120
|
// 使用 INCRBY 一次性获取 N 个连续计数
|
|
124
121
|
const startCounter = await this.client.incrby(key, count);
|
|
@@ -143,7 +140,7 @@ export class RedisHelper {
|
|
|
143
140
|
*/
|
|
144
141
|
async setString(key: string, value: string, ttl: number | null = null): Promise<string | null> {
|
|
145
142
|
try {
|
|
146
|
-
const pkey = `${prefix}${key}`;
|
|
143
|
+
const pkey = `${this.prefix}${key}`;
|
|
147
144
|
if (ttl) {
|
|
148
145
|
return await this.client.setex(pkey, ttl, value);
|
|
149
146
|
}
|
|
@@ -160,7 +157,7 @@ export class RedisHelper {
|
|
|
160
157
|
*/
|
|
161
158
|
async getString(key: string): Promise<string | null> {
|
|
162
159
|
try {
|
|
163
|
-
const pkey = `${prefix}${key}`;
|
|
160
|
+
const pkey = `${this.prefix}${key}`;
|
|
164
161
|
return await this.client.get(pkey);
|
|
165
162
|
} catch (error: any) {
|
|
166
163
|
Logger.error('Redis getString 错误', error);
|
|
@@ -174,7 +171,7 @@ export class RedisHelper {
|
|
|
174
171
|
*/
|
|
175
172
|
async exists(key: string): Promise<number> {
|
|
176
173
|
try {
|
|
177
|
-
const pkey = `${prefix}${key}`;
|
|
174
|
+
const pkey = `${this.prefix}${key}`;
|
|
178
175
|
return await this.client.exists(pkey);
|
|
179
176
|
} catch (error: any) {
|
|
180
177
|
Logger.error('Redis exists 错误', error);
|
|
@@ -189,7 +186,7 @@ export class RedisHelper {
|
|
|
189
186
|
*/
|
|
190
187
|
async expire(key: string, seconds: number): Promise<number> {
|
|
191
188
|
try {
|
|
192
|
-
const pkey = `${prefix}${key}`;
|
|
189
|
+
const pkey = `${this.prefix}${key}`;
|
|
193
190
|
return await this.client.expire(pkey, seconds);
|
|
194
191
|
} catch (error: any) {
|
|
195
192
|
Logger.error('Redis expire 错误', error);
|
|
@@ -203,7 +200,7 @@ export class RedisHelper {
|
|
|
203
200
|
*/
|
|
204
201
|
async ttl(key: string): Promise<number> {
|
|
205
202
|
try {
|
|
206
|
-
const pkey = `${prefix}${key}`;
|
|
203
|
+
const pkey = `${this.prefix}${key}`;
|
|
207
204
|
return await this.client.ttl(pkey);
|
|
208
205
|
} catch (error: any) {
|
|
209
206
|
Logger.error('Redis ttl 错误', error);
|
|
@@ -221,7 +218,7 @@ export class RedisHelper {
|
|
|
221
218
|
try {
|
|
222
219
|
if (members.length === 0) return 0;
|
|
223
220
|
|
|
224
|
-
const pkey = `${prefix}${key}`;
|
|
221
|
+
const pkey = `${this.prefix}${key}`;
|
|
225
222
|
return await this.client.sadd(pkey, ...members);
|
|
226
223
|
} catch (error: any) {
|
|
227
224
|
Logger.error('Redis sadd 错误', error);
|
|
@@ -237,7 +234,7 @@ export class RedisHelper {
|
|
|
237
234
|
*/
|
|
238
235
|
async sismember(key: string, member: string): Promise<number> {
|
|
239
236
|
try {
|
|
240
|
-
const pkey = `${prefix}${key}`;
|
|
237
|
+
const pkey = `${this.prefix}${key}`;
|
|
241
238
|
return await this.client.sismember(pkey, member);
|
|
242
239
|
} catch (error: any) {
|
|
243
240
|
Logger.error('Redis sismember 错误', error);
|
|
@@ -252,7 +249,7 @@ export class RedisHelper {
|
|
|
252
249
|
*/
|
|
253
250
|
async scard(key: string): Promise<number> {
|
|
254
251
|
try {
|
|
255
|
-
const pkey = `${prefix}${key}`;
|
|
252
|
+
const pkey = `${this.prefix}${key}`;
|
|
256
253
|
return await this.client.scard(pkey);
|
|
257
254
|
} catch (error: any) {
|
|
258
255
|
Logger.error('Redis scard 错误', error);
|
|
@@ -267,7 +264,7 @@ export class RedisHelper {
|
|
|
267
264
|
*/
|
|
268
265
|
async smembers(key: string): Promise<string[]> {
|
|
269
266
|
try {
|
|
270
|
-
const pkey = `${prefix}${key}`;
|
|
267
|
+
const pkey = `${this.prefix}${key}`;
|
|
271
268
|
return await this.client.smembers(pkey);
|
|
272
269
|
} catch (error: any) {
|
|
273
270
|
Logger.error('Redis smembers 错误', error);
|
|
@@ -282,7 +279,7 @@ export class RedisHelper {
|
|
|
282
279
|
*/
|
|
283
280
|
async del(key: string): Promise<number> {
|
|
284
281
|
try {
|
|
285
|
-
const pkey = `${prefix}${key}`;
|
|
282
|
+
const pkey = `${this.prefix}${key}`;
|
|
286
283
|
return await this.client.del(pkey);
|
|
287
284
|
} catch (error: any) {
|
|
288
285
|
Logger.error('Redis del 错误', error);
|
package/loader/loadApis.ts
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* API 加载器
|
|
3
|
-
* 负责扫描和加载所有 API
|
|
3
|
+
* 负责扫描和加载所有 API 路由(组件、项目)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
// 内部依赖
|
|
7
7
|
import { existsSync } from 'node:fs';
|
|
8
|
+
|
|
9
|
+
// 外部依赖
|
|
10
|
+
import { relative, basename, join } from 'pathe';
|
|
8
11
|
import { isPlainObject } from 'es-toolkit/compat';
|
|
12
|
+
import { calcPerfTime, scanFiles, scanAddons, getAddonDir, addonDirExists } from 'befly-util';
|
|
13
|
+
|
|
14
|
+
// 相对导入
|
|
9
15
|
import { Logger } from '../lib/logger.js';
|
|
10
|
-
import { calcPerfTime } from '../util.js';
|
|
11
16
|
import { projectApiDir } from '../paths.js';
|
|
12
|
-
import { scanAddons, getAddonDir, addonDirExists } from '../util.js';
|
|
13
|
-
import type { ApiRoute } from '../types/api.js';
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
// 类型导入
|
|
19
|
+
import type { ApiRoute } from '../types/api.js';
|
|
16
20
|
|
|
17
21
|
/**
|
|
18
22
|
* API 默认字段定义
|
|
@@ -52,103 +56,6 @@ const DEFAULT_API_FIELDS = {
|
|
|
52
56
|
}
|
|
53
57
|
} as const;
|
|
54
58
|
|
|
55
|
-
/**
|
|
56
|
-
* 扫描用户 API 文件
|
|
57
|
-
*/
|
|
58
|
-
async function scanUserApis(): Promise<Array<{ file: string; routePrefix: string; displayName: string }>> {
|
|
59
|
-
const apis: Array<{ file: string; routePrefix: string; displayName: string }> = [];
|
|
60
|
-
|
|
61
|
-
if (!existsSync(projectApiDir)) {
|
|
62
|
-
return apis;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const glob = new Bun.Glob(API_GLOB_PATTERN);
|
|
66
|
-
|
|
67
|
-
for await (const file of glob.scan({
|
|
68
|
-
cwd: projectApiDir,
|
|
69
|
-
onlyFiles: true,
|
|
70
|
-
absolute: true
|
|
71
|
-
})) {
|
|
72
|
-
if (file.endsWith('.d.ts')) {
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
const apiPath = relative(projectApiDir, file).replace(/\.(ts|js)$/, '');
|
|
76
|
-
if (apiPath.indexOf('_') !== -1) continue;
|
|
77
|
-
|
|
78
|
-
apis.push({
|
|
79
|
-
file: file,
|
|
80
|
-
routePrefix: '',
|
|
81
|
-
displayName: '用户'
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return apis;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* 扫描组件 API 文件
|
|
90
|
-
*/
|
|
91
|
-
async function scanAddonApis(): Promise<Array<{ file: string; routePrefix: string; displayName: string }>> {
|
|
92
|
-
const apis: Array<{ file: string; routePrefix: string; displayName: string }> = [];
|
|
93
|
-
const glob = new Bun.Glob(API_GLOB_PATTERN);
|
|
94
|
-
const addons = scanAddons();
|
|
95
|
-
|
|
96
|
-
for (const addon of addons) {
|
|
97
|
-
if (!addonDirExists(addon, 'apis')) continue;
|
|
98
|
-
|
|
99
|
-
const addonApiDir = getAddonDir(addon, 'apis');
|
|
100
|
-
for await (const file of glob.scan({
|
|
101
|
-
cwd: addonApiDir,
|
|
102
|
-
onlyFiles: true,
|
|
103
|
-
absolute: true
|
|
104
|
-
})) {
|
|
105
|
-
if (file.endsWith('.d.ts')) {
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
const apiPath = relative(addonApiDir, file).replace(/\.(ts|js)$/, '');
|
|
109
|
-
if (apiPath.indexOf('_') !== -1) continue;
|
|
110
|
-
|
|
111
|
-
apis.push({
|
|
112
|
-
file: file,
|
|
113
|
-
routePrefix: `addon/${addon}`,
|
|
114
|
-
displayName: `组件${addon}`
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return apis;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* 初始化单个 API
|
|
124
|
-
*/
|
|
125
|
-
async function initApi(apiRoutes: Map<string, ApiRoute>, apiInfo: { file: string; routePrefix: string; displayName: string }): Promise<void> {
|
|
126
|
-
const { file, routePrefix, displayName } = apiInfo;
|
|
127
|
-
const apiDir = routePrefix === '' ? projectApiDir : getAddonDir(routePrefix.replace('addon/', ''), 'apis');
|
|
128
|
-
const apiPath = relative(apiDir, file).replace(/\.(ts|js)$/, '');
|
|
129
|
-
|
|
130
|
-
try {
|
|
131
|
-
// Windows 下路径需要转换为正斜杠格式
|
|
132
|
-
const filePath = file.replace(/\\/g, '/');
|
|
133
|
-
const apiImport = await import(filePath);
|
|
134
|
-
const api = apiImport.default;
|
|
135
|
-
|
|
136
|
-
// 设置默认值
|
|
137
|
-
api.method = api.method || 'POST';
|
|
138
|
-
api.auth = api.auth !== undefined ? api.auth : true;
|
|
139
|
-
// 合并默认字段:默认字段作为基础,API 自定义字段优先级更高
|
|
140
|
-
api.fields = { ...DEFAULT_API_FIELDS, ...(api.fields || {}) };
|
|
141
|
-
api.required = api.required || [];
|
|
142
|
-
|
|
143
|
-
// 构建路由
|
|
144
|
-
api.route = `${api.method.toUpperCase()}/api/${routePrefix ? routePrefix + '/' : ''}${apiPath}`;
|
|
145
|
-
apiRoutes.set(api.route, api);
|
|
146
|
-
} catch (error: any) {
|
|
147
|
-
Logger.error(`[${displayName}] 接口 ${apiPath} 加载失败`, error);
|
|
148
|
-
process.exit(1);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
59
|
/**
|
|
153
60
|
* 加载所有 API 路由
|
|
154
61
|
* @param apiRoutes - API 路由映射表
|
|
@@ -157,19 +64,67 @@ export async function loadApis(apiRoutes: Map<string, ApiRoute>): Promise<void>
|
|
|
157
64
|
try {
|
|
158
65
|
const loadStartTime = Bun.nanoseconds();
|
|
159
66
|
|
|
160
|
-
//
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
67
|
+
// 1. 扫描项目 API
|
|
68
|
+
const projectApiFiles = await scanFiles(projectApiDir);
|
|
69
|
+
const projectApiList = projectApiFiles.map((file) => ({
|
|
70
|
+
filePath: file.filePath,
|
|
71
|
+
relativePath: file.relativePath,
|
|
72
|
+
type: 'project' as const,
|
|
73
|
+
routePrefix: '',
|
|
74
|
+
typeName: '项目'
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
// 2. 扫描组件 API
|
|
78
|
+
const addonApiList: Array<{
|
|
79
|
+
filePath: string;
|
|
80
|
+
relativePath: string;
|
|
81
|
+
type: 'addon';
|
|
82
|
+
routePrefix: string;
|
|
83
|
+
typeName: string;
|
|
84
|
+
}> = [];
|
|
85
|
+
const addons = scanAddons();
|
|
86
|
+
for (const addon of addons) {
|
|
87
|
+
if (!addonDirExists(addon, 'apis')) continue;
|
|
88
|
+
|
|
89
|
+
const addonApiDir = getAddonDir(addon, 'apis');
|
|
90
|
+
const addonApiFiles = await scanFiles(addonApiDir);
|
|
91
|
+
|
|
92
|
+
for (const file of addonApiFiles) {
|
|
93
|
+
addonApiList.push({
|
|
94
|
+
filePath: file.filePath,
|
|
95
|
+
relativePath: file.relativePath,
|
|
96
|
+
type: 'addon' as const,
|
|
97
|
+
routePrefix: `addon/${addon}`,
|
|
98
|
+
typeName: `组件${addon}`
|
|
99
|
+
});
|
|
100
|
+
}
|
|
168
101
|
}
|
|
169
102
|
|
|
170
|
-
//
|
|
171
|
-
|
|
172
|
-
|
|
103
|
+
// 3. 合并所有 API 文件
|
|
104
|
+
const allApiFiles = [...projectApiList, ...addonApiList];
|
|
105
|
+
|
|
106
|
+
// 4. 遍历处理所有 API 文件
|
|
107
|
+
for (const apiFile of allApiFiles) {
|
|
108
|
+
try {
|
|
109
|
+
// Windows 下路径需要转换为正斜杠格式
|
|
110
|
+
const normalizedFilePath = apiFile.filePath.replace(/\\/g, '/');
|
|
111
|
+
const apiImport = await import(normalizedFilePath);
|
|
112
|
+
const api = apiImport.default;
|
|
113
|
+
|
|
114
|
+
// 设置默认值
|
|
115
|
+
api.method = api.method || 'POST';
|
|
116
|
+
api.auth = api.auth !== undefined ? api.auth : true;
|
|
117
|
+
// 合并默认字段:默认字段作为基础,API 自定义字段优先级更高
|
|
118
|
+
api.fields = { ...DEFAULT_API_FIELDS, ...(api.fields || {}) };
|
|
119
|
+
api.required = api.required || [];
|
|
120
|
+
|
|
121
|
+
// 构建路由
|
|
122
|
+
api.route = `${api.method.toUpperCase()}/api/${apiFile.routePrefix ? apiFile.routePrefix + '/' : ''}${apiFile.relativePath}`;
|
|
123
|
+
apiRoutes.set(api.route, api);
|
|
124
|
+
} catch (error: any) {
|
|
125
|
+
Logger.error(`[${apiFile.typeName}] 接口 ${apiFile.relativePath} 加载失败`, error);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
173
128
|
}
|
|
174
129
|
|
|
175
130
|
const totalLoadTime = calcPerfTime(loadStartTime);
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 钩子加载器
|
|
3
|
+
* 负责扫描和初始化所有钩子(核心、组件、项目)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// 外部依赖
|
|
7
|
+
import { scanAddons, getAddonDir } from 'befly-util';
|
|
8
|
+
|
|
9
|
+
// 相对导入
|
|
10
|
+
import { Logger } from '../lib/logger.js';
|
|
11
|
+
import { coreHookDir, projectHookDir } from '../paths.js';
|
|
12
|
+
import { sortModules, scanModules } from '../util.js';
|
|
13
|
+
|
|
14
|
+
// 类型导入
|
|
15
|
+
import type { Hook } from '../types/hook.js';
|
|
16
|
+
|
|
17
|
+
export async function loadHooks(befly: {
|
|
18
|
+
//
|
|
19
|
+
hookLists: Hook[];
|
|
20
|
+
pluginsConfig?: Record<string, any>;
|
|
21
|
+
}): Promise<void> {
|
|
22
|
+
try {
|
|
23
|
+
const allHooks: Hook[] = [];
|
|
24
|
+
|
|
25
|
+
// 1. 扫描核心钩子
|
|
26
|
+
const coreHooks = await scanModules<Hook>(coreHookDir, 'core', '钩子', befly.pluginsConfig);
|
|
27
|
+
|
|
28
|
+
// 2. 扫描组件钩子
|
|
29
|
+
const addonHooks: Hook[] = [];
|
|
30
|
+
const addons = scanAddons();
|
|
31
|
+
for (const addon of addons) {
|
|
32
|
+
const dir = getAddonDir(addon, 'hooks');
|
|
33
|
+
const hooks = await scanModules<Hook>(dir, 'addon', '钩子', befly.pluginsConfig, addon);
|
|
34
|
+
addonHooks.push(...hooks);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 3. 扫描项目钩子
|
|
38
|
+
const appHooks = await scanModules<Hook>(projectHookDir, 'app', '钩子', befly.pluginsConfig);
|
|
39
|
+
|
|
40
|
+
// 4. 合并所有钩子
|
|
41
|
+
allHooks.push(...coreHooks);
|
|
42
|
+
allHooks.push(...addonHooks);
|
|
43
|
+
allHooks.push(...appHooks);
|
|
44
|
+
|
|
45
|
+
// 5. 过滤禁用的钩子
|
|
46
|
+
const disableHooks = (befly as any).config?.disableHooks || [];
|
|
47
|
+
const enabledHooks = allHooks.filter((hook) => !disableHooks.includes(hook.name));
|
|
48
|
+
|
|
49
|
+
if (disableHooks.length > 0) {
|
|
50
|
+
Logger.info(`禁用钩子: ${disableHooks.join(', ')}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 6. 排序
|
|
54
|
+
const sortedHooks = sortModules(enabledHooks);
|
|
55
|
+
if (sortedHooks === false) {
|
|
56
|
+
Logger.error('钩子依赖关系错误,请检查 after 属性');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
befly.hookLists.push(...sortedHooks);
|
|
61
|
+
} catch (error: any) {
|
|
62
|
+
Logger.error('加载钩子时发生错误', error);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
}
|