tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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'));