const fs = require('fs'); const yaml = require('js-yaml'); const path = require('path'); // HTML转义函数,防止XSS攻击 function escapeHtml(unsafe) { if (unsafe === undefined || unsafe === null) { return ''; } return String(unsafe) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } // 读取配置文件 function loadConfig() { let config = null; try { // 优先尝试读取用户配置 if (fs.existsSync('config.user.yml')) { const userConfigFile = fs.readFileSync('config.user.yml', 'utf8'); config = yaml.load(userConfigFile); console.log('Using user configuration from config.user.yml'); } // 如果没有用户配置,则使用默认配置 else { const defaultConfigFile = fs.readFileSync('config.yml', 'utf8'); config = yaml.load(defaultConfigFile); console.log('No user configuration found, using default config.yml'); } } catch (e) { console.error('Error loading configuration file:', e); process.exit(1); } // 尝试读取书签配置 try { let bookmarksConfig = null; // 优先尝试读取用户书签配置 if (fs.existsSync('bookmarks.user.yml')) { const userBookmarksFile = fs.readFileSync('bookmarks.user.yml', 'utf8'); bookmarksConfig = yaml.load(userBookmarksFile); console.log('Using user bookmarks configuration from bookmarks.user.yml'); } // 如果没有用户书签配置,则尝试读取默认书签配置 else if (fs.existsSync('bookmarks.yml')) { const bookmarksFile = fs.readFileSync('bookmarks.yml', 'utf8'); bookmarksConfig = yaml.load(bookmarksFile); console.log('Using default bookmarks configuration from bookmarks.yml'); } // 添加书签页面配置 if (bookmarksConfig) { config.bookmarks = bookmarksConfig; // 确保导航中有书签页面 const hasBookmarksNav = config.navigation.some(nav => nav.id === 'bookmarks'); if (!hasBookmarksNav) { config.navigation.push({ name: '书签', icon: 'fas fa-bookmark', id: 'bookmarks', active: false }); } } } catch (e) { console.error('Error loading bookmarks configuration:', e); } return config; } // 生成导航菜单 function generateNavigation(navigation) { return navigation.map(nav => `
${escapeHtml(nav.name)}
`).join('\n'); } // 生成网站卡片HTML function generateSiteCards(sites) { return sites.map(site => `

${escapeHtml(site.name)}

${escapeHtml(site.description)}

`).join('\n'); } // 生成分类HTML function generateCategories(categories) { return categories.map(category => `

${escapeHtml(category.name)}

${generateSiteCards(category.sites)}
`).join('\n'); } // 生成社交链接HTML function generateSocialLinks(social) { return social.map(link => `
${escapeHtml(link.name)}
`).join('\n'); } // 生成欢迎区域和首页内容 function generateHomeContent(config) { return `

${escapeHtml(config.profile.title)}

${escapeHtml(config.profile.subtitle)}

${escapeHtml(config.profile.description)}

${generateCategories(config.categories)}`; } // 生成页面内容 function generatePageContent(pageId, data) { // 如果是book、marks页面,使用bookmarks配置 if (pageId === 'bookmarks' && data) { return `

${escapeHtml(data.title)}

${escapeHtml(data.subtitle)}

${generateCategories(data.categories)}`; } return `

${escapeHtml(data.title)}

${escapeHtml(data.subtitle)}

${generateCategories(data.categories)}`; } // 生成搜索结果页面 function generateSearchResultsPage(config) { // 获取所有导航页面ID const pageIds = config.navigation.map(nav => nav.id); // 生成所有页面的搜索结果区域 const searchSections = pageIds.map(pageId => { // 根据页面ID获取对应的图标和名称 const navItem = config.navigation.find(nav => nav.id === pageId); const icon = navItem ? navItem.icon : 'fas fa-file'; const name = navItem ? navItem.name : pageId; return ` `; }).join('\n'); return `

搜索结果

在所有页面中找到的匹配项

${searchSections}
`; } // 生成Google Fonts链接 function generateGoogleFontsLink(config) { const fonts = config.fonts; const googleFonts = []; // 收集需要加载的Google字体 Object.values(fonts).forEach(font => { if (font.source === 'google') { const fontName = font.family.replace(/["']/g, ''); const fontWeight = font.weight || 400; googleFonts.push(`family=${fontName}:wght@${fontWeight}`); } }); return googleFonts.length > 0 ? `` : ''; } // 生成字体CSS变量 function generateFontVariables(config) { const fonts = config.fonts; let css = ':root {\n'; Object.entries(fonts).forEach(([key, font]) => { css += ` --font-${key}: ${font.family};\n`; if (font.weight) { css += ` --font-weight-${key}: ${font.weight};\n`; } }); css += '}'; return css; } // 生成完整的HTML function generateHTML(config) { const googleFontsLink = generateGoogleFontsLink(config); const fontVariables = generateFontVariables(config); const currentYear = new Date().getFullYear(); // 处理所有页面内容 const pageContents = {}; // 首页内容 pageContents.home = generateHomeContent(config); // 如果配置了项目页面 if (config.projects) { pageContents.projects = generatePageContent('projects', config.projects); } // 如果配置了文章页面 if (config.articles) { pageContents.articles = generatePageContent('articles', config.articles); } // 如果配置了友链页面 if (config.friends) { pageContents.friends = generatePageContent('friends', config.friends); } // 如果配置了书签页面 if (config.bookmarks) { pageContents.bookmarks = generatePageContent('bookmarks', config.bookmarks); } // 生成所有页面的HTML const pagesHTML = Object.entries(pageContents).map(([id, content]) => `
${content}
`).join('\n'); // 生成搜索结果页面 const searchResultsHTML = generateSearchResultsPage(config); return ` ${escapeHtml(config.site.title)} ${googleFontsLink}
${pagesHTML}
${searchResultsHTML}
`; } // 复制静态文件 function copyStaticFiles(config) { // 确保dist目录存在 if (!fs.existsSync('dist')) { fs.mkdirSync('dist', { recursive: true }); } // 复制CSS文件 try { fs.copyFileSync('assets/style.css', 'dist/style.css'); console.log('Copied style.css to dist/'); } catch (e) { console.error('Error copying style.css:', e); } // 复制JavaScript文件 try { fs.copyFileSync('src/script.js', 'dist/script.js'); console.log('Copied script.js to dist/'); } catch (e) { console.error('Error copying script.js:', e); } // 如果配置了favicon,确保文件存在并复制 if (config.site.favicon) { try { if (fs.existsSync(`assets/${config.site.favicon}`)) { fs.copyFileSync(`assets/${config.site.favicon}`, `dist/${path.basename(config.site.favicon)}`); console.log(`Copied favicon: ${config.site.favicon} to dist/`); } else if (fs.existsSync(config.site.favicon)) { fs.copyFileSync(config.site.favicon, `dist/${path.basename(config.site.favicon)}`); console.log(`Copied favicon: ${config.site.favicon} to dist/`); } else { console.warn(`Warning: Favicon file not found: ${config.site.favicon}`); } } catch (e) { console.error('Error copying favicon:', e); } } } // 处理模板文件,替换占位符 function processTemplate(template, config) { const currentYear = new Date().getFullYear(); const googleFontsLink = generateGoogleFontsLink(config); const fontVariables = generateFontVariables(config); // 创建替换映射 const replacements = { '{{SITE_TITLE}}': escapeHtml(config.site.title), '{{SITE_LOGO_TEXT}}': escapeHtml(config.site.logo_text || '导航站'), // 从配置中获取,如果不存在则使用默认值 '{{GOOGLE_FONTS}}': googleFontsLink, '{{{FONT_VARIABLES}}}': fontVariables, '{{NAVIGATION}}': generateNavigation(config.navigation), '{{SOCIAL_LINKS}}': generateSocialLinks(config.social), '{{CURRENT_YEAR}}': currentYear, '{{HOME_CONTENT}}': generateHomeContent(config), '{{PROJECTS_CONTENT}}': generatePageContent('projects', config.projects), '{{ARTICLES_CONTENT}}': generatePageContent('articles', config.articles), '{{FRIENDS_CONTENT}}': generatePageContent('friends', config.friends), '{{BOOKMARKS_CONTENT}}': generatePageContent('bookmarks', config.bookmarks), '{{SEARCH_RESULTS}}': generateSearchResultsPage(config) }; // 执行替换 let processedTemplate = template; for (const [placeholder, value] of Object.entries(replacements)) { // 使用正则表达式进行全局替换 const regex = new RegExp(placeholder.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'); processedTemplate = processedTemplate.replace(regex, value || ''); } return processedTemplate; } // 调试函数 function debugConfig(config) { console.log('==== DEBUG INFO ===='); console.log('Navigation items:', config.navigation.map(nav => nav.id)); console.log('Has bookmarks config:', !!config.bookmarks); if (config.bookmarks) { console.log('Bookmarks title:', config.bookmarks.title); console.log('Bookmarks categories:', config.bookmarks.categories.length); } console.log('=================='); } // 主函数 function main() { const config = loadConfig(); // 输出调试信息 debugConfig(config); try { // 确保dist目录存在 if (!fs.existsSync('dist')) { fs.mkdirSync('dist', { recursive: true }); } // 读取模板文件 const templatePath = 'templates/index.html'; let htmlContent = ''; if (fs.existsSync(templatePath)) { // 读取模板并处理 const template = fs.readFileSync(templatePath, 'utf8'); htmlContent = processTemplate(template, config); console.log(`Using template from ${templatePath} and injecting content`); } else { // 如果没有模板文件,使用生成的HTML htmlContent = generateHTML(config); console.log('No template file found, using generated HTML'); } // 生成HTML fs.writeFileSync('dist/index.html', htmlContent); console.log('Successfully generated dist/index.html'); // 复制静态文件 copyStaticFiles(config); } catch (e) { console.error('Error in main function:', e); process.exit(1); } } main();