Cliffy

Command

The command module supports type safe options and arguments, input validation, auto generated help, built-in shell completions, and more.

Usage

To create a program with cliffy you can import the Command class from the main module @cliffy/command.

The Command class is used for main and sub-commands. The main command has two predefined options, a global help option (-h, --help) which is also available on all child commands and a version option (-V, --version) which is only available on the main command. All methods from the command class are chainable.

The main command

You should start with creating a main command and adding a name, version and description to it. It is required to define the name for your main command manually, because cliffy cannot know the name of the installed script.

import { Command } from "@cliffy/command";

await new Command()
  .name("cliffy")
  .version("0.1.0")
  .description("Command line framework for Deno")
  .parse(Deno.args);Copy

You can run this example and print the auto-generated help by executing the following command:

$ deno run examples/command/usage.ts --helpCopy

This shows you the default help if no additional options and arguments are defined.

Defining your command

You can than add options, environment variables, arguments and custom types to your command as many you want.

Environment variables will be merged into the options object. They have the same naming as options. For example the option --template-engine and the environment variable TEMPLATE_ENGINE will both renamed to templateEngine. If both are defined at the same time the option will override the value of the environment variable.

The action method allows you to define a callback function that will be called when the command is executed. Options and environment variables are passed as first argument to the action handler, followed by the command arguments in the same order they were defined with the arguments method.

i

❕ Cliffy infers all types and names from all option, argument and environment variable definitions automatically and applies them properly to the types of the options object and arguments array 🚀.

Here is an example of a simple command with some options, arguments, environment variables and types.

import { Command, EnumType } from "@cliffy/command";

const logLevelType = new EnumType(["debug", "info", "warn", "error"]);

await new Command()
  .name("cliffy")
  .version("0.1.0")
  .description("Command line framework for Deno")
  .type("log-level", logLevelType)
  .env("DEBUG=<enable:boolean>", "Enable debug output.")
  .option("-d, --debug", "Enable debug output.")
  .option("-l, --log-level <level:log-level>", "Set log level.", {
    default: "info",
  })
  .arguments("<input:string> [output:string]")
  .action((options, ...args) => {})
  .parse(Deno.args);Copy

The type of the options object will look like this:

type Options = {
  debug?: boolean | undefined;
  logLevel: "debug" | "info" | "warn" | "error";
};Copy

and the type of the arguments array will look like this:

type Arguments = [string, (string | undefined)?];Copy

Sub commands and globals

Sub commands can be added with the command method. You can add as many sub commands you want. There is no limit for the maximum number of sub and nested sub commands.

The .command() method always returns the newly created subcommand. All other methods return either the main command (if the .command() method has not been called yet) or the last created subcommand. This means that after calling the .command() method to add a new subcommand, options, arguments, etc. will be added to the newly created subcommand and no longer to the main command.

You can read more about sub commands here.

import { Command } from "@cliffy/command";

await new Command()
  // Main command.
  .name("cliffy")
  .version("0.1.0")
  .description("Command line framework for Deno")
  .globalOption("-d, --debug", "Enable debug output.")
  .action((options, ...args) => console.log("Main command called."))
  // Child command 1.
  .command("foo", "Foo sub-command.")
  .option("-f, --foo", "Foo option.")
  .arguments("<value:string>")
  .action((options, ...args) => console.log("Foo command called."))
  // Child command 2.
  .command("bar", "Bar sub-command.")
  .option("-b, --bar", "Bar option.")
  .arguments("<input:string> [output:string]")
  .action((options, ...args) => console.log("Bar command called."))
  .parse(Deno.args);Copy
i

To make types, options and environment variables also available on child commands you can use the .globalOption(), .globalEnv() and .globalType() methods.

The types of options and args for the 3 action handlers will look like this.

Main command:

type Options = {
  debug?: boolean | undefined;
  logLevel: "debug" | "info" | "warn" | "error";
};

type Args = [];Copy

Foo command:

type Options = {
  debug?: boolean | undefined;
  logLevel: "debug" | "info" | "warn" | "error";
  foo?: true | undefined;
};

type Args = [string];Copy

Bar command:

type Options = {
  debug?: boolean | undefined;
  logLevel: "debug" | "info" | "warn" | "error";
  bar?: true | undefined;
};

type Args = [string, (string | undefined)?];Copy