1

I am trying to create a CLI that can dynamically create subcommands with options. I have a main program named 'dashboard'. I want to create 'login' and a 'logout' subcommands that have options. I can't seem to get the subcommand options to run. It exits the code, saying the option doesn't exist. Ideally using the switch case, that would be the only place I would need to change If I wanted to add more commands. How can I create this dynamically?

I've tried using the .hook() on the main command to add options but it hasn't worked for me. I've tried creating a separate executable file, doesn't work. This is what I have so far

// main command
const program = new Command();

  program
    .name("dashboard")
    .description("CLI")
    .option("-d, --debug", "outputs extra debugging", false)
    .hook("preSubcommand", (commandObj) => {
      if (commandObj.args[1] === "login") {
        program.addOption(new Option("-e, --email")); // this doesn't work
      }
    });

  program.parse(process.argv);

//subcommands
 const commander = program.command("dashboard");
  const subcommand = program.args[1];

  try {
    let func: any;
    let description: string;
    let option: any;

    switch (subcommand) {
      case "login":
        description = "Logging you in!";
        func = (await import("./commands/login.js")).default;
        option = "-e, --email";
        break;
      case "logout":
        description = "Logging you out";
        func = (await import("./commands/logout.js")).default;
        break;
      default:
        description = "";
        func = null;
        break;
    }


    commander
      .command(subcommand)
      .on("command:login", () => console.log("Does this work?")) // No, it doesn't
      .hook("preSubcommand", () => {
        if (subcommand === "login") {
          console.log("What about this?"); // Still no
        }
      });
      .description(description)
      .option(option) // Tried adding the option from the switch case here, doesn't work
      .action(async (arg, option) => {
        console.log(" ~ file: index.ts:128 ~ .action ~ option:", option);
        console.log("test");
      await func(client);
    });
    console.log(" ~ file: index.ts:131 ~ main ~ commander:", commander.opts());
  } catch (err: unknown) {
    console.log(err);
  }
  program.parse();

When I run the command dashboard login -e, I get an error:

error: unknown option '-e'
 ELIFECYCLE  Command failed with exit code 1.

It will run the subcommand if there is not option given, so just dashboard login works fine. Otherwise, it throws that error above.

CeCe
  • 19
  • 1
  • Why are you trying to add subcommands dynamically? Commander does expect the full command tree defined at parse time for built-in subcommands with action handlers. However, you can have external stand-alone subcommands (which can also be called directly), so say `git clone --foo` implemented by spawning `git-clone --foo`). – shadowspawn Apr 19 '23 at 05:36
  • For simplicity I suppose and if anyone wanted to add to the commands other than me, it would be relatively easy – CeCe Apr 19 '23 at 14:59
  • The switch case works fine when there are no options, for `logout` as an example. But once you introduce options, it fails. – CeCe Apr 19 '23 at 15:08
  • For defining and adding commands written in other files, one approach is using `program.addCommand(someCommand)`. – shadowspawn Apr 20 '23 at 05:30
  • In case you didn't realise, commands can be nested. So you can add a "login" subcommand to the "dashboard" subcommand to have Commander handle a command-line like "util dashboard login --user=admin". – shadowspawn Apr 20 '23 at 05:34

0 Answers0