befly 3.4.2 → 3.4.4
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/checks/conflict.ts +94 -82
- package/lifecycle/loader.ts +43 -32
- package/package.json +2 -2
package/checks/conflict.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { relative, basename } from 'pathe';
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
7
8
|
import { Logger } from '../lib/logger.js';
|
|
8
9
|
import { projectPluginDir, coreTableDir, projectTableDir, projectApiDir } from '../paths.js';
|
|
9
10
|
import { scanAddons, getAddonDir, addonDirExists } from '../util.js';
|
|
@@ -38,6 +39,11 @@ interface ConflictResult {
|
|
|
38
39
|
* 收集核心插件
|
|
39
40
|
*/
|
|
40
41
|
async function collectCorePlugins(registry: ResourceRegistry): Promise<void> {
|
|
42
|
+
// 检查插件目录是否存在
|
|
43
|
+
if (!existsSync(projectPluginDir)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
41
47
|
try {
|
|
42
48
|
const glob = new Bun.Glob('*.ts');
|
|
43
49
|
for await (const file of glob.scan({
|
|
@@ -55,7 +61,7 @@ async function collectCorePlugins(registry: ResourceRegistry): Promise<void> {
|
|
|
55
61
|
}
|
|
56
62
|
}
|
|
57
63
|
} catch (error: any) {
|
|
58
|
-
Logger.error('
|
|
64
|
+
Logger.error('收集插件时出错:', error);
|
|
59
65
|
}
|
|
60
66
|
}
|
|
61
67
|
|
|
@@ -184,104 +190,110 @@ async function collectUserResources(registry: ResourceRegistry): Promise<string[
|
|
|
184
190
|
|
|
185
191
|
// 收集用户表定义
|
|
186
192
|
const userTablesDir = projectTableDir;
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
193
|
+
if (existsSync(userTablesDir)) {
|
|
194
|
+
try {
|
|
195
|
+
const glob = new Bun.Glob('*.json');
|
|
196
|
+
for await (const file of glob.scan({
|
|
197
|
+
cwd: userTablesDir,
|
|
198
|
+
onlyFiles: true,
|
|
199
|
+
absolute: true
|
|
200
|
+
})) {
|
|
201
|
+
const fileName = basename(file, '.json');
|
|
202
|
+
if (fileName.startsWith('_')) continue;
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
const tableDefine = await Bun.file(file).json();
|
|
206
|
+
const tableName = tableDefine.tableName || fileName;
|
|
207
|
+
|
|
208
|
+
// 检查是否使用保留前缀
|
|
209
|
+
if (RESERVED_NAMES.tablePrefix.some((prefix) => tableName.startsWith(prefix))) {
|
|
210
|
+
conflicts.push(`用户表 "${tableName}" 使用了保留前缀,保留前缀包括: ${RESERVED_NAMES.tablePrefix.join(', ')}`);
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// 检查是否与已有表冲突
|
|
215
|
+
if (registry.tables.has(tableName)) {
|
|
216
|
+
conflicts.push(`用户表 "${tableName}" 与 ${registry.tables.get(tableName)} 冲突`);
|
|
217
|
+
} else {
|
|
218
|
+
registry.tables.set(tableName, 'user');
|
|
219
|
+
}
|
|
220
|
+
} catch (error: any) {
|
|
221
|
+
// 表定义解析错误会在 table.ts 中处理,这里跳过
|
|
205
222
|
}
|
|
206
|
-
|
|
207
|
-
// 检查是否与已有表冲突
|
|
208
|
-
if (registry.tables.has(tableName)) {
|
|
209
|
-
conflicts.push(`用户表 "${tableName}" 与 ${registry.tables.get(tableName)} 冲突`);
|
|
210
|
-
} else {
|
|
211
|
-
registry.tables.set(tableName, 'user');
|
|
212
|
-
}
|
|
213
|
-
} catch (error: any) {
|
|
214
|
-
// 表定义解析错误会在 table.ts 中处理,这里跳过
|
|
215
223
|
}
|
|
224
|
+
} catch (error: any) {
|
|
225
|
+
Logger.error('收集用户表定义时出错:', error);
|
|
216
226
|
}
|
|
217
|
-
} catch (error: any) {
|
|
218
|
-
// 用户可能没有 tables 目录,这是正常的
|
|
219
227
|
}
|
|
220
228
|
|
|
221
229
|
// 收集用户 API 路由
|
|
222
230
|
const userApisDir = projectApiDir;
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
231
|
+
if (existsSync(userApisDir)) {
|
|
232
|
+
try {
|
|
233
|
+
const glob = new Bun.Glob('**/*.ts');
|
|
234
|
+
for await (const file of glob.scan({
|
|
235
|
+
cwd: userApisDir,
|
|
236
|
+
onlyFiles: true,
|
|
237
|
+
absolute: true
|
|
238
|
+
})) {
|
|
239
|
+
const apiPath = relative(userApisDir, file).replace(/\.ts$/, '');
|
|
240
|
+
if (apiPath.indexOf('_') !== -1) continue;
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
const api = (await import(file)).default;
|
|
244
|
+
const route = `${(api.method || 'POST').toUpperCase()}/api/${apiPath}`;
|
|
245
|
+
|
|
246
|
+
// 检查是否使用保留路由前缀 /api
|
|
247
|
+
if (apiPath.startsWith('api/') || apiPath === 'api') {
|
|
248
|
+
conflicts.push(`用户路由 "${route}" 使用了保留路径前缀 "/api"`);
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// 检查是否与已有路由冲突
|
|
253
|
+
if (registry.routes.has(route)) {
|
|
254
|
+
conflicts.push(`用户路由 "${route}" 与 ${registry.routes.get(route)} 冲突`);
|
|
255
|
+
} else {
|
|
256
|
+
registry.routes.set(route, 'user');
|
|
257
|
+
}
|
|
258
|
+
} catch (error: any) {
|
|
259
|
+
// API 加载错误会在 loader.ts 中处理,这里跳过
|
|
248
260
|
}
|
|
249
|
-
} catch (error: any) {
|
|
250
|
-
// API 加载错误会在 loader.ts 中处理,这里跳过
|
|
251
261
|
}
|
|
262
|
+
} catch (error: any) {
|
|
263
|
+
Logger.error('收集用户 API 路由时出错:', error);
|
|
252
264
|
}
|
|
253
|
-
} catch (error: any) {
|
|
254
|
-
// 用户可能没有 apis 目录,这是正常的
|
|
255
265
|
}
|
|
256
266
|
|
|
257
267
|
// 收集用户插件
|
|
258
268
|
const userPluginsDir = projectPluginDir;
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
269
|
+
if (existsSync(userPluginsDir)) {
|
|
270
|
+
try {
|
|
271
|
+
const glob = new Bun.Glob('*.ts');
|
|
272
|
+
for await (const file of glob.scan({
|
|
273
|
+
cwd: userPluginsDir,
|
|
274
|
+
onlyFiles: true,
|
|
275
|
+
absolute: true
|
|
276
|
+
})) {
|
|
277
|
+
const pluginName = basename(file).replace(/\.ts$/, '');
|
|
278
|
+
if (pluginName.startsWith('_')) continue;
|
|
279
|
+
|
|
280
|
+
// 检查是否使用保留名称(检测核心插件名或点号前缀是保留名称)
|
|
281
|
+
const isReserved = RESERVED_NAMES.plugins.includes(pluginName) || (pluginName.includes('.') && RESERVED_NAMES.plugins.includes(pluginName.split('.')[0]));
|
|
282
|
+
if (isReserved) {
|
|
283
|
+
conflicts.push(`用户插件 "${pluginName}" 使用了保留名称,保留名称包括: ${RESERVED_NAMES.plugins.join(', ')}`);
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
275
286
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
287
|
+
// 检查是否与已有插件冲突
|
|
288
|
+
if (registry.plugins.has(pluginName)) {
|
|
289
|
+
conflicts.push(`用户插件 "${pluginName}" 与 ${registry.plugins.get(pluginName)} 冲突`);
|
|
290
|
+
} else {
|
|
291
|
+
registry.plugins.set(pluginName, 'user');
|
|
292
|
+
}
|
|
281
293
|
}
|
|
294
|
+
} catch (error: any) {
|
|
295
|
+
Logger.error('收集用户插件时出错:', error);
|
|
282
296
|
}
|
|
283
|
-
} catch (error: any) {
|
|
284
|
-
// 用户可能没有 plugins 目录,这是正常的
|
|
285
297
|
}
|
|
286
298
|
|
|
287
299
|
return conflicts;
|
package/lifecycle/loader.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { relative, basename } from 'pathe';
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
7
8
|
import { isPlainObject } from 'es-toolkit/compat';
|
|
8
9
|
import { Logger } from '../lib/logger.js';
|
|
9
10
|
import { calcPerfTime } from '../util.js';
|
|
@@ -260,41 +261,45 @@ export class Loader {
|
|
|
260
261
|
}
|
|
261
262
|
|
|
262
263
|
// 扫描用户插件目录
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
Logger.debug(`准备导入用户插件: ${fileName}`);
|
|
281
|
-
const plugin = await importWithTimeout(file);
|
|
282
|
-
const importTime = calcPerfTime(importStart);
|
|
283
|
-
Logger.debug(`用户插件 ${fileName} 导入成功,耗时: ${importTime}`);
|
|
284
|
-
|
|
285
|
-
const pluginInstance = plugin.default;
|
|
286
|
-
pluginInstance.pluginName = fileName;
|
|
287
|
-
userPlugins.push(pluginInstance);
|
|
264
|
+
if (!existsSync(projectPluginDir)) {
|
|
265
|
+
Logger.info(`项目插件目录不存在,跳过加载: ${projectPluginDir}`);
|
|
266
|
+
} else {
|
|
267
|
+
const userPluginsScanStart = Bun.nanoseconds();
|
|
268
|
+
for await (const file of glob.scan({
|
|
269
|
+
cwd: projectPluginDir,
|
|
270
|
+
onlyFiles: true,
|
|
271
|
+
absolute: true
|
|
272
|
+
})) {
|
|
273
|
+
const fileName = basename(file).replace(/\.ts$/, '');
|
|
274
|
+
if (fileName.startsWith('_')) continue;
|
|
275
|
+
|
|
276
|
+
// 检查是否已经加载了同名的核心插件
|
|
277
|
+
if (loadedPluginNames.has(fileName)) {
|
|
278
|
+
Logger.info(`跳过用户插件 ${fileName},因为同名的核心插件已存在`);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
288
281
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
282
|
+
try {
|
|
283
|
+
const importStart = Bun.nanoseconds();
|
|
284
|
+
Logger.debug(`准备导入用户插件: ${fileName}`);
|
|
285
|
+
const plugin = await importWithTimeout(file);
|
|
286
|
+
const importTime = calcPerfTime(importStart);
|
|
287
|
+
Logger.debug(`用户插件 ${fileName} 导入成功,耗时: ${importTime}`);
|
|
288
|
+
|
|
289
|
+
const pluginInstance = plugin.default;
|
|
290
|
+
pluginInstance.pluginName = fileName;
|
|
291
|
+
userPlugins.push(pluginInstance);
|
|
292
|
+
|
|
293
|
+
Logger.info(`用户插件 ${fileName} 导入耗时: ${importTime}`);
|
|
294
|
+
} catch (err: any) {
|
|
295
|
+
hadUserPluginError = true;
|
|
296
|
+
Logger.error(`用户插件 ${fileName} 导入失败`, error);
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
294
299
|
}
|
|
300
|
+
const userPluginsScanTime = calcPerfTime(userPluginsScanStart);
|
|
301
|
+
Logger.info(`用户插件扫描完成,耗时: ${userPluginsScanTime},共找到 ${userPlugins.length} 个插件`);
|
|
295
302
|
}
|
|
296
|
-
const userPluginsScanTime = calcPerfTime(userPluginsScanStart);
|
|
297
|
-
Logger.info(`用户插件扫描完成,耗时: ${userPluginsScanTime},共找到 ${userPlugins.length} 个插件`);
|
|
298
303
|
|
|
299
304
|
const sortedUserPlugins = sortPlugins(userPlugins);
|
|
300
305
|
if (sortedUserPlugins === false) {
|
|
@@ -400,6 +405,12 @@ export class Loader {
|
|
|
400
405
|
apiDir = projectApiDir;
|
|
401
406
|
}
|
|
402
407
|
|
|
408
|
+
// 检查目录是否存在
|
|
409
|
+
if (!existsSync(apiDir)) {
|
|
410
|
+
Logger.info(`${dirDisplayName}接口目录不存在,跳过加载: ${apiDir}`);
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
|
|
403
414
|
let totalApis = 0;
|
|
404
415
|
let loadedApis = 0;
|
|
405
416
|
let failedApis = 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "befly",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.4",
|
|
4
4
|
"description": "Befly - 为 Bun 专属打造的 TypeScript API 接口框架核心引擎",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -81,5 +81,5 @@
|
|
|
81
81
|
"ora": "^9.0.0",
|
|
82
82
|
"pathe": "^2.0.3"
|
|
83
83
|
},
|
|
84
|
-
"gitHead": "
|
|
84
|
+
"gitHead": "dca9c1118414acb32adbf58208a00c34f8fdff13"
|
|
85
85
|
}
|