diff --git a/__e2e__/config.test.ts b/__e2e__/config.test.ts index 322b63829..94f0e49b6 100644 --- a/__e2e__/config.test.ts +++ b/__e2e__/config.test.ts @@ -157,6 +157,31 @@ export default { }; `; +const USER_CONFIG_COMMAND_ARG_REQUIRED = `; +module.exports = { + commands: [ + { + name: 'test-command-arg-required', + description: 'test command', + func: () => { + console.log('ok'); + }, + options: [ + { + name: '--required-arg ', + description: 'test command arg', + required: true, + }, + { + name: '--optional-arg ', + description: 'test command arg', + }, + ], + }, + ], +}; +`; + test('should read user config from react-native.config.js', () => { writeFiles(path.join(DIR, 'TestProject'), { 'react-native.config.js': USER_CONFIG, @@ -275,3 +300,27 @@ test('should read config if using import/export in react-native.config.mjs with const {stdout} = runCLI(path.join(DIR, 'TestProject'), ['test-command-esm']); expect(stdout).toMatch('test-command-esm'); }); + +test('should fail if a required arg is missing, pass if optional missing', () => { + writeFiles(path.join(DIR, 'TestProject'), { + 'react-native.config.cjs': USER_CONFIG_COMMAND_ARG_REQUIRED, + }); + + const {stderr} = runCLI( + path.join(DIR, 'TestProject'), + ['test-command-arg-required', '--optional-arg', 'foo'], + { + expectedFailure: true, + }, + ); + expect(stderr).toMatch( + "required option '--required-arg ' not specified", + ); + + const {stdout} = runCLI(path.join(DIR, 'TestProject'), [ + 'test-command-arg-required', + '--required-arg', + 'bar', + ]); + expect(stdout).toMatch('ok'); +}); diff --git a/docs/plugins.md b/docs/plugins.md index 072e2096d..de4d7e3db 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -47,6 +47,7 @@ type Command = { | boolean | number | ((config: ConfigT) => string | boolean | number); + required?: boolean; }>; examples?: Array<{ desc: string; @@ -102,6 +103,10 @@ Default value for the option when not provided. Can be either a primitive value Useful when you want to use project settings as default value for your option. +##### `options.required` + +If true and no default is specified, fail with a friendly error if the user doesn't supply this option. + #### `examples` An array of example usage of the command to be printed to the user. diff --git a/packages/cli-config/src/schema.ts b/packages/cli-config/src/schema.ts index 0eb8e5013..f7bf5c6cf 100644 --- a/packages/cli-config/src/schema.ts +++ b/packages/cli-config/src/schema.ts @@ -27,6 +27,7 @@ const command = t.object({ default: t .alternatives() .try(t.bool(), t.number(), t.string().allow(''), t.func()), + required: t.bool(), }) .rename('command', 'name', {ignoreUndefined: true}), ), diff --git a/packages/cli-types/src/index.ts b/packages/cli-types/src/index.ts index 1e07ae297..63bbf5057 100644 --- a/packages/cli-types/src/index.ts +++ b/packages/cli-types/src/index.ts @@ -27,6 +27,7 @@ export type CommandOption OptionValue> = { description?: string; parse?: (val: string) => any; default?: OptionValue | T; + required?: boolean; }; export type DetachedCommandFunction = ( diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index c2841f402..e151fb1e8 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -127,7 +127,7 @@ function attachCommand>( cmd.addHelpText('after', printExamples(command.examples)); for (const opt of command.options || []) { - cmd.option( + cmd[opt.required ? 'requiredOption' : 'option']( opt.name, opt.description ?? '', opt.parse || ((val: any) => val),