curtis
14小時前 3af5c004b4f2d2005d22ee85dccc2c80a66b1556
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
108
109
const fs = require('fs');
const readline = require('readline');
const path = require('path');
 
/**
 * 尋找 Pascal 檔案中的影像處理相關關鍵字,並生成 CSV 輸出。
 * @param {string} filePath - Pascal 檔案路徑
 * @param {string} outputCsv - 輸出的 CSV 檔案名稱
 */
async function findImageKeywords(filePath, keywordsStr, exclusionStr, outputCsv) {
  const keeper = {}
  // 拆分並使用 Set 移除重複的關鍵字
  const keywords = [...new Set(keywordsStr.split('|'))];
  const exclusion = exclusionStr ? [...new Set(exclusionStr.split('|'))] : [];
 
  // 用於匹配 Delphi procedure 或 function 的正規表達式
  const methodPattern = /^\s*(?:procedure|function)\s+([a-zA-Z0-9_\.]+)/i;
 
  const results = [];
  let currentMethod = "Global_Or_Interface"; // 預設狀態 (尚未進入任何方法)
 
  if (!fs.existsSync(filePath)) {
    console.error(`❌ 錯誤: 找不到指定的檔案 '${filePath}'`);
    console.log("請確保檔案名稱正確,並與此腳本放在同一目錄下。");
    return;
  }
 
  console.log(`🔍 開始掃描檔案: ${filePath} ...\n`);
  const fileName = path.basename(filePath);
 
  // 建立逐行讀取流 (使用 latin1 讀取以避免 Big5 中文字元在原生 Node 環境報錯)
  // 由於我們只比對英文關鍵字,編碼不會影響比對結果
  const fileStream = fs.createReadStream(filePath, {encoding: 'latin1'});
  const rl = readline.createInterface({
    input: fileStream,
    crlfDelay: Infinity
  });
 
  let lineNum = 0;
 
  for await (const line of rl) {
    lineNum++;
 
    // 檢查是否進入了新的方法實作區塊
    const methodMatch = line.match(methodPattern);
    if (methodMatch) {
      currentMethod = methodMatch[1];
    }
 
    const lineLower = line.toLowerCase();
 
    // 檢查該行是否包含我們關注的關鍵字
    for (const kw of keywords) {
      if (lineLower.includes(kw.toLowerCase()) && !exclusion.some((_)=>lineLower.includes(_.toLowerCase()))) {
        keeper[currentMethod] ??= new Set([])
        if (keeper[currentMethod].has(kw))
          continue
        keeper[currentMethod].add(kw)
        results.push({
          MethodName: currentMethod,
          FileName: fileName,
          LineNumber: lineNum,
          Dependency: kw,
          CodeSnippet: line.trim().replace(/"/g, '""') // 處理 CSV 雙引號跳脫
        });
      }
    }
  }
 
  // 1. 輸出至 Console
  console.log("-".repeat(90));
 
  // 為了讓 Console 顯示更乾淨,我們按方法名稱進行分組顯示
  const groupedResults = results.reduce((acc, curr) => {
    if (!acc[curr.MethodName]) acc[curr.MethodName] = [];
    acc[curr.MethodName].push(curr);
    return acc;
  }, {});
 
  for (const [method, items] of Object.entries(groupedResults)) {
    // 收集該方法內找到的所有相依關鍵字 (去重複)
    const deps = [...new Set(items.map(i => i.Dependency))].join(', ');
    const lines = [...new Set(items.map(i => i.LineNumber))].join(', ');
 
    console.log(`${method.padEnd(30)} | ${fileName.padEnd(20)} | ${lines.padEnd(5)} | ${deps}`);
  }
 
  console.log(`\n✅ 掃描成功!共找到 ${results.length} 次相依引用。`);
 
  // 2. 輸出至 CSV 檔案
  // 加入 BOM 以確保 Excel 能正確解析 UTF-8 中文
  const csvHeader = '\uFEFF[ ],方法名,所在檔名,行號,引用了什麼相依,方法描述(請手動填寫),原始程式碼片段\n';
  
  const csvContent = results.map(r =>
    `[V],"${r.MethodName}","${r.FileName}","${r.LineNumber}","${r.Dependency}","","${r.CodeSnippet}"`
  ).join('\n');
  if (results.length) {
    fs.writeFileSync(outputCsv, csvHeader + csvContent, 'utf8');
  }
  console.log(`📁 詳細結果已匯出至: ${outputCsv} (可直接用 Excel 打開)`);
  return {
    csvHeader,
    csvContent
  } 
}
 
module.exports = {
  findImageKeywords