Cliffy

Options

Options are defined with the .option() method and can be accessed as properties on the options object which is passed to the .action() handler and returned by the .parse() method.

With the first argument of the .options() method you define the option names and arguments. Each option can have multiple short and long flags, separated by comma. The name of the first long flag will be unused as an option name. If no long flag is provided the first short flag will be used.

Multi-word options such as --template-engine are camel-cased to templateEngine and multiple short flags may be combined as a single arg, for example -abc is equivalent to -a -b -c and -n5 is equivalent to -n 5 and -n=5.

The second parameter of the .options() method is the description and the third parameter can be an options object.

Arguments

An option can have multiple required and optional arguments, separated by space. Required values are declared using angle brackets <port> and optional values with square brackets [hostname]. Optionally you can define types and completions for the arguments of the option. If no type is specified the type defaults to string. If no argument is specified, the type defaults to true.

import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.3/command/mod.ts";

const { options } = await new Command()
  .option("-s, --silent", "disable output.")
  .option("-d, --debug [level]", "output extra debugging.")
  .option("-p, --port <port>", "the port number.")
  .option("-h, --host=[hostname]", "the host name.", { default: "localhost" })
  .parse(Deno.args);

console.log("server running at %s:%s", options.host, options.port);Copy
$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/options.ts -p 80
server running at localhost:80Copy
i

Note: There is a difference of defining option values without an equals sign like --foo <bar> and with an equals sign like --foo=<bar>.

  • If the option is defined without an equals sign, the option can be called with and without an equals sign.
  • If the option is defined with an equals sign, the option must be called with an equals sign as well.

The difference is, an option with an optional value which is defined with an equals sign can be used before an argument without the option value:

  • deno run --allow-env mod.ts
  • deno run --allow-env=FOO,BAR mod.ts

Variadic arguments

The last argument of an option can be variadic. To make an argument variadic you can append or prepend ... to the argument name. For example:

import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.3/command/mod.ts";

const { options } = await new Command()
  .version("0.1.0")
  .option("-d, --dir [otherDirs...:string]", "Variadic option.")
  .parse(Deno.args);

console.log(options);Copy

The variadic option is returned as an array.

$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/variadic_options.ts -d dir1 dir2 dir3
{ dir: [ "dir1", "dir2", "dir3" ] }Copy

Dotted options

Dotted options allows you to group your options together in nested objects. There is no limit for the level of nested objects.

import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.3/command/mod.ts";

const { options } = await new Command()
  .option(
    "-b.a, --bitrate.audio, --audio-bitrate <bitrate:number>",
    "Audio bitrate",
  )
  .option(
    "-b.v, --bitrate.video, --video-bitrate <bitrate:number>",
    "Video bitrate",
  )
  .parse();

console.log(options);Copy
$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/dotted_options.ts -b.a 300 -b.v 900
{ bitrate: { audio: 300, video: 900 } }

$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/dotted_options.ts --bitrate.audio 300 --bitrate.video 900
{ bitrate: { audio: 300, video: 900 } }

$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/dotted_options.ts --audio-bitrate 300 --video-bitrate 900
{ bitrate: { audio: 300, video: 900 } }Copy

Wildcard options

Wildcard options are options with wildcard names. A wildcard option allows any name matching the wildcard pattern. Wildcard options can be specified in following ways:

  • --*: Matches all options.
  • --foo.*: Matches options like --foo.bar but not --foo or --foo.bar.baz.
  • --foo.*.bar: Matches options like --foo.any-name.bar.
  • --foo.*.* Matches options like --foo.bar.baz but not --foo or --foo.bar.
i

The * means any name is allowed.

Default option value

You can specify a default value for an option with an optional value.

import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.3/command/mod.ts";

const { options } = await new Command()
  .option("-c, --cheese [type:string]", "add the specified type of cheese", {
    default: "blue",
  })
  .parse(Deno.args);

console.log(`cheese: ${options.cheese}`);Copy
$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/default_option_value.ts
cheese: blue

$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/default_option_value.ts --cheese mozzarella
cheese: mozzarellaCopy

Required options

You may specify a required (mandatory) option.

