const fs = require('node:fs'); const path = require('node:path'); const Handlebars = require('handlebars'); const { registerAllHelpers } = require('../../helpers'); const { createLogger, isVerbose } = require('../utils/logger'); const log = createLogger('template'); const handlebars = Handlebars.create(); registerAllHelpers(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)) { throw new Error('Layouts directory not found. Cannot proceed without layout templates.'); } fs.readdirSync(layoutsDir) .filter((file) => file.endsWith('.hbs')) .sort() .forEach((file) => { const layoutName = path.basename(file, '.hbs'); const layoutPath = path.join(layoutsDir, file); const layoutContent = fs.readFileSync(layoutPath, 'utf8'); handlebars.registerPartial(layoutName, layoutContent); }); const componentsDir = path.join(templatesDir, 'components'); if (!fs.existsSync(componentsDir)) { throw new Error('Components directory not found. Cannot proceed without component templates.'); } fs.readdirSync(componentsDir) .filter((file) => file.endsWith('.hbs')) .sort() .forEach((file) => { const componentName = path.basename(file, '.hbs'); const componentPath = path.join(componentsDir, file); const componentContent = fs.readFileSync(componentPath, 'utf8'); handlebars.registerPartial(componentName, componentContent); }); const defaultLayoutPath = path.join(layoutsDir, 'default.hbs'); if (!fs.existsSync(defaultLayoutPath)) { throw new Error('Default layout template not found. Cannot proceed without default layout.'); } } 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, }; } catch (error) { throw new Error(`Error loading default layout template: ${error.message}`); } } function renderTemplate(templateName, data, useLayout = true) { const templatePath = path.join(process.cwd(), 'templates', 'pages', `${templateName}.hbs`); if (!fs.existsSync(templatePath)) { const genericTemplatePath = path.join(process.cwd(), 'templates', 'pages', 'page.hbs'); if (!fs.existsSync(genericTemplatePath)) { throw new Error( `Template ${templateName}.hbs not found and generic template page.hbs not found. Cannot proceed without template.` ); } if (isVerbose()) { log.info('页面模板不存在,已回退到通用模板 page.hbs', { template: `${templateName}.hbs` }); } else { log.warn('页面模板不存在,已回退到通用模板 page.hbs', { template: `${templateName}.hbs` }); } const genericTemplateContent = fs.readFileSync(genericTemplatePath, 'utf8'); const genericTemplate = handlebars.compile(genericTemplateContent); const enhancedData = { ...data, pageId: data && data.pageId ? data.pageId : templateName, }; const pageContent = genericTemplate(enhancedData); if (!useLayout) return pageContent; const { template: layoutTemplate } = getDefaultLayoutTemplate(); return layoutTemplate({ ...enhancedData, body: pageContent }); } try { const templateContent = fs.readFileSync(templatePath, 'utf8'); const template = handlebars.compile(templateContent); const pageContent = template(data); if (!useLayout) return pageContent; const { template: layoutTemplate } = getDefaultLayoutTemplate(); return layoutTemplate({ ...data, body: pageContent }); } catch (error) { throw new Error(`Error rendering template ${templateName}: ${error.message}`); } } module.exports = { handlebars, loadHandlebarsTemplates, getDefaultLayoutTemplate, renderTemplate, };