merge-changelogs.ts (2599B)
1 /** 2 * @license 3 * Copyright 2025 Google Inc. 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7 /** 8 * This script merges puppeteer and puppeteer-core changelogs into a single 9 * changelog file. 10 */ 11 12 import {readFileSync, writeFileSync} from 'node:fs'; 13 14 interface Version { 15 version: string; 16 header: string; 17 lines: string[]; 18 } 19 20 function parseChangelog(file: string) { 21 const log = readFileSync(file, 'utf-8').split('\n'); 22 23 const parsed: Version[] = []; 24 let version: Version | undefined = undefined; 25 for (const line of log) { 26 if (line.startsWith('## ')) { 27 if (version) { 28 parsed.push(version); 29 } 30 const matches = line.match(/## \[(\d+\.\d+\.\d+)\]/); 31 if (!matches) { 32 throw new Error('Cannot parse the version'); 33 } 34 version = { 35 version: matches[1], 36 lines: [], 37 header: line, 38 }; 39 } else if (version && line.trim() !== '') { 40 version.lines.push(line); 41 } 42 } 43 if (version) { 44 parsed.push(version); 45 } 46 return parsed; 47 } 48 49 function mergeVersions(a: Version, b: Version): Version { 50 const result: Version = { 51 version: a.version, 52 header: a.header, 53 lines: [], 54 }; 55 const sectionEntries = new Map<string, Set<string>>(); 56 57 function walkLines(lines: string[]) { 58 let currentSection: string | undefined = undefined; 59 for (const lineA of lines) { 60 if (lineA.trim() === '') { 61 continue; 62 } 63 if (lineA.startsWith('### ')) { 64 if (lineA !== currentSection) { 65 sectionEntries.set(lineA, new Set()); 66 } 67 currentSection = lineA; 68 } else if (currentSection) { 69 sectionEntries.get(currentSection)!.add(lineA); 70 } 71 } 72 } 73 74 walkLines(a.lines); 75 walkLines(b.lines); 76 77 for (const [section, lines] of sectionEntries) { 78 result.lines.push('\n\n' + section + '\n'); 79 result.lines.push(...lines); 80 } 81 82 result.lines[result.lines.length - 1] += '\n\n'; 83 84 return result; 85 } 86 87 const puppeteerChangelog = parseChangelog('./packages/puppeteer/CHANGELOG.md'); 88 const puppeteerCoreChangelog = parseChangelog( 89 './packages/puppeteer-core/CHANGELOG.md', 90 ); 91 92 const combinedChangelog: string[] = [ 93 '# Changelog', 94 '', 95 'Combined changelog for puppeteer and puppeteer-core.', 96 '', 97 ]; 98 99 for (let entry of puppeteerChangelog) { 100 for (const coreEntry of puppeteerCoreChangelog) { 101 if (coreEntry.version === entry.version) { 102 entry = mergeVersions(entry, coreEntry); 103 } 104 } 105 combinedChangelog.push(entry.header); 106 combinedChangelog.push(...entry.lines); 107 } 108 109 writeFileSync('./CHANGELOG.md', combinedChangelog.join('\n'));