main
js 158 lines 4.63 KB
Raw
1 'use strict';
2
3 const {stablePackages} = require('../../../ReactVersions');
4 const {parseArgs} = require('./args');
5 const {
6 fetchNpmInfo,
7 collectCommitsSince,
8 loadCommitDetails,
9 extractPrNumber,
10 fetchPullRequestMetadata,
11 } = require('./data');
12 const {summarizePackages} = require('./summaries');
13 const {buildChangelogEntries, renderChangelog} = require('./formatters');
14 const {noopLogger} = require('./utils');
15
16 async function main() {
17 const {packageSpecs, summarizer, debug, format} = parseArgs(
18 process.argv.slice(2)
19 );
20 const log = debug
21 ? (...args) => console.log('[generate-changelog]', ...args)
22 : noopLogger;
23
24 const allStablePackages = Object.keys(stablePackages);
25 const packageTargets = new Map();
26 for (let i = 0; i < packageSpecs.length; i++) {
27 const spec = packageSpecs[i];
28 if (!allStablePackages.includes(spec.name)) {
29 throw new Error(
30 `Package "${spec.name}" is not listed in stablePackages.`
31 );
32 }
33 if (packageTargets.has(spec.name)) {
34 throw new Error(`Package "${spec.name}" was specified more than once.`);
35 }
36 packageTargets.set(spec.name, spec);
37 }
38
39 const targetPackages = packageSpecs.map(spec => spec.name);
40 log(
41 `Starting changelog generation for: ${packageSpecs
42 .map(spec => `${spec.name}@${spec.displayVersion || spec.version}`)
43 .join(', ')}`
44 );
45
46 const packageInfoMap = new Map();
47 const packageInfoResults = await Promise.all(
48 targetPackages.map(async pkg => {
49 const info = await fetchNpmInfo(pkg, {log});
50 return {pkg, info};
51 })
52 );
53 for (let i = 0; i < packageInfoResults.length; i++) {
54 const entry = packageInfoResults[i];
55 packageInfoMap.set(entry.pkg, entry.info);
56 }
57
58 const commitPackagesMap = new Map();
59 const commitCollections = await Promise.all(
60 targetPackages.map(async pkg => {
61 const {gitHead} = packageInfoMap.get(pkg);
62 const commits = await collectCommitsSince(pkg, gitHead, {log});
63 log(`Package ${pkg} has ${commits.length} commit(s) since ${gitHead}.`);
64 return {pkg, commits};
65 })
66 );
67 for (let i = 0; i < commitCollections.length; i++) {
68 const entry = commitCollections[i];
69 const pkg = entry.pkg;
70 const commits = entry.commits;
71 for (let j = 0; j < commits.length; j++) {
72 const sha = commits[j];
73 if (!commitPackagesMap.has(sha)) {
74 commitPackagesMap.set(sha, new Set());
75 }
76 commitPackagesMap.get(sha).add(pkg);
77 }
78 }
79 log(`Found ${commitPackagesMap.size} commits touching target packages.`);
80
81 if (commitPackagesMap.size === 0) {
82 console.log('No commits found for the selected packages.');
83 return;
84 }
85
86 const commitDetails = await Promise.all(
87 Array.from(commitPackagesMap.entries()).map(
88 async ([sha, packagesTouched]) => {
89 const detail = await loadCommitDetails(sha, {log});
90 detail.packages = packagesTouched;
91 detail.prNumber = extractPrNumber(detail.subject, detail.body);
92 return detail;
93 }
94 )
95 );
96
97 commitDetails.sort((a, b) => a.timestamp - b.timestamp);
98 log(`Ordered ${commitDetails.length} commit(s) chronologically.`);
99
100 const commitsByPackage = new Map();
101 commitDetails.forEach(commit => {
102 commit.packages.forEach(pkgName => {
103 if (!commitsByPackage.has(pkgName)) {
104 commitsByPackage.set(pkgName, []);
105 }
106 commitsByPackage.get(pkgName).push(commit);
107 });
108 });
109
110 const uniquePrNumbers = Array.from(
111 new Set(commitDetails.map(commit => commit.prNumber).filter(Boolean))
112 );
113 log(`Identified ${uniquePrNumbers.length} unique PR number(s).`);
114
115 const prMetadata = new Map();
116 log(`Summarizer selected: ${summarizer || 'none (using commit titles)'}`);
117 const prMetadataResults = await Promise.all(
118 uniquePrNumbers.map(async prNumber => {
119 const meta = await fetchPullRequestMetadata(prNumber, {log});
120 return {prNumber, meta};
121 })
122 );
123 for (let i = 0; i < prMetadataResults.length; i++) {
124 const entry = prMetadataResults[i];
125 if (entry.meta) {
126 prMetadata.set(entry.prNumber, entry.meta);
127 }
128 }
129 log(`Fetched metadata for ${prMetadata.size} PR(s).`);
130
131 const summariesByPackage = await summarizePackages({
132 summarizer,
133 packageSpecs,
134 packageTargets,
135 commitsByPackage,
136 log,
137 });
138
139 const changelogEntries = buildChangelogEntries({
140 packageSpecs,
141 commitsByPackage,
142 summariesByPackage,
143 prMetadata,
144 });
145
146 log('Generated changelog sections.');
147 const output = renderChangelog(changelogEntries, format);
148 console.log(output);
149 }
150
151 if (require.main === module) {
152 main().catch(error => {
153 process.stderr.write(`${error.message}\n`);
154 process.exit(1);
155 });
156 } else {
157 module.exports = main;
158 }