Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nested Commands: If the parent command has a description, the nested commands are added on first level instead of 2nd level #2316

Closed
andygrunwald opened this issue Jan 23, 2025 · 4 comments

Comments

@andygrunwald
Copy link

The following code

import { Command } from 'commander';
const program = new Command();

const brew = program.command('brew');
brew.command('tea').action(() => {
  console.log('brew tea');
});
brew.command('coffee').action(() => {
  console.log('brew coffee');
});

program.parse(process.argv);

Generates

$ node -r ts-node/register src/index.ts
Usage: index [options] [command]

Options:
  -h, --help      display help for command

Commands:
  brew
  help [command]  display help for command

$ node -r ts-node/register src/index.ts brew
Usage: index brew [options] [command]

Options:
  -h, --help      display help for command

Commands:
  tea
  coffee
  help [command]  display help for command

If you change the line

const brew = program.command('brew');

to

const brew = program.command('brew', 'All sub-commands to brew your favorite drink');

the subcommand will appear on the top-level:

$ node -r ts-node/register src/index.ts
Usage: index [options] [command]

Options:
  -h, --help      display help for command

Commands:
  brew            All sub-commands to brew your favorite drink
  tea
  coffee
  help [command]  display help for command

I would have expected that the sub-commands tea and coffee still appear on the 2nd level.
Is this a bug or expected behaviour?

@andygrunwald
Copy link
Author

I just found out, using

const brew = program.command('brew').description('Brew your drinks');

is producing the expected behaviour:

$ node -r ts-node/register src/index.ts
Usage: pm [options] [command]

Options:
  -h, --help      display help for command

Commands:
  brew            Brew your drinks
  help [command]  display help for command

$ node -r ts-node/register src/index.ts brew
Usage: pm brew [options] [command]

Brew your drinks

Options:
  -h, --help      display help for command

Commands:
  tea
  coffee
  help [command]  display help for command

It may be helpful to document the limitations of the shorthand syntax with

const brew = program.command('brew', 'All sub-commands to brew your favorite drink');

Or maybe even deprecating this way of setting the description in favor of always using .description().

@shadowspawn
Copy link
Collaborator

Have another look at the example under the Commands section in the README: https://github.com/tj/commander.js/blob/master/Readme.md#commands

For legacy reasons, there are two quite different ways of adding commands using .command(). If you include the description as the second parameter, you are declaring an external command, and the parent command is returned for chaining. e.g. for a git-like command structure:

program.name('git')
   .command('clone <repo>', 'clone repo')
   .command('push', 'push changes')
   .command('pull', 'pull changes');

If you include just the command name, you are creating a subcommand and the new subcommand is returned for configuring, such as by adding a description using .description() and by adding an action handler.

const cloneCommand = program.command('clone')
  .argument('<repo>')
  .description('clone repo')
  .action(repo => doClone(repo));

The two approaches are described further as using an action handler or using a stand-alone executable:

@shadowspawn
Copy link
Collaborator

The confusion between commands with an action handler and ones implemented as stand-alone executable files was more common before the README got updated, but can still cause confusion: #938

@andygrunwald
Copy link
Author

Thanks for the details.
Indeed, I was confused by this.
I think this can be closed now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants