diff --git a/src/generator.js b/src/generator.js index 9f83ce5..df023cd 100644 --- a/src/generator.js +++ b/src/generator.js @@ -13,12 +13,12 @@ registerAllHelpers(handlebars); // 加载和注册Handlebars模板的函数 function loadHandlebarsTemplates() { const templatesDir = path.join(process.cwd(), 'templates'); - + // 检查基本模板目录是否存在 if (!fs.existsSync(templatesDir)) { throw new Error('Templates directory not found. Cannot proceed without templates.'); } - + // 加载布局模板 const layoutsDir = path.join(templatesDir, 'layouts'); if (fs.existsSync(layoutsDir)) { @@ -33,7 +33,7 @@ function loadHandlebarsTemplates() { } else { throw new Error('Layouts directory not found. Cannot proceed without layout templates.'); } - + // 加载组件模板 const componentsDir = path.join(templatesDir, 'components'); if (fs.existsSync(componentsDir)) { @@ -48,7 +48,7 @@ function loadHandlebarsTemplates() { } else { throw new Error('Components directory not found. Cannot proceed without component templates.'); } - + // 识别并检查默认布局模板是否存在 const defaultLayoutPath = path.join(layoutsDir, 'default.hbs'); if (!fs.existsSync(defaultLayoutPath)) { @@ -62,17 +62,17 @@ function loadHandlebarsTemplates() { */ function getDefaultLayoutTemplate() { const defaultLayoutPath = path.join(process.cwd(), 'templates', 'layouts', 'default.hbs'); - + // 检查默认布局模板是否存在 if (!fs.existsSync(defaultLayoutPath)) { throw new Error('Default layout template not found. Cannot proceed without default layout.'); } - + try { // 读取布局内容并编译模板 const layoutContent = fs.readFileSync(defaultLayoutPath, 'utf8'); const layoutTemplate = handlebars.compile(layoutContent); - + return { path: defaultLayoutPath, template: layoutTemplate @@ -85,41 +85,41 @@ function getDefaultLayoutTemplate() { // 渲染Handlebars模板函数 function renderTemplate(templateName, data, useLayout = true) { const templatePath = path.join(process.cwd(), 'templates', 'pages', `${templateName}.hbs`); - + // 检查模板是否存在 if (!fs.existsSync(templatePath)) { // 尝试使用通用模板 page.hbs const genericTemplatePath = path.join(process.cwd(), 'templates', 'pages', 'page.hbs'); - + if (fs.existsSync(genericTemplatePath)) { console.log(`模板 ${templateName}.hbs 不存在,使用通用模板 page.hbs 代替`); const genericTemplateContent = fs.readFileSync(genericTemplatePath, 'utf8'); const genericTemplate = handlebars.compile(genericTemplateContent); - + // 添加 pageId 到数据中,以便通用模板使用 const enhancedData = { ...data, pageId: templateName // 确保pageId在模板中可用 }; - + // 渲染页面内容 const pageContent = genericTemplate(enhancedData); - + // 如果不使用布局,直接返回页面内容 if (!useLayout) { return pageContent; } - + try { // 使用辅助函数获取默认布局模板 const { template: layoutTemplate } = getDefaultLayoutTemplate(); - + // 准备布局数据,包含页面内容 const layoutData = { ...enhancedData, body: pageContent }; - + // 渲染完整页面 return layoutTemplate(layoutData); } catch (layoutError) { @@ -129,29 +129,29 @@ function renderTemplate(templateName, data, useLayout = true) { throw new Error(`Template ${templateName}.hbs not found and generic template page.hbs not found. Cannot proceed without template.`); } } - + try { const templateContent = fs.readFileSync(templatePath, 'utf8'); const template = handlebars.compile(templateContent); - + // 渲染页面内容 const pageContent = template(data); - + // 如果不使用布局,直接返回页面内容 if (!useLayout) { return pageContent; } - + try { // 使用辅助函数获取默认布局模板 const { template: layoutTemplate } = getDefaultLayoutTemplate(); - + // 准备布局数据,包含页面内容 const layoutData = { ...data, body: pageContent }; - + // 渲染完整页面 return layoutTemplate(layoutData); } catch (layoutError) { @@ -193,7 +193,7 @@ function safeLoadYamlConfig(filePath) { if (!fs.existsSync(filePath)) { return null; } - + try { const fileContent = fs.readFileSync(filePath, 'utf8'); return yaml.load(fileContent); @@ -228,7 +228,7 @@ function loadModularConfig(dirPath) { if (siteConfig) { // 将site.yml中的内容分配到正确的配置字段 config.site = siteConfig; - + // 提取特殊字段到顶层配置 if (siteConfig.fonts) config.fonts = siteConfig.fonts; if (siteConfig.profile) config.profile = siteConfig.profile; @@ -245,22 +245,22 @@ function loadModularConfig(dirPath) { // 加载页面配置 const pagesPath = path.join(dirPath, 'pages'); if (fs.existsSync(pagesPath)) { - const files = fs.readdirSync(pagesPath).filter(file => + const files = fs.readdirSync(pagesPath).filter(file => file.endsWith('.yml') || file.endsWith('.yaml')); - + files.forEach(file => { const filePath = path.join(pagesPath, file); const fileConfig = safeLoadYamlConfig(filePath); - + if (fileConfig) { // 提取文件名(不含扩展名)作为配置键 const configKey = path.basename(file, path.extname(file)); - + // 特殊处理home.yml中的categories字段 if (configKey === 'home' && fileConfig.categories) { config.categories = fileConfig.categories; } - + // 将页面配置添加到主配置对象 config[configKey] = fileConfig; } @@ -278,7 +278,7 @@ function loadModularConfig(dirPath) { function ensureConfigDefaults(config) { // 创建一个新对象,避免修改原始配置 const result = { ...config }; - + // 确保基本结构存在 result.site = result.site || {}; result.navigation = result.navigation || []; @@ -286,30 +286,30 @@ function ensureConfigDefaults(config) { result.profile = result.profile || {}; result.social = result.social || []; result.categories = result.categories || []; - + // 站点基本信息默认值 result.site.title = result.site.title || 'MeNav导航'; result.site.favicon = result.site.favicon || 'favicon.ico'; result.site.logo = result.site.logo || null; result.site.footer = result.site.footer || ''; - result.site.theme = result.site.theme || { + result.site.theme = result.site.theme || { primary: '#4a89dc', background: '#f5f7fa', modeToggle: true }; - + // 用户资料默认值 result.profile = result.profile || {}; result.profile.title = result.profile.title || '欢迎使用'; result.profile.subtitle = result.profile.subtitle || 'MeNav个人导航系统'; result.profile.description = result.profile.description || '简单易用的个人导航站点'; - + // 为每个类别和站点设置默认值 result.categories = result.categories || []; result.categories.forEach(category => { category.name = category.name || '未命名分类'; category.sites = category.sites || []; - + // 为每个站点设置默认值 category.sites.forEach(site => { site.name = site.name || '未命名站点'; @@ -319,7 +319,7 @@ function ensureConfigDefaults(config) { site.external = typeof site.external === 'boolean' ? site.external : true; }); }); - + return result; } @@ -334,10 +334,10 @@ function validateConfig(config) { console.error('配置无效: 配置必须是一个对象'); return false; } - + // 所有其他验证被移除,因为它们只是检查但没有实际操作 // 配置默认值和数据修复已经在ensureConfigDefaults函数中处理 - + return true; } @@ -351,7 +351,7 @@ function getSubmenuForNavItem(navItem, config) { if (!navItem || !navItem.id || !config) { return null; } - + // 首页页面添加子菜单(分类) if (navItem.id === 'home' && Array.isArray(config.categories)) { return config.categories; @@ -376,7 +376,7 @@ function getSubmenuForNavItem(navItem, config) { else if (config[navItem.id] && config[navItem.id].categories && Array.isArray(config[navItem.id].categories)) { return config[navItem.id].categories; } - + return null; } @@ -388,20 +388,27 @@ function getSubmenuForNavItem(navItem, config) { function prepareRenderData(config) { // 创建渲染数据对象,包含原始配置 const renderData = { ...config }; - + // 添加额外渲染数据 renderData._meta = { generated_at: new Date(), version: process.env.npm_package_version || '1.0.0', generator: 'MeNav' }; - + // 确保navigation是数组 if (!Array.isArray(renderData.navigation)) { renderData.navigation = []; // 移除警告日志,数据处理逻辑保留 } - + + // 添加序列化的配置数据,用于浏览器扩展 + renderData.configJSON = JSON.stringify({ + version: process.env.npm_package_version || '1.0.0', + timestamp: new Date().toISOString(), + data: config + }); + // 添加导航项的活动状态标记和子菜单 if (Array.isArray(renderData.navigation)) { renderData.navigation = renderData.navigation.map((item, index) => { @@ -411,7 +418,7 @@ function prepareRenderData(config) { id: item.id || `nav-${index}`, active: index === 0 // 兼容原有逻辑 }; - + // 使用辅助函数获取子菜单 const submenu = getSubmenuForNavItem(navItem, renderData); if (submenu) { @@ -421,15 +428,15 @@ function prepareRenderData(config) { return navItem; }); } - + // 为Handlebars模板特别准备navigationData数组 renderData.navigationData = renderData.navigation; - + // 确保social数据格式正确 if (Array.isArray(renderData.social)) { renderData.socialLinks = renderData.social; // 兼容模板中的不同引用名 } - + return renderData; } @@ -444,11 +451,11 @@ function loadConfig() { social: [], categories: [] }; - + // 检查模块化配置来源是否存在 const hasUserModularConfig = fs.existsSync('config/user'); const hasDefaultModularConfig = fs.existsSync('config/_default'); - + // 根据优先级顺序选择最高优先级的配置 if (hasUserModularConfig) { // 1. 最高优先级: config/user/ 目录 @@ -459,7 +466,7 @@ function loadConfig() { } else { // 3. 最低优先级: 旧版单文件配置 (config.yml or config.yaml) const legacyConfigPath = fs.existsSync('config.yml') ? 'config.yml' : 'config.yaml'; - + if (fs.existsSync(legacyConfigPath)) { try { const fileContent = fs.readFileSync(legacyConfigPath, 'utf8'); @@ -475,14 +482,14 @@ function loadConfig() { // 确保配置有默认值并通过验证 config = ensureConfigDefaults(config); - + if (!validateConfig(config)) { // 移除警告日志,保留函数调用 } - + // 准备渲染数据 const renderData = prepareRenderData(config); - + return renderData; } @@ -491,10 +498,10 @@ function generateNavigation(navigation, config) { return navigation.map(nav => { // 根据页面ID获取对应的子菜单项(分类) let submenuItems = ''; - + // 使用辅助函数获取子菜单数据 const submenu = getSubmenuForNavItem(nav, config); - + // 如果存在子菜单,生成HTML if (submenu && Array.isArray(submenu)) { submenuItems = ` @@ -527,7 +534,7 @@ function generateSiteCards(sites) { if (!sites || !Array.isArray(sites) || sites.length === 0) { return `

