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 .option() 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 used 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 .option() 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 "@cliffy/command";
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();
console.log("server running at %s:%s", options.host, options.port);
$ deno run https://cliffy.io/examples/v1.3.1/command/options.ts -p 80
server running at localhost:80
iNOTE
The equals sign only has an effect for options with an optional value. For options with a required value, the
=in the definition (and theequalsSignoption) have no effect, the option can always be called with or without an equals sign.For options with an optional value, there is a difference between defining the value 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 consuming it as the option value:
deno run --allow-env mod.tsdeno 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 "@cliffy/command";
const { options } = await new Command()
.version("0.1.0")
.option("-d, --dir [otherDirs...:string]", "Variadic option.")
.parse();
console.log(options);
The variadic option is returned as an array.
$ deno run https://cliffy.io/examples/v1.3.1/command/variadic_options.ts -d dir1 dir2 dir3
{ dir: [ "dir1", "dir2", "dir3" ] }
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 "@cliffy/command";
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);
$ deno run https://cliffy.io/examples/v1.3.1/command/dotted_options.ts -b.a 300 -b.v 900
{ bitrate: { audio: 300, video: 900 } }
$ deno run https://cliffy.io/examples/v1.3.1/command/dotted_options.ts --bitrate.audio 300 --bitrate.video 900
{ bitrate: { audio: 300, video: 900 } }
$ deno run https://cliffy.io/examples/v1.3.1/command/dotted_options.ts --audio-bitrate 300 --video-bitrate 900
{ bitrate: { audio: 300, video: 900 } }
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.barbut not--fooor--foo.bar.baz.--foo.*.bar: Matches options like--foo.any-name.bar.--foo.*.*Matches options like--foo.bar.bazbut not--fooor--foo.bar.
iNOTE
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 "@cliffy/command";
const { options } = await new Command()
.option("-c, --cheese [type:string]", "add the specified type of cheese", {
default: "blue",
defaultText: "some cheese",
})
.parse();
console.log(`cheese: ${options.cheese}`);
$ deno run https://cliffy.io/examples/v1.3.1/command/default_option_value.ts
cheese: blue
$ deno run https://cliffy.io/examples/v1.3.1/command/default_option_value.ts --cheese mozzarella
cheese: mozzarella
Default value display text
By default the default value is displayed in the help text. You can change the
display text in the help output with the defaultText option.
The defaultText option can be a string or a function which returns a string.
If the defaultText option is a function, the function receives the default
value as argument and the return value of the function is used as display text
in the help output.
$ deno run https://cliffy.io/examples/v1.3.1/command/default_option_value.ts --help
Usage: COMMAND
Options:
-h, --help - Show this help.
-c, --cheese [type] - add the specified type of cheese (Default: "some cheese")
Empty values
An empty value means the option was not provided. Both --cheese "" and
--cheese= are treated the same as leaving the option out, so the default value
applies. This is what you want when the value comes from a shell variable that
may be unset, as in --cheese "$CHEESE".
A required option has no such fallback, so an empty value is rejected with the same error as a missing one.
import { Command } from "@cliffy/command";
const { options } = await new Command()
.option("-c, --cheese <type:string>", "Type of cheese.", { default: "blue" })
.option("-n, --name <name:string>", "Your name.", { required: true })
.parse();
console.log(options);
$ deno run example.ts --name Tom --cheese ""
{ name: "Tom", cheese: "blue" }
$ deno run example.ts --name ""
Error: Missing value for option "--name".
An empty string can therefore not be passed as a value. To let the user turn an option off from the command line, declare a negatable option.
The same rule applies to command arguments.
Required options
You may specify a required (mandatory) option.
import { Command } from "@cliffy/command";
await new Command()
.option("-c, --cheese [type:string]", "pizza must have cheese", {
required: true,
})
.parse();
$ deno run https://cliffy.io/examples/v1.3.1/command/required_options.ts
Error: Missing required option "--cheese".
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 "@cliffy/command";
new Command()
.option("--foo", "...", { required: true })
.allowEmpty()
.action(function ({ foo }) {
if (!foo) {
this.showHelp();
return;
}
// Do something else...
});
Environment variables
With the env option an option falls back to an environment variable when the
flag is not used, so there is no need to register a matching environment
variable separately with the .env() method. The
value is read into the property of the option, with the precedence
flag > environment variable > default value.
truederives the name from the long flag.--install-rootbecomesINSTALL_ROOT.- A string sets the name explicitly.
{ prefix }prepends a prefix to the derived name.
import { Command } from "@cliffy/command";
await new Command()
.option("--cache <dir:string>", "Cache directory.", { env: true })
.option("--token <token:string>", "Auth token.", { env: "MY_TOKEN" })
.option("--install-root <path:string>", "Set install root.", {
env: { prefix: "DENO_" },
})
.option("--port <port:number>", "Port to listen on.", {
env: true,
default: 8080,
})
.action((options) => console.log(options))
.parse();
$ CACHE=/tmp/cache MY_TOKEN=secret DENO_INSTALL_ROOT=foo/bar PORT=3000 deno run --allow-env example.ts
{ cache: "/tmp/cache", token: "secret", installRoot: "foo/bar", port: 3000 }
$ PORT=3000 deno run --allow-env example.ts --port 9000
{ port: 9000 }
$ deno run --allow-env example.ts
{ port: 8080 }
The linked environment variable is listed in the environment variables section of the help and as a hint on the option itself:
--port <port> - Port to listen on. (Default: 8080, env: PORT)
A required option is satisfied by its environment variable, so no error is thrown when the flag is missing but the variable is set.
Negated environment variables
A negatable option registers a negated environment
variable and inverts its value, the same way the flag does. --no-check reads
NO_CHECK and stores the result in check.
import { Command } from "@cliffy/command";
await new Command()
.option("--no-check", "Disable type checking.", { env: true })
.action((options) => console.log(options))
.parse();
$ NO_CHECK=true deno run --allow-env example.ts
{ check: false }
$ deno run --allow-env example.ts
{ check: true }
Environment variable type
An option and its linked environment variable share a type. For a flag without a
value that type is boolean, which is wrong for a variable like NO_COLOR, where
any non-empty value counts and the value itself is irrelevant. It would be
parsed, so NO_COLOR=false would enable colors and NO_COLOR=yes would fail
with a type error. The presence type exists for
those.
The type option of env sets the type of the variable on its own.
import { Command } from "@cliffy/command";
await new Command()
.option("--no-color", "Disable colors.", { env: { type: "presence" } })
.action((options) => console.log(options))
.parse();
$ NO_COLOR=1 deno run --allow-env example.ts
{ color: false }
$ NO_COLOR=whatever deno run --allow-env example.ts
{ color: false }
$ deno run --allow-env example.ts
{ color: true }
The option itself is untouched, it still takes no value and the help shows no value hint for it.
$ deno run example.ts --no-color=true
error: Option "--no-color" doesn't take a value, but got "true".
Any registered type works, not only presence, and it can be combined with
prefix. When the type of the variable differs from the type of the option, the
value of the option becomes the union of both.
import { Command } from "@cliffy/command";
await new Command()
.option("--port <port:string>", "Port to listen on.", {
env: { type: "number" },
})
.action((options) => console.log(options))
.parse();
// options.port is of type `string | number | undefined`.
$ PORT=80 deno run --allow-env example.ts
{ port: 80 }
Restrictions
- An option without a long flag needs an explicit name, for example
.option("-f", "Force.", { env: "FORCE" }). - Dotted options are not supported. Register the environment
variable with the
.env()method instead.
Negatable 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.
This is also the way to clear a default value from the command line, since an
empty value is treated as not provided. A negated option
resolves to false, not to an empty string. Use .value()
if you need a different value.
You can specify a default value for a flag and it can be overridden on command line.
import { Command } from "@cliffy/command";
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();
console.log(options);
$ deno run https://cliffy.io/examples/v1.3.1/command/negatable_options.ts
{ check: true, color: "yellow" }
$ deno run https://cliffy.io/examples/v1.3.1/command/negatable_options.ts --no-check --no-color --no-remote
{ check: false, color: false, remote: false }
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 "@cliffy/command";
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();
Global options can also be placed before a sub-command.
$ deno run https://cliffy.io/examples/v1.3.1/command/global_options.ts -g test command1 command2
{ global: "test" }
$ deno run https://cliffy.io/examples/v1.3.1/command/global_options.ts command1 -g test command2
{ global: "test" }
$ deno run https://cliffy.io/examples/v1.3.1/command/global_options.ts command1 command2 -g test
{ global: "test" }
Global options and method chaining
A global option is registered on the command that is currently selected in the
chain, which is not always the main command. The .command() method selects the
new sub command, so a global option that is added after a .command() call
belongs to that sub command. It is shared with the child commands of that sub
command, but not with the main command or its sibling commands. See
reset for how command chaining works.
import { Command } from "@cliffy/command";
await new Command()
.name("my-cli")
.globalOption("-a, --alpha", "Available on all commands.")
.command(
"foo",
new Command()
.description("Foo command.")
.command("baz", "Baz command."),
)
// Registered on the foo command, not on the main command:
.globalOption("-b, --beta", "Available on foo and baz.")
.command("bar", "Bar command.")
.parse();
--alpha is available on every command, but --beta only on foo and its
child command baz:
$ my-cli foo baz --beta
$ my-cli bar --beta
error: Unknown option "--beta". Did you mean option "--help"?
To register a global option on the main command after a sub command was added,
use the .reset() method to select the main command again.
import { Command } from "@cliffy/command";
await new Command()
.name("my-cli")
.command("foo", "Foo command.")
.reset()
// Registered on the main command:
.globalOption("-b, --beta", "Available on all commands.")
.parse();
Conditional options
With the enabled option you can decide at runtime whether an option is
registered. It defaults to true. A disabled option is not added to the
command, so it is missing from the help output, is rejected as an unknown flag
and has no property on the options object.
import { Command } from "@cliffy/command";
const isWindows = Deno.build.os === "windows";
await new Command()
.option("-m, --mode <mode:string>", "File mode of the created file.", {
enabled: !isWindows,
})
.action((options) => console.log(options))
.parse();
$ deno run example.ts --mode 644
{ mode: "644" }
# On Windows:
$ deno run example.ts --mode 644
error: Unknown option "--mode". Did you mean option "--help"?
The type follows: the value of a conditional option is <type> | undefined,
because the option may not have been registered. This also applies to a
required option, which is otherwise never undefined.
import { Command } from "@cliffy/command";
const isWindows = Deno.build.os === "windows";
await new Command()
.option("-m, --mode <mode:string>", "File mode of the created file.", {
required: true,
enabled: !isWindows,
})
.action(({ mode }) => console.log("mode: %s", mode))
.parse();
Standalone options
Standalone options cannot be combined with any command and option. For example
the --help and --version flag. You can achieve this with the standalone
option.
import { Command } from "@cliffy/command";
await new Command()
.option("-s, --standalone [value:boolean]", "Some standalone option.", {
standalone: true,
})
.option("-o, --other [value:boolean]", "Some other option.")
.parse();
$ deno run https://cliffy.io/examples/v1.3.1/command/standalone_options.ts --standalone --other
Error: Option --standalone cannot be combined with other options.
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.
Conflicts are only checked against explicitly provided options (as command line
argument or environment variable). A conflicting option that only has its
default value does not trigger an error. For example, an option --table with
conflicts: ["json"] can be used even when a --json option defaults to
true, but fails when --json is provided explicitly.
import { Command } from "@cliffy/command";
const { options } = await new Command()
.option("-f, --file <file:string>", "read from file ...")
.option("-i, --stdin [stdin:boolean]", "read from stdin ...", {
conflicts: ["file"],
})
.parse();
console.log(options);
$ deno run https://cliffy.io/examples/v1.3.1/command/conflicting_options.ts -f file1
{ file: "file1" }
$ deno run https://cliffy.io/examples/v1.3.1/command/conflicting_options.ts -i
{ stdin: true }
$ deno run https://cliffy.io/examples/v1.3.1/command/conflicting_options.ts -if file1
Error: Option --stdin conflicts with option: --file
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 "@cliffy/command";
const { options } = await new Command()
.option("-u, --audio-codec <type:string>", "description ...")
.option("-p, --video-codec <type:string>", "description ...", {
depends: ["audio-codec"],
})
.parse();
$ deno run https://cliffy.io/examples/v1.3.1/command/depending_options.ts -a aac
{ audioCodec: "aac" }
$ deno run https://cliffy.io/examples/v1.3.1/command/depending_options.ts -v x265
Error: Option "--video-codec" depends on option "--audio-codec".
$ deno run https://cliffy.io/examples/v1.3.1/command/depending_options.ts -a aac -v x265
{ audioCodec: "aac", videoCodec: "x265" }
Collect options
An option can occur multiple times in the command line to collect multiple
values. To do this, you have to activate the collect option.
import { Command } from "@cliffy/command";
const { options } = await new Command()
.option("-c, --color <color:string>", "read from file ...", { collect: true })
.parse();
console.log(options);
$ deno run https://cliffy.io/examples/v1.3.1/command/collect_options.ts --color yellow --color red --color blue
{ color: [ "yellow", "red", "blue" ] }
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 "@cliffy/command";
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 run https://cliffy.io/examples/v1.3.1/command/custom_option_processing.ts --object a
{ object: { value: "a" } }
$ deno run https://cliffy.io/examples/v1.3.1/command/custom_option_processing.ts --color blue \
--color yellow \
--color red
{ color: [ "blue", "yellow", "red" ] }
Option action handler
Options can have an action handler same as commands. You can add an action
handler with the action option.
iNOTE
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 "@cliffy/command";
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();
console.log("main context");
$ deno run https://cliffy.io/examples/v1.3.1/command/action_options.ts --foo
--foo action
main action
main context
$ deno run https://cliffy.io/examples/v1.3.1/command/action_options.ts --bar
--bar action
main context
$ deno run https://cliffy.io/examples/v1.3.1/command/action_options.ts --baz
--baz action
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 "@cliffy/command";
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 run https://cliffy.io/examples/v1.3.1/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.