sindresorhus / meow

🐈 CLI app helper
MIT License
3.53k stars 150 forks source link

Add subcommands support #211

Closed aaronccasanova closed 1 year ago

aaronccasanova commented 2 years ago

I've been experimenting with an alternative approach to PR# 205 for adding subcommands support. My favorite feature of meow is the objects as configuration convention for flags and I was interested in exploring how to carry over that pattern for subcommands. I'm pretty satisfied with the POC and would love some initial feedback before I continue iterating and moving this PR out of draft.

Key features:

Here is a subset of the proposed API! Though, I recommend pulling down the branch and testing out barista.js with the provided examples.

const cli = meow({
  description: "Your friendly neighborhood barista",
  help: `
    Usage:
      barista brew <beverage> <options>`,
  commands: {
    brew: {
      description: "A prompt to select what to brew",
      help: `
        Usage:
          barista brew <beverage> <options>`,
      flags: {
        temp: { type: "string", default: "hot" },
      },
      handler: () => console.log("What do you want to brew?"),
      commands: {
        coffee: {
          description: "Brew coffee",
          help: `
            Usage:
              barista brew coffee <options>`,
          handler: (cli) => console.log(`Here's your ${cli.flags.temp} coffee!`),
        },
        tea: {
          description: "Brew tea",
          help: `
            Usage:
              barista brew tea <options>`,
          handler: (cli) => console.log(`Here's your ${cli.flags.temp} tea!`),
        },
      },
    },
  },
});

cli.command.options.handler?.(cli)
image
skoshx commented 2 years ago

@aaronccasanova I actually prefer this implementation over mine, seems simpler. Just need Sindre's approval though I think they are quite busy with other projects :D

corysimmons commented 2 years ago

Great solution. Using this and it works perfect.

It does break redent (because it's looking at options.help instead of however deeply nested .help could be, so I'm just wrapping my nested helps in redent('my help text', 2) to fix it for now.

sindresorhus commented 2 years ago

Sorry for the late reply. I also think this is the way to go.

skoshx commented 2 years ago

@aaronccasanova Do you want help with getting this draft merged? I'm willing to help to speed up the merging process :)

aaronccasanova commented 2 years ago

Do you want help with getting this draft merged?

Hey @skoshx! Yes! My bandwidth is limited at the moment and I would greatly appreciate help getting this PR out of draft 🙌

Here are some outstanding todo items:

aaronccasanova commented 1 year ago

Closing to avoid leaving stale PRs in this repository

tommy-mitchell commented 1 year ago

@aaronccasanova I'd like to help get this merged. Some thoughts from trying to use this:

You'll notice in my example I'm passing a handler callback to each subcommand options.

I don't think this kind of usage should be encouraged.

I tentatively included an allowParentFlags to restrict top level options.flags from being used with a given subcommand. Do we want this functionality?

I think this could be useful. Imagine if the top-level CLI defines a --verbose flag. Subcommands shouldn't have to redeclare that flag just to get access to it. There are some potential issues though:

I'd be happy to help, especially with types/documentation. I can also take a look at the redent issue.