befly 3.4.6 → 3.4.7

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/bin/index.ts CHANGED
@@ -3,6 +3,20 @@
3
3
  * Befly CLI - 命令行工具入口
4
4
  * 为 Befly 框架提供项目管理和脚本执行功能
5
5
  */
6
+
7
+ /**
8
+ * 环境启动器
9
+ * 必须在任何模块导入前执行
10
+ * 父进程会在这里启动子进程并退出
11
+ * 只有子进程会继续执行后面的代码
12
+ */
13
+ import { launch } from './launcher.js';
14
+ await launch(import.meta.path);
15
+
16
+ /**
17
+ * 以下代码只在子进程中执行
18
+ * 此时环境变量已正确加载
19
+ */
6
20
  import { Command } from 'commander';
7
21
  import { scriptCommand } from '../commands/script.js';
8
22
  import { devCommand } from '../commands/dev.js';
@@ -110,16 +124,16 @@ program.command('build').description('构建项目').option('-o, --outdir <path>
110
124
  program.command('start').description('启动生产服务器').option('-p, --port <number>', '端口号', '3000').option('-h, --host <string>', '主机地址', '0.0.0.0').option('-c, --cluster <instances>', '集群模式(数字或 max)').action(startCommand);
111
125
 
112
126
  // syncDb 命令 - 同步数据库
113
- program.command('syncDb').description('同步数据库表结构').option('-t, --table <name>', '指定表名').option('--dry-run', '预览模式,只显示不执行', false).action(syncDbCommand);
127
+ program.command('syncDb').description('同步数据库表结构').option('-t, --table <name>', '指定表名').option('--dry-run', '预览模式,只显示不执行', false).option('-e, --env <environment>', '指定环境 (development, production, test)').action(syncDbCommand);
114
128
 
115
129
  // syncApi 命令 - 同步 API 接口
116
- program.command('syncApi').description('同步 API 接口到数据库').option('--plan', '计划模式,只显示不执行', false).action(syncApiCommand);
130
+ program.command('syncApi').description('同步 API 接口到数据库').option('--plan', '计划模式,只显示不执行', false).option('-e, --env <environment>', '指定环境 (development, production, test)').action(syncApiCommand);
117
131
 
118
132
  // syncMenu 命令 - 同步菜单
119
- program.command('syncMenu').description('同步菜单配置到数据库').option('--plan', '计划模式,只显示不执行', false).action(syncMenuCommand);
133
+ program.command('syncMenu').description('同步菜单配置到数据库').option('--plan', '计划模式,只显示不执行', false).option('-e, --env <environment>', '指定环境 (development, production, test)').action(syncMenuCommand);
120
134
 
121
135
  // syncDev 命令 - 同步开发者账号
122
- program.command('syncDev').description('同步开发者管理员账号').option('--plan', '计划模式,只显示不执行', false).action(syncDevCommand);
136
+ program.command('syncDev').description('同步开发者管理员账号').option('--plan', '计划模式,只显示不执行', false).option('-e, --env <environment>', '指定环境 (development, production, test)').action(syncDevCommand);
123
137
 
124
138
  // addon 命令 - 插件管理
125
139
  const addon = program.command('addon').description('管理 Befly 插件');
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Befly CLI 环境启动器
3
+ * 负责检测环境参数并在正确的环境中启动子进程
4
+ *
5
+ * 工作原理:
6
+ * 1. 父进程:检测 --env 参数 → 启动子进程 → 退出
7
+ * 2. 子进程:跳过启动逻辑 → 继续执行 CLI 命令
8
+ */
9
+
10
+ import { readdirSync, existsSync } from 'node:fs';
11
+ import { join } from 'node:path';
12
+
13
+ /**
14
+ * 解析环境名称
15
+ * 支持前缀匹配(至少3个字符)
16
+ *
17
+ * @param input 用户输入的环境名称
18
+ * @returns 完整的环境名称
19
+ */
20
+ function resolveEnvName(input: string): string {
21
+ // 如果输入少于3个字符,直接返回(不做匹配)
22
+ if (input.length < 3) {
23
+ return input;
24
+ }
25
+
26
+ // 获取项目根目录的所有 .env.* 文件
27
+ const rootDir = process.cwd();
28
+ let envFiles: string[] = [];
29
+
30
+ try {
31
+ const files = readdirSync(rootDir);
32
+ envFiles = files.filter((file) => file.startsWith('.env.') && file !== '.env.example').map((file) => file.replace('.env.', ''));
33
+ } catch (error) {
34
+ // 读取失败时直接返回原始输入
35
+ return input;
36
+ }
37
+
38
+ // 精确匹配
39
+ if (envFiles.includes(input)) {
40
+ return input;
41
+ }
42
+
43
+ // 前缀匹配
44
+ const matches = envFiles.filter((env) => env.startsWith(input.toLowerCase()));
45
+
46
+ if (matches.length === 1) {
47
+ return matches[0];
48
+ }
49
+
50
+ if (matches.length > 1) {
51
+ console.error(`❌ 环境名称 "${input}" 匹配到多个环境: ${matches.join(', ')}`);
52
+ console.error('请使用更具体的名称');
53
+ process.exit(1);
54
+ }
55
+
56
+ // 未匹配到,返回原始输入(让 Bun 处理文件不存在的情况)
57
+ return input;
58
+ }
59
+
60
+ /**
61
+ * 启动环境检测和子进程
62
+ * 如果在父进程中,会启动子进程并退出(不返回)
63
+ * 如果在子进程中,直接返回(继续执行)
64
+ *
65
+ * @param entryFile CLI 入口文件的绝对路径(通常是 bin/index.ts)
66
+ */
67
+ export async function launch(entryFile: string): Promise<void> {
68
+ // 如果已经在子进程中,直接返回
69
+ if (process.env.BEFLY_SUBPROCESS) {
70
+ return;
71
+ }
72
+
73
+ // 确定环境名称
74
+ const envArgIndex = process.argv.indexOf('--env');
75
+ let envInput = 'development'; // 默认环境
76
+
77
+ if (envArgIndex !== -1 && process.argv[envArgIndex + 1]) {
78
+ // 如果指定了 --env 参数
79
+ envInput = process.argv[envArgIndex + 1];
80
+ } else if (process.env.NODE_ENV) {
81
+ // 如果设置了 NODE_ENV 环境变量
82
+ envInput = process.env.NODE_ENV;
83
+ }
84
+
85
+ // 解析环境名称(支持前缀匹配)
86
+ const envName = resolveEnvName(envInput);
87
+ const envFile = `.env.${envName}`;
88
+
89
+ // 过滤掉 --env 参数(已通过 --env-file 处理)
90
+ const filteredArgs = process.argv.slice(2).filter((arg, i, arr) => {
91
+ if (arg === '--env') return false;
92
+ if (arr[i - 1] === '--env') return false;
93
+ return true;
94
+ });
95
+
96
+ // 启动子进程,使用指定的环境文件
97
+ const proc = Bun.spawn(['bun', 'run', `--env-file=${envFile}`, entryFile, ...filteredArgs], {
98
+ cwd: process.cwd(),
99
+ stdio: ['inherit', 'inherit', 'inherit'],
100
+ env: {
101
+ ...process.env,
102
+ BEFLY_SUBPROCESS: '1', // 标记为子进程
103
+ NODE_ENV: envName // 同时设置 NODE_ENV
104
+ }
105
+ });
106
+
107
+ // 等待子进程结束并退出(父进程不会返回)
108
+ process.exit(await proc.exited);
109
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "3.4.6",
3
+ "version": "3.4.7",
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": "157f48d9170ab671919a4999050531469072bddf"
84
+ "gitHead": "754e5afe468859c551011ba4f8e5d70b3d0c0659"
85
85
  }