暂无网站

`; } - + return sites.map(site => ` @@ -545,7 +552,7 @@ function generateCategories(categories) {

请在配置文件中添加分类

`; } - + return categories.map(category => `

${escapeHtml(category.name)}

@@ -560,7 +567,7 @@ function generateSocialLinks(social) { if (!social || !Array.isArray(social) || social.length === 0) { return ''; } - + // 尝试使用Handlebars模板 try { const socialLinksPath = path.join(process.cwd(), 'templates', 'components', 'social-links.hbs'); @@ -574,7 +581,7 @@ function generateSocialLinks(social) { console.error('Error rendering social-links template:', error); // 出错时回退到原始生成方法 } - + // 回退到原始生成方法 return social.map(link => `
@@ -597,11 +604,11 @@ function generatePageContent(pageId, data) {

请配置 ${pageId} 页面

`; } - + // 首页使用profile数据,其他页面使用自身数据 if (pageId === 'home') { const profile = data.profile || {}; - + return `

${escapeHtml(profile.title || '欢迎使用')}

@@ -614,7 +621,7 @@ ${generateCategories(data.categories)}`; const title = data.title || `${pageId} 页面`; const subtitle = data.subtitle || ''; const categories = data.categories || []; - + return `

${escapeHtml(title)}

@@ -628,14 +635,14 @@ ${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 `
- + + + - \ No newline at end of file + \ No newline at end of file