diff --git a/lib/validation.js b/lib/validation.js index d44d3507f..17f0d9f30 100644 --- a/lib/validation.js +++ b/lib/validation.js @@ -57,10 +57,10 @@ module.exports = function validation (yargs, usage, y18n) { // make sure that any args that require an // value (--foo=bar), have a value. self.missingArgumentValue = function missingArgumentValue (argv) { - const defaultValues = [true, false, '', undefined] const options = yargs.getOptions() if (options.requiresArg.length > 0) { const missingRequiredArgs = [] + const defaultValues = new Set([true, false, '']) options.requiresArg.forEach((key) => { // if the argument is not set in argv no need to check @@ -71,7 +71,7 @@ module.exports = function validation (yargs, usage, y18n) { // if a value is explicitly requested, // flag argument as missing if it does not // look like foo=bar was entered. - if (~defaultValues.indexOf(value) || + if (defaultValues.has(value) || (Array.isArray(value) && !value.length)) { missingRequiredArgs.push(key) } diff --git a/test/validation.js b/test/validation.js index 5e8bafeb9..c37be6c89 100644 --- a/test/validation.js +++ b/test/validation.js @@ -369,46 +369,6 @@ describe('validation tests', () => { .argv }) - it('fails when a required argument of type number is missing', (done) => { - yargs('-w') - .option('w', {type: 'number', requiresArg: true}) - .fail((msg) => { - msg.should.equal('Missing argument value: w') - return done() - }) - .argv - }) - - it('fails when a required argument of type string is missing', (done) => { - yargs('-w') - .option('w', {type: 'string', requiresArg: true}) - .fail((msg) => { - msg.should.equal('Missing argument value: w') - return done() - }) - .argv - }) - - it('fails when a required argument of type boolean is missing', (done) => { - yargs('-w') - .option('w', {type: 'boolean', requiresArg: true}) - .fail((msg) => { - msg.should.equal('Missing argument value: w') - return done() - }) - .argv - }) - - it('fails when a required argument of type array is missing', (done) => { - yargs('-w') - .option('w', {type: 'array', requiresArg: true}) - .fail((msg) => { - msg.should.equal('Missing argument value: w') - return done() - }) - .argv - }) - it('fails when required arguments are present, but a command is missing', (done) => { yargs('-w 10 -m wombat') .demand(1, ['w', 'm']) @@ -419,16 +379,6 @@ describe('validation tests', () => { .argv }) - // see: https://github.com/yargs/yargs/issues/1041 - it('does not fail if required argument is not provided', (done) => { - yargs('') - .option('w', {type: 'array', requiresArg: true}) - .parse('', (err, argv, output) => { - expect(err).to.equal(null) - return done() - }) - }) - it('fails without a message if msg is null', (done) => { yargs([]) .demand(1, null) @@ -475,6 +425,69 @@ describe('validation tests', () => { }) }) + describe('requiresArg', () => { + it('fails when a required argument value of type number is missing', (done) => { + yargs() + .option('w', {type: 'number', requiresArg: true}) + .parse('-w', (err, argv, output) => { + expect(err).to.exist + expect(err).to.have.property('message', 'Not enough arguments following: w') + return done() + }) + }) + + it('fails when a required argument value of type string is missing', (done) => { + yargs() + .option('w', {type: 'string', requiresArg: true}) + .parse('-w', (err, argv, output) => { + expect(err).to.exist + expect(err).to.have.property('message', 'Missing argument value: w') + return done() + }) + }) + + it('fails when a required argument value of type boolean is missing', (done) => { + yargs() + .option('w', {type: 'boolean', requiresArg: true}) + .parse('-w', (err, argv, output) => { + expect(err).to.exist + expect(err).to.have.property('message', 'Missing argument value: w') + return done() + }) + }) + + it('fails when a required argument value of type array is missing', (done) => { + yargs() + .option('w', {type: 'array', requiresArg: true}) + .parse('-w', (err, argv, output) => { + expect(err).to.exist + expect(err).to.have.property('message', 'Missing argument value: w') + return done() + }) + }) + + // see: https://github.com/yargs/yargs/issues/1041 + it('does not fail if argument with required value is not provided', (done) => { + yargs() + .option('w', {type: 'number', requiresArg: true}) + .command('woo') + .parse('', (err, argv, output) => { + expect(err).to.equal(null) + return done() + }) + }) + + it('does not fail if argument with required value is not provided to subcommand', (done) => { + yargs() + .option('w', {type: 'number', requiresArg: true}) + .command('woo') + .parse('woo', (err, argv, output) => { + expect(err).to.equal(null) + return done() + }) + }) + }) + describe('choices', () => { it('fails with one invalid value', (done) => { yargs(['--state', 'denial']) diff --git a/yargs.js b/yargs.js index 524404fb3..0194e9449 100644 --- a/yargs.js +++ b/yargs.js @@ -964,6 +964,18 @@ function Yargs (processArgs, cwd, parentRequire) { options.__ = y18n.__ options.configuration = pkgUp()['yargs'] || {} + + // numbers are defaulted to `undefined`, which makes it impossible to verify requiresArg + // so _unlike_ other types, we will implicitly add them to narg when requiresArg is true + const numberTyped = new Set(options.number) + options.requiresArg + .filter(key => numberTyped.has(key)) + .forEach(key => { + // if narg _and_ requiresArg are configured for an option, + // we should probably throw an error somewhere indicating conflicting configuration + options.narg[key] = 1 + }) + const parsed = Parser.detailed(args, options) let argv = parsed.argv if (parseContext) argv = Object.assign({}, argv, parseContext)