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 }