From dd6e6880056debcea218e0ffa7598892e59d288e Mon Sep 17 00:00:00 2001 From: rbetree Date: Fri, 31 Oct 2025 00:53:08 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=B9=A6=E7=AD=BE?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bookmark-processor.js | 149 ++++++++++++++++++++++++++++++++------ 1 file changed, 126 insertions(+), 23 deletions(-) diff --git a/src/bookmark-processor.js b/src/bookmark-processor.js index e42853d..944d2c8 100644 --- a/src/bookmark-processor.js +++ b/src/bookmark-processor.js @@ -72,9 +72,14 @@ const ICON_MAPPING = { // 获取最新的书签文件 function getLatestBookmarkFile() { try { + console.log('[DEBUG] 开始查找书签文件...'); + console.log('[DEBUG] 书签目录:', BOOKMARKS_DIR); + // 确保书签目录存在 if (!fs.existsSync(BOOKMARKS_DIR)) { + console.log('[DEBUG] 书签目录不存在,创建目录...'); fs.mkdirSync(BOOKMARKS_DIR, { recursive: true }); + console.log('[WARN] 书签目录为空,未找到HTML文件'); return null; } @@ -82,7 +87,13 @@ function getLatestBookmarkFile() { const files = fs.readdirSync(BOOKMARKS_DIR) .filter(file => file.toLowerCase().endsWith('.html')); + console.log('[DEBUG] 找到的HTML文件数量:', files.length); + if (files.length > 0) { + console.log('[DEBUG] HTML文件列表:', files); + } + if (files.length === 0) { + console.log('[WARN] 未找到任何HTML书签文件'); return null; } @@ -95,16 +106,23 @@ function getLatestBookmarkFile() { // 找出最新的文件 fileStats.sort((a, b) => b.mtime - a.mtime); const latestFile = fileStats[0].file; + const latestFilePath = path.join(BOOKMARKS_DIR, latestFile); - return path.join(BOOKMARKS_DIR, latestFile); + console.log('[INFO] 选择最新的书签文件:', latestFile); + console.log('[DEBUG] 完整路径:', latestFilePath); + + return latestFilePath; } catch (error) { - console.error('Error finding bookmark file:', error); + console.error('[ERROR] 查找书签文件时出错:', error); return null; } } // 解析书签HTML内容,支持2-4层级嵌套结构 function parseBookmarks(htmlContent) { + console.log('[DEBUG] 开始解析书签HTML内容...'); + console.log('[DEBUG] HTML内容长度:', htmlContent.length, '字符'); + // 正则表达式匹配文件夹和书签 const folderRegex = /
]*)>(.*?)<\/H3>/g; const bookmarkRegex = /
]*>(.*?)<\/A>/g; @@ -116,40 +134,78 @@ function parseBookmarks(htmlContent) { // 递归解析嵌套文件夹 function parseNestedFolder(htmlContent, parentPath = [], level = 1) { + console.log(`[DEBUG] parseNestedFolder 被调用 - 层级:${level}, 路径:${parentPath.join('/')}, 内容长度:${htmlContent.length}`); + const folders = []; let match; + let matchCount = 0; - // 使用正则表达式匹配文件夹 - folderRegex.lastIndex = 0; + // 创建新的正则表达式实例,避免全局正则的 lastIndex 问题 + const localFolderRegex = /
]*)>(.*?)<\/H3>/g; - while ((match = folderRegex.exec(htmlContent)) !== null) { + while ((match = localFolderRegex.exec(htmlContent)) !== null) { + matchCount++; const folderName = match[2].trim(); const folderStart = match.index; const folderEnd = match.index + match[0].length; + console.log(`[DEBUG] 找到文件夹 #${matchCount}: "${folderName}" (层级${level}) 在位置 ${folderStart}`); + // 查找文件夹的结束位置 let folderContentEnd = htmlContent.length; let depth = 1; let pos = folderEnd; + let loopCount = 0; + const maxLoops = 10000; // 防止无限循环 + + console.log(`[DEBUG] 开始查找文件夹"${folderName}"的边界,起始位置:${pos}`); while (pos < htmlContent.length && depth > 0) { - const dlStart = htmlContent.substring(pos).match(/

/gi); - const dlEnd = htmlContent.substring(pos).match(/<\/DL>

/gi); + loopCount++; + if (loopCount > maxLoops) { + console.error(`[ERROR] 检测到可能的无限循环! 文件夹:"${folderName}", 层级:${level}, 循环次数:${loopCount}`); + console.error(`[ERROR] 当前位置:${pos}, 深度:${depth}`); + console.error(`[ERROR] 周围内容:`, htmlContent.substring(pos, pos + 100)); + break; + } - if (dlStart && dlStart.index < (dlEnd ? dlEnd.index : htmlContent.length)) { + // 修复:使用 search() 而不是 match(),因为 match() 返回数组没有 index 属性 + const remainingContent = htmlContent.substring(pos); + const dlStartIndex = remainingContent.search(/

/i); + const dlEndIndex = remainingContent.search(/<\/DL>

/i); + + if (loopCount % 100 === 0) { + console.log(`[DEBUG] 循环 ${loopCount}: pos=${pos}, depth=${depth}, dlStart=${dlStartIndex}, dlEnd=${dlEndIndex}`); + } + + // 找到开始标签且在结束标签之前(或没有结束标签) + if (dlStartIndex !== -1 && (dlEndIndex === -1 || dlStartIndex < dlEndIndex)) { depth++; - pos += dlStart.index + dlStart[0].length; - } else if (dlEnd) { + pos += dlStartIndex + '

'.length; + console.log(`[DEBUG] 找到

在位置 ${pos}, depth增加到 ${depth}`); + } + // 找到结束标签 + else if (dlEndIndex !== -1) { depth--; - pos += dlEnd.index + dlEnd[0].length; - } else { + pos += dlEndIndex + '

'.length; + console.log(`[DEBUG] 找到

在位置 ${pos}, depth减少到 ${depth}`); + } + // 都没找到,退出循环 + else { + console.log(`[DEBUG] 未找到更多标签,退出循环`); break; } } + if (loopCount > 100) { + console.log(`[DEBUG] 文件夹"${folderName}"边界查找循环${loopCount}次`); + } + folderContentEnd = pos; const folderContent = htmlContent.substring(folderEnd, folderContentEnd); + console.log(`[DEBUG] 文件夹"${folderName}"内容长度: ${folderContent.length}`); + // 解析文件夹内容 const folder = { name: folderName, @@ -157,14 +213,19 @@ function parseBookmarks(htmlContent) { path: [...parentPath, folderName] }; - // 检查是否包含子文件夹 - const hasSubfolders = folderRegex.test(folderContent); - folderRegex.lastIndex = 0; + // 检查是否包含子文件夹 - 创建新的正则实例避免干扰主循环 + const testFolderRegex = /

]*)>(.*?)<\/H3>/; + const hasSubfolders = testFolderRegex.test(folderContent); + + console.log(`[DEBUG] 文件夹"${folderName}"包含子文件夹: ${hasSubfolders}`); if (hasSubfolders && level < 4) { + console.log(`[DEBUG] 递归解析文件夹"${folderName}"的子文件夹...`); // 递归解析子文件夹 const subfolders = parseNestedFolder(folderContent, folder.path, level + 1); + console.log(`[DEBUG] 文件夹"${folderName}"解析到 ${subfolders.length} 个子项`); + // 根据层级深度决定数据结构 if (level === 1) { folder.subcategories = subfolders; @@ -175,6 +236,7 @@ function parseBookmarks(htmlContent) { folder.sites = parseSitesInFolder(folderContent); } } else { + console.log(`[DEBUG] 解析文件夹"${folderName}"中的书签...`); // 解析书签 folder.sites = parseSitesInFolder(folderContent); } @@ -185,10 +247,14 @@ function parseBookmarks(htmlContent) { folder.groups && folder.groups.length > 0; if (hasContent) { + console.log(`[DEBUG] 添加文件夹"${folderName}" (包含内容)`); folders.push(folder); + } else { + console.log(`[DEBUG] 跳过空文件夹"${folderName}"`); } } + console.log(`[DEBUG] parseNestedFolder 完成 - 层级:${level}, 返回 ${folders.length} 个文件夹`); return folders; } @@ -196,9 +262,11 @@ function parseBookmarks(htmlContent) { function parseSitesInFolder(folderContent) { const sites = []; let bookmarkMatch; + let siteCount = 0; bookmarkRegex.lastIndex = 0; while ((bookmarkMatch = bookmarkRegex.exec(folderContent)) !== null) { + siteCount++; const url = bookmarkMatch[1]; const name = bookmarkMatch[2].trim(); @@ -219,11 +287,14 @@ function parseBookmarks(htmlContent) { }); } + console.log(`[DEBUG] parseSitesInFolder 完成 - 找到 ${siteCount} 个书签`); return sites; } // 开始解析 + console.log('[DEBUG] 开始递归解析顶层分类...'); bookmarks.categories = parseNestedFolder(htmlContent); + console.log(`[INFO] 解析完成 - 共找到 ${bookmarks.categories.length} 个顶层分类`); return bookmarks; } @@ -343,53 +414,85 @@ function updateNavigationFile(filePath) { // 主函数 async function main() { + console.log('========================================'); + console.log('[INFO] 书签处理脚本启动'); + console.log('[INFO] 时间:', new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })); + console.log('========================================\n'); + // 获取最新的书签文件 + console.log('[步骤 1/5] 查找书签文件...'); const bookmarkFile = getLatestBookmarkFile(); if (!bookmarkFile) { + console.log('[ERROR] 未找到书签文件,处理终止'); return; } + console.log('[SUCCESS] ✓ 找到书签文件\n'); try { // 读取文件内容 + console.log('[步骤 2/5] 读取书签文件...'); const htmlContent = fs.readFileSync(bookmarkFile, 'utf8'); + console.log('[SUCCESS] ✓ 文件读取成功,大小:', htmlContent.length, '字符\n'); // 解析书签 + console.log('[步骤 3/5] 解析书签结构...'); const bookmarks = parseBookmarks(htmlContent); if (bookmarks.categories.length === 0) { - console.error('ERROR: No bookmark categories found in the HTML file. Processing aborted.'); + console.error('[ERROR] HTML文件中未找到书签分类,处理终止'); return; } + console.log('[SUCCESS] ✓ 解析完成\n'); // 生成YAML - const yaml = generateBookmarksYaml(bookmarks); - if (!yaml) { - console.error('ERROR: Failed to generate YAML from bookmarks. Processing aborted.'); + console.log('[步骤 4/5] 生成YAML配置...'); + const yamlContent = generateBookmarksYaml(bookmarks); + if (!yamlContent) { + console.error('[ERROR] YAML生成失败,处理终止'); return; } + console.log('[DEBUG] YAML内容长度:', yamlContent.length, '字符'); + console.log('[SUCCESS] ✓ YAML生成成功\n'); + // 保存文件 + console.log('[步骤 5/5] 保存配置文件...'); try { // 确保目标目录存在 if (!fs.existsSync(CONFIG_USER_PAGES_DIR)) { + console.log('[DEBUG] 创建目录:', CONFIG_USER_PAGES_DIR); fs.mkdirSync(CONFIG_USER_PAGES_DIR, { recursive: true }); } // 保存YAML到模块化位置 - fs.writeFileSync(MODULAR_OUTPUT_FILE, yaml, 'utf8'); + console.log('[DEBUG] 写入文件:', MODULAR_OUTPUT_FILE); + fs.writeFileSync(MODULAR_OUTPUT_FILE, yamlContent, 'utf8'); // 验证文件是否确实被创建 if (!fs.existsSync(MODULAR_OUTPUT_FILE)) { - console.error(`ERROR: File was not created: ${MODULAR_OUTPUT_FILE}`); + console.error(`[ERROR] 文件未能创建: ${MODULAR_OUTPUT_FILE}`); process.exit(1); } + console.log('[SUCCESS] ✓ 文件保存成功'); + console.log('[INFO] 输出文件:', MODULAR_OUTPUT_FILE, '\n'); + // 更新导航 + console.log('[附加步骤] 更新导航配置...'); updateNavigationWithBookmarks(); + console.log('[SUCCESS] ✓ 导航配置已更新\n'); + } catch (writeError) { - console.error(`ERROR writing file:`, writeError); + console.error(`[ERROR] 写入文件时出错:`, writeError); + console.error('[ERROR] 错误堆栈:', writeError.stack); process.exit(1); } + + console.log('========================================'); + console.log('[SUCCESS] ✓✓✓ 书签处理完成!✓✓✓'); + console.log('========================================'); + } catch (error) { - console.error('Error processing bookmark file:', error); + console.error('[FATAL] 处理书签文件时发生错误:', error); + console.error('[ERROR] 错误堆栈:', error.stack); process.exit(1); } }