// 主进程 .env 加载器:.env.{mode} → .env.local → OS 环境变量(后者覆盖前者) import { readFileSync } from 'node:fs'; import { join, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; // ESM 中 __dirname 不可用,手动计算 const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); /** * 解析单个 .env 文件,将 key=value 写入 process.env。 * 已存在的 process.env 值不会被覆盖(OS 环境变量优先)。 * 返回成功加载的变量数。 */ function parseEnvFile(filePath: string, fileName: string): number { try { const content = readFileSync(filePath, 'utf-8'); let count = 0; for (const line of content.split('\n')) { const trimmed = line.trim(); // 跳过空行和注释 if (!trimmed || trimmed.startsWith('#')) continue; // 解析 key=value const eqIdx = trimmed.indexOf('='); if (eqIdx === -1) continue; const key = trimmed.slice(0, eqIdx).trim(); let value = trimmed.slice(eqIdx + 1).trim(); // 去除引号(支持 "value" 和 'value') if ( (value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")) ) { value = value.slice(1, -1); } // 不覆盖已在 OS 环境变量中设置的值 if (key && !process.env[key]) { process.env[key] = value; count++; } } if (count > 0) { console.log(`[load-env] 从 ${fileName} 加载了 ${count} 个环境变量`); } return count; } catch (err) { // 文件不存在 — 静默跳过(打包后 .env.* 不在 ASAR 中,这是正常情况) if ((err as NodeJS.ErrnoException).code === 'ENOENT') { return 0; } // 其他错误(权限、编码等)— 警告但不中断 console.warn(`[load-env] 读取 ${fileName} 失败:`, (err as Error).message); return 0; } } /** * 加载环境变量文件到 process.env。 * 开发模式加载 .env.development,生产模式加载 .env.production, * 之后再加载 .env.local 作为覆盖层。 */ export function loadEnvFile(): void { // 项目根目录 // APP_ROOT 在 main.ts 中设置(dist-electron/.. = 项目根) // fallback:dist-electron/main/load-env.js → __dirname/../.. = 项目根 const projectRoot = process.env.APP_ROOT || join(__dirname, '..', '..'); // 确定当前模式 const mode = !process.env.NODE_ENV || process.env.NODE_ENV === 'development' ? 'development' : 'production'; console.log( `[load-env] 模式: ${mode}, projectRoot: ${projectRoot}`, ); // ① 基础环境文件(按模式选择) const baseFile = `.env.${mode}`; parseEnvFile(join(projectRoot, baseFile), baseFile); // ② 本地覆盖文件(不进 git,用于打包后连测试服务器等特殊场景) const localFile = '.env.local'; parseEnvFile(join(projectRoot, localFile), localFile); }