Skip to content

Commit 08ab5e1

Browse files
huntiefacebook-github-bot
authored andcommitted
Remove commander dependency from main package (#57645)
Summary: Simplify the `scripts/bundle.js` wrapper and remove the `commander` dependency from `packages/react-native`. This script is a lightweight wrapper around `npx react-native bundle`, with extra local config arg handling (for now, unchanged / remains located here). **Refactor only** with script behaviour unchanged. **Other changes** - Combined arg parsing is hoisted into `community-cli-plugin` (which does have `commander`) via `unstable_createBundleCommandParser`. - Drop `program` value export (also unused). **Notes** After this change, only `yargs` remains as a direct dependency `react-native` package (`packages/react-native/scripts/`) (a future cleanup). Changelog: [Internal] Reviewed By: cortinico Differential Revision: D113389458
1 parent 3a41372 commit 08ab5e1

5 files changed

Lines changed: 118 additions & 54 deletions

File tree

packages/community-cli-plugin/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"dependencies": {
3434
"@react-native/asset-utils": "0.87.0-main",
3535
"@react-native/dev-middleware": "0.87.0-main",
36+
"commander": "^12.0.0",
3637
"debug": "^4.4.0",
3738
"invariant": "^2.2.4",
3839
"metro": "^0.87.0",

packages/community-cli-plugin/src/commands/bundle/index.js

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,22 @@
88
* @format
99
*/
1010

11-
import type {Command} from '@react-native-community/cli-types';
11+
import type {Command as CommunityCommand} from '@react-native-community/cli-types';
1212

1313
import buildBundle from './buildBundle';
14+
import {Command} from 'commander';
1415
import path from 'node:path';
1516

1617
export type {BundleCommandArgs} from './buildBundle';
1718

18-
const bundleCommand: Command = {
19+
type CommandOption = Readonly<NonNullable<CommunityCommand['options']>[number]>;
20+
21+
type BundleCommandParser = {
22+
parser: Command,
23+
baseHelpInformation: string,
24+
};
25+
26+
const bundleCommand: CommunityCommand = {
1927
name: 'bundle',
2028
description: 'Build the bundle for the provided JavaScript entry file.',
2129
func: buildBundle,
@@ -123,4 +131,43 @@ const bundleCommand: Command = {
123131
],
124132
};
125133

134+
function addOptions(
135+
command: Command,
136+
options: ReadonlyArray<CommandOption>,
137+
): void {
138+
for (const option of options) {
139+
const description = option.description ?? '';
140+
const defaultValue =
141+
typeof option.default === 'function' ? undefined : option.default;
142+
143+
if (option.parse != null) {
144+
command.option(option.name, description, option.parse, defaultValue);
145+
} else if (
146+
typeof defaultValue === 'string' ||
147+
typeof defaultValue === 'boolean' ||
148+
Array.isArray(defaultValue)
149+
) {
150+
command.option(option.name, description, defaultValue);
151+
} else {
152+
command.option(option.name, description);
153+
}
154+
}
155+
}
156+
157+
export function unstable_createBundleCommandParser(
158+
additionalOptions: ReadonlyArray<CommandOption> = [],
159+
): BundleCommandParser {
160+
const parser = new Command()
161+
.name(bundleCommand.name)
162+
.description(bundleCommand.description ?? '')
163+
.helpOption('--help', 'Display help for command')
164+
.allowUnknownOption();
165+
166+
addOptions(parser, bundleCommand.options ?? []);
167+
const baseHelpInformation = parser.helpInformation();
168+
addOptions(parser, additionalOptions);
169+
170+
return {parser, baseHelpInformation};
171+
}
172+
126173
export default bundleCommand;

packages/community-cli-plugin/src/index.flow.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
* @format
99
*/
1010

11-
export {default as bundleCommand} from './commands/bundle';
11+
export {
12+
default as bundleCommand,
13+
unstable_createBundleCommandParser,
14+
} from './commands/bundle';
1215
export {default as startCommand} from './commands/start';
1316

1417
export {unstable_buildBundleWithConfig} from './commands/bundle/buildBundle';

packages/react-native/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,6 @@
159159
"ansi-regex": "^5.0.0",
160160
"babel-plugin-syntax-hermes-parser": "0.37.0",
161161
"base64-js": "^1.5.1",
162-
"commander": "^12.0.0",
163162
"flow-enums-runtime": "^0.0.6",
164163
"hermes-compiler": "0.0.0",
165164
"invariant": "^2.2.4",

packages/react-native/scripts/bundle.js

Lines changed: 64 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -10,65 +10,79 @@
1010

1111
'use strict';
1212

13-
const {bundleCommand: bc} = require('@react-native/community-cli-plugin');
14-
const commander = require('commander');
13+
const {
14+
bundleCommand: bundleCommandBase,
15+
unstable_createBundleCommandParser,
16+
} = require('@react-native/community-cli-plugin');
1517
const {execSync} = require('node:child_process');
1618

17-
// Commander 12.0.0 changes from the global to named export
18-
// $FlowFixMe[signature-verification-failure]
19-
const program = commander.program ?? commander;
19+
/*::
20+
type BundleOptions = {
21+
configCmd?: string,
22+
loadConfig?: string,
23+
...
24+
};
25+
*/
2026

21-
program
22-
.name(bc.name)
23-
.description(bc.description ?? '')
24-
.option(
25-
'--config-cmd <string>',
26-
'Command to generate a JSON project config',
27-
'npx react-native config',
28-
)
29-
.option('--load-config <string>', 'JSON project config')
30-
.option('--verbose', 'Additional logs', () => true, false)
31-
.allowUnknownOption()
32-
.action(async function handleAction() {
33-
let config = null;
34-
let options = program
35-
.opts /*::<{
36-
configCmd?: string,
37-
loadConfig?: string,
38-
verbose: boolean,
39-
...
40-
}>*/
41-
();
42-
if (options.loadConfig != null) {
43-
config = JSON.parse(
44-
options.loadConfig.replace(/^\W*'/, '').replace(/'\W*$/, ''),
45-
);
46-
} else if (options.configCmd != null) {
47-
config = JSON.parse(
48-
execSync(options.configCmd.trim(), {encoding: 'utf8'}),
49-
);
50-
}
27+
const baseOptions = [
28+
{
29+
name: '--config-cmd <string>',
30+
description: 'Command to generate a JSON project config',
31+
default: 'npx react-native config',
32+
},
33+
{
34+
name: '--load-config <string>',
35+
description: 'JSON project config',
36+
},
37+
];
5138

52-
if (config == null) {
53-
throw new Error('No config provided');
54-
}
39+
const {
40+
parser: bundleCommandParser,
41+
baseHelpInformation: bundleCommandBaseHelp,
42+
} = unstable_createBundleCommandParser(baseOptions);
5543

56-
await bc.func(program.args, config, options);
57-
});
44+
function formatOptions(
45+
options /*: ReadonlyArray<Readonly<{description?: string, name: string, ...}>> */,
46+
) {
47+
return options
48+
.map(option => ` ${option.name.padEnd(35)} ${option.description ?? ''}`)
49+
.join('\n');
50+
}
51+
52+
function printHelp() {
53+
console.log(`${bundleCommandBaseHelp}
54+
Additional options:
55+
${formatOptions(baseOptions)}
56+
`);
57+
}
58+
59+
async function main(argv /*: ReadonlyArray<string> */ = process.argv.slice(2)) {
60+
if (argv.includes('--help')) {
61+
printHelp();
62+
return;
63+
}
5864

59-
if (bc.options != null) {
60-
for (const o of bc.options) {
61-
program.option(
62-
o.name,
63-
o.description ?? '',
64-
o.parse ?? (value => value),
65-
o.default,
65+
bundleCommandParser.parse(argv, {from: 'user'});
66+
const options = bundleCommandParser
67+
.opts /*::<BundleOptions>*/
68+
();
69+
70+
let config = null;
71+
if (options.loadConfig != null) {
72+
config = JSON.parse(
73+
options.loadConfig.replace(/^\W*'/, '').replace(/'\W*$/, ''),
6674
);
75+
} else if (options.configCmd != null) {
76+
config = JSON.parse(execSync(options.configCmd.trim(), {encoding: 'utf8'}));
77+
}
78+
79+
if (config == null) {
80+
throw new Error('No config provided');
6781
}
82+
83+
await bundleCommandBase.func(bundleCommandParser.args, config, options);
6884
}
6985

7086
if (require.main === module) {
71-
program.parse(process.argv);
87+
void main();
7288
}
73-
74-
module.exports = program;

0 commit comments

Comments
 (0)