| /* This file is a part of @mdn/browser-compat-data |
| * See LICENSE file for more information. */ |
| |
| /** |
| * @typedef {object} FeatureChange |
| * @property {string} [mergeCommit] |
| * @property {number} number |
| * @property {string} url |
| * @property {string} feature |
| */ |
| |
| /** |
| * @typedef {object} Changes |
| * @property {FeatureChange[]} added |
| * @property {FeatureChange[]} removed |
| */ |
| |
| import { styleText } from 'node:util'; |
| |
| import cliProgress from 'cli-progress'; |
| |
| import diffFeatures from '../diff-features.js'; |
| |
| import { queryPRs } from './utils.js'; |
| |
| /** |
| * Format a feature change in Markdown |
| * @param {FeatureChange} obj The feature change to format |
| * @returns {string} The formatted feature change |
| */ |
| const featureBullet = (obj) => |
| `- \`${obj.feature}\` ([#${obj.number}](${obj.url}))`; |
| |
| /** |
| * Format all the feature changes in Markdown |
| * @param {Changes} changes The changes to format |
| * @returns {string} The formatted changes |
| */ |
| export const formatChanges = (changes) => { |
| /** @type {string[]} */ |
| const output = []; |
| |
| if (changes.removed.length) { |
| output.push('### Removals', ''); |
| for (const removal of changes.removed) { |
| output.push(featureBullet(removal)); |
| } |
| output.push(''); |
| } |
| |
| if (changes.added.length) { |
| output.push('### Additions', ''); |
| for (const addition of changes.added) { |
| output.push(featureBullet(addition)); |
| } |
| output.push(''); |
| } |
| |
| return output.join('\n'); |
| }; |
| |
| /** |
| * Get all the pulls that have been merged on GitHub |
| * @param {string} fromDate The start date to get merged pulls from |
| * @returns {FeatureChange[]} The pull requests that have been merged |
| */ |
| const pullsFromGitHub = (fromDate) => |
| queryPRs({ |
| search: `is:pr merged:>=${fromDate}`, |
| json: 'number,url,mergeCommit', |
| jq: '[.[] | { mergeCommit: .mergeCommit.oid, number: .number, url: .url }]', // Flatten the structure provided by GitHub |
| }); |
| |
| /** |
| * Get the diff from the pull request |
| * @param {FeatureChange} pull The pull request to test |
| * @returns {Promise<{ added: string[]; removed: string[] }>} The changes from the pull request |
| */ |
| const getDiff = async (pull) => { |
| let diff; |
| |
| try { |
| diff = await diffFeatures({ ref1: pull.mergeCommit, quiet: true }); |
| } catch (e) { |
| throw new Error( |
| `${styleText('red', String(e))}\n ${styleText('yellow', `(Failed to diff features for #${pull.number}, skipping)`)}`, |
| ); |
| } |
| |
| if (diff.added.length && diff.removed.length) { |
| console.log( |
| ` | #${pull.number} - ${styleText('blue', `(${styleText('green', `${diff.added.length} added`)}, ${styleText('red', `${diff.removed.length} removed`)})`)}`, |
| ); |
| } else if (diff.added.length) { |
| console.log( |
| ` | #${pull.number} - ${styleText('blue', `(${styleText('green', `${diff.added.length} added`)})`)}`, |
| ); |
| } else if (diff.removed.length) { |
| console.log( |
| ` | #${pull.number} - ${styleText('blue', `(${styleText('red', `${diff.removed.length} removed`)})`)}`, |
| ); |
| } else { |
| console.log( |
| ` | #${pull.number} - ${styleText('blue', '(No feature count changes)')}`, |
| ); |
| } |
| |
| return diff; |
| }; |
| |
| /** |
| * Get changes from the pull requests that have been merged since a specified date |
| * @param {string} date The starting date to query pull requests from |
| * @returns {Promise<Changes>} The changes from all of the pull requests |
| */ |
| export const getChanges = async (date) => { |
| const progressBar = new cliProgress.SingleBar( |
| {}, |
| cliProgress.Presets.shades_classic, |
| ); |
| const pulls = pullsFromGitHub(date); |
| |
| /** @type {Changes} */ |
| const changes = { |
| added: [], |
| removed: [], |
| }; |
| |
| progressBar.start(pulls.length, 0); |
| |
| await Promise.all( |
| pulls.map(async (pull) => { |
| const diff = await getDiff(pull); |
| |
| changes.added.push( |
| ...diff.added.map((feature) => ({ |
| number: pull.number, |
| url: pull.url, |
| feature, |
| })), |
| ); |
| |
| changes.removed.push( |
| ...diff.removed.map((feature) => ({ |
| number: pull.number, |
| url: pull.url, |
| feature, |
| })), |
| ); |
| |
| progressBar.increment(); |
| }), |
| ); |
| |
| progressBar.stop(); |
| console.log('\n'); |
| |
| changes.added.sort((a, b) => a.feature.localeCompare(b.feature)); |
| changes.removed.sort((a, b) => a.feature.localeCompare(b.feature)); |
| |
| return changes; |
| }; |