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.
@@ -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('收集核心插件时出错:', 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
- try {
188
- const glob = new Bun.Glob('*.json');
189
- for await (const file of glob.scan({
190
- cwd: userTablesDir,
191
- onlyFiles: true,
192
- absolute: true
193
- })) {
194
- const fileName = basename(file, '.json');
195
- if (fileName.startsWith('_')) continue;
196
-
197
- try {
198
- const tableDefine = await Bun.file(file).json();
199
- const tableName = tableDefine.tableName || fileName;
200
-
201
- // 检查是否使用保留前缀
202
- if (RESERVED_NAMES.tablePrefix.some((prefix) => tableName.startsWith(prefix))) {
203
- conflicts.push(`用户表 "${tableName}" 使用了保留前缀,保留前缀包括: ${RESERVED_NAMES.tablePrefix.join(', ')}`);
204
- continue;
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
- try {
224
- const glob = new Bun.Glob('**/*.ts');
225
- for await (const file of glob.scan({
226
- cwd: userApisDir,
227
- onlyFiles: true,
228
- absolute: true
229
- })) {
230
- const apiPath = relative(userApisDir, file).replace(/\.ts$/, '');
231
- if (apiPath.indexOf('_') !== -1) continue;
232
-
233
- try {
234
- const api = (await import(file)).default;
235
- const route = `${(api.method || 'POST').toUpperCase()}/api/${apiPath}`;
236
-
237
- // 检查是否使用保留路由前缀 /api
238
- if (apiPath.startsWith('api/') || apiPath === 'api') {
239
- conflicts.push(`用户路由 "${route}" 使用了保留路径前缀 "/api"`);
240
- continue;
241
- }
242
-
243
- // 检查是否与已有路由冲突
244
- if (registry.routes.has(route)) {
245
- conflicts.push(`用户路由 "${route}" 与 ${registry.routes.get(route)} 冲突`);
246
- } else {
247
- registry.routes.set(route, 'user');
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
- try {
260
- const glob = new Bun.Glob('*.ts');
261
- for await (const file of glob.scan({
262
- cwd: userPluginsDir,
263
- onlyFiles: true,
264
- absolute: true
265
- })) {
266
- const pluginName = basename(file).replace(/\.ts$/, '');
267
- if (pluginName.startsWith('_')) continue;
268
-
269
- // 检查是否使用保留名称(检测核心插件名或点号前缀是保留名称)
270
- const isReserved = RESERVED_NAMES.plugins.includes(pluginName) || (pluginName.includes('.') && RESERVED_NAMES.plugins.includes(pluginName.split('.')[0]));
271
- if (isReserved) {
272
- conflicts.push(`用户插件 "${pluginName}" 使用了保留名称,保留名称包括: ${RESERVED_NAMES.plugins.join(', ')}`);
273
- continue;
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
- if (registry.plugins.has(pluginName)) {
278
- conflicts.push(`用户插件 "${pluginName}" 与 ${registry.plugins.get(pluginName)} 冲突`);
279
- } else {
280
- registry.plugins.set(pluginName, 'user');
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;
@@ -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
- const userPluginsScanStart = Bun.nanoseconds();
264
- for await (const file of glob.scan({
265
- cwd: projectPluginDir,
266
- onlyFiles: true,
267
- absolute: true
268
- })) {
269
- const fileName = basename(file).replace(/\.ts$/, '');
270
- if (fileName.startsWith('_')) continue;
271
-
272
- // 检查是否已经加载了同名的核心插件
273
- if (loadedPluginNames.has(fileName)) {
274
- Logger.info(`跳过用户插件 ${fileName},因为同名的核心插件已存在`);
275
- continue;
276
- }
277
-
278
- try {
279
- const importStart = Bun.nanoseconds();
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
- Logger.info(`用户插件 ${fileName} 导入耗时: ${importTime}`);
290
- } catch (err: any) {
291
- hadUserPluginError = true;
292
- Logger.error(`用户插件 ${fileName} 导入失败`, error);
293
- process.exit(1);
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.2",
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": "62974502c25562f83b840c5fe1d831dbf6476255"
84
+ "gitHead": "dca9c1118414acb32adbf58208a00c34f8fdff13"
85
85
  }