Something went wrong. Try again.
A CLI for publishing standard.site documents to ATProto
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418import { describe, expect, it } from "bun:test";import { parseFrontmatter, resolvePostPath } from "../src/lib/markdown";import type { BlogPost } from "../src/lib/types";
describe("parseFrontmatter", () => { describe("delimiters", () => { it("parses YAML frontmatter (--- delimiter)", () => { const content = `---title: Hello World---Body content here.`; const { frontmatter, body } = parseFrontmatter(content); expect(frontmatter.title).toBe("Hello World"); expect(body).toBe("Body content here."); });
it("parses TOML frontmatter (+++ delimiter)", () => { const content = `+++title = "Hugo Post"+++Body content here.`; const { frontmatter, body } = parseFrontmatter(content); expect(frontmatter.title).toBe("Hugo Post"); expect(body).toBe("Body content here."); });
it("parses alternative frontmatter (*** delimiter)", () => { const content = `***title: Alt Post***Body content here.`; const { frontmatter, body } = parseFrontmatter(content); expect(frontmatter.title).toBe("Alt Post"); expect(body).toBe("Body content here."); });
it("throws when no frontmatter is present", () => { const content = "Just plain content with no frontmatter."; expect(() => parseFrontmatter(content)).toThrow( "Could not parse frontmatter", ); }); });
describe("scalar values", () => { it("parses a string value", () => { const content = `---title: My Postdescription: A short description---`; const { frontmatter } = parseFrontmatter(content); expect(frontmatter.title).toBe("My Post"); expect(frontmatter.description).toBe("A short description"); });
it("strips double quotes from values", () => { const content = `---title: "Quoted Title"---`; const { frontmatter } = parseFrontmatter(content); expect(frontmatter.title).toBe("Quoted Title"); });
it("strips single quotes from values", () => { const content = `---title: 'Single Quoted'---`; const { frontmatter } = parseFrontmatter(content); expect(frontmatter.title).toBe("Single Quoted"); });
it("parses YAML folded multiline string", () => { const content = `---excerpt: > This is a folded multiline string---`; const { rawFrontmatter } = parseFrontmatter(content); expect(rawFrontmatter.excerpt).toBe( "This is a folded multiline string\n", ); });
it("parses YAML stripped folded multiline string", () => { const content = `---excerpt: >- This is a stripped folded multiline string---`; const { rawFrontmatter } = parseFrontmatter(content); expect(rawFrontmatter.excerpt).toBe( "This is a stripped folded multiline string", ); });
it("parses YAML literal multiline string", () => { const content = `---excerpt: | This is a literal multiline string---`; const { rawFrontmatter } = parseFrontmatter(content); expect(rawFrontmatter.excerpt).toBe( "This is a literal\nmultiline string\n", ); });
it("parses YAML kept literal multiline string", () => { const content = `---excerpt: |+ This is a kept literal multiline string
end: true---`; const { rawFrontmatter } = parseFrontmatter(content); expect(rawFrontmatter.excerpt).toBe( "This is a kept literal\nmultiline string\n\n", ); });
it("parses boolean true", () => { const content = `---draft: true---`; const { frontmatter } = parseFrontmatter(content); expect(frontmatter.draft).toBe(true); });
it("parses boolean false", () => { const content = `---draft: false---`; const { frontmatter } = parseFrontmatter(content); expect(frontmatter.draft).toBe(false); });
it('parses string "true" in draft field as boolean true', () => { const content = `---draft: true---`; const { rawFrontmatter } = parseFrontmatter(content); expect(rawFrontmatter.draft).toBe(true); }); });
describe("arrays", () => { it("parses inline YAML arrays", () => { const content = `---tags: [typescript, bun, testing]---`; const { frontmatter } = parseFrontmatter(content); expect(frontmatter.tags).toEqual(["typescript", "bun", "testing"]); });
it("parses inline YAML arrays with quoted items", () => { const content = `---tags: ["typescript", "bun", "testing"]---`; const { frontmatter } = parseFrontmatter(content); expect(frontmatter.tags).toEqual(["typescript", "bun", "testing"]); });
it("parses YAML block arrays", () => { const content = `---tags: - typescript - bun - testing---`; const { frontmatter } = parseFrontmatter(content); expect(frontmatter.tags).toEqual(["typescript", "bun", "testing"]); });
it("parses YAML block arrays with quoted items", () => { const content = `---tags: - "typescript" - 'bun'---`; const { frontmatter } = parseFrontmatter(content); expect(frontmatter.tags).toEqual(["typescript", "bun"]); });
it("parses inline TOML arrays", () => { const content = `+++tags = ["typescript", "bun"]+++`; const { frontmatter } = parseFrontmatter(content); expect(frontmatter.tags).toEqual(["typescript", "bun"]); }); });
describe("publish date fallbacks", () => { it("uses publishDate field directly", () => { const content = `---publishDate: 2024-01-15---`; const { frontmatter } = parseFrontmatter(content); expect(frontmatter.publishDate).toBe("2024-01-15"); });
it("falls back to pubDate", () => { const content = `---pubDate: 2024-02-01---`; const { frontmatter } = parseFrontmatter(content); expect(frontmatter.publishDate).toBe("2024-02-01"); });
it("falls back to date", () => { const content = `---date: 2024-03-10---`; const { frontmatter } = parseFrontmatter(content); expect(frontmatter.publishDate).toBe("2024-03-10"); });
it("falls back to createdAt", () => { const content = `---createdAt: 2024-04-20---`; const { frontmatter } = parseFrontmatter(content); expect(frontmatter.publishDate).toBe("2024-04-20"); });
it("falls back to created_at", () => { const content = `---created_at: 2024-05-30---`; const { frontmatter } = parseFrontmatter(content); expect(frontmatter.publishDate).toBe("2024-05-30"); });
it("prefers publishDate over other fallbacks", () => { const content = `---publishDate: 2024-01-01date: 2023-01-01---`; const { frontmatter } = parseFrontmatter(content); expect(frontmatter.publishDate).toBe("2024-01-01"); }); });
describe("rawFrontmatter", () => { it("returns all raw fields", () => { const content = `---title: Raw Testcustom: value---`; const { rawFrontmatter } = parseFrontmatter(content); expect(rawFrontmatter.title).toBe("Raw Test"); expect(rawFrontmatter.custom).toBe("value"); });
it("preserves atUri in both frontmatter and rawFrontmatter", () => { const content = `---title: PostatUri: at://did:plc:abc123/app.bsky.feed.post/xyz---`; const { frontmatter, rawFrontmatter } = parseFrontmatter(content); expect(frontmatter.atUri).toBe( "at://did:plc:abc123/app.bsky.feed.post/xyz", ); expect(rawFrontmatter.atUri).toBe( "at://did:plc:abc123/app.bsky.feed.post/xyz", ); }); });
describe("FrontmatterMapping", () => { it("maps a custom title field", () => { const content = `---name: My Mapped Title---`; const { frontmatter } = parseFrontmatter(content, { title: "name" }); expect(frontmatter.title).toBe("My Mapped Title"); });
it("maps a custom description field", () => { const content = `---summary: Custom description---`; const { frontmatter } = parseFrontmatter(content, { description: "summary", }); expect(frontmatter.description).toBe("Custom description"); });
it("maps a custom publishDate field", () => { const content = `---publishedOn: 2024-06-15---`; const { frontmatter } = parseFrontmatter(content, { publishDate: "publishedOn", }); expect(frontmatter.publishDate).toBe("2024-06-15"); });
it("maps a custom coverImage field", () => { const content = `---heroImage: /images/cover.jpg---`; const { frontmatter } = parseFrontmatter(content, { coverImage: "heroImage", }); expect(frontmatter.ogImage).toBe("/images/cover.jpg"); });
it("maps a custom tags field", () => { const content = `---categories: [news, updates]---`; const { frontmatter } = parseFrontmatter(content, { tags: "categories" }); expect(frontmatter.tags).toEqual(["news", "updates"]); });
it("maps a custom draft field", () => { const content = `---unpublished: true---`; const { frontmatter } = parseFrontmatter(content, { draft: "unpublished", }); expect(frontmatter.draft).toBe(true); });
it("falls back to standard field name when mapped field is absent", () => { const content = `---title: Standard Title---`; const { frontmatter } = parseFrontmatter(content, { title: "heading" }); expect(frontmatter.title).toBe("Standard Title"); }); });
describe("body", () => { it("returns the body content after the closing delimiter", () => { const content = `---title: Post---# Heading
Some paragraph text.`; const { body } = parseFrontmatter(content); expect(body).toBe("# Heading\n\nSome paragraph text."); });
it("returns an empty body when there is no content after frontmatter", () => { const content = `---title: Post---`; const { body } = parseFrontmatter(content); expect(body).toBe(""); }); });});
describe("resolvePostPath", () => { const post: BlogPost = { filePath: "/tmp/hello.md", slug: "hello-world", frontmatter: { title: "Hello" } as BlogPost["frontmatter"], content: "", rawContent: "", rawFrontmatter: {}, };
it("defaults to /posts when pathPrefix is undefined", () => { expect(resolvePostPath(post)).toBe("/posts/hello-world"); });
it("uses custom prefix when provided", () => { expect(resolvePostPath(post, "/blog")).toBe("/blog/hello-world"); });
it("omits prefix when pathPrefix is an empty string", () => { expect(resolvePostPath(post, "")).toBe("/hello-world"); });
it("pathTemplate overrides pathPrefix", () => { expect(resolvePostPath(post, "", "/custom/{slug}")).toBe( "/custom/hello-world", ); });});