import Handlebars from "handlebars"; import type { TemplateData } from "./types.ts"; /** * Default template for generating filenames */ export const DEFAULT_FILENAME_TEMPLATE = `{{dateFormat publishedAt "YYYY-MM-DD"}}_{{slug (default path title)}}.md`; /** * Default template for generating file content */ export const DEFAULT_CONTENT_TEMPLATE = `--- title: "{{title}}" date: {{publishedAt}} {{#if tags.length}} tags: {{#each tags}} - {{this}} {{/each}} {{/if}} --- {{content}} `; /** * Convert a string to a URL-safe slug */ export function slugify(text: string): string { return text .toString() .toLowerCase() .trim() .replace(/^\/+/, "") // Remove leading slashes .replace(/\/+$/, "") // Remove trailing slashes .replace(/\s+/g, "-") // Replace spaces with - .replace(/[^\w-]+/g, "") // Remove non-word chars (except -) .replace(/--+/g, "-") // Replace multiple - with single - .replace(/^-+/, "") // Trim - from start .replace(/-+$/, ""); // Trim - from end } /** * Format a date string */ function formatDate(dateStr: string | undefined, format: string): string { if (!dateStr) { return ""; } const date = new Date(dateStr); if (Number.isNaN(date.getTime())) { return ""; } // Simple format replacements const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, "0"); const day = String(date.getDate()).padStart(2, "0"); return format .replace("YYYY", String(year)) .replace("MM", month) .replace("DD", day); } /** * Register custom Handlebars helpers */ function registerHelpers(hbs: typeof Handlebars): void { // Slug helper: {{slug text}} hbs.registerHelper("slug", (text: unknown) => { if (typeof text !== "string") { return ""; } return slugify(text); }); // Date format helper: {{dateFormat date "YYYY-MM-DD"}} hbs.registerHelper("dateFormat", (date: unknown, format: unknown) => { if (typeof date !== "string" || typeof format !== "string") { return ""; } return formatDate(date, format); }); // Default helper: {{default value fallback}} hbs.registerHelper("default", (value: unknown, fallback: unknown) => { if (value !== undefined && value !== null && value !== "") { return value; } return fallback; }); } /** * Create a configured Handlebars instance with helpers registered */ export function createHandlebars(): typeof Handlebars { const hbs = Handlebars.create(); registerHelpers(hbs); return hbs; } /** * Compile and render a template with the given data */ export function renderTemplate( hbs: typeof Handlebars, template: string, data: TemplateData, ): string { const compiled = hbs.compile(template); return compiled(data); } /** * Generate a filename from template and data */ export function generateFilename( hbs: typeof Handlebars, template: string, data: TemplateData, ): string { const filename = renderTemplate(hbs, template, data); // Sanitize filename - remove any path traversal attempts return filename.replace(/\.\./g, "").replace(/[<>:"|?*]/g, ""); } /** * Generate file content from template and data */ export function generateContent( hbs: typeof Handlebars, template: string, data: TemplateData, ): string { return renderTemplate(hbs, template, data); }