this repo has no description
at main 139 lines 3.3 kB view raw
1import Handlebars from "handlebars"; 2import type { TemplateData } from "./types.ts"; 3 4/** 5 * Default template for generating filenames 6 */ 7export const DEFAULT_FILENAME_TEMPLATE = `{{dateFormat publishedAt "YYYY-MM-DD"}}_{{slug (default path title)}}.md`; 8 9/** 10 * Default template for generating file content 11 */ 12export const DEFAULT_CONTENT_TEMPLATE = `--- 13title: "{{title}}" 14date: {{publishedAt}} 15{{#if tags.length}} 16tags: 17{{#each tags}} 18 - {{this}} 19{{/each}} 20{{/if}} 21--- 22 23{{content}} 24`; 25 26/** 27 * Convert a string to a URL-safe slug 28 */ 29export function slugify(text: string): string { 30 return text 31 .toString() 32 .toLowerCase() 33 .trim() 34 .replace(/^\/+/, "") // Remove leading slashes 35 .replace(/\/+$/, "") // Remove trailing slashes 36 .replace(/\s+/g, "-") // Replace spaces with - 37 .replace(/[^\w-]+/g, "") // Remove non-word chars (except -) 38 .replace(/--+/g, "-") // Replace multiple - with single - 39 .replace(/^-+/, "") // Trim - from start 40 .replace(/-+$/, ""); // Trim - from end 41} 42 43/** 44 * Format a date string 45 */ 46function formatDate(dateStr: string | undefined, format: string): string { 47 if (!dateStr) { 48 return ""; 49 } 50 51 const date = new Date(dateStr); 52 if (Number.isNaN(date.getTime())) { 53 return ""; 54 } 55 56 // Simple format replacements 57 const year = date.getFullYear(); 58 const month = String(date.getMonth() + 1).padStart(2, "0"); 59 const day = String(date.getDate()).padStart(2, "0"); 60 61 return format 62 .replace("YYYY", String(year)) 63 .replace("MM", month) 64 .replace("DD", day); 65} 66 67/** 68 * Register custom Handlebars helpers 69 */ 70function registerHelpers(hbs: typeof Handlebars): void { 71 // Slug helper: {{slug text}} 72 hbs.registerHelper("slug", (text: unknown) => { 73 if (typeof text !== "string") { 74 return ""; 75 } 76 return slugify(text); 77 }); 78 79 // Date format helper: {{dateFormat date "YYYY-MM-DD"}} 80 hbs.registerHelper("dateFormat", (date: unknown, format: unknown) => { 81 if (typeof date !== "string" || typeof format !== "string") { 82 return ""; 83 } 84 return formatDate(date, format); 85 }); 86 87 // Default helper: {{default value fallback}} 88 hbs.registerHelper("default", (value: unknown, fallback: unknown) => { 89 if (value !== undefined && value !== null && value !== "") { 90 return value; 91 } 92 return fallback; 93 }); 94} 95 96/** 97 * Create a configured Handlebars instance with helpers registered 98 */ 99export function createHandlebars(): typeof Handlebars { 100 const hbs = Handlebars.create(); 101 registerHelpers(hbs); 102 return hbs; 103} 104 105/** 106 * Compile and render a template with the given data 107 */ 108export function renderTemplate( 109 hbs: typeof Handlebars, 110 template: string, 111 data: TemplateData, 112): string { 113 const compiled = hbs.compile(template); 114 return compiled(data); 115} 116 117/** 118 * Generate a filename from template and data 119 */ 120export function generateFilename( 121 hbs: typeof Handlebars, 122 template: string, 123 data: TemplateData, 124): string { 125 const filename = renderTemplate(hbs, template, data); 126 // Sanitize filename - remove any path traversal attempts 127 return filename.replace(/\.\./g, "").replace(/[<>:"|?*]/g, ""); 128} 129 130/** 131 * Generate file content from template and data 132 */ 133export function generateContent( 134 hbs: typeof Handlebars, 135 template: string, 136 data: TemplateData, 137): string { 138 return renderTemplate(hbs, template, data); 139}