import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.3/command/mod.ts";

await new Command()
  .option("-c, --cheese [type:string]", "pizza must have cheese", {
    required: true,
  })
  .parse(Deno.args);Copy
$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/required_options.ts
Error: Missing required option "--cheese".Copy

Allow empty

If .allowEmpty() is called, the command will not throw an error if the command has a required option but no argument is passed to the command.

This can be used for example if you have required option but want to show the help by default if no arguments are passed to the command.

import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.3/command/mod.ts";

new Command()
  .option("--foo", "...", { required: true })
  .allowEmpty()
  .action(function ({ foo }) {
    if (!foo) {
      this.showHelp();
      return;
    }
    // Do something else...
  });Copy

Negateable options

You can specify a boolean option long name with a leading no- to set the option value to false when used. Defined alone this also makes the option true by default.

If you define --foo, adding --no-foo does not change the default value from what it would otherwise be.

You can specify a default value for a flag and it can be overridden on command line.

import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.3/command/mod.ts";

const { options } = await new Command()
  // default value will be automatically set to true if no --check option exists
  .option("--no-check", "No check.")
  .option("--color <color:string>", "Color name.", { default: "yellow" })
  .option("--no-color", "No color.")
  // no default value
  .option("--remote <url:string>", "Remote url.")
  .option("--no-remote", "No remote.")
  .parse(Deno.args);

console.log(options);Copy
$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/negatable_options.ts
{ check: true, color: "yellow" }

$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/negatable_options.ts --no-check --no-color --no-remote
{ check: false, color: false, remote: false }Copy

Global options

To share options with child commands you can use the .globalOption() method or the .option() method together with the global option.

import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.3/command/mod.ts";

await new Command()
  .option("-l, --local [val:string]", "Only available on this command.")
  .globalOption(
    "-g, --global [val:string]",
    "Available on this and all nested child commands.",
  )
  .action(console.log)
  .command(
    "command1",
    new Command()
      .description("Some sub command.")
      .action(console.log)
      .command(
        "command2",
        new Command()
          .description("Some nested sub command.")
          .action(console.log),
      ),
  )
  .parse(Deno.args);Copy

Global options can also be placed before a sub-command.

$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/global_options.ts -g test command1 command2
{ global: "test" }
$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/global_options.ts command1 -g test command2
{ global: "test" }
$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/global_options.ts command1 command2 -g test
{ global: "test" }Copy

Hidden options

To exclude options from the help and completion commands you can use the hidden option.

import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.3/command/mod.ts";

await new Command()
  .option("-H, --hidden [hidden:boolean]", "Nobody knows about me!", {
    hidden: true,
  })
  .parse(Deno.args);Copy
$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/hidden_options.ts -hCopy

Standalone options

Standalone options cannot be combine with any command and option. For example the --help and --version flag. You can achieve this with the standalone option.

import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.3/command/mod.ts";

await new Command()
  .option("-s, --standalone [value:boolean]", "Some standalone option.", {
    standalone: true,
  })
  .option("-o, --other [value:boolean]", "Some other option.")
  .parse(Deno.args);Copy
$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/standalone_options.ts --standalone --other
Error: Option --standalone cannot be combined with other options.Copy

Conflicting options

To define options which conflicts with other options you can use the conflicts option by defining an array with the names of these options.

import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.3/command/mod.ts";

const { options } = await new Command()
  .option("-f, --file <file:string>", "read from file ...")
  .option("-i, --stdin [stdin:boolean]", "read from stdin ...", {
    conflicts: ["file"],
  })
  .parse(Deno.args);

console.log(options);Copy
$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/conflicting_options.ts -f file1
{ file: "file1" }

$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/conflicting_options.ts -i
{ stdin: true }

$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/conflicting_options.ts -if file1
Error: Option --stdin conflicts with option: --fileCopy

Depending options

To define options which depends on other options you can use the depends option by defining an array with the names of these options.

import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.3/command/mod.ts";

const { options } = await new Command()
  .option("-u, --audio-codec <type:string>", "description ...")
  .option("-p, --video-codec <type:string>", "description ...", {
    depends: ["audio-codec"],
  })
  .parse(Deno.args);Copy
