this repo has no description
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}