diff --git a/scripts/commitRecords.js b/scripts/commitRecords.js index d0d3390967..a0d078d1d8 100644 --- a/scripts/commitRecords.js +++ b/scripts/commitRecords.js @@ -65,15 +65,13 @@ function changelog(base, tip) { name, git(["show", `${tip}:${f}`]), ); - const emoji = - /:\w+:/.exec( - categories[/** @type {keyof typeof categories} */ (category)], - )?.[0] ?? ""; - return { category, emoji, text }; + const heading = + categories[/** @type {keyof typeof categories} */ (category)]; + return { category, heading, text }; } catch { return { category: "", - emoji: ":grey_question:", + heading: "#### :grey_question: Not a changelog fragment", text: `- \`${name}\``, }; } diff --git a/scripts/commitRecords.test.js b/scripts/commitRecords.test.js index 69399910db..705a14ad06 100644 --- a/scripts/commitRecords.test.js +++ b/scripts/commitRecords.test.js @@ -933,7 +933,7 @@ test("the comment lists the changelog entries a range adds, breaking first", asy const comment = await fs.readFile(md, "utf8"); has( comment, - "### Changelog\n\n- :boom: Drop -x.\n- :bug: Recover the watcher.\n- :grey_question: `Bad Name.md`\n\n", + "### Changelog\n\n#### :boom: Breaking Change\n\n- Drop -x.\n\n#### :bug: Bug fix\n\n- Recover the watcher.\n\n#### :grey_question: Not a changelog fragment\n\n- `Bad Name.md`\n\n", ); lacks(comment, "Old."); lacks(comment, "Tmp."); diff --git a/scripts/recordMarkdown.js b/scripts/recordMarkdown.js index 44b39ebf94..f8e49bf880 100644 --- a/scripts/recordMarkdown.js +++ b/scripts/recordMarkdown.js @@ -38,8 +38,9 @@ import { /** * A changelog fragment the pull request adds: its category, the category's - * emoji as the changelog heads it (`:boom:`), and its Markdown bullets. - * @typedef {{ category: string, emoji: string, text: string }} ChangelogEntry + * heading as the changelog has it (`#### :boom: Breaking Change`), and its + * Markdown bullets. + * @typedef {{ category: string, heading: string, text: string }} ChangelogEntry */ /** The characters a comment holds at most, with room for the action's tag. */ @@ -442,10 +443,10 @@ export function commitMarkdown(r, full, change = "") { } /** - * The changelog entries as a list, each bullet led by its entry's emoji, in - * at most `room` characters: the comment shortens commits to fit its - * budget but not the changelog, so entries past the room are counted, not - * drawn. + * The changelog entries grouped under their categories' headings, as the + * changelog groups them (the entries come in its order), in at most `room` + * characters: the comment shortens commits to fit its budget but not the + * changelog, so entries past the room are counted, not drawn. * @param {ChangelogEntry[]} changelog * @param {number} room */ @@ -453,16 +454,20 @@ function changelogList(changelog, room) { if (!changelog.length) return "No changelog entry."; const drawn = []; let size = 0; + let heading = ""; + let count = 0; for (const e of changelog) { - const lines = unmentioned(e.text).replace(/^- /gm, `- ${e.emoji} `); - size += lines.length + 1; + const lines = `${e.heading === heading ? "" : `${e.heading}\n\n`}${unmentioned(e.text)}`; + size += lines.length + 2; if (size > room) break; - drawn.push(lines); + drawn.push(e.heading === heading || !count ? lines : `\n${lines}`); + heading = e.heading; + count++; } - const left = changelog.length - drawn.length; + const left = changelog.length - count; if (left) drawn.push( - `\n${left}${drawn.length ? " more" : ""} changelog entr${left === 1 ? "y" : "ies"} under \`changes/\`.`, + `\n${left}${count ? " more" : ""} changelog entr${left === 1 ? "y" : "ies"} under \`changes/\`.`, ); return drawn.join("\n"); } @@ -474,8 +479,8 @@ function changelogList(changelog, room) { * last commits are left to the page. The commits the last push added or * edited (`changes`) are marked. * - * The changelog entries the pull request adds (`changelog`, each fragment's - * bullets led by its category's emoji) describe it as a whole, as its squash + * The changelog entries the pull request adds (`changelog`, grouped under + * the changelog's category headings) describe it as a whole, as its squash * commit will; a commit's `Breaking:` sections describe that commit. They * come first, under their own heading, which also notes a pull request * whose commits break something while no entry is a breaking change, or diff --git a/scripts/recordMarkdown.test.js b/scripts/recordMarkdown.test.js index 5ab6ccdeb3..c33a27c80d 100644 --- a/scripts/recordMarkdown.test.js +++ b/scripts/recordMarkdown.test.js @@ -270,13 +270,13 @@ test("the heading lists the changelog entries and notes a breaking mismatch", () const plain = `Fix a typo\n\n${SIGNED}\n`; const fix = { category: "fix", - emoji: ":bug:", + heading: "#### :bug: Bug fix", text: "- Recover @someone's `@raw` build.\n- Keep **watching**,\n after errors.", }; const md = comment([plain], { changelog: [fix] }); has( md, - "### Changelog\n\n- :bug: Recover `@someone`'s `@raw` build.\n- :bug: Keep **watching**,\n after errors.\n\n### Semantic Diff\n\n1 commit\n\n:boom: A commit says what it breaks (`Breaking:`), but no changelog entry is a breaking change.\n\n### Semantic Diff", ); - const boom = { category: "breaking", emoji: ":boom:", text: "- -x is gone." }; + const boom = { + category: "breaking", + heading: "#### :boom: Breaking Change", + text: "- -x is gone.", + }; + has( + comment([breaking], { + changelog: [boom, fix, { ...fix, text: "- More." }], + }), + "### Changelog\n\n#### :boom: Breaking Change\n\n- -x is gone.\n\n#### :bug: Bug fix\n\n- Recover `@someone`'s `@raw` build.\n- Keep **watching**,\n after errors.\n- More.\n\n### Semantic Diff", + ); const both = comment([breaking], { changelog: [boom] }); assert.ok(!both.includes(":boom:")); has( @@ -299,7 +309,7 @@ test("the heading lists the changelog entries and notes a breaking mismatch", () test("past a quarter of the budget, changelog entries are counted, not drawn", () => { const entry = (/** @type {number} */ k) => ({ category: "fix", - emoji: ":bug:", + heading: "#### :bug: Bug fix", text: `- Fix ${k}: ${"x".repeat(300)}`, }); const md = comment([`Fix\n\n${SIGNED}\n`], { @@ -307,7 +317,7 @@ test("past a quarter of the budget, changelog entries are counted, not drawn", ( changelog: Array.from({ length: 20 }, (_, k) => entry(k)), }); assert.ok(md.length <= 4000, `${md.length}`); - has(md, "- :bug: Fix 0:"); + has(md, "#### :bug: Bug fix\n\n- Fix 0:"); assert.ok(!md.includes("Fix 3:")); has( md, @@ -316,7 +326,11 @@ test("past a quarter of the budget, changelog entries are counted, not drawn", ( const huge = comment([`Fix\n\n${SIGNED}\n`], { budget: 4000, changelog: [ - { category: "fix", emoji: ":bug:", text: `- ${"y".repeat(5000)}` }, + { + category: "fix", + heading: "#### :bug: Bug fix", + text: `- ${"y".repeat(5000)}`, + }, ], }); assert.ok(huge.length <= 4000, `${huge.length}`);