diff --git a/src/prd.mjs b/src/prd.mjs index c6c22d9..76bcbda 100644 --- a/src/prd.mjs +++ b/src/prd.mjs @@ -108,9 +108,16 @@ How the goals will be measured. `; } +// The seed marker is a hidden HTML comment, so a `-->` inside the idea would +// close it early and render the rest of the idea (plus the real closer) as +// visible text at the top of the Problem section. Neutralise the terminator. +function commentSafe(text) { + return String(text).replace(/--+>/g, (run) => `${run.slice(0, -1)}>`); +} + /** A numbered PRD body (prd/NNNN-slug.md), seeded from an idea. */ export function renderPrd({ id, title, idea, author }) { - const seed = idea && idea !== title ? `\n` : ""; + const seed = idea && idea !== title ? `\n` : ""; return `--- openprd: "${OPENPRD.version}" id: "${id}" diff --git a/test/prd.test.mjs b/test/prd.test.mjs index 6fc7abf..2c66dde 100644 --- a/test/prd.test.mjs +++ b/test/prd.test.mjs @@ -77,3 +77,34 @@ test("regenerateIndex escapes a pipe in a title so the row keeps its columns", ( fs.rmSync(root, { recursive: true, force: true }); } }); + +test("renderPrd keeps a `-->` in the idea from closing the seed comment", () => { + const idea = "add a dark mode toggle and then some more idea text"; + const body = renderPrd({ id: "0007", title: "Add a dark mode toggle", idea, author: "you@example.com" }); + + const lines = body.split(/\r?\n/); + const at = lines.findIndex((l) => l.startsWith("/g).length, 1, `seed comment closes early: ${seedLine}`); + assert.ok(seedLine.endsWith(" and then some more idea text -->"), seedLine); + assert.ok(seedLine.includes(" arrows in specs", root); + + const lines = fs.readFileSync(file, "utf8").split(/\r?\n/); + const at = lines.findIndex((l) => l.startsWith("/g).length, 1, `seed comment closes early: ${lines[at]}`); + assert.match(lines[at + 1], /^_Describe the user\/business problem/); + } finally { + fs.rmSync(root, { recursive: true, force: true }); + } +});