import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // 路径配置 const NOTES_SOURCE_PATH = path.join(__dirname, '../public/mengyanote'); const OUTPUT_PATH = path.join(__dirname, '../src/data'); const PUBLIC_OUTPUT_PATH = path.join(__dirname, '../public/data'); const IGNORE_FILE_PATH = path.join(NOTES_SOURCE_PATH, 'ignore.json'); // 文件节点类型 const NODE_TYPES = { FOLDER: 'folder', FILE: 'file' }; // 读取忽略配置 function loadIgnoreConfig() { try { if (fs.existsSync(IGNORE_FILE_PATH)) { const ignoreContent = fs.readFileSync(IGNORE_FILE_PATH, 'utf-8'); const ignoreConfig = JSON.parse(ignoreContent); console.log('✓ 已加载忽略配置:', ignoreConfig.ignore); return ignoreConfig.ignore || []; } } catch (error) { console.warn('⚠️ 读取ignore.json失败,使用默认配置:', error.message); } return []; } // 检查是否为Markdown文件 function isMarkdownFile(filename) { return filename.toLowerCase().endsWith('.md'); } // 检查是否应该忽略的文件/文件夹 function shouldIgnore(name, customIgnoreList = []) { const defaultIgnoredItems = ['.obsidian', '.trash', '.git', 'node_modules']; const allIgnoredItems = [...defaultIgnoredItems, ...customIgnoreList]; return allIgnoredItems.includes(name) || name.startsWith('.'); } // 创建文件树节点 function createNode(name, type, path, children = []) { return { id: path, name, type, path, children, isExpanded: false }; } // 递归读取目录结构 function readDirectoryTree(dirPath, basePath = NOTES_SOURCE_PATH, ignoreList = []) { try { const items = fs.readdirSync(dirPath, { withFileTypes: true }); const children = []; for (const item of items) { if (shouldIgnore(item.name, ignoreList)) { continue; } const itemPath = path.join(dirPath, item.name); const relativePath = path.relative(basePath, itemPath).replace(/\\/g, '/'); if (item.isDirectory()) { const subChildren = readDirectoryTree(itemPath, basePath, ignoreList); if (subChildren.length > 0) { children.push(createNode(item.name, NODE_TYPES.FOLDER, relativePath, subChildren)); } } else if (isMarkdownFile(item.name)) { children.push(createNode(item.name, NODE_TYPES.FILE, relativePath)); } } // 排序:文件夹在前,文件在后,同类型按名称排序 children.sort((a, b) => { if (a.type !== b.type) { return a.type === NODE_TYPES.FOLDER ? -1 : 1; } return a.name.localeCompare(b.name, 'zh-CN'); }); return children; } catch (error) { console.error('Error reading directory:', error); return []; } } // 读取所有Markdown文件内容 function readAllMarkdownFiles(dirPath, basePath = NOTES_SOURCE_PATH, fileContents = {}, ignoreList = []) { try { const items = fs.readdirSync(dirPath, { withFileTypes: true }); for (const item of items) { if (shouldIgnore(item.name, ignoreList)) { continue; } const itemPath = path.join(dirPath, item.name); const relativePath = path.relative(basePath, itemPath).replace(/\\/g, '/'); if (item.isDirectory()) { readAllMarkdownFiles(itemPath, basePath, fileContents, ignoreList); } else if (isMarkdownFile(item.name)) { try { const content = fs.readFileSync(itemPath, 'utf-8'); fileContents[relativePath] = content; console.log(`✓ 读取文件: ${relativePath}`); } catch (error) { console.error(`✗ 读取文件失败: ${relativePath}`, error.message); } } } return fileContents; } catch (error) { console.error('Error reading markdown files:', error); return fileContents; } } // 生成数据文件 function generateData() { console.log('🚀 开始生成静态数据...'); // 检查源目录是否存在 if (!fs.existsSync(NOTES_SOURCE_PATH)) { console.error(`❌ 源目录不存在: ${NOTES_SOURCE_PATH}`); process.exit(1); } // 创建输出目录 if (!fs.existsSync(OUTPUT_PATH)) { fs.mkdirSync(OUTPUT_PATH, { recursive: true }); } if (!fs.existsSync(PUBLIC_OUTPUT_PATH)) { fs.mkdirSync(PUBLIC_OUTPUT_PATH, { recursive: true }); } // 加载忽略配置 console.log('📋 加载忽略配置...'); const ignoreList = loadIgnoreConfig(); console.log('📁 读取目录结构...'); const directoryTree = readDirectoryTree(NOTES_SOURCE_PATH, NOTES_SOURCE_PATH, ignoreList); console.log('📄 读取Markdown文件内容...'); const fileContents = readAllMarkdownFiles(NOTES_SOURCE_PATH, NOTES_SOURCE_PATH, {}, ignoreList); // 生成统计信息 const stats = { totalFiles: Object.keys(fileContents).length, totalFolders: countFolders(directoryTree), generatedAt: new Date().toISOString(), sourceDirectory: NOTES_SOURCE_PATH }; // 生成到 src/data 目录 const treeOutputPath = path.join(OUTPUT_PATH, 'directoryTree.json'); const contentOutputPath = path.join(OUTPUT_PATH, 'fileContents.json'); const statsOutputPath = path.join(OUTPUT_PATH, 'stats.json'); fs.writeFileSync(treeOutputPath, JSON.stringify(directoryTree, null, 2), 'utf-8'); fs.writeFileSync(contentOutputPath, JSON.stringify(fileContents, null, 2), 'utf-8'); fs.writeFileSync(statsOutputPath, JSON.stringify(stats, null, 2), 'utf-8'); console.log(`✓ 目录树已生成: ${treeOutputPath}`); console.log(`✓ 文件内容已生成: ${contentOutputPath}`); console.log(`✓ 统计信息已生成: ${statsOutputPath}`); // 生成到 public/data 目录 const publicTreeOutputPath = path.join(PUBLIC_OUTPUT_PATH, 'directoryTree.json'); const publicContentOutputPath = path.join(PUBLIC_OUTPUT_PATH, 'fileContents.json'); const publicStatsOutputPath = path.join(PUBLIC_OUTPUT_PATH, 'stats.json'); fs.writeFileSync(publicTreeOutputPath, JSON.stringify(directoryTree, null, 2), 'utf-8'); fs.writeFileSync(publicContentOutputPath, JSON.stringify(fileContents, null, 2), 'utf-8'); fs.writeFileSync(publicStatsOutputPath, JSON.stringify(stats, null, 2), 'utf-8'); console.log(`✓ 目录树已生成到public: ${publicTreeOutputPath}`); console.log(`✓ 文件内容已生成到public: ${publicContentOutputPath}`); console.log(`✓ 统计信息已生成到public: ${publicStatsOutputPath}`); console.log('🎉 数据生成完成!'); console.log(`📊 统计信息:`); console.log(` - 文件数量: ${stats.totalFiles}`); console.log(` - 文件夹数量: ${stats.totalFolders}`); console.log(` - 生成时间: ${stats.generatedAt}`); } // 计算文件夹数量 function countFolders(nodes) { let count = 0; for (const node of nodes) { if (node.type === NODE_TYPES.FOLDER) { count++; if (node.children && node.children.length > 0) { count += countFolders(node.children); } } } return count; } // 执行生成 generateData();