I have the following code:
const version = JSON.parse(fs.readFileSync(path.join(__dirname, './package.json')) + '').version;
program
.version(version)
.arguments('<engine>')
.option('-u, --url <link>','indicates that the address parameter is to be interpreted as an url (value by default)')
.option('-f, --file <link>','indicates that the address parameter is to be interpreted as a file')
.action(main)
program.parse(process.argv);
if (!process.argv.slice(2).length) {
program.outputHelp();
}
function main(engine){
console.log(engine);
if(program.file) {
renderer.execute(program.file,engine, {file: true}).then(console.log);
} else {
renderer.execute(program.url, engine).then(console.log).done();
}
};
I was trying to have the program output the help text if the argument engine is not specified. Is there any built-in way to do this? At the moment the code works perfectly if I specify the engine. but if I just execute with any arguments then nothing is shown.
Thanks for taking the time to read this.
@pedro93
You could try testing commander.args in addition to process.argv. Bear in mind that it may contain a circular reference to commander.
I'm seeing inconsistent behaviour with the <required> arguments. I have a subcommand with arguments('<one> <two>') and a check similar to yours because the action isn't triggered if they aren't passed (as per the docs). So I was surprised to see that if I pass <one> but not <two> I get the error:
error: missing required argument 'two'
So I updated test/test.arguments.js to match this scenario and it passes. Unless I add a <third> required argument. The code has provision for flagging arguments as required/optional but isn't checking them consistently.
The original poster asked about mandatory non-option arguments. There is not currently automatic support for this.
In the simple example with an argument to the program you can make a simple test for whether anything is left after the options are consumed by testing program.args.
const program = require("commander");
program
.arguments('<engine>')
.option('-u, --url <link>','indicates that the address parameter is to be interpreted as an url (value by default)')
.option('-f, --file <link>','indicates that the address parameter is to be interpreted as a file')
.action((engine) => {
console.log(`Action called with ${engine}`);
});
program.parse(process.argv);
if (program.args.length == 0) {
console.log("Would show help");
}
$ node index.js
Would show help
$ node index.js aaa
Action called with aaa
$ node index.js -u bbb
Would show help
$ node index.js -u ccc ddd
Action called with ddd
(Note: deleted a couple of comments in this thread that were not contributing to improving this package.)
This issue has not seen much activity in the passing years. Feel free to open a new issue if it comes up again, with new information and renewed interest.
Closing in favour of #230.
Thank you for your contributions.
Hi guys,
I am working on simple cli tool, and encountered the same error but I feel like it was never really addressed.
Indeed #230 propose a way to use ".requiredOption" for mandatory flag options. But the point here is the missing ".arguments(
I there a proper way to handle that missing argument if we want to display an error message or the help (without having to write our own argv parser)? If not, I am okay to work on that.
There is not currently automatic support for displaying the help if no arguments are supplied, in the example program of original poster.
I gave one approach for detecting in: https://github.com/tj/commander.js/issues/546#issuecomment-478201124
And there is an open issue about detecting including more complicated cases, with some suggestions, in: #1088
Oh indeed your first approach could solve my issue.
I will take time reading about the more complicated cases and see if I can help on that.
Thank you.
If interested, see also #1062 for some discussion and digging into current state of affairs, and multiple ways command:* gets sent and used.
As of today, https://github.com/tj/commander.js/issues/546#issuecomment-478201124 does not work anymore ([email protected])
C:\code\lib>node index.js
error: missing required argument 'engine'
C:\code\lib>node index.js aaa
Action called with aaa
C:\code\lib>node index.js -u bbb
error: missing required argument 'engine'
Finally, I cannot get this sample to display "no command given!"
https://github.com/tj/commander.js#specify-the-argument-syntax
~~I'll just check the process.argv.length before the parse command.~~
let args = process.argv.slice(2);
let arg, foundArg = false;
do {
arg = args.shift();
if (arg) {
arg.replace(/^--/, '-');
switch (arg[1]) {
case 'c':
case 'n':
// c and n are 2 optional parameters that specify a single, assuming the user did things correctly here :/
args.shift();
break;
case 'd':
// optional boolean parameter
break;
default:
foundArg = true;
break;
}
}
} while (!foundArg && args.length > 0);
if (!foundArg) {
program.help();
}
program.parse(process.argv);
Slightly uglier workaround but more reliable
program.exitOverride();
try {
program.parse(process.argv);
} catch (err) {
program._exitCallback = null;
program.help();
}
@michga
The behaviour changed in 5.0.0:
Looks like I missed that entry in the README! I opened #1311 to update that.
I think you could use .parseOptions() to do the work for you to look for missing non-option arguments, which would be in the spirit of the previous pattern. Instead of hand parsing. It leaves behind state so you'll want to make a different copy of the program. If that sounds like what you want and you would like an example, I can add one.
@shadowspawn thank you for your input.
I couldn't find a way with .parseOptions().
After some additional digging, I came up with this, which seems acceptable in terms of compliancy since .outputHelp() is marked as public:
program.exitOverride(() => {
// program.args is not defined when asking for the version -V
let args = program.args || [];
// this prevents duplicating the help when asking for it, not ideal but it seems to work
if (!(args.includes('-h') || args.includes('--help'))) {
program.outputHelp();
}
});
program.parse(process.argv);
The only minor issue that remains is that the help is now displayed when asking for the version since it doesn't show up in the args but I can live with that.
https://github.com/tj/commander.js/blob/c5a5e7b70d425d6f739bd84cc622c1d8775743f1/index.js#L1587-L1596
I had to remind myself what .parseOptions returns. This is the pre-flighting I had in mind to check for no arguments after top-level parse.
const { Command } = require("commander");
function makeProgram() {
const program = new Command();
program
.arguments('<engine>')
.option('-u, --url <link>','indicates that the address parameter is to be interpreted as an url (value by default)')
.option('-f, --file <link>','indicates that the address parameter is to be interpreted as a file')
.action((engine) => {
console.log(`Action called with ${engine}`);
});
return program;
}
if (makeProgram().parseOptions(process.argv).operands.length <= 2) {
makeProgram().help();
}
makeProgram().parse(process.argv);
And here is a more specific implementation of .exitOverride to look for the error of interest:
const { program } = require("commander");
program.exitOverride((err) => {
if (err.code === 'commander.missingArgument') {
program.outputHelp();
}
process.exit(err.exitCode);
});
program
.arguments('<engine>')
.option('-u, --url <link>','indicates that the address parameter is to be interpreted as an url (value by default)')
.action((engine) => {
console.log(`Action called with ${engine}`);
});
program.parse();
@shadowspawn Thank you, this implementation you gave does exactly what I wanted.
Most helpful comment
And here is a more specific implementation of
.exitOverrideto look for the error of interest: