Adding a default value to an option with parseInt will result in NaN in many cases.
parseInt take two arguments -- string and radix -- and if you pass a default value to the option, the option will call parseInt with the default value as the radix. For example, an option with a default value of 90 and an actual value of 100 will invoke parseInt(100, 90).
This is not a problem with parseFloat because it only takes one argument.
Source:
#!/usr/bin/env node
'use strict';
const program = require('commander');
const version = require('./package.json').version;
process.title = 'my-app';
program
.name('my-app')
.version(version, '-v, --version');
program
.command('start')
.description('start the app')
.option('-d, --directory <directory>', 'directory for the thing', '/srv')
.option('-p, --port <port>', 'port number for the thing', parseInt, 8080)
.action(({ directory, port }) => console.log(directory, port));
program.parse(process.argv);
if (!program.args.length) program.help();
Expected:
$ ./cli.js start --port 80
/srv 80
Actual:
$ ./cli.js start --port 80
/srv NaN
I'm not sure what the fix should be in this case. Based on the Coercion section of the REAMDE, the way that commander deals with the default value argument is desirable. The collect function in particular shows that the default value of an empty array is being used as part of the coercion:
(reproduced from README)
function range(val) {
return val.split('..').map(Number);
}
function list(val) {
return val.split(',');
}
function collect(val, memo) {
memo.push(val);
return memo;
}
function increaseVerbosity(v, total) {
return total + 1;
}
program
.version('0.1.0')
.usage('[options] <file ...>')
.option('-i, --integer <n>', 'An integer argument', parseInt)
.option('-f, --float <n>', 'A float argument', parseFloat)
.option('-r, --range <a>..<b>', 'A range', range)
.option('-l, --list <items>', 'A list', list)
.option('-o, --optional [value]', 'An optional value')
.option('-c, --collect [value]', 'A repeatable value', collect, [])
.option('-v, --verbose', 'A value that can be increased', increaseVerbosity, 0)
.parse(process.argv);
console.log(' int: %j', program.integer);
console.log(' float: %j', program.float);
console.log(' optional: %j', program.optional);
program.range = program.range || [];
console.log(' range: %j..%j', program.range[0], program.range[1]);
console.log(' list: %j', program.list);
console.log(' collect: %j', program.collect);
console.log(' verbosity: %j', program.verbose);
console.log(' args: %j', program.args);
If this really is desirable, then I think a note about it in the Coercion section is appropriate. It's still possible to use parseInt, you just have to wrap it:
#!/usr/bin/env node
'use strict';
const program = require('commander');
const version = require('./package.json').version;
process.title = 'my-app';
program
.name('my-app')
.version(version, '-v, --version');
const int = (value) => parseInt(value);
program
.command('start')
.description('start the app')
.option('-d, --directory <directory>', 'directory for the thing', '/srv')
.option('-p, --port <port>', 'port number for the thing', int, 8080)
.action(({ directory, port }) => console.log(directory, port));
program.parse(process.argv);
if (!program.args.length) program.help();
As a work around it seems Number or Math.floor work as expected.
Example
program
.option( '-i, --integer [intVal]', 'Integer value', Math.floor, 5 ) // Integer
.option( '-f, --float [floatVal]', 'Float value', Number, 3.14 ) // Float
Usagee
test -i 4.321 -f 4.321
test -i -f
Output
{ i: 4, f: 4.321 }
{ i: 5, f: 3.14 }
Good detailed description, thanks @jaredpetersen
The problem behaviour got added way back in #198 to support the repeated options, but it added a parameter that a straight-forward coercion function would not expect.
Not sure if worth the pain to untangle the behaviours now, or just document it!
Closing as covered by #201 and #943
Most helpful comment
As a work around it seems Number or Math.floor work as expected.
Example
Usagee
Output