Commander.js: Action never called when no arguments/options passed

Created on 5 Dec 2017  路  7Comments  路  Source: tj/commander.js

I am using git style sub command and have a command that requires no args/options but needs to use the .action() call for an async callback.

````

!/usr/bin/env node

var program = require('commander')

program
.action( () => {
console.log('never called')
})
````
The above will never happen. How can I specify that I want this called? Right now I have to make a dummy option.

Most helpful comment

@abetomo, I have the same problem as in this issue. Default action executes only if some arguments were passed. Will this behavior be fixed? I want to have an opportunity to run default action without arguments.

All 7 comments

I have the same problem I believe. In my setup I have:

program
  .arguments("[files]")
  .description("Run mocha tests with ts-node")
  .option(
    "-s, --stage [env]",
    "Environment to use",
    /^(dev|test|stage|prod)^/,
    "test"
  )
  .option("--skip-lint", "Skip the linting checks")
  .option(
    "-f, --files",
    "an alternative syntax to just specifying files as first argument on command line"
  )
  .action(async files => {
    console.log("start");
    await cleanJSTests();
    const stage = program.stage;
    const scope = getScope(files);
    if (!program.skipLint) {
      await lint();
    }
    await executeTests(stage, scope);
  })
  .parse(process.argv);

and when I pass in a file to the script it works as expected but when I don't pass any arguments -- which should be valid considering files is stated as a non-required argument -- it never calls the action defined.

Commander doesn't do anything if it doesn't think it has to-- there doesn't seem to be a way to invoke a "default action". I think the general idea is that some people would only want to use Commander to make parsing command line args easier, and may not even want to have different sub-actions at all.

So, @cyberwombat, rather than passing a callback to .action, and then calling parse, you're just supposed to include the code to run immediately afterwards, like so:

    .option('-w', 'whatever')
    .parse(process.argv);

let [arg1, arg2, arg3] = program.args;
console.log('just start doing stuff immediately.');

It's awkward if you have a program with a lot of sub-commands, but I can see the logic behind it. Commander can just be dropped into an existing script without having to really restructure the it (or learn new habits) to work with the library.

This quirk also makes error handling a nightmare with async functions... There's no real bottleneck that everything in a Commander program has to pass through, so you have to attach error handling logic to everything-- which isn't unreasonable, but it could be made easier.

In my ideal, I'd like to see is a method that CAN call a default function (having parse just parse when there's no obvious action to take makes at least a little bit of sense, so something like .execute might be better) which you can treat like a promise. Then you could put teardown logic in .then, and a unified error handler in .catch.

All your actions would need to return a promise or be async functions (effectively the same thing) for this to work though.

I'm almost thinking about writing a PR to implement this.

@abetomo, I have the same problem as in this issue. Default action executes only if some arguments were passed. Will this behavior be fixed? I want to have an opportunity to run default action without arguments.

merge this please!

I seem to have the same problem with sub-commands. My action does get called but the options object is empty.

See output for following command

commandName example --customer=1

program
      .command('example')
      .option('-c, --customer <customer>')
      .action((env, options) => {
        console.log(options); // undefined
      });
program
      .command('example [args]')
      .option('-c, --customer <customer>')
      .action((env, options) => {
        console.log(options.customer); // outputs 1
      });

@realappie
You might not have noticed, but this is a closed issue so not as likely to get responses.

You have a different problem than the original poster. I'll quote the updated README description shipping soon in v3:

You can add options to a command that uses an action handler. The action handler gets passed a parameter for each argument you declared, and one additional argument which is the command object itself. This command argument has the values for the command-specific options added as properties.

So you want:

      .command('example')
      .option('-c, --customer <customer>')
      .action((options) => {
        console.log(options); // undefined
      });
Was this page helpful?
0 / 5 - 0 ratings

Related issues

akki005 picture akki005  路  5Comments

oknoorap picture oknoorap  路  4Comments

jaredpetersen picture jaredpetersen  路  3Comments

shadowspawn picture shadowspawn  路  4Comments

hossamelmansy picture hossamelmansy  路  4Comments