Basic Usage

Register Command

In command line applications, we usually have two kinds of commands: Root Commands and Sub command. Root commands are invoked with your binary name directly like:

$ create-react-app [dir] [options]

And sub commands are invoked with your binary name and sub command name like:

$ vite build
$ parcel build
$ git push

The root command and subcommands can co-exist in a command line program and provide more flexible usage. For example, instead of specifying a specific logic for the root command, you can just use it as a prompt for the usage of a subcommand, such as git, or treat the root command as a shortcut to another subcommand, such as parcel and parcel dev.

There are no advantages or disadvantages of these two commands, just the need to choose according to the scenario.

In Mustard, we register root commands with @RootCommand decorator, and sub commands with @Command decorator.

import { RootCommand, Command } from "mustard-cli/decorator";
import type { CommandStruct } from "mustard-cli/cli";

@RootCommand()
class RootCommandHandle implements CommandStruct {
  public run() {}
}

@Command("update")
class UpdateCommand implements CommandStruct {
  public run() {}
}

Root command receives no args, as we cannot specify how it was invoked, but sub command receives configuration including invoke-name, invoke-alias and command-description.

Also, the command class should implement CommandStruct interface, which is a simple interface with only one method run(). This method will be invoked when the command got invoked.

Define Command Options

Another important part of the command line program is the handling of input and options:

bin this-is-input --this-is-option1 foo --this-is-option2 bar

In Mustard, we define input with @Input decorator, and options with @Option decorator.

import { Option, Input } from "mustard-cli/decorator";

@RootCommand()
class RootCommandHandle implements CommandStruct {
  @Input()
  public input = "default value of input";

  @Option("message")
  public msg = "default value of msg";

  public run(): void { }
}

By applying @Input decorator to a property, we can define an input for the command. The input will be parsed as a string and assigned to the property, and when there is no input provided, we can use the default value of property, so the same as @Option decorator.

If you're using variadic option like --projects p1 p2 p3, you will need @VariadicOption decorator to mark the property as variadic:

import { VariadicOption } from "mustard-cli/decorator";

@RootCommand()
class RootCommandHandle implements CommandStruct {
  @VariadicOption()
  public projects: string[] = [];

  public run(): void { }
}

Start Your App!

After completing the registration of commands and options, you can add these commands to your project and start your application:

import { MustardFactory } from "mustard-cli";
import { App } from "mustard-cli/decorator";
import type { MustardApp } from "mustard-cli/cli";

@App({
  name: "my-awesome-app",
  commands: [RootCommandHandle, UpdateCommand],
  configurations: { },
})
class Project implements MustardApp {
  onStart() {}

  onComplete() {}

  onError() {}
}

MustardFactory.init(Project).start();

When the start method is called, Mustard automatically distributes the corresponding command handler based on the info from the command line, injects the input and option information into the command handler.

You can also do some additional work during execution through the life cycle provided by the MustardApp interface.