Mirror of static site repo git.pfy.ch/pfych/static
Something went wrong. Try again.
TypeScript
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185import Handlebars from 'handlebars';import { readFile } from 'node:fs/promises';import { resolve } from 'node:path';import { format } from 'prettier';import { createScriptPath } from '../../utils/create-script-path';import { dateDiffInDays, formatDate } from '../../utils/dates';import { path2emoji } from '../../utils/path2emoji';import { getFormattedNow, setLastBuild } from '../../utils/set-last-build';import { mdToHtml } from './marked';import { apiUrl } from 'web-shared/src/lib/api';
export interface KnownFrontMatter { updatedAt?: string; publishedAt?: string; attribution?: string; alt?: string; image?: string; summary?: string; title?: string; scripts?: string; hidden?: string;}
export interface Post { file: { filePath: string; fileName: string; relativeFolder: string; }; emoji: string; frontMatter: KnownFrontMatter; mdContent: string; htmlContent: string; templatedContent: string;}
interface TemplateValues { title: string; titleEncoded: string; hidden?: boolean; summary?: string; summaryEncoded: string; image?: string; content: string; lastBuildDate: string; publishDate?: string; createDate?: { daysAgo: number; string: string; }; updateDate?: { daysAgo: number; string: string; }; headScript?: string[]; stylesheet?: string[]; footerScript?: string[]; apiUrl: string;}
const rawTemplate = await readFile(resolve('build', 'templates', 'post.hbs'), { encoding: 'utf-8',});const template = Handlebars.compile<TemplateValues>(rawTemplate);
export const renderTemplate = async (args: { content: string; title: string; hidden?: boolean; summary?: string; image?: string; scriptString?: string; dates: { published?: Date; updated?: Date; };}): Promise<{ templated: string; html: string }> => { const { content, title, hidden, summary, image, scriptString, dates } = args; const { published, updated } = dates;
const htmlContent = await mdToHtml(content);
return { html: htmlContent, templated: await format( setLastBuild( template({ title: title, summary: summary, titleEncoded: encodeURIComponent(title), summaryEncoded: encodeURIComponent(summary || ' '), image: image, hidden: hidden, content: htmlContent, headScript: createScriptPaths(scriptString), stylesheet: [`/styles/style.css`], lastBuildDate: getFormattedNow(), publishDate: published ? formatDate(published) : undefined, updateDate: updated ? { daysAgo: dateDiffInDays(new Date(), updated), string: formatDate(updated), } : undefined, apiUrl: apiUrl, }), ), { parser: 'html', htmlWhitespaceSensitivity: 'strict', bracketSameLine: true, }, ), };};
const createScriptPaths = (scriptsString?: string): string[] => { if (!scriptsString) { return []; }
const split = scriptsString.split(' ');
return split.map((path) => createScriptPath(path));};
export const createPostObject = async ( path: string, cwd: string,): Promise<Post> => { const content = await readFile(`${path}`, { encoding: 'utf-8', });
const split = content.split('---'); if (split.length < 3) { throw new Error('Missing metadata'); }
const attributes = split[1].split('\n').reduce((acc, val) => { return { // Regex grabs the value on the line after the first occurrence of ":" [val.split(':')[0].trim()]: val.replace(/^.+?:/, '')?.trim(), ...acc, }; }, {} as KnownFrontMatter);
const fileName = path.split(/([\\/])/g).pop()!; const { html, templated } = await renderTemplate({ content, title: attributes.title!, hidden: !!attributes.hidden, summary: attributes.summary, image: attributes.image, scriptString: attributes.scripts, dates: { published: attributes.publishedAt ? new Date(parseInt(attributes.publishedAt)) : undefined, updated: attributes.updatedAt ? new Date(parseInt(attributes.updatedAt)) : undefined, }, });
return { file: { filePath: path, fileName: fileName, relativeFolder: path.replace(`${cwd}/`, '').replace(fileName, ''), }, emoji: path2emoji(path), frontMatter: attributes, mdContent: content, htmlContent: html, templatedContent: templated, };};
export const createPostsObject = ( paths: string[], cwd: string,): Promise<Post[]> => Promise.all(paths.map((path) => createPostObject(path, cwd)));