$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/depending_options.ts -a aac
{ audioCodec: "aac" }

$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/depending_options.ts -v x265
Error: Option "--video-codec" depends on option "--audio-codec".

$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/depending_options.ts -a aac -v x265
{ audioCodec: "aac", videoCodec: "x265" }Copy

Collect options

An option can occur multiple times in the command line to collect multiple values. Todo this, you have to activate the collect option.

import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.3/command/mod.ts";

const { options } = await new Command()
  .option("-c, --color <color:string>", "read from file ...", { collect: true })
  .parse(Deno.args);

console.log(options);Copy
$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/collect_options.ts --color yellow --color red --color blue
{ color: [ "yellow", "red", "blue" ] }Copy

Map option value

You may specify a function to do custom processing of option values. The callback function receives one parameter, the user specified value which is already parsed into the specified type. The return value of this method will be used as option value.

If collect is enabled the function receives as second parameter the previous value. This allows you to coerce the option value to the desired type, or accumulate values, or do entirely custom processing.

import {
  Command,
  ValidationError,
} from "https://deno.land/x/cliffy@v1.0.0-rc.3/command/mod.ts";

const { options } = await new Command()
  .option(
    "-o, --object <item:string>",
    "map string to object",
    (value: string): { value: string } => {
      return { value };
    },
  )
  .option("-C, --color <item:string>", "collect colors", {
    collect: true,
    value: (value: string, previous: Array<string> = []): Array<string> => {
      if (["blue", "yellow", "red"].indexOf(value) === -1) {
        throw new ValidationError(
          `Color must be one of "blue, yellow or red", but got "${value}".`,
          // optional you can set the exitCode which is used if .throwErrors()
          // is not called. Default is: 1
          { exitCode: 1 },
        );
      }
      previous.push(value);
      return previous;
    },
  })
  .parse(Deno.args);Copy
$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/custom_option_processing.ts --object a
{ object: { value: "a" } }

$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/custom_option_processing.ts --color blue \
                                                                                   --color yellow \
                                                                                   --color red
{ color: [ "blue", "yellow", "red" ] }Copy

Option action handler

Options can have an action handler same as commands. You can add an action handler with the action option.

i

Prior to v0.20.0, when an option action was executed, the command action was not executed. Since v0.20.0, this has changed. The command action is now executed by default. Only standalone options do not execute the command actions.

import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.3/command/mod.ts";

await new Command()
  .version("0.1.0")
  .option("--foo", "Foo option.", {
    action: () => {
      console.log("--foo action");
    },
  })
  .option("--bar", "Bar option.", {
    standalone: true,
    action: () => {
      console.log("--bar action");
    },
  })
  .option("--baz", "Baz option.", {
    action: () => {
      console.log("--baz action");
      Deno.exit(0);
    },
  })
  .action(() => console.log("main action"))
  .parse(Deno.args);

console.log("main context");Copy
$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/action_options.ts --foo
--foo action
main action
main context
$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/action_options.ts --bar
--bar action
main context
$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/action_options.ts --baz
--baz actionCopy

Grouped options

Options can be grouped with the .group() method to display them separately in the help text.

When the .group() method has been called, all options which are registered after the .group() method will be added to this group.

import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.3/command/mod.ts";

await new Command()
  .version("0.1.0")
  .description("Grouped options example.")
  .option("--foo", "Foo option.")
  .group("Other options")
  .option("--bar", "Bar option.")
  .option("--baz", "Baz option.")
  .group("Other options 2")
  .option("--beep", "Beep option.")
  .option("--boop", "Boop option.")
  .parse(Deno.args);Copy
$ deno run https://deno.land/x/cliffy@v1.0.0-rc.3/examples/command/grouped_options.ts --help

  Usage:   COMMAND
  Version: 0.1.0

  Description:

    Grouped options example.

  Options:

    -h, --help     - Show this help.
    -V, --version  - Show the version number for this program.
    --foo          - Foo option.

  Other options:

    --bar  - Bar option.
    --baz  - Baz option.

  Other options 2:

    --beep  - Beep option.
    --boop  - Boop option.Copy