From 210b96a888a9410489d0a1febcbe7e9fc2a596e5 Mon Sep 17 00:00:00 2001 From: Lubos Date: Mon, 6 Oct 2025 12:04:23 +0800 Subject: [PATCH] chore: fix changelog --- .changeset/changelog.js | 35 +++++++++++++++++-- __tests__/changelog.test.ts | 70 +++++++++++++++++++++++++++++++++++++ package.json | 4 +-- 3 files changed, 105 insertions(+), 4 deletions(-) diff --git a/.changeset/changelog.js b/.changeset/changelog.js index 4f23b7a6b..e62b4dbd7 100644 --- a/.changeset/changelog.js +++ b/.changeset/changelog.js @@ -33,6 +33,7 @@ export default { /** @type string[] */ const usersFromSummary = []; + // Remove PR, commit, author/user lines from summary const replacedChangelog = changeset.summary .replace(/^\s*(?:pr|pull|pull\s+request):\s*#?(\d+)/im, (_, pr) => { const num = Number(pr); @@ -68,7 +69,12 @@ export default { } const commitToFetchFrom = commitFromSummary || changeset.commit; if (commitToFetchFrom) { - const { links } = await getInfo({ commit: commitToFetchFrom, repo }); + let { links } = await getInfo({ commit: commitToFetchFrom, repo }); + const shortCommitId = commitToFetchFrom.slice(0, 7); + links = { + ...links, + commit: `[\`${shortCommitId}\`](https://github.com/${repo}/commit/${commitToFetchFrom})`, + }; return links; } return { @@ -93,6 +99,31 @@ export default { users === null ? '' : ` by ${users}`, ].join(''); - return `\n- ${replacedChangelog}${metadata}`; + // Split summary into first line and the rest + const [firstLine, ...rest] = replacedChangelog.split('\n'); + const restSummary = rest.join('\n').trim(); + + // Post-process code blocks: replace triple backtick code blocks with indented code blocks + function convertCodeBlocks(text) { + // Replace ```lang\n...\n``` with indented code + return text.replace(/```(\w*)\n([\s\S]*?)```/g, (match, lang, code) => { + const langComment = lang ? `// ${lang}\n` : ''; + return ( + '\n' + + langComment + + code + .split('\n') + .map((line) => ' ' + line) + .join('\n') + + '\n' + ); + }); + } + + let releaseLine = `\n- ${firstLine}${metadata}`; + if (restSummary) { + releaseLine += '\n\n' + convertCodeBlocks(restSummary); + } + return releaseLine; }, }; diff --git a/__tests__/changelog.test.ts b/__tests__/changelog.test.ts index 621d74f6b..ff6fb6709 100644 --- a/__tests__/changelog.test.ts +++ b/__tests__/changelog.test.ts @@ -134,6 +134,76 @@ describe('changelog', () => { ); }); + it('places metadata on the first line and does not append it after code blocks', async () => { + const summary = [ + 'refactor(config): replace `off` with null to disable options', + '', + '### Updated `output` options', + '', + 'We made the `output` configuration more consistent by using `null` to represent disabled options. [This change](https://heyapi.dev/openapi-ts/migrating#updated-output-options) does not affect boolean options.', + '', + '```js', + 'export default {', + ' input: "hey-api/backend", // sign up at app.heyapi.dev', + ' output: {', + ' format: null,', + ' lint: null,', + ' path: "src/client",', + ' tsConfigPath: null,', + ' },', + '};', + '```', + ].join('\n'); + const changeset = { + commit: 'fcdd73b816d74babf47e6a1f46032f5b8ebb4b48', + id: 'fake-id', + releases: [], + summary, + }; + const line = await changelog.getReleaseLine(changeset, 'minor', { + repo: 'hey-api/openapi-ts', + }); + // Metadata should be on the first line + expect(line).toMatch( + /^\n- refactor\(config\): replace `off` with null to disable options \(\[#1613\]\(https:\/\/github.com\/hey-api\/openapi-ts\/pull\/1613\)\) \(\[`fcdd73b`\]\(https:\/\/github.com\/hey-api\/openapi-ts\/commit\/fcdd73b816d74babf47e6a1f46032f5b8ebb4b48\)\) by \[@someone\]\(https:\/\/github.com\/someone\)/, + ); + // There should be no metadata at the end + expect(line.trim().endsWith('```')).toBe(false); + // Should not contain quadruple backticks + expect(line).not.toContain('````'); + // Should contain indented code block + expect(line).toContain(' export default {'); + }); + + it('converts multiple code blocks and preserves non-code content', async () => { + const summary = [ + 'feat: add foo', + '', + '```js', + 'console.log(1);', + '```', + '', + 'Some text.', + '', + '```ts', + 'console.log(2);', + '```', + ].join('\n'); + const changeset = { + commit: 'abc123', + id: 'fake-id', + releases: [], + summary, + }; + const line = await changelog.getReleaseLine(changeset, 'minor', { + repo: 'hey-api/openapi-ts', + }); + expect(line).toContain(' console.log(1);'); + expect(line).toContain(' console.log(2);'); + expect(line).toContain('Some text.'); + expect(line).not.toContain('```'); + }); + describe.each(['author', 'user'])( 'override author with %s keyword', (keyword) => { diff --git a/package.json b/package.json index b24f1796b..30009c9aa 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,8 @@ "lint:fix": "prettier --check --write . && eslint . --fix", "lint": "prettier --check . && eslint .", "prepare": "husky", - "test:changelog": "vitest run .changeset", - "test:changelog:watch": "vitest watch .changeset", + "test:changelog": "vitest run __tests__/*.test.ts", + "test:changelog:watch": "vitest watch __tests__/*.test.ts", "test:coverage": "turbo run test:coverage", "test:update": "turbo run test:update", "test:watch": "turbo run test:watch", -- 2.51.2