diff --git a/package-lock.json b/package-lock.json index 4a5aa42..df5ea15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "express": "^4.21.0", "express-rate-limit": "^7.5.0", "helmet": "^8.0.0", - "marked": "^15.0.0", + "marked": "^17.0.4", "nanoid": "^5.0.0", "nodemailer": "^8.0.2", "pg": "^8.20.0", @@ -2953,15 +2953,15 @@ } }, "node_modules/marked": { - "version": "15.0.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", - "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.4.tgz", + "integrity": "sha512-NOmVMM+KAokHMvjWmC5N/ZOvgmSWuqJB8FoYI019j4ogb/PeRMKoKIjReZ2w3376kkA8dSJIP8uD993Kxc0iRQ==", "license": "MIT", "bin": { "marked": "bin/marked.js" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/math-intrinsics": { diff --git a/package.json b/package.json index 7192b22..0d4e047 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "express": "^4.21.0", "express-rate-limit": "^7.5.0", "helmet": "^8.0.0", - "marked": "^15.0.0", + "marked": "^17.0.4", "nanoid": "^5.0.0", "nodemailer": "^8.0.2", "pg": "^8.20.0", diff --git a/src/__tests__/markdown-lists.test.ts b/src/__tests__/markdown-lists.test.ts new file mode 100644 index 0000000..4d50334 --- /dev/null +++ b/src/__tests__/markdown-lists.test.ts @@ -0,0 +1,111 @@ +import { describe, it, expect } from "vitest"; +import { marked } from "marked"; + +/** Tests for marked list rendering — covering v17 breaking changes */ +describe("Markdown list rendering", () => { + const parse = (md: string) => marked.parse(md, { async: false }) as string; + + describe("loose lists (paragraphs inside list items)", () => { + it("renders loose list items with
tags", () => { + const md = `- Item one\n\n- Item two\n\n- Item three\n`; + const html = parse(md); + expect(html).toContain("
+ expect(html).toContain("
Item one
"); + expect(html).toContain("Item two
"); + expect(html).toContain("Item three
"); + }); + + it("renders tight list items withouttags", () => { + const md = `- Item one\n- Item two\n- Item three\n`; + const html = parse(md); + expect(html).toContain("
"); + expect(html).toContain("Item one"); + }); + }); + + describe("checkbox/task lists", () => { + it("renders unchecked checkboxes", () => { + const md = `- [ ] Todo item\n`; + const html = parse(md); + expect(html).toContain(' { + const md = `- [x] Done item\n`; + const html = parse(md); + expect(html).toContain('checked'); + expect(html).toContain("Done item"); + }); + + it("renders mixed task list", () => { + const md = `- [x] Done\n- [ ] Pending\n- Regular item\n`; + const html = parse(md); + expect(html).toContain("Done"); + expect(html).toContain("Pending"); + expect(html).toContain("Regular item"); + // Should have exactly 2 checkboxes + const checkboxCount = (html.match(/type="checkbox"/g) || []).length; + expect(checkboxCount).toBe(2); + }); + }); + + describe("nested lists", () => { + it("renders nested unordered lists", () => { + const md = `- Parent\n - Child\n - Grandchild\n`; + const html = parse(md); + expect(html).toContain("Parent"); + expect(html).toContain("Child"); + expect(html).toContain("Grandchild"); + // Should have nested
npm install");
+ expect(html).toContain("npm test");
+ });
+
+ it("renders bold and italic in list items", () => {
+ const md = `- **Bold item**\n- *Italic item*\n`;
+ const html = parse(md);
+ expect(html).toContain("Bold item");
+ expect(html).toContain("Italic item");
+ });
+ });
+});