File size: 3,368 Bytes
ddce7e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import fs from 'fs';

/**
 * 解析 .env 文件内容为对象
 * 支持多行字符串(用双引号或单引号包裹)
 */
export function parseEnvFile(filePath) {
  const envData = {};
  const content = fs.readFileSync(filePath, 'utf8');
  const lines = content.split('\n');
  
  let currentKey = null;
  let currentValue = '';
  let inMultiline = false;
  let quoteChar = null;
  
  for (let i = 0; i < lines.length; i++) {
    let line = lines[i];
    
    if (inMultiline) {
      // 继续收集多行值
      currentValue += '\n' + line;
      // 检查是否结束(以引号结尾)
      if (line.trimEnd().endsWith(quoteChar)) {
        // 移除结尾引号
        currentValue = currentValue.slice(0, -1);
        envData[currentKey] = currentValue;
        inMultiline = false;
        currentKey = null;
        currentValue = '';
        quoteChar = null;
      }
    } else {
      line = line.trim();
      if (!line || line.startsWith('#')) continue;
      
      const eqIndex = line.indexOf('=');
      if (eqIndex === -1) continue;
      
      const key = line.slice(0, eqIndex).trim();
      let value = line.slice(eqIndex + 1);
      
      // 检查是否是引号开头的多行字符串
      const trimmedValue = value.trimStart();
      if ((trimmedValue.startsWith('"') || trimmedValue.startsWith("'")) &&
          !trimmedValue.endsWith(trimmedValue[0])) {
        // 多行字符串开始
        quoteChar = trimmedValue[0];
        currentKey = key;
        currentValue = trimmedValue.slice(1); // 移除开头引号
        inMultiline = true;
      } else {
        // 单行值,移除可能的引号
        value = value.trim();
        if ((value.startsWith('"') && value.endsWith('"')) ||
            (value.startsWith("'") && value.endsWith("'"))) {
          value = value.slice(1, -1);
        }
        envData[key] = value;
      }
    }
  }
  
  // 处理未闭合的多行字符串
  if (inMultiline && currentKey) {
    envData[currentKey] = currentValue;
  }
  
  return envData;
}

/**
 * 更新 .env 文件中的键值对
 * 支持多行字符串(自动用双引号包裹)
 */
export function updateEnvFile(filePath, updates) {
  let content = fs.readFileSync(filePath, 'utf8');
  
  Object.entries(updates).forEach(([key, value]) => {
    // 检查值是否包含换行符,如果包含则用双引号包裹
    let formattedValue = value;
    if (typeof value === 'string' && value.includes('\n')) {
      // 多行字符串:用双引号包裹
      formattedValue = `"${value}"`;
    }
    
    // 匹配整个键值对(包括可能的多行值)
    // 1. 先尝试匹配单行格式
    const singleLineRegex = new RegExp(`^${key}=.*$`, 'm');
    // 2. 再尝试匹配多行格式(以引号开头,可能跨多行,以引号结尾)
    const multiLineRegex = new RegExp(`^${key}=["']([\\s\\S]*?)["']$`, 'm');
    
    if (multiLineRegex.test(content)) {
      // 替换多行格式
      content = content.replace(multiLineRegex, `${key}=${formattedValue}`);
    } else if (singleLineRegex.test(content)) {
      // 替换单行格式
      content = content.replace(singleLineRegex, `${key}=${formattedValue}`);
    } else {
      // 键不存在,追加到文件末尾
      content += `\n${key}=${formattedValue}`;
    }
  });
  
  fs.writeFileSync(filePath, content, 'utf8');
}