diff --git a/publish/src/core/frontmatter.ts b/publish/src/core/frontmatter.ts new file mode 100644 index 0000000..2e49ff7 --- /dev/null +++ b/publish/src/core/frontmatter.ts @@ -0,0 +1,47 @@ +import { parse as parseYaml } from '@std/yaml' + +export interface Author { + name: string + url?: string + orcid?: string + [key: string]: unknown +} + +export interface ImageEntry { + file: string + role?: 'cover' + alt: string + caption?: string + license: string | 'UNKNOWN' + authors: Author[] | 'UNKNOWN' + source_url?: string + modifications?: string + [key: string]: unknown +} + +export interface Frontmatter { + title: string + slug: string + date: Date + description?: string + image?: string + cover?: { image?: string; alt?: string; caption?: string } + tags?: string[] + draft?: boolean + license?: string + authors?: Author[] + images?: ImageEntry[] + [key: string]: unknown +} + +export function parseFrontmatter(md: string): { fm: Frontmatter; body: string } { + const match = md.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/) + if (!match) { + throw new Error('Frontmatter: no leading --- / --- block found') + } + const fm = parseYaml(match[1]) as Frontmatter + if (!fm || typeof fm !== 'object') { + throw new Error('Frontmatter: YAML did not produce an object') + } + return { fm, body: match[2] } +} diff --git a/publish/tests/fixtures/sample-post.md b/publish/tests/fixtures/sample-post.md new file mode 100644 index 0000000..252b854 --- /dev/null +++ b/publish/tests/fixtures/sample-post.md @@ -0,0 +1,17 @@ +--- +layout: post +title: "Sample Title" +slug: "sample-slug" +description: "A short summary" +image: cover.png +cover: + image: cover.png + alt: "Alt text" +date: 2024-01-15 +tags: ["Foo", "Bar"] +draft: false +--- + +Body content here. + +![pic](image1.jpg) diff --git a/publish/tests/frontmatter_test.ts b/publish/tests/frontmatter_test.ts new file mode 100644 index 0000000..bf01b8f --- /dev/null +++ b/publish/tests/frontmatter_test.ts @@ -0,0 +1,27 @@ +import { assertEquals, assertThrows } from '@std/assert' +import { parseFrontmatter } from '../src/core/frontmatter.ts' + +Deno.test('parseFrontmatter: zerlegt Frontmatter und Body', async () => { + const md = await Deno.readTextFile('./tests/fixtures/sample-post.md') + const { fm, body } = parseFrontmatter(md) + assertEquals(fm.title, 'Sample Title') + assertEquals(fm.slug, 'sample-slug') + assertEquals(fm.date instanceof Date, true) + assertEquals(fm.tags, ['Foo', 'Bar']) + assertEquals(fm.cover?.image, 'cover.png') + assertEquals(body.trim().startsWith('Body content here.'), true) +}) + +Deno.test('parseFrontmatter: wirft bei fehlendem Frontmatter', () => { + assertThrows(() => parseFrontmatter('no frontmatter here'), Error, 'Frontmatter') +}) + +Deno.test('parseFrontmatter: wirft bei unvollständigem Frontmatter', () => { + assertThrows(() => parseFrontmatter('---\ntitle: x\n'), Error, 'Frontmatter') +}) + +Deno.test('parseFrontmatter: erhält Leerzeichen in String-Werten', () => { + const md = '---\ntitle: "Hello World"\nslug: "h-w"\ndate: 2024-01-01\n---\n\nbody' + const { fm } = parseFrontmatter(md) + assertEquals(fm.title, 'Hello World') +})