| /* This file is a part of @mdn/browser-compat-data |
| * See LICENSE file for more information. */ |
| |
| export interface Stats { |
| commits: number; |
| changed: number; |
| insertions: number; |
| deletions: number; |
| |
| releaseContributors: number; |
| totalContributors: number; |
| features: number; |
| stars: number; |
| start: string; |
| end: string; |
| } |
| |
| type ChangeStats = Pick< |
| Stats, |
| 'commits' | 'changed' | 'insertions' | 'deletions' |
| >; |
| |
| import chalk from 'chalk-template'; |
| |
| import { walk } from '../../utils/index.js'; |
| import pluralize from '../lib/pluralize.js'; |
| |
| import { exec, queryPRs, githubAPI } from './utils.js'; |
| |
| /** |
| * Get stargazers for the repository |
| * @returns The number of stargazer |
| */ |
| const stargazers = async (): Promise<number> => { |
| const json = githubAPI(''); |
| return json.stargazers_count; |
| }; |
| |
| /** |
| * Get the number of contributors that have committed to the repository |
| * @returns The number of contributors that have contributed to the repository |
| */ |
| const contributors = (): number => { |
| const data = exec( |
| 'gh api /repos/mdn/browser-compat-data/contributors?anon=1 --paginate', |
| ); |
| return JSON.parse('[' + data.replace(/\]\[/g, '],[') + ']').flat(1).length; |
| }; |
| |
| /** |
| * Get all of the stats for the release |
| * @param start The last version number |
| * @returns The statistics |
| */ |
| const stats = (start: string): ChangeStats => { |
| // Get just the diff stats summary |
| const diff = exec(`git diff --shortstat ${start}...origin/main`); |
| if (diff === '') { |
| console.error(chalk`{red No changes for which to generate statistics.}`); |
| process.exit(1); |
| } |
| |
| // Extract the numbers from a line like this: |
| // 50 files changed, 1988 insertions(+), 2056 deletions(-) |
| const match = diff.match( |
| /(?<changed>\d+) files? changed(?:, (?<insertions>\d+) insertions?(\(\+\)))?(?:, (?<deletions>\d+) deletions?\(-\))?/, |
| ); |
| if (!match) { |
| console.error(chalk`{red No changes for which to generate statistics.}`); |
| process.exit(1); |
| } |
| |
| const { changed, insertions, deletions } = match.groups as any; |
| |
| // Get the number of commits |
| const commits = exec(`git rev-list --count ${start}...origin/main`); |
| |
| return { |
| commits: Number(commits), |
| changed: Number(changed) || 0, |
| insertions: Number(insertions) || 0, |
| deletions: Number(deletions) || 0, |
| }; |
| }; |
| |
| /** |
| * Get the number of contributors that have committed to this release |
| * @param fromDate The date of the last release |
| * @returns The authors of the commits |
| */ |
| const getReleaseContributors = (fromDate: string): Set<string> => { |
| const prs = queryPRs({ |
| json: 'author', |
| search: `merged:>=${fromDate}`, |
| }); |
| return new Set(prs.map((pr) => pr.author.login)); |
| }; |
| |
| /** |
| * Count the number of features in BCD |
| * @returns The number of features |
| */ |
| const countFeatures = (): number => [...walk()].length; |
| |
| /** |
| * Format the stats as Markdown |
| * @param details The stats to format |
| * @returns The formatted stats |
| */ |
| export const formatStats = (details: Stats): string => |
| [ |
| '### Statistics', |
| '', |
| `- ${pluralize('contributor', details.releaseContributors)} ${ |
| details.releaseContributors > 1 ? 'have' : 'has' |
| } changed ${pluralize('file', details.changed)} with ${pluralize( |
| 'addition', |
| details.insertions, |
| )} and ${pluralize('deletion', details.deletions)} in ${pluralize( |
| 'commit', |
| details.commits, |
| )} ([\`${details.start}...${ |
| details.end |
| }\`](https://github.com/mdn/browser-compat-data/compare/${ |
| details.start |
| }...${details.end}))`, |
| `- ${pluralize('total feature', details.features)}`, |
| `- ${pluralize('total contributor', details.totalContributors)}`, |
| `- ${pluralize('total stargazer', details.stars)}`, |
| '', |
| ].join('\n'); |
| |
| /** |
| * Get the statistics for the release |
| * @param start The last release number |
| * @param end This release number |
| * @param startDate The date of the last release |
| * @returns The release statistics |
| */ |
| export const getStats = async ( |
| start: string, |
| end: string, |
| startDate: string, |
| ): Promise<Stats> => ({ |
| start, |
| end, |
| ...stats(start), |
| releaseContributors: getReleaseContributors(startDate).size, |
| totalContributors: contributors(), |
| stars: await stargazers(), |
| features: countFeatures(), |
| }); |