blob: 648e89aa277b1633459fcdefed77fc1ad6a00df6 [file] [log] [blame]
/* 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;
};