main
js 128 lines 3.35 KB
Raw
1 'use strict';
2
3 const semver = require('semver');
4 const yargs = require('yargs/yargs');
5
6 const {stablePackages} = require('../../../ReactVersions');
7 const {isCommandAvailable} = require('./utils');
8
9 function parseArgs(argv) {
10 const parser = yargs(argv)
11 .usage(
12 'Usage: yarn generate-changelog [--codex|--claude] [--debug] [--format <text|csv|json>] [<pkg@version> ...]'
13 )
14 .example(
15 '$0 --codex eslint-plugin-react-hooks@7.0.1',
16 'Generate changelog for a single package using Codex.'
17 )
18 .example(
19 '$0 --claude react@19.3 react-dom@19.3',
20 'Generate changelog entries for multiple packages using Claude.'
21 )
22 .example(
23 '$0 --codex',
24 'Generate changelog for all stable packages using recorded versions.'
25 )
26 .option('codex', {
27 type: 'boolean',
28 describe: 'Use Codex for commit summarization.',
29 })
30 .option('claude', {
31 type: 'boolean',
32 describe: 'Use Claude for commit summarization.',
33 })
34 .option('debug', {
35 type: 'boolean',
36 describe: 'Enable verbose debug logging.',
37 default: false,
38 })
39 .option('format', {
40 type: 'string',
41 describe: 'Output format for the generated changelog.',
42 choices: ['text', 'csv', 'json'],
43 default: 'text',
44 })
45 .help('help')
46 .alias('h', 'help')
47 .version(false)
48 .parserConfiguration({
49 'parse-numbers': false,
50 'parse-positional-numbers': false,
51 });
52
53 const args = parser.scriptName('generate-changelog').parse();
54 const packageSpecs = [];
55 const debug = !!args.debug;
56 const format = args.format || 'text';
57 let summarizer = null;
58
59 if (args.codex && args.claude) {
60 throw new Error('Choose either --codex or --claude, not both.');
61 }
62 if (args.codex) {
63 summarizer = 'codex';
64 } else if (args.claude) {
65 summarizer = 'claude';
66 }
67
68 const positionalArgs = Array.isArray(args._) ? args._ : [];
69 for (let i = 0; i < positionalArgs.length; i++) {
70 const token = String(positionalArgs[i]).trim();
71 if (!token) {
72 continue;
73 }
74
75 const atIndex = token.lastIndexOf('@');
76 if (atIndex <= 0 || atIndex === token.length - 1) {
77 throw new Error(`Invalid package specification: ${token}`);
78 }
79
80 const packageName = token.slice(0, atIndex);
81 const versionText = token.slice(atIndex + 1);
82 const validVersion =
83 semver.valid(versionText) || semver.valid(semver.coerce(versionText));
84 if (!validVersion) {
85 throw new Error(`Invalid version for ${packageName}: ${versionText}`);
86 }
87
88 packageSpecs.push({
89 name: packageName,
90 version: validVersion,
91 displayVersion: versionText,
92 });
93 }
94
95 if (packageSpecs.length === 0) {
96 Object.keys(stablePackages).forEach(pkgName => {
97 const versionText = stablePackages[pkgName];
98 const validVersion = semver.valid(versionText);
99 if (!validVersion) {
100 throw new Error(
101 `Invalid stable version configured for ${pkgName}: ${versionText}`
102 );
103 }
104 packageSpecs.push({
105 name: pkgName,
106 version: validVersion,
107 displayVersion: versionText,
108 });
109 });
110 }
111
112 if (summarizer && !isCommandAvailable(summarizer)) {
113 throw new Error(
114 `Requested summarizer "${summarizer}" is not available on the PATH.`
115 );
116 }
117
118 return {
119 debug,
120 format,
121 summarizer,
122 packageSpecs,
123 };
124 }
125
126 module.exports = {
127 parseArgs,
128 };