# Introduction to Cliffy Cliffy is a powerful and versatile library designed to help you create interactive command-line tools with ease. It offers a range of modules that simplify the process of building complex command-line interfaces and feature-rich CLI prompts. Cliffy is platform-independent and works seamlessly with [Deno](https://deno.com/), [Node.js](https://nodejs.org/en) and [bun](https://bun.sh/). ## Key Modules - **[command](https://cliffy.io/docs/v1.2.1/command/index.md)**: This module allows you to create sophisticated command-line interfaces effortlessly. It provides a robust framework for defining commands, options, and arguments, making it easy to build and manage CLI applications. - **[prompt](https://cliffy.io/docs/v1.2.1/prompt/index.md)**: The prompt module offers a variety of beautiful and highly customizable CLI prompts. It supports different types of prompts such as input, select, toggle, and more, enabling you to create interactive and user-friendly command-line experiences. ## Getting Started To get started with Cliffy, you can explore the [command](https://cliffy.io/docs/v1.2.1/command/index.md) and [prompt](https://cliffy.io/docs/v1.2.1/prompt/index.md) modules. These modules provide comprehensive documentation and examples to help you understand how to use Cliffy effectively. ## Version Information > Cliffy is exclusively published on [jsr.io](https://jsr.io/@cliffy) starting > from version > [v1.0.0-rc.5](https://github.com/c4spar/deno-cliffy/releases/tag/v1.0.0-rc.5). > The latest version available on deno.land is > [v1.0.0-rc.4](https://deno.land/x/cliffy@v1.0.0-rc.4). > > Please note that versions > [v1.0.0-rc.6](https://deno.land/x/cliffy@v1.0.0-rc.6) and > [v1.0.0-rc.7](https://deno.land/x/cliffy@v1.0.0-rc.7) published on deno.land > are not functional and should only be used from > [jsr.io](https://jsr.io/@cliffy). --- # Command The command module supports type safe options and arguments, input validation, auto generated help, built-in shell completions, and more. ## Installation ### Deno ```bash deno add jsr:@cliffy/command ``` ### Pnpm ```bash pnpm add jsr:@cliffy/command ``` or (using pnpm 10.8 or older): ```bash pnpm dlx jsr add @cliffy/command ``` ### Yarn ```bash yarn add jsr:@cliffy/command ``` or (using Yarn 4.8 or older): ```bash yarn dlx jsr add @cliffy/command ``` ### Vlt ```bash vlt install jsr:@cliffy/command ``` ### Npm ```bash npx jsr add @cliffy/command ``` ### Bun ```bash bunx jsr add @cliffy/command ``` ## 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. ```typescript import { Command } from "@cliffy/command"; await new Command() .name("cliffy") .version("0.1.0") .description("Command line framework for Deno") .parse(); ``` You can run this example and print the auto-generated help by executing the following command: ```console $ deno run examples/command/usage.ts --help ``` This shows you the default help if no additional options and arguments are defined. ![](https://cliffy.io/docs/v1.2.1/command/assets/img/usage.gif) ### Defining your command You can then 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. > [!NOTE] > 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. ```typescript 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 debug output.") .option("-d, --debug", "Enable debug output.") .option("-l, --log-level ", "Set log level.", { default: "info", }) .arguments(" [output:string]") .action((options, ...args) => {}) .parse(); ``` The type of the options object will look like this: ```ts type Options = { debug?: boolean | undefined; logLevel: "debug" | "info" | "warn" | "error"; }; ``` and the type of the arguments array will look like this: ```ts type Arguments = [string, (string | undefined)?]; ``` ### 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](https://cliffy.io/docs/v1.2.1/command/sub_commands.md). ```typescript 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("") .action((options, ...args) => console.log("Foo command called.")) // Child command 2. .command("bar", "Bar sub-command.") .option("-b, --bar", "Bar option.") .arguments(" [output:string]") .action((options, ...args) => console.log("Bar command called.")) .parse(); ``` > To make types, options and environment variables also available on child > commands you can use the [.globalOption()](https://cliffy.io/docs/v1.2.1/command/options.md#global-options), > [.globalEnv()](https://cliffy.io/docs/v1.2.1/command/environment_variables.md#global-environment-variables) and > [.globalType()](https://cliffy.io/docs/v1.2.1/command/types.md#global-types) methods. The types of `options` and `args` for the 3 action handlers will look like this. _Main command:_ ```ts type Options = { debug?: boolean | undefined; logLevel: "debug" | "info" | "warn" | "error"; }; type Args = []; ``` _Foo command:_ ```ts type Options = { debug?: boolean | undefined; logLevel: "debug" | "info" | "warn" | "error"; foo?: true | undefined; }; type Args = [string]; ``` _Bar command:_ ```ts type Options = { debug?: boolean | undefined; logLevel: "debug" | "info" | "warn" | "error"; bar?: true | undefined; }; type Args = [string, (string | undefined)?]; ``` --- # Commands The command class is used to create main and sub commands. All methods from the command class are chainable and return the current command instance. There are only two exceptions: - The `.command()` method returns the new created sub command, so you can add options, argument, environment variables and types to your sub commands in a chainable way. - The `.reset()` method returns the main command from your current command chain. ## Name With the `.name()` method you can define the name of your main command. The name should match the name of your program. It is displayed in the auto generated help and used for shell completions. ## Version With the `.version()` method you can define the version of your cli. The version is displayed in the auto generated help and the [version](https://cliffy.io/docs/v1.2.1/command/help.md#version-option) option. ## Description The `.description()` method adds a description for the command that will be displayed in the auto generated help. If the help option is called with the short flag `-h` only the first line is displayed. If called with the long name `--help`, the full description is displayed. For better multiline formatting, unnecessary indentations and empty leading and trailing lines will be automatically removed. For example, following description: ```ts import { Command } from "@cliffy/command"; new Command() .description(` This is a multiline description. The indentation of this line will be preserved. `); ``` is formatted as follows: ```console This is a multiline description. The indentation of this line will be preserved. ``` ## Usage With the `.usage()` method you can override the usage text that is displayed at the top of the auto generated help which defaults to the command arguments. The usage is always prefixed with the command name. ```ts import { Command } from "@cliffy/command"; await new Command() .name("script-runner") .description("Simple script runner.") .usage("[options] [script] [script options]") .parse(); ``` ## Arguments You can use the `.arguments()` and `.argument()` methods to specify the arguments for your commands. Angled brackets (e.g. ``) indicate required input and square brackets (e.g. `[optional]`) indicate optional input. A required input cannot be defined after an optional input. Arguments can be also defined with the `.command()` method. You can read more about the `.command()` method [here](#sub-commands). Optionally you can define [types](https://cliffy.io/docs/v1.2.1/command/types.md) and [completions](https://cliffy.io/docs/v1.2.1/command/shell_completions.md) for your arguments. If no type is specified the type defaults to `string`. ### `.arguments()` The arguments method accepts a string with space separated arguments. Optionally you can provide an array with descriptions for each argument. ```typescript import { Command } from "@cliffy/command"; const { args } = await new Command() .arguments(" [output:string]", [ "The input file.", "The output file.", ]) .parse(); ``` ```console $ deno run examples/command/arguments_syntax.ts Error: Missing argument(s): input ``` ### `.argument()` The argument method can be called multiple times to define arguments one by one. You can also define a default value and value handler for the argument. The value handler is a function that is called with the argument value and should return the processed value or a promise that resolves to the processed value. ```typescript import { Command } from "@cliffy/command"; const { args } = await new Command() .argument("", "The input file.") .argument("[output:string]", "The output file.", { default: "out.txt", value: (value: string) => Promise.resolve({ path: value }), }) .action((_, input, output) => { console.log("input:", input); // "input: " console.log("output:", output); // "output: { path: 'out.txt' }" }) .parse(); ``` ### Variadic arguments The last argument of a command can be variadic. To make an argument variadic you can append or prepend `...` to the argument name (`<...NAME>` or ``). Required rest arguments `<...args>` requires at least one argument, optional rest args `[...args]` are completely optional. ```typescript import { Command } from "@cliffy/command"; const { args: dirs } = await new Command() .description("Remove directories.") .arguments("") .action((_, ...rest) => console.log(`removing ${rest}`)) .parse(); for (const dir of dirs) { console.log("rmdir %s", dir); } ``` ```console $ deno run examples/command/variadic_arguments.ts dir1 dir2 dir3 removing dir1,dir2,dir3 rmdir dir1 rmdir dir2 rmdir dir3 ``` ## Option Options can be added with the `.option()` method. The first two arguments of the method are required. With the first argument you specify the flags and arguments of your option. The second argument is the description and with the third argument you can set some option specific settings. Optionally you can define [types](https://cliffy.io/docs/v1.2.1/command/types.md) and [completions](https://cliffy.io/docs/v1.2.1/command/shell_completions.md) 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`. You can read more about options [here](https://cliffy.io/docs/v1.2.1/command/options.md). ```typescript import { Command } from "@cliffy/command"; const { args } = await new Command() .option("-f, --file ", "Force option.") .parse(); ``` ## Action handler The action handler is called when the command is executed and can be registered with the `.action()` method. The first arguments is the options object, which contains all options and environment variables, followed by all arguments, in the same order in which they were defined with the `.arguments()` method. Options and arguments will be automatically typed by inferring the types and names of all options, arguments and environment variables 🚀. ```typescript import { Command } from "@cliffy/command"; await new Command() .name("rm") .description("Remove directory.") .option("-r, --recursive", "Remove directory recursively.") .arguments("") .action(({ recursive }, dir) => { console.log("remove " + dir + (recursive ? " recursively" : "")); }) .parse(); ``` ```console $ deno run examples/command/action_handler.ts rm dir remove dir ``` ## Global action handler A global action handler works similar to the normal action handler but is called when the command or any child command is executed and can be registered with the `.globalAction()` method. ## Use raw args When `.useRawArgs()` is called, all options and arguments are passed as raw arguments to the action handler without validation. ```typescript import { Command } from "@cliffy/command"; await new Command() .option("-f, --foo ", "Foo option.") .option("-b, --bar ", "Bar option.") .useRawArgs() // <-- enable raw args .action((options, ...args) => { console.log("options:", options); console.log("args:", args); }) .parse(); ``` ```console $ deno run examples/command/use_raw_args.ts --foo abc --bar xyz options: {} args: [ "--foo", "abc", "--bar", "xyz" ] ``` ## Stop early If enabled, all arguments starting from the first non option argument will be interpreted as raw argument. ```typescript import { Command } from "@cliffy/command"; await new Command() .option("-d, --debug-level ", "Debug level.") .arguments("[script] [...args]") .stopEarly() // <-- enable stop early .action((options, script?, ...args) => { console.log("options:", options); console.log("script:", script); console.log("args:", args); }) .parse(); ``` ```console $ deno run examples/command/stop_early.ts -d warning server -p 80 options: { debugLevel: "warning" } script: server args: [ "-p", "80" ] ``` ## Sub commands With the `.command()` method you can add and nest sub commands as many as you want. The first argument is the command name with optional arguments. The second argument is optional and can be either the description of the command or an instance of `Command`. You can read more about sub commands [here](https://cliffy.io/docs/v1.2.1/command/sub_commands.md). ```typescript import { Command } from "@cliffy/command"; await new Command() .command("foo [val:string]") .description("Foo command.") .action(() => console.log("Foo action.")) .command("bar [val:string]", "Foo command.") .action(() => console.log("Bar action.")) .parse(); ``` ## Global commands To share commands with sub commands you can use the `.global()` method. ```typescript import { Command } from "@cliffy/command"; await new Command() .command("global [val:string]", "global ...") .global() .action(console.log) .command( "command1", new Command() .description("Some sub command.") .command( "command2", new Command() .description("Some nested sub command."), ), ) .parse(); ``` ```console $ deno run examples/command/global_commands.ts command1 command2 global test {} test ``` ### Disable globals With the `.noGlobals()` method you can disable inheriting global _commands_, _options_ and _environment variables_ from parent commands. > The built-in `--help` option and the `help` command are excluded from the > `.noGlobals()` method. ```ts import { Command } from "@cliffy/command"; await new Command() .globalOption("--beep", "Beep...") .command("foo", "Foo...") .command("bar", "Bar...") // disable global --foo option and all other globals for command bar: .noGlobals() .parse(); ``` ## Hidden commands To exclude sub commands from the auto generated help and shell completions you can use the `.hidden()` method. ```typescript import { Command } from "@cliffy/command"; await new Command() .command("debug", "Some internal debugging command.") .hidden() .parse(); ``` ```console $ deno run examples/command/hidden_commands.ts -h ``` ## Command alias With the `.alias()` method you can define an alias for the command name. ```ts import { Command } from "@cliffy/command"; await new Command() .command("install") .alias("i") .action(() => console.log("Install...")) .parse(); ``` ## Parse The `.parse()` method processes all arguments, leaving any options and environment variables consumed by the command in the `options` object, all arguments in the `args` array and all arguments defined after the double dash (`--`) in the `literal` array. For all unknown or invalid options, arguments and types the command will throw an error and exit the program with `Deno.exit(1)`. Read more about error handling [here](https://cliffy.io/docs/v1.2.1/command/error_handling.md). The parse method accepts optionally as first argument an array of command-line arguments that should be consumed. By default `Deno.args`, `process.argv.slice(2)` or `Bun.argv.slice(2)` is used, depending on the runtime. ```ts import { Command } from "@cliffy/command"; const { args, options, literal, cmd } = await new Command() .env("DEBUG", "Enable debugging.") .option("--debug", "Enable debugging.") .arguments("") .parse(Deno.args); ``` All types and names for options, arguments and environment variables are automatically inferred and properly typed. If the command has sub commands, options and arguments will be of type `Record` and `Array`, because cliffy can not know which sub command was executed to provide the specific types. In this case use action handlers. --- # Sub commands Sub commands can be added using the `.command()` method. The first argument specifies the name and optionally arguments for your sub command. The arguments may be `` or `[optional]` and the last argument may also be variadic. The second argument of the `.command()` method is optional and can be either the command description or an instance of a `Command` class. The description can be also defined with the `.description()` method. > [!NOTE] > The `.command()` method returns the instance of the added command. If you call > the `.action()` method after the `.command()` method is called, the action > will be registered to the sub command and not to your main command. You can > use the `.reset()` method to get the instance of the parent command back. There are two ways to specify sub-commands with the `.command()` method which are explained in the following section. ## Chained commands Sub-command implemented using the `.command()` method with an action handler. ```typescript import { Command } from "@cliffy/command"; await new Command() .command( "clone [destination:string]", "Clone a repository into a newly created directory.", ) .option("-r, --recursive", "Clone recursive.") .action(({ recursive }, source, destination?) => { console.log( "clone %s to %s", source, destination, recursive ? " (recursive)" : "", ); }) .parse(); ``` ## Command instance The command method accepts as second argument an instance of a command. This way you can move your sub-commands into different files. ```typescript import { Command } from "@cliffy/command"; const clone = new Command() .arguments(" [destination:string]") .description("Clone a repository into a newly created directory.") .action((options, source, destination?) => { console.log("clone command called"); }); await new Command() .command("clone", clone) .parse(); ``` ## Command Literal Arguments For arguments with `--` the following can be used. ```typescript import { Command } from "@cliffy/command"; await new Command() .name("my-command") .arguments("[...args:string]") .option("--foo", "Foo option.") .action(function (options, ...args) { console.log("Options:", options); console.log("Arguments:", args); console.log("Literal arguments:", this.getLiteralArgs()); }) .parse(); ``` ```console $ my-command --foo bar -- --baz Options: { foo: true } Arguments: [ "bar" ] Literal arguments: [ "--baz" ] ``` --- # 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 `` and optional values with square brackets `[hostname]`. Optionally you can define [types](https://cliffy.io/docs/v1.2.1/command/types.md) and [completions](https://cliffy.io/docs/v1.2.1/command/shell_completions.md) 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`. ```typescript 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 ", "the port number.") .option("-h, --host=[hostname]", "the host name.", { default: "localhost" }) .parse(); console.log("server running at %s:%s", options.host, options.port); ``` ```console $ deno run examples/command/options.ts -p 80 server running at localhost:80 ``` > Note: The equals sign only has an effect for options with an **optional** > value. For options with a **required** value, the `=` in the definition (and > the [`equalsSign`](https://cliffy.io/docs/v1.2.1/flags/flag_options.md#equals-sign) option) 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.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: ```typescript 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. ```console $ deno run examples/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. ```typescript import { Command } from "@cliffy/command"; const { options } = await new Command() .option( "-b.a, --bitrate.audio, --audio-bitrate ", "Audio bitrate", ) .option( "-b.v, --bitrate.video, --video-bitrate ", "Video bitrate", ) .parse(); console.log(options); ``` ```console $ deno run examples/command/dotted_options.ts -b.a 300 -b.v 900 { bitrate: { audio: 300, video: 900 } } $ deno run examples/command/dotted_options.ts --bitrate.audio 300 --bitrate.video 900 { bitrate: { audio: 300, video: 900 } } $ deno run examples/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.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`. > The `*` means any name is allowed. ## Default option value You can specify a default value for an option with an optional value. ```typescript 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}`); ``` ```console $ deno run examples/command/default_option_value.ts cheese: blue $ deno run examples/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. ```console $ deno run examples/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") ``` ## Required options You may specify a required (mandatory) option. ```typescript import { Command } from "@cliffy/command"; await new Command() .option("-c, --cheese [type:string]", "pizza must have cheese", { required: true, }) .parse(); ``` ```console $ deno run examples/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. ```ts import { Command } from "@cliffy/command"; new Command() .option("--foo", "...", { required: true }) .allowEmpty() .action(function ({ foo }) { if (!foo) { this.showHelp(); return; } // Do something else... }); ``` ## 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. You can specify a default value for a flag and it can be overridden on command line. ```typescript 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 name.", { default: "yellow" }) .option("--no-color", "No color.") // no default value .option("--remote ", "Remote url.") .option("--no-remote", "No remote.") .parse(); console.log(options); ``` ```console $ deno run examples/command/negatable_options.ts { check: true, color: "yellow" } $ deno run examples/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. ```typescript 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. ```console $ deno run examples/command/global_options.ts -g test command1 command2 { global: "test" } $ deno run examples/command/global_options.ts command1 -g test command2 { global: "test" } $ deno run examples/command/global_options.ts command1 command2 -g test { global: "test" } ``` ## Hidden options To exclude options from the help and completion commands you can use the `hidden` option. ```typescript import { Command } from "@cliffy/command"; await new Command() .option("-H, --hidden [hidden:boolean]", "Nobody knows about me!", { hidden: true, }) .parse(); ``` ```console $ deno run examples/command/hidden_options.ts -h ``` ## 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. ```typescript 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(); ``` ```console $ deno run examples/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. ```typescript import { Command } from "@cliffy/command"; const { options } = await new Command() .option("-f, --file ", "read from file ...") .option("-i, --stdin [stdin:boolean]", "read from stdin ...", { conflicts: ["file"], }) .parse(); console.log(options); ``` ```console $ deno run examples/command/conflicting_options.ts -f file1 { file: "file1" } $ deno run examples/command/conflicting_options.ts -i { stdin: true } $ deno run examples/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. ```typescript import { Command } from "@cliffy/command"; const { options } = await new Command() .option("-u, --audio-codec ", "description ...") .option("-p, --video-codec ", "description ...", { depends: ["audio-codec"], }) .parse(); ``` ```console $ deno run examples/command/depending_options.ts -a aac { audioCodec: "aac" } $ deno run examples/command/depending_options.ts -v x265 Error: Option "--video-codec" depends on option "--audio-codec". $ deno run examples/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. ```typescript import { Command } from "@cliffy/command"; const { options } = await new Command() .option("-c, --color ", "read from file ...", { collect: true }) .parse(); console.log(options); ``` ```console $ deno run examples/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. ```typescript import { Command, ValidationError } from "@cliffy/command"; const { options } = await new Command() .option( "-o, --object ", "map string to object", (value: string): { value: string } => { return { value }; }, ) .option("-C, --color ", "collect colors", { collect: true, value: (value: string, previous: Array = []): Array => { 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(); ``` ```console $ deno run examples/command/custom_option_processing.ts --object a { object: { value: "a" } } $ deno run examples/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. > **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. ```typescript 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"); ``` ```console $ deno run examples/command/action_options.ts --foo --foo action main action main context $ deno run examples/command/action_options.ts --bar --bar action main context $ deno run examples/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. ```typescript 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(); ``` ```console $ deno run 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. ``` --- # Types Cliffy provides some default types which are used to validate user input, providing shell completions and adding info to the help text. It is also possible to register custom types. You can read more about custom types [here](#custom-types). Types can be declared after the argument name, separated by colon ``. If no type is specified, the type defaults to `string`. If an option with no arguments is defined the type defaults to `true`. ## Built-in types Following types are available by default on all commands. - **boolean:** Can be one of: `true`, `false`, `1` or `0`. - **string:** Can be any value. - **number:** Can be any numeric value. - **integer:** Can be any integer value. - **file:** Same as string but adds support for path completion. - **secret:** Same as string but hides the value in the help and shell completions. ```typescript import { Command } from "@cliffy/command"; const { options } = await new Command() // Env value must be always required. .env("DEBUG=", "Enable debugging.") // Option with no value. .option("-d, --debug", "Enable debugging.") // Option with optional boolean value. .option("-s, --small [small:boolean]", "Small pizza size.") // Option with required string value. .option("-p, --pizza-type ", "Flavour of pizza.") // Option with required number value. .option("-a, --amount ", "Pieces of pizza.") // Option that hides its default value. .option("-t, --token ", "Token.", { default: () => "SECRET" }) // One required and one optional command argument. .arguments(" [output:file]") .parse(); console.log(options); ``` ```console $ deno run examples/command/common_option_types.ts -p Error: Missing value for option "--pizza-type". $ deno run examples/command/common_option_types.ts -sp vegetarian --amount 3 { small: true, pizzaType: "vegetarian", amount: 3 } ``` ## Enum type The `EnumType` can be used to define a list of allowed values. The constructor accepts either an `Array` or an `enum`. The values are used for input validation and shell completions and displayed in the help text and types will be automatically inferred and applied to the values of the command options and arguments. ```typescript import { Command, EnumType } from "@cliffy/command"; const Animal = { Dog: "dog", Cat: "cat", } as const; type Animal = typeof Animal[keyof typeof Animal]; // Enum type with enum. const animal = new EnumType(Animal); // Enum type with array. const color = new EnumType(["blue", "yellow", "red"]); await new Command() .type("color", color) .type("animal", animal) .option( "-c, --color [name:color]", "Choose a color.", ) .option( "-a, --animal [name:animal]", "Choose an animal.", ) .action(({ color, animal }) => { console.log("color: %s", color); console.log("animal: %s", animal); }) .parse(); ``` ```console $ deno run examples/command/enum_option_type.ts --color red --animal dog color: red animal: dog $ deno run examples/command/enum_option_type.ts --color foo error: Option "--color" must be of type "color", but got "foo". Expected values: "blue", "yellow", "red" ``` ### Type inference with enums When using a TypeScript `enum`, pass the enum type explicitly as a type argument to preserve the enum type in inferred option and argument types: ```typescript import { Command, EnumType } from "@cliffy/command"; enum Animal { Dog = "dog", Cat = "cat", } // Without explicit type argument, the EnumType infers the decomposed union // `Animal.Dog | Animal.Cat` instead of `Animal`. const animal = new EnumType(Animal); await new Command() .type("animal", animal) .arguments("") .action((_options, name) => { // `name` is inferred as `Animal` (not `Animal.Dog | Animal.Cat`) const a: Animal = name; console.log("animal: %s", a); }) .parse(); ``` ## List types Each type can be used as a list. A list type accepts a `,` separated list of items with the specified type. The default separator is `,` but can be changed with the `separator` option. ```typescript import { Command } from "@cliffy/command"; const { options } = await new Command() // comma separated list .option("-l, --list ", "comma separated list of numbers.") // space separated list .option( "-o, --other-list ", "space separated list of strings.", { separator: " " }, ) .parse(); console.log(options); ``` ```console $ deno run examples/command/list_option_type.ts -l 1,2,3 { list: [ 1, 2, 3 ] } $ deno run examples/command/list_option_type.ts -o "1 2 3" { otherList: [ "1", "2", "3" ] } ``` ## Global types To make a type also available for child commands, you can use the `.globalType()` method. You can also make a type global with the `global` option in `.type()` method. ```typescript import { Command, EnumType } from "@cliffy/command"; await new Command() .globalType("color", new EnumType(["red", "blue", "yellow"])) .command("foo", "...") .option("-c, --color ", "Chose a color.") .action(console.log) .command("bar", "...") .option("-b, --background-color [name:color]", "Choose a background color.") .action(console.log) .parse(); ``` ```console $ deno run examples/command/global_custom_type.ts login --color "red" { color: "red" } ``` ## Custom types You can register custom types with the `.type()` method. The first argument is the name of the type, the second can be either a function or an instance of `Type` and the third argument can be an options object. ### Function types This example shows you how to use a function as type handler. ```typescript import { ArgumentValue, Command } from "@cliffy/command"; const colors = ["red", "blue", "yellow"]; function colorType({ label, name, value }: ArgumentValue): string { if (!colors.includes(value.toLowerCase())) { throw new Error( `${label} "${name}" must be a valid color, but got "${value}". Possible values are: ${ colors.join(", ") }`, ); } return value; } const { options } = await new Command() .type("color", colorType) .arguments("[color-name:color]") .option("-c, --color ", "...") .command("foo [color-name:color]", "...") .parse(); ``` ```console $ deno run examples/command/custom_option_type.ts -c "red" { color: "red" } $ deno run examples/command/custom_option_type.ts -c "green" Error: Option "--color" must be a valid color, but got "green". Possible values are: red, blue, yellow ``` ### Class types This example shows you how to create a custom type that extends the base `Type`. ```typescript import { ArgumentValue, Command, Type, ValidationError } from "@cliffy/command"; class ColorType extends Type { private readonly colors = ["red", "blue", "yellow"]; public parse({ label, name, value }: ArgumentValue): string { if (!this.colors.includes(value)) { throw new ValidationError( `${label} "${name}" must be a valid color, but got "${value}". Possible values are: ${ this.colors.join(", ") }`, ); } return value; } } const { options } = await new Command() .type("color", new ColorType()) .arguments("[color-name:color]") .option("-c, --color ", "...") .command("foo [color-name:color]", "...") .parse(); ``` ```console $ deno run examples/command/custom_option_type_class.ts -c "red" { color: "red" } $ deno run examples/command/custom_option_type_class.ts -c "green" Error: Option "--color" must be a valid color, but got "green". Possible values are: red, blue, yellow ``` The `ValidationError` ensures that the help is displayed before the program exits. You can read more about error handling [here](https://cliffy.io/docs/v1.2.1/command/error_handling.md). #### Shell completions You can also add shell completions to custom types by adding a `complete` method to your type. Read more about shell completions [here](https://cliffy.io/docs/v1.2.1/command/shell_completions.md#custom-type). ```ts import { ArgumentValue, Command, Type } from "@cliffy/command"; class ColorType extends Type { override complete(): Array { return ["red", "blue", "yellow"]; } parse(type: ArgumentValue): string { return type.value; } } ``` #### Override possible values in help text To override possible values listed in the auto generated help, you can add a `.values()` method to your custom type. ```ts import { ArgumentValue, Command, Type } from "@cliffy/command"; class ColorType extends Type { override values(): Array { return ["red", "blue", "yellow"]; } parse(type: ArgumentValue): string { return type.value; } } ``` --- # Environment variables > [!NOTE] > To allow deno to access environment variables the `--allow-env=` > flag is required. If the `--allow-env` flag is not provided, environment > variables will be ignored if not marked as required. Environment variables added with the `.env()` method will be validated when the command is executed. Only environment variables that are available for the executed command will be validated. Valid environment variables will be stored in the options object and for invalid or missing environment variables an error is thrown. They are also shown in the auto generated [help](https://cliffy.io/docs/v1.2.1/command/help.md). Environment variable names will be camel cased. For example `SOME_ENV_VAR=true` will be parsed to `{ someEnvVar: true }`. > [!NOTE] > If an option with the same name is defined, the option will override the > environment variable. ```typescript import { Command } from "@cliffy/command"; await new Command() .env("SOME_ENV_VAR=", "Description ...") .action((options) => console.log(options)) .parse(); ``` ```console $ SOME_ENV_VAR=abc deno run --allow-env=SOME_ENV_VAR examples/command/environment_variables.ts Error: Environment variable "SOME_ENV_VAR" must be of type "number", but got "abc". $ SOME_ENV_VAR=1 deno run --allow-env=SOME_ENV_VAR examples/command/environment_variables.ts { someEnvVar: 1 } ``` ## Global environment variables Global environment variables are also available on all sub commands. You can add global environment variables either with the `.env()` method and the `global` option or with the `.globalEnv()` method. ```ts import { Command } from "@cliffy/command"; await new Command() .env("SOME_ENV_VAR=", "Description ...", { global: true }) .globalEnv("SOME_OTHER_ENV_VAR=", "Description ...") .action((options) => console.log(options)) .command("hello", "world ...") .action((options) => console.log(options)) .parse(); ``` ## Required environment variables Required environment variables can be added with the `required` option. If a required environment variable is not defined on command line an error is thrown. ```ts import { Command } from "@cliffy/command"; await new Command() .env("SOME_ENV_VAR=", "Description ...", { required: true }) .action((options) => console.log(options)) .parse(); ``` ```console $ deno run examples/command/environment_variables.ts error: Missing required environment variable "SOME_ENV_VAR". $ SOME_ENV_VAR=abc deno run --allow-env=SOME_ENV_VAR examples/command/environment_variables.ts Error: Environment variable "SOME_ENV_VAR" must be of type "number", but got "abc". $ SOME_ENV_VAR=1 deno run --allow-env=SOME_ENV_VAR examples/command/environment_variables.ts { someEnvVar: 1 } ``` ## Hidden environment variables Hidden environment variables can be added with the `hidden` option and will be not displayed in the auto generated help. ```ts import { Command } from "@cliffy/command"; await new Command() .env("SOME_ENV_VAR=", "Description ...", { hidden: true }) .action((options) => console.log(options)) .parse(); ``` ## Prefix It is very common to prefix environment variables with a name like `DENO_DIR` and `DENO_INSTALL_ROOT`. With the `prefix` option you can ensure the prefix is removed before the value is added to the options object. This works also in combination with options. ```typescript import { Command } from "@cliffy/command"; await new Command() .env( "DENO_INSTALL_ROOT=", "Set install root.", { prefix: "DENO_" }, ) .option( "--install-root ", "Set install root.", ) .action((options) => console.log(options)) .parse(); ``` ```console $ DENO_INSTALL_ROOT=foo/bar deno run --allow-env=DENO_INSTALL_ROOT examples/command/environment_variables_prefix.ts { installRoot: "foo/bar" } ``` --- # Auto generated help The help information is auto-generated based on the information you have defined on your commands. The name, version, description, meta information, options, commands, environment variables and examples are displayed in the help. To display the help you can invoke the [help option](#help-option) (`-h` or `--help`) or the [help command](https://cliffy.io/docs/v1.2.1/command/built_in_commands.md#help-command) (`help`) on the main or on one of the sub commands. The `help` command needs to be registered manually. To retrieve the help text programmatically you can use the [.showHelp()](#print-help) and [.getHelp()](#get-help) methods. ```typescript import { Command } from "@cliffy/command"; await new Command() .name("help-option-and-command") .version("0.1.0") .description("Sample description ...") .env( "EXAMPLE_ENVIRONMENT_VARIABLE=", "Environment variable description ...", ) .example( "Some example", "Example content ...\n\nSome more example content ...", ) .parse(); ``` ```console $ deno run examples/command/help.ts --help ``` ![](https://cliffy.io/docs/v1.2.1/command/assets/img/help.gif) ## Print help You can use the `.showHelp()` method to output the help to stdout manually. Commands that have sub-commands but no action handler automatically print the help when called without arguments. This behaviour can be disabled with `.help({ auto: false })`. See [Auto help for container commands](#auto-help-for-container-commands). For example, to show the help programmatically inside an action handler, you can call `.showHelp()` directly: ```ts ignore import { Command } from "@cliffy/command"; const cmd = new Command() .name("git") .action(() => cmd.showHelp()) .command("pull", "Pull changes from remote repository.") .action(() => console.log("Pulling...")) .command("fetch", "Fetch changes from remote repository.") .action(() => console.log("Fetching...")); await cmd.parse(); ``` You can also use `this` to refer to the current command instance inside the action handler: ```ts import { Command } from "@cliffy/command"; await new Command() .name("git") .action(function () { this.showHelp(); }) .command("pull", "Pull changes from remote repository.") .action(() => console.log("Pulling...")) .command("fetch", "Fetch changes from remote repository.") .action(() => console.log("Fetching...")) .parse(); ``` ## Get help The `.getHelp()` method returns the auto generated help as string. ## Additional info You can add some additional information to the help text with the `.meta(name, value)` method. ```ts import { Command } from "@cliffy/command"; await new Command() .name("example") .version("1.0.0") .description("Example command.") .meta("deno", Deno.version.deno) .meta("v8", Deno.version.v8) .meta("typescript", Deno.version.typescript) .parse(); ``` The additional information is displayed below the command version in the auto generated help. ```console $ deno run example.ts --help Usage: example Version: 0.1.0 deno: 1.16.1 v8: 9.7.106.2 typescript: 4.4.2 Description: Example command. ``` ## Customize help The `.help()` method can be used to customize the auto generated help. The help output is fully responsive and adapts to the terminal width by default. ```typescript import { Command } from "@cliffy/command"; await new Command() .help({ // Show argument types. types: true, // default: false // Show hints. hints: true, // default: true // Enable/disable colors. colors: false, // default: true // Set the target width of the help output in columns. // Defaults to the terminal width (or 150 if not a TTY). width: 120, // Set the maximum width for the help output. maxWidth: 160, }) .option("-f, --foo [val:number]", "Some description.", { required: true, default: 2, }) .parse(); ``` ### Responsive help The help output is responsive by default: it reads the current terminal width and wraps text accordingly. The `width` option overrides the detected terminal width, and `maxWidth` caps it. ### Auto help for container commands By default, when a command has sub-commands but no action handler, calling it without any arguments automatically prints the help text. This is the `auto` behaviour, which is enabled by default. You can disable it by passing `{ auto: false }` to `.help()`. ```ts import { Command } from "@cliffy/command"; await new Command() .name("git") // Disable automatic help for container commands (help must be shown manually). .help({ auto: false }) .command("pull", "Pull changes from remote repository.") .action(() => console.log("Pulling...")) .command("fetch", "Fetch changes from remote repository.") .action(() => console.log("Fetching...")) .parse(); ``` ## Override help The `.help()` method can be also used to override the help output. This overrides the output of the `.getHelp()` and `.showHelp()` methods which are used by the help option and help command. The help handler will also be used for all sub commands, but can be overridden in each sub command separately. ```typescript import { Command } from "@cliffy/command"; await new Command() .help("My custom help") // Can be also a function. .help(() => "My custom help") .parse(); ``` ## Help option The `-h` and `--help` option flag prints the auto generated help to stdout. The short flag `-h` prints only the first line of each option and command description. With the long flag (`--help`) the full description is printed for each option and command. Optionally you can also register the pre-defined [help](https://cliffy.io/docs/v1.2.1/command/built_in_commands.md#help-command) command to display the help. ### Customize help option The help option is completely customizable with the `.helpOption()` method. It has the same arguments as the normal `.option()` method. With the first argument you specify the flags followed by the description. The third argument can be an action handler or an options object. The second and third arguments are optional. ```typescript import { Command } from "@cliffy/command"; await new Command() .helpOption("-i, --info", "Print help info.", function (this: Command) { console.log("some help info ...", this.getHelp()); }) .parse(); ``` You can also override the default options of the help option. The options are the same as for the `.option()` method. ```typescript import { Command } from "@cliffy/command"; await new Command() .helpOption(" -x, --xhelp", "Print help info.", { global: true }) .parse(); ``` To disable the help option you can pass false to the `.helpOption()` method. ```typescript import { Command } from "@cliffy/command"; await new Command() .helpOption(false) .parse(); ``` ## Version option The `--version` and `-V` option flag prints the version number defined with the `version()` method. The version number will also be displayed in the auto generated help. If the long `--version` option is used, the long format will be printed including command name and all meta data defined with the [.meta()](#additional-info) method. ```typescript import { Command } from "@cliffy/command"; await new Command() .version("0.1.0") .parse(); ``` ```console $ deno run examples/command/version_options.ts -V 0.0.1 $ deno run examples/command/version_options.ts --version 0.0.1 ``` ### Customize version option The version option is completely customizable with the `.versionOption()` method. It has the same arguments as the normal `.option()` method. With the first argument you specify the flags followed by the description. The third argument can be an action handler or an options object. The second and third arguments are optional. ```typescript import { Command } from "@cliffy/command"; await new Command() .version("0.1.0") .versionOption( " -x, --xversion", "Print version info.", function (this: Command) { console.log("Version: %s", this.getVersion()); }, ) .parse(); ``` You can also override the default options of the version option. The options are the same as for the `.option()` method. ```typescript import { Command } from "@cliffy/command"; await new Command() .version("0.1.0") .versionOption(" -x, --xversion", "Print version info.", { global: true }) .parse(); ``` The version option can be also disabled. ```typescript import { Command } from "@cliffy/command"; await new Command() .versionOption(false) .parse(); ``` ## Add examples You can add some examples for your command which will be displayed in the auto generated help. ```typescript import { red } from "@std/fmt/colors"; import { Command } from "@cliffy/command"; await new Command() .name("examples") .example( "example name", `Description ...\n\nCan have multiple lines and ${red("colors")}.`, ) .parse(); ``` ```console $ deno run examples/command/examples.ts help ``` ![](https://cliffy.io/docs/v1.2.1/command/assets/img/examples.gif) --- # Shell completion Cliffy supports shell completion out of the box. To enable shell completions it is required to register the [completions](https://cliffy.io/docs/v1.2.1/command/built_in_commands.md#completions-command) command. The completions command generates a shell completions script for your specific shell environment. Currently supported shells are: - [bash](https://cliffy.io/docs/v1.2.1/command/built_in_commands.md#bash-completions) - [fish](https://cliffy.io/docs/v1.2.1/command/built_in_commands.md#fish-completions) - [zsh](https://cliffy.io/docs/v1.2.1/command/built_in_commands.md#zsh-completions) The completions command enables completions for all sub-commands, options and arguments. ## Adding shell completions There are three ways to add shell completions to types which are explained in the following sections. ### Enum type One way is to use the `EnumType`. All values defined with the `EnumType` will be used for shell completions. Read more about the enum type [here](https://cliffy.io/docs/v1.2.1/command/types.md#enum-type). ```typescript import { Command, EnumType } from "@cliffy/command"; const colorType = new EnumType(["red", "blue", "yellow"]); await new Command() .type("color", colorType) .arguments("[color-name:color]") .option("-c, --color ", "Choose a color.") .parse(); ``` ### Complete method Another way to add completions is by registering a completions action with the `.complete()` or `.globalComplete()` method. The values returned by the callback method from the `.complete()` or `.globalComplete()` method will be used for shell completions. To use these completions you can add the name of the action after the type separated by colon. > [!NOTE] > Completions defined with the `.complete()` or `.globalComplete()` method will > override completions defined with the `.type()` or `.globalType()` method. ```typescript import { Command } from "@cliffy/command"; await new Command() .complete("color", () => ["red", "blue", "yellow"]) .arguments("[color-name:string:color]") .option("-c, --color ", "Choose a color.") .parse(); ``` ### Custom type The 3rd way to add shell completions is by creating a custom type with a `.complete()` method. The values returned by the `.complete()` method will be used for shell completions. You can read more about custom types [here](https://cliffy.io/docs/v1.2.1/command/types.md#custom-types). ```typescript import { Command, StringType } from "@cliffy/command"; class ColorType extends StringType { override complete(): Array { return ["red", "blue", "yellow"]; } } await new Command() .type("color", new ColorType()) .option("-c, --color ", "Choose a color.") .parse(); ``` ## Dynamically generating shell completions You can also dynamically generate the completions shell script. To do this you can call the `generateShellCompletions()` function. The function accepts the command instance and the shell name as arguments and returns the generated shell completions script as string. Optionally you can also pass a third argument to the `generateShellCompletions()` function. The third argument is an options object with the following properties: - `name`: The name of the binary. Default is the name of the command. ```ts ignore import { Command, generateShellCompletions } from "@cliffy/command"; const cmd = await new Command() .name("mycmd") .complete("color", () => ["red", "blue", "yellow"]) .arguments("[color-name:string:color]") .parse(); const bashCompletions = generateShellCompletions(cmd, "bash"); console.log(bashCompletions); ``` --- # Built-in commands Cliffy provides some predefined commands like `help`, `completions` and `upgrade`. These commands are optional and must be registered manually if you want to use them. ## Help command The `HelpCommand` prints the auto generated help. It is mostly the same as the `--help` option but it also accepts the name of a child command as optional argument to show the help of the given sub-command. To make the help command globally available for all child commands you can use the `.global()` method on the help command. ```typescript import { Command } from "@cliffy/command"; import { HelpCommand } from "@cliffy/command/help"; await new Command() .version("0.1.0") .description("Sample description ...") .env( "EXAMPLE_ENVIRONMENT_VARIABLE=", "Environment variable description ...", ) .command("help", new HelpCommand().global()) .parse(); ``` ```console $ deno run examples/command/help_option_and_command.ts help $ deno run examples/command/help_option_and_command.ts help completions $ deno run examples/command/help_option_and_command.ts completions help ``` ## Completions command The `CompletionsCommand` includes sub commands for all supported shell environments. The sub commands generate the shell completions script and outputs it to stdout. The completions command must be registered manually. ```ts import { Command } from "@cliffy/command"; import { CompletionsCommand } from "@cliffy/command/completions"; await new Command() .command("completions", new CompletionsCommand()) .parse(); ``` By calling ` completions `, the command will output the completions script for the specified shell to stdout. Each shell command provides also a `--name` option which allows you to override the command name: ` completions --name ` ### Bash Completions To add support for bash completions you can either register the `CompletionsCommand` or directly the `BashCompletionsCommand`. To enable bash completions add the following line to your `~/.bashrc`: ```shell source <(COMMAND completions bash) ``` > [!NOTE] > Replace `COMMAND` with the name of your cli. ### Fish Completions To add support for fish completions you can either register the `CompletionsCommand` or directly the `FishCompletionsCommand`. To enable fish completions add the following line to your `~/.config/fish/config.fish`: ```shell script source (COMMAND completions fish | psub) ``` > [!NOTE] > Replace `COMMAND` with the name of your cli. ### Zsh Completions To add support for zsh completions you can either register the `CompletionsCommand` or directly the `ZshCompletionsCommand`. To enable zsh completions add the following line to your `~/.zshrc`: ```shell script source <(COMMAND completions zsh) ``` or run following command to use **zsh fpath** completions: ```shell script COMMAND completions zsh > /path/to/zsh/site-functions/_COMMAND ``` > [!NOTE] > Replace `COMMAND` with the name of your CLI. ## Upgrade command The `UpgradeCommand` can be used to upgrade your cli to a given or latest version. If the `UpgradeCommand` is registered, a hint is shown in the help and the long version output if a new version is available. ```shell COMMAND upgrade --version 1.0.2 ``` ```typescript import { Command } from "@cliffy/command"; import { UpgradeCommand } from "@cliffy/command/upgrade"; import { DenoLandProvider } from "@cliffy/command/upgrade/provider/deno-land"; new Command() .command( "upgrade", new UpgradeCommand({ main: "cliffy.ts", args: ["--allow-net"], provider: new DenoLandProvider(), }), ); ``` With the `provider` option you specify which registries are supported. This option is required. The `main` option is the entry file of your cli. With the `name` option you can optionally define the name of your cli which defaults to the name of your main file (`[name].ts`). > ❗️ The name cannot have spaces! If you use spaces, you (or your users!) will > get an error when upgrading. If your cli needs some permissions, you can specify the permissions with the `args` option which are passed to `deno install`. > - When `args` is defined, `--force` and `--name` is set by default. > - When `args` is not defined, `--force`, `--name`, `--quiet` and `--no-check` > is set by default. ### Providers There are a few built-in providers: [jsr](https://jsr.io), [npm](https://www.npmjs.com/), [deno.land](https://deno.land/x), [nest.land](https://nest.land) and [github](https://github.com). If multiple providers are registered, you can specify the registry that should be used with the `--registry` option provided by the `UpgradeCommand`. The github provider can also be used to `upgrade` to any git branch. ```shell COMMAND upgrade --registry github --version main ``` The `--registry` option is hidden if only one provider is registered. If the `upgrade` command is called without the `--registry` option, the default registry is used. The default registry is the first registered provider. The package name defaults to the command name for all providers. If you want to use a different module name, you can override it with the `name` option. #### Package providers The `JsrProvider` and `NpmProvider` can be used if your cli is published as a package. The `scope` option is required for the `JsrProvider` and the `NpmProvider`. ```typescript import { Command } from "@cliffy/command"; import { UpgradeCommand } from "@cliffy/command/upgrade"; import { JsrProvider } from "@cliffy/command/upgrade/provider/jsr"; import { NpmProvider } from "@cliffy/command/upgrade/provider/npm"; new Command() .name("my-package") .command( "upgrade", new UpgradeCommand({ provider: [ new JsrProvider({ scope: "@my-scope" }), new NpmProvider({ scope: "@my-scope" }), ], }), ); ``` > [!NOTE] > When upgrading to `latest`, the `JsrProvider` and `NpmProvider` let the > runtime resolve the concrete version instead of pinning it themselves. This > way a configured minimum dependency age policy is respected > (`minimumDependencyAge` in Deno, `min-release-age` in npm/pnpm/bun): the > upgrade installs the newest version allowed by the policy rather than the > absolute latest. #### CDN providers The following providers can be used if your CLI is published to a CDN from which it can be imported from a URL. ```typescript import { Command } from "@cliffy/command"; import { UpgradeCommand } from "@cliffy/command/upgrade"; import { DenoLandProvider } from "@cliffy/command/upgrade/provider/deno-land"; import { GithubProvider } from "@cliffy/command/upgrade/provider/github"; import { NestLandProvider } from "@cliffy/command/upgrade/provider/nest-land"; new Command() .name("my-package") .command( "upgrade", new UpgradeCommand({ provider: [ new DenoLandProvider(), new NestLandProvider(), new GithubProvider({ repository: "c4spar/deno-cliffy" }), ], }), ); ``` ### List available versions The upgrade command can also be used to list all available versions with the `-l` or `--list-versions` option. The current installed version is highlighted and prefixed with a `*`. ```console $ COMMAND upgrade -l * v0.2.2 v0.2.1 v0.2.0 v0.1.0 ``` The github registry shows all available tags and branches. Branches can be disabled with the `branches` option `GithubProvider({ branches: false })`. If the versions list is larger than `25`, the versions are displayed as table. ```console $ COMMAND upgrade --registry github --list-versions Tags: v0.18.2 v0.17.0 v0.14.1 v0.11.2 v0.8.2 v0.6.1 v0.3.0 v0.18.1 * v0.16.0 v0.14.0 v0.11.1 v0.8.1 v0.6.0 v0.2.0 v0.18.0 v0.15.0 v0.13.0 v0.11.0 v0.8.0 v0.5.1 v0.1.0 v0.17.2 v0.14.3 v0.12.1 v0.10.0 v0.7.1 v0.5.0 v0.17.1 v0.14.2 v0.12.0 v0.9.0 v0.7.0 v0.4.0 Branches: main (Protected) keypress/add-keypress-module keycode/refactoring command/upgrade-command ``` --- # Error and exit handling Cliffy throws a `ValidationError` for invalid options, arguments and environment variables. `ValidationError`s can also be thrown manually. By default, when a `ValidationError` is thrown, cliffy prints the auto generated help and the error message and calls `Deno.exit(validationError.exitCode ?? 1)` to exit the program. This behaviour can be changed by calling [`.throwErrors()`](#throw-errors) or [`.noExit()`](#no-exit) or by adding an [error handler](#error-handler). ## Throw errors By default, cliffy prints the help text of the failed command together with the error message and calls `Deno.exit()` when a `ValidationError` is thrown. All other errors will be thrown by default. You can override this behaviour with the `.throwErrors()` method to always throw errors. ## No exit The `.noExit()` method does the same as `.throwErrors()` but also prevents the command from calling `Deno.exit()` for example when the help or version option is called. ## Error handler Errors can be caught by simply wrapping the `.parse()` method into a try catch block. But you can also register an error handler with the `.error()` method. The error handler is inherited by all nested sub-commands, at any depth. Child commands can override error handlers from ancestor commands, in which case the handler closest to the failed command is executed. If the error handler doesn't throw or call `Deno.exit`, the default error handler is executed. The first argument of the error handler is the error, the second argument is the instance of the failed command, and the third argument is an `ErrorContext` object. You can use the command instance to print the help text from this command. ```ts import { Command, ValidationError } from "@cliffy/command"; await new Command() .error((error, cmd) => { if (error instanceof ValidationError) { cmd.showHelp(); } console.error(error); Deno.exit(error instanceof ValidationError ? error.exitCode : 1); }) .action(() => { throw new ValidationError("validation error message."); }) .parse(); ``` ### ErrorContext The `ErrorContext` is the third argument of the error handler and contains the options and arguments that were parsed at the time the error occurred. This is useful when the error output should depend on the options the user passed — for example a global `--verbose` flag that switches between a slim and a detailed error message. ```ts import { Command, ValidationError } from "@cliffy/command"; await new Command() .globalOption("-v, --verbose", "Enable verbose error output.") .error((error, cmd, ctx) => { if (error instanceof ValidationError) { cmd.showHelp(); console.error(`\nerror: ${error.message}`); } else if (ctx.options.verbose) { // Detailed output: full stack trace when --verbose was passed. console.error(error); } else { // Slim output: just the message. console.error(`error: ${error.message}`); } Deno.exit(error instanceof ValidationError ? error.exitCode : 1); }) .command("run ", "Run a script.") .action((_, script) => { throw new Error(`Script not found: ${script}`); }) .parse(); ``` ```console $ deno run example.ts run missing.ts error: Script not found: missing.ts $ deno run example.ts --verbose run missing.ts Error: Script not found: missing.ts at ... ``` ## Runtime errors This example will catch only runtime errors. ```typescript import { Command } from "@cliffy/command"; const cmd = new Command() .option("-p, --pizza-type ", "Flavour of pizza.") .action(() => { throw new Error("Some error happened."); }); try { await cmd.parse(); } catch (error) { console.error("[CUSTOM_ERROR]", error); Deno.exit(1); } ``` ```console $ deno run examples/command/general_error_handling.ts -t Unknown option "-t". Did you mean option "-h"? $ deno run examples/command/general_error_handling.ts [CUSTOM_ERROR] Some error happened. ``` ## Validation errors This example will catch all errors. You can differentiate between runtime and validation errors by checking if the `error` is an instance of `ValidationError`. The validation error has a `exitCode` property that should be used to exit the program. It also provides a `cmd` property which references the failed command. ```typescript import { Command, ValidationError } from "@cliffy/command"; const cmd = new Command() .throwErrors() // <-- throw also validation errors. .option("-p, --pizza-type ", "Flavour of pizza.") .action(() => { throw new Error("Some error happened."); }); try { await cmd.parse(); } catch (error) { if (error instanceof ValidationError) { error.cmd?.showHelp(); console.error("Usage error: %s", error.message); Deno.exit(error.exitCode); } else { console.error("Runtime error: %s", error); Deno.exit(1); } } ``` ```console $ deno run examples/command/validation_error_handling.ts -t Usage error: Unknown option "-t". Did you mean option "-h"? ``` ### Custom validation errors You can throw custom validation errors by throwing an instance of `ValidationError`. Optionally you can define the exit code that should be used when `Deno.exit()` is called after displaying the auto generated help and the error message. ```typescript import { Command, ValidationError } from "@cliffy/command"; await new Command() .option("-c, --color ", "Choose a color.") .action(({ color }) => { if (color === "black") { throw new ValidationError("Black is not supported.", { exitCode: 1 }); } }) .parse(); ``` ## Throw errors outside the command context The `.throw()` method can be used to throw errors outside the command context so you have the same behaviour as when you throw an error for example within an action handler or a type. ```ts import { Command, ValidationError } from "@cliffy/command"; const { options, cmd } = await new Command() .error((_error, _cmd) => { console.error("error handler..."); // Throw or call Deno.exit() to disable the default error handler. // throw _error; // Deno.exit(_error instanceof ValidationError ? _error.exitCode : 1); }) .option("-r, --runtime-error", "Triggers a runtime error.") .option("-v, --validation-error", "Triggers a validation error.") .parse(); if (options.validationError) { cmd.throw(new ValidationError("validation error message.")); } if (options.runtimeError) { cmd.throw(new Error("runtime error message.")); } ``` ```console $ deno run examples/command/throw.ts --runtime-error error handler... error: Uncaught Error: runtime error message. cmd.throw(new Error("runtime error message.")); ^ at examples/command/throw.ts:21:13 ``` ```console $ deno run examples/command/throw.ts --validation-error error handler... Usage: COMMAND Options: -h, --help - Show this help. -r, --runtime-error - Triggers a runtime error. -v, --validation-error - Triggers a validation error. error: validation error message. ``` --- # Generic options and types Since `v0.21.0`, cliffy has strict types by default. All types, option and environment-variable names will be automatically magically inferred 🪄. > [!NOTE] > It is no longer recommended to define the types manually with the generic > parameters. The only exception where you define generics manually is when you want to organize your sub commands in separate files, then you can use the first two generic constructor parameters which are used to define required global options and types which is explained in [Generic parent types](#generic-parent-types). Another exception is, if you want to extend the command class to share it with other projects like cliffy it does with the `HelpCommand`. This is explained in [extending commands](#extending-commands). ## Generic parent types If you want to organize your sub commands into different files, you can define required global parent options and types in the constructor of the child command. The first parameter defines required global options and/or environment variables. The second parameter defines required global custom types. ```typescript import { Command, EnumType } from "@cliffy/command"; const colorType = new EnumType(["red", "blue"]); const fooCommand = new Command< { debug?: true }, { color: typeof colorType } >() .option("-b, --bar", "...") .option("-c, --color ", "...") .action((options) => { if (options.debug) { console.log("debug"); } if (options.bar) { console.log("bar"); } if (options.color) { console.log("color", options.color); } }); await new Command() .globalType("color", colorType) .globalOption("-d, --debug", "...") .command("foo", fooCommand) .parse(); ``` The types of the options object will look like this: ```ts type Options = { debug?: true | undefined; bar?: true | undefined; color?: "red" | "blue" | undefined; }; ``` ## Extending commands If you want to extend the `Command` class like cliffy it does with the `HelpCommand`. New instances of this command will have `void` types by default. In this case you can specify the types manually with the generic constructor parameters of the `Command` class. But this is completely optional. You can add `void` commands to any other command. > When you specify the types, it is recommended to return the command instance > in the constructor. This way you can ensure the constructor typings matches your command typings, because in typescript you can only return an instance in the constructor that is compatible to itself. ```ts import { Command } from "@cliffy/command"; class FooCommand extends Command< void, void, { foo?: string; bar?: number }, [string, string?] > { constructor() { super(); return this .name("foo") .description("Foo command.") .option("--foo ", "...") .option("--bar ", "...") .arguments(" [output:string]") .action(() => console.log("foo")); } } ``` --- # Others ## Did you mean Cliffy has built-in _did-you-mean_ support to improve the user and developer experience. For example, cliffy prints some suggestions, when the user executes an invalid command, or the developer has a typo in the name of a type. ```console $ deno run examples/command/demo.ts --barr error: Unknown option "--barr". Did you mean option "--bar"? ``` --- # Flags Command line arguments parser with built-in validations. ## Installation ### Deno ```bash deno add jsr:@cliffy/flags ``` ### Pnpm ```bash pnpm add jsr:@cliffy/flags ``` or (using pnpm 10.8 or older): ```bash pnpm dlx jsr add @cliffy/flags ``` ### Yarn ```bash yarn add jsr:@cliffy/flags ``` or (using Yarn 4.8 or older): ```bash yarn dlx jsr add @cliffy/flags ``` ### Vlt ```bash vlt install jsr:@cliffy/flags ``` ### Npm ```bash npx jsr add @cliffy/flags ``` ### Bun ```bash bunx jsr add @cliffy/flags ``` ## Usage The `parseFlags` method takes as its first argument the arguments to be parsed, usually `Deno.args`, or a [parse context](#parse-context). As the second argument you can pass an options object. A list of all available options can be found [here](https://cliffy.io/docs/v1.2.1/flags/parse_options.md). ### Basic usage If `parseFlags` is called without defining specific flags with the options object, all arguments are parsed and added to the flags object returned by the `parseFlags` method. All non-options arguments are added to the `unknown` array and all flags specified after the double dash (`--`) are added to the `literal` array. ```typescript import { parseFlags } from "@cliffy/flags"; console.log(parseFlags()); // or: console.log(parseFlags(Deno.args)); ``` ```console $ deno run examples/flags/flags.ts -a foo -b bar { flags: { a: "foo", b: "bar" }, literal: [], unknown: [], stopEarly: false, stopOnUnknown: false } $ deno run examples/flags/flags.ts \ -x 3 \ -y.z -n5 \ -abc \ --beep=boop \ foo bar baz \ --deno.land \ --deno.com -- --cliffy { flags: { x: "3", y: { z: true }, n: "5", a: true, b: true, c: true, beep: "boop", deno: { land: true, com: true } }, literal: [ "--cliffy" ], unknown: [ "foo", "bar", "baz" ], stopEarly: false, stopOnUnknown: false } ``` ### Define flags You can specify flags with the options object. For all unknown or invalid flags an `ValidationError` is thrown. Read more about error handling [here](https://cliffy.io/docs/v1.2.1/flags/error_handling.md). A list of all available flag options can be found [here](https://cliffy.io/docs/v1.2.1/flags/flag_options.md). ```typescript import { parseFlags } from "@cliffy/flags"; const { flags } = parseFlags(Deno.args, { flags: [{ name: "help", aliases: ["h"], standalone: true, }, { name: "verbose", aliases: ["v"], collect: true, value: (val: boolean, previous = 0) => val ? previous + 1 : 0, }, { name: "file", aliases: ["f"], type: "string", }], }); console.log(flags); ``` ```console $ deno run examples/flags/options.ts -vvv -f ./example.ts { verbose: 3, file: "./example.ts" } ``` ### Positional arguments The `args` option allows you to define typed positional arguments alongside flags. Positional arguments are collected into the `args` array in the returned context and removed from the `unknown` array. ```typescript import { parseFlags } from "@cliffy/flags"; const { flags, args } = parseFlags(["--count", "3", "file.txt"], { flags: [{ name: "count", type: "number" }], args: [{ name: "file", type: "string" }], }); console.log(flags); // { count: 3 } console.log(args); // [ "file.txt" ] ``` Positional arguments can be required, optional, or variadic — the same `ArgumentOptions` interface is used for both options and positional arguments. Positional arguments that have a leading dash (`-foo`) are allowed when the argument is explicitly defined via the `args` option. ```typescript import { parseFlags } from "@cliffy/flags"; const { flags, args } = parseFlags(["--foo", "bar", "a", "b", "c"], { flags: [{ name: "foo", type: "string" }], args: [{ name: "files", type: "string", variadic: true }], }); console.log(args); // [ "a", "b", "c" ] ``` ### Parse context The `parseFlags` method accepts also a parse context as first argument. The context can either be a manually created object or the result of a previously called `parseFlags` method. This can be used to parse command line flags in multiple steps, for example, when parsing options that precede a subcommand. ```ts import { parseFlags } from "@cliffy/flags"; const globalFlags = [{ name: "foo-global", alias: ["g"], collect: true, }]; const flags = [{ name: "foo", alias: ["f"], collect: true, }]; const args = ["--foo-global", "cmd1", "--foo-global", "--foo", "arg1", "--foo"]; // Parse main command args (all flags until the first unknown argument). const ctx = parseFlags(args, { flags: globalFlags, stopEarly: true, // Stop on first non option argument. stopOnUnknown: true, // Stop on first option argument. dotted: false, // Don't convert dotted option keys to nested objects. }); // Shift sub-command from arguments. const subCommand = ctx.unknown.shift(); // Parse all sub command args. parseFlags(ctx, { flags: [ ...globalFlags, ...flags, ], }); console.log("sub-command:", subCommand); // -> cmd1 console.log("options:", ctx.flags); // -> { fooGlobal: [ true, true ], foo: [ true, true ] } console.log("arguments:", ctx.unknown); // -> [ "arg1" ] ``` ### Parsed flags The parse context returned by `parseFlags` contains a `parsedFlags` property: a read-only array of the raw **flag** tokens in the order they were encountered, preserving the exact form used on the command line. Positional arguments are **not** included — only flag names and their values appear here. - **Space form** — flag and value as separate tokens: `command --port 80` → `['--port', '80']` - **Equals form** — flag and value as one token: `command --port=80` → `['--port=80']` ```typescript import { parseFlags } from "@cliffy/flags"; const { parsedFlags } = parseFlags([ "--port=80", "--host", "localhost", "file.txt", ], { flags: [ { name: "port", type: "number" }, { name: "host", type: "string" }, ], args: [{ name: "file", type: "string" }], }); console.log(parsedFlags); // [ "--port=80", "--host", "localhost" ] ``` --- # Parse options ## Flags By default the `parseFlags` method parses all flags and tries to autodetect the type. With the `flags` option you can specify an Array of flag options. If the `flags` option is set the `parseFlags` method will throw an error for all unknown or invalid flags. You can find a list of all possible flag options [here](https://cliffy.io/docs/v1.2.1/flags/flag_options.md). ## Args The `args` option accepts an array of `ArgumentOptions` objects that define the expected positional arguments. When defined, non-flag tokens are validated and typed as positional arguments instead of being added to the `unknown` array. A `Error` is thrown if more positional arguments are provided than defined. See [positional arguments](https://cliffy.io/docs/v1.2.1/flags/index.md#positional-arguments) for usage examples. ## Parse With the `parse` method you can add a custom handler for handling and parsing types. > [!NOTE] > The `parse` method will be called for all types, which means it overrides also > all built-in types! ```typescript import { ArgumentValue, parseFlags } from "@cliffy/flags"; parseFlags(Deno.args, { flags: [{ name: "foo", type: "float", }], parse: ({ label, name, value, type }: ArgumentValue) => { switch (type) { case "float": if (isNaN(Number(value))) { throw new Error( `${label} "${name}" must be of type "${type}", but got "${value}".`, ); } return parseFloat(value); default: throw new Error(`Unknown type "${type}".`); } }, }); ``` ```console $ deno run examples/flags/custom_option_processing.ts --foo 1.2 { flags: { foo: 1.2 }, unknown: [], literal: [] } $ deno run examples/flags/custom_option_processing.ts --foo abc error: Uncaught Error: Option "--foo" must be of type "float", but got "abc". ``` ## Option callback The `option` callback method is called for each parsed option. ## Stop early If `stopEarly` is enabled, all values starting from the first non option argument will be added to the `unknown` array (can be combined with [stopOnUnknown](#stop-on-unknown)). ## Stop on unknown If `stopOnUnknown` is enabled, all values starting from the first unknown option argument will be added to the `unknown` array (can be combined with [stopEarly](#stop-early)). ## Allow empty If a required option is specified, by default an error is thrown if the command is invoked without any flags. To disable this behavior you can set `allowEmpty` to `true`. The default is `false`. ## Dotted By default, all option names that have dots in their names are converted to nested objects. For example, `{ "foo.bar": 1 }` becomes `{ "foo": { "bar": 1 } }`. You can disable this behavior by setting the `dotted` option to `false`. This is required when parsing command line arguments in multiple steps (see [parse-context](https://cliffy.io/docs/v1.2.1/flags/index.md#parse-context)). --- # Flag options ## Name With the name you can specify the name of the flag. The name is also used as property name for the options object. ## Type With the `type` you specify the type of the argument. The type can be one of the following types or any custom types. - `boolean` - `string` - `number` - `integer` The `type` option is optional. If no type is specified the flag is a boolean option without a value. If `type` is set to `boolean` the flag can have a boolean value. ## Optional value By default the value is required for all options with an explicitly defined type. You can make the value optional by setting `optionalValue` to `true`. ## Variadic value You can make an argument variadic with the `variadic` option. A variadic value can occur multiple times and is collected in an array. ## List value If `list` is set to `true`, the argument will be split by `,` and the values will be stored in an array. You can override the separator with the `separator` option. ## Aliases You can specify some aliases for the flag with the `aliases` option. ## Standalone If `standalone` is set to `true`, this option cannot be combined with other options. If the `standalone` option is set to `true`, the option cannot be combined with other options. If additionally an option action handler is defined, only the option action handler is executed and not the command action handler. ## Default The `default` option specifies the default value for the flag when the flag is not specified on commandline. ## Required If `required` is set to `true` an error is thrown if the flag is not set on commandline. ## Depends You can specify flags which must be provided if a specific flag is set. If `depends` is set to `["foo", "bar"]`, an error is thrown if one of this flags are not provided on commandline. ## Conflicts This is the opposite of the `depends` option. You can specify flags which can not be provided if a specific flag is set. If `conflicts` is set to `["foo", "bar"]`, an error is thrown if one of this flags is provided on commandline. Conflicts are only checked against explicitly provided flags (as command line argument or environment variable). A conflicting flag that only has its default value does not trigger an error. ## Equals sign If `equalsSign` is set to `true` the option must be called with an equals sign `--foo=bar`. This only has an effect for options with an **optional** value. For options with a **required** value, `equalsSign` has no effect, the option can always be called with or without an equals sign. ## Collect values If `collect` is enabled, a flag can be specified multiple times on commandline. All values will be collected into an array. ```typescript import { parseFlags } from "@cliffy/flags"; const { flags } = parseFlags(Deno.args, { flags: [{ name: "color", type: "string", collect: true, }], }); console.log(flags); ``` ```console $ deno run examples/flags/collect.ts --color red --color blue { color: ["red", "blue"] } ``` ## Map values You may specify a function to do custom processing of flag 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 flag value. If [collect](#collect-values) 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. ```typescript import { parseFlags, ValidationError } from "@cliffy/flags"; const { flags } = parseFlags(Deno.args, { flags: [{ name: "color", type: "string", collect: true, value(value: string, previous: Array): Array | undefined { if (["foo", "bar", "baz"].includes(value)) { return [...previous, value]; } // if no value is returned, a default validation error will be thrown. // You can use the `ValidationError` to provide a custom error message. throw new ValidationError( `Option "--value" must be one of "foo", "bar" or "baz", but got "${value}".`, ); }, }], }); console.log(flags); ``` ```console $ deno run examples/flags/value.ts --value fooo error: Uncaught Error: Option "--value" must be one of "foo", "bar" or "baz", but got "fooo". $ deno run examples/flags/value.ts --value foo { value: ["foo"] } ``` ## Args The `args` array can be used if the flag has multiple values. Following options can be set for each argument: - [type](#type) - optional (see [optionalValue](#optional-value)) - [variadic](#variadic-value) - [list](#list-value) ## Ignore defaults The `ignoreDefaults` option can be used to ignore the default values from specific options. The `ignoreDefaults` option is an object. The keys have to match the option name but the value can be anything (values are ignored). --- # Error handling You can catch validation errors with the `ValidationError` class. A validation error is thrown when an invalid command is invoked by the user. ```typescript import { parseFlags, ValidationError } from "@cliffy/flags"; try { const flags = parseFlags(Deno.args, { flags: [{ name: "debug", }], }); console.log(flags); } catch (error) { // Flags validation error. if (error instanceof ValidationError) { console.log("[VALIDATION_ERROR] %s", error.message); Deno.exit(1); } // General error. throw error; } ``` ```console $ deno run examples/flags/error_handling.ts -d [VALIDATION_ERROR] Unknown option "-d". Did you mean option "--debug"? ``` --- # Prompt Cliffy's prompt module allows you to create simple yet powerful interactive prompts for your command-line applications. ## Installation ### Deno ```bash deno add jsr:@cliffy/prompt ``` ### Pnpm ```bash pnpm add jsr:@cliffy/prompt ``` or (using pnpm 10.8 or older): ```bash pnpm dlx jsr add @cliffy/prompt ``` ### Yarn ```bash yarn add jsr:@cliffy/prompt ``` or (using Yarn 4.8 or older): ```bash yarn dlx jsr add @cliffy/prompt ``` ### Vlt ```bash vlt install jsr:@cliffy/prompt ``` ### Npm ```bash npx jsr add @cliffy/prompt ``` ### Bun ```bash bunx jsr add @cliffy/prompt ``` ## Usage There are two ways of using this module. You can either use standalone (single) prompts or you can run a list of prompts that can be dynamically controlled. ### Single prompt Each prompt type is usable as standalone module and can be imported directly from the prompt specific module or from the main module. Each prompt has a static `.prompt()` method which accepts a prompt message or an options object and returns the prompt result. Execute a single prompt with a message as first argument. ```typescript import { Input } from "@cliffy/prompt"; const name = await Input.prompt(`What's your name?`); ``` Execute a single prompt with an options object as first argument. ```typescript import { Input } from "@cliffy/prompt"; const name = await Input.prompt({ message: `Choose a username`, minLength: 8, }); ``` ### Prompt list To execute a list of prompts you can use the `prompt()` method. The prompt method accepts an array of [prompt options](https://cliffy.io/docs/v1.2.1/prompt/types/index.md) combined with a `name` and `type` property. > [!NOTE] > Make sure to give each prompt a unique name to prevent overwriting values! Unlike npm's inquirer, the `type` property accepts a prompt object and not the name of the prompt. ```typescript import { Checkbox, Confirm, Input, Number, prompt } from "@cliffy/prompt"; const result = await prompt([{ name: "name", message: "What's your name?", type: Input, }, { name: "age", message: "How old are you?", type: Number, }, { name: "like", message: "Do you like animals?", type: Confirm, }, { name: "animals", message: "Select some animals", type: Checkbox, options: ["dog", "cat", "snake"], }]); console.log(result); ``` ```console $ deno run examples/prompt/prompt_list.ts ``` --- # Types ## Base Options All prompt types have the following base options. ### Prompt message With the `message` option you specify the prompt message to display. This option is required for all prompts. ### Default value You can set a default value with the `default` option. The type depends on the prompt type. ### Hide default value By default the default value is displayed in the prompt. To hide the default value, you can set the `hideDefault` option to `true`. ### Transform displayed value The `transform` callback option lets you transform the value to display. The method receives the user input and the returned value will be displayed. ### Validate value With the `validate` callback option you can validate the user input. It receives as first argument the sanitized user input. - If `true` is returned, the value is valid. - If `false` is returned, an error message is shown. - If a `string` is returned, the value will be used as error message. ### Info message With the `hint` option you can display an info message that is displayed below the prompt. ### Pointer icon With the `pointer` option the pointer icon can be changed. ```ts import { colors } from "@cliffy/ansi/colors"; import { Input } from "@cliffy/prompt/input"; const result = await Input.prompt({ message: "Say hallo!", pointer: colors.bold.brightBlue("-->"), }); console.log({ result }); ``` ### Prompt indentation With the `indent` option you can change the prompt indentation. Default is `" "`. ### OS signals > [!WARNING] > The [cbreak](https://deno.land/api@v1.31.1?s=Deno.SetRawOptions#prop_cbreak) > option works currently only on Linux and macOS and is not supported with Bun! The `cbreak` option enables pass-through of os signals to deno, allowing you to register your own signal handler. Read more about os signals [here](https://cliffy.io/docs/v1.2.1/prompt/os_signals.md). ### Prefix By default each prompt message is prefixed with `yellow("? ")`. The prefix can be changed with the `prefix` option. The value supports ansi color codes. To disable the prefix, set the `prefix` option to an empty string `""`. ### Reader With the `reader` option you can change the input stream which defaults to `Deno.stdin`. ### Writer With the `writer` option you can change the output stream which defaults to `Deno.stdout`. ### Keymap With the `keys` option you can assign custom key names to prompt actions. The available actions depend on the prompt type. All text input prompts (`Input`, `Number`, `List`, `Secret` and `Confirm`), and also the search input of the `Select` and `Checkbox` prompts, support readline-style cursor navigation and word editing with the following default key bindings: | Action | Keymap name | Default keys | | ----------------- | ----------------- | ------------------------- | | Move cursor left | `moveCursorLeft` | `left` | | Move cursor right | `moveCursorRight` | `right` | | Move word left | `moveWordLeft` | `alt+left`, `ctrl+left` | | Move word right | `moveWordRight` | `alt+right`, `ctrl+right` | | Delete char left | `deleteCharLeft` | `backspace` | | Delete char right | `deleteCharRight` | `delete` | | Delete word left | `deleteWordLeft` | `ctrl+w`, `alt+backspace` | | Delete word right | `deleteWordRight` | `alt+d` | ```ts import { Input } from "@cliffy/prompt/input"; const sentence = await Input.prompt({ message: "Enter a sentence", keys: { deleteWordLeft: ["ctrl+w", "ctrl+backspace"], }, }); console.log({ sentence }); ``` --- # Input The `Input` prompt is a simple text input with support for [auto suggestions](#auto-suggestions). ![](https://cliffy.io/docs/v1.2.1/prompt/assets/img/input.gif) The `Input` prompt can be imported from `prompt/mod.ts` or `prompt/input.ts`. ```typescript import { Input } from "@cliffy/prompt/input"; const name = await Input.prompt("What's your github user name?"); ``` ```console $ deno run examples/prompt/input.ts ``` ## Options The `Input` prompt implements all [base](https://cliffy.io/docs/v1.2.1/prompt/types/index.md) and [auto suggestion](https://cliffy.io/docs/v1.2.1/prompt/auto_suggestions.md) options and the following prompt specific options. ### Min input length The `minLength` option specifies the minimum length of the input value. Default is `0`. ### Max input length The `maxLength` option specifies the maximum length of the input value. Default is `infinity`. ### List pointer With the `listPointer` you specify the list pointer icon. Default is `❯`. ### Display usage info The `info` option enables the info bar which displays some usage information. ### Auto suggestions Tab-completions can be enabled with the `suggestions` and/or `id` option. If an `id` is provided, the value will be saved to the local storage using the `id` as local storage key. With `suggestions` you can provide some default suggestions. Both options can be defined at the same time. You can read more about auto suggestions [here](https://cliffy.io/docs/v1.2.1/prompt/auto_suggestions.md). > [!NOTE] > The `id` option requires deno >= `1.10` and the `--location` flag. Since deno > `1.16.0` the `--location` flag is optional. ```typescript import { Input } from "@cliffy/prompt/input"; const color = await Input.prompt({ message: "Choose a color", id: "", suggestions: [ "Abbey", "Absolute Zero", "Acadia", "Acapulco", "Acid Green", "Aero", "Aero Blue", "Affair", "African Violet", "Air Force Blue", ], }); console.log({ color }); ``` ```console $ deno run examples/prompt/suggestions_list_prompt.ts ``` ![](https://cliffy.io/docs/v1.2.1/prompt/assets/img/suggestions.gif) --- # Number The `Number` prompt is a simple number input with support for [auto suggestions](#auto-suggestions). ![](https://cliffy.io/docs/v1.2.1/prompt/assets/img/number.gif) ```typescript import { Number } from "@cliffy/prompt/number"; const age = await Number.prompt("How old are you?"); ``` ```console $ deno run examples/prompt/number.ts ``` ## Options The `Number` prompt implements all [base](https://cliffy.io/docs/v1.2.1/prompt/types/index.md) and [auto suggestion](https://cliffy.io/docs/v1.2.1/prompt/auto_suggestions.md) options and the following prompt specific options. ### Min input value The `min` option specifies the minimum input value. Defaults to `-Infinity`. ### Max input value The `max` option specifies the maximum input value. Defaults to `Infinity`. ### Decimal value You can allow floating point inputs with the `float` option which defaults to `false`. The `round` option specifies the number of decimals to round. Defaults to `2`. ### List pointer With the `listPointer` you specify the list pointer icon. Default is `❯`. ### Display usage info The `info` option enables the info bar which displays some usage information. ### Auto suggestions Tab-completions can be enabled with the `suggestions` and/or `id` option. If an `id` is provided, the value will be saved to the local storage using the `id` as local storage key. With `suggestions` you can provide some default suggestions. Both options can be defined at the same time. You can read more about auto suggestions [here](https://cliffy.io/docs/v1.2.1/prompt/auto_suggestions.md). > [!NOTE] > The `id` option requires deno >= `1.10` and the `--location` flag. Since deno > `1.16.0` the `--location` flag is optional. ```typescript import { Number } from "@cliffy/prompt/number"; const color = await Number.prompt({ message: "Choose a number", id: "", suggestions: [ "111", "222", "333", ], }); console.log({ color }); ``` --- # Secret The `Secret` prompt is a hidden text input which doesn't display the input value. ![](https://cliffy.io/docs/v1.2.1/prompt/assets/img/secret.gif) ```typescript import { Secret } from "@cliffy/prompt/secret"; const password = await Secret.prompt("Enter your password"); ``` ```console $ deno run examples/prompt/secret.ts ``` ## Options The `Secret` prompt has all [base options](https://cliffy.io/docs/v1.2.1/prompt/types/index.md) and the following prompt specific options. ### Secret name You can change the displayed name of secret with the `label` option. Default is `Password`. ### Min secret length The `minLength` option specifies the minimum length of the input value. Default is `0`. ### Max secret length The `maxLength` option specifies the maximum length of the input value. Default is `infinity`. ### Hide input value By default a `*` is shown for each user input. If the `hidden` option is enabled the user input is hidden during typing and a fixed number of `*` characters is shown on success. --- # Confirm The `Confirm` prompt is a simple yes or no prompt. ![](https://cliffy.io/docs/v1.2.1/prompt/assets/img/confirm.gif) ```typescript import { Confirm } from "@cliffy/prompt/confirm"; const confirmed = await Confirm.prompt("Can you confirm?"); ``` ```console $ deno run examples/prompt/confirm.ts ``` ## Options The `Confirm` prompt has all [base options](https://cliffy.io/docs/v1.2.1/prompt/types/index.md) and the following prompt specific options. ### Active label The text for the active state can be changed with the `active` option. Defaults to `'Yes'`. ### Inactive label The text for the inactive state can be changed with the `inactive` option. Defaults to `'No'`. --- # Toggle The `Toggle` prompt is a simple yes or no switch. ![](https://cliffy.io/docs/v1.2.1/prompt/assets/img/toggle.gif) ```typescript import { Toggle } from "@cliffy/prompt/toggle"; const confirmed = await Toggle.prompt("Can you confirm?"); ``` ```console $ deno run examples/prompt/toggle.ts ``` ## Options The `Toggle` prompt has all [base options](https://cliffy.io/docs/v1.2.1/prompt/types/index.md) and the following prompt specific options. ### Active label The text for the active state can be changed with the `active` option. Defaults to `'Yes'`. ### Inactive label The text for the inactive state can be changed with the `inactive` option. Defaults to `'No'`. --- # List The `List` prompt is a text input that lets you input multiple values with support for [auto suggestions](#auto-suggestions). ![](https://cliffy.io/docs/v1.2.1/prompt/assets/img/list.gif) ```typescript import { List } from "@cliffy/prompt/list"; const keywords = await List.prompt("Enter some keywords"); ``` ```console $ deno run examples/prompt/list.ts ``` ## Options The `List` prompt implements all [base](https://cliffy.io/docs/v1.2.1/prompt/types/index.md) and [auto suggestion](https://cliffy.io/docs/v1.2.1/prompt/auto_suggestions.md) options and the following prompt specific options. ### Tag separator With the `separator` option you specify the delimiter that is used for separating the tags. Default is `,`. ### Min input length The `minLength` option specifies the minimum length of a tag. Default is `0`. ### Max input length The `maxLength` option specifies the maximum length of a tag. Default is `infinity`. ### Min tags The `minTags` option specifies the minimum amount of tags. Default is `0`. ### Max tags The `maxTags` option specifies the maximum amount of tags. Default is `infinity`. ### List pointer With the `listPointer` you specify the list pointer icon. Default is `❯`. ### Display usage info The `info` option enables the info bar which displays some usage information. ### Auto suggestions Tab-completions can be enabled with the `suggestions` and/or `id` option. If an `id` is provided, the value will be saved to the local storage using the `id` as local storage key. With `suggestions` you can provide some default suggestions. Both options can be defined at the same time. You can read more about auto suggestions [here](https://cliffy.io/docs/v1.2.1/prompt/auto_suggestions.md). > [!NOTE] > The `id` option requires deno >= `1.10` and the `--location` flag. Since deno > `1.16.0` the `--location` flag is optional. ```typescript import { List } from "@cliffy/prompt/list"; const color = await List.prompt({ message: "Choose a color", id: "", suggestions: [ "Abbey", "Absolute Zero", "Acadia", "Acapulco", "Acid Green", "Aero", "Aero Blue", "Affair", "African Violet", "Air Force Blue", ], }); console.log({ color }); ``` ```console $ deno run examples/prompt/suggestions_list_prompt.ts ``` ![](https://cliffy.io/docs/v1.2.1/prompt/assets/img/suggestions_list_prompt.gif) --- # Select The `Select` prompt lets you select a option from an options list. ![](https://cliffy.io/docs/v1.2.1/prompt/assets/img/select.gif) ```typescript import { Select } from "@cliffy/prompt/select"; const color = await Select.prompt({ message: "Pick a color", options: [ { name: "Red", value: "#ff0000" }, { name: "Green", value: "#00ff00", disabled: true }, { name: "Blue", value: "#0000ff" }, Select.separator("--------"), { name: "White", value: "#ffffff" }, { name: "Black", value: "#000000" }, ], }); ``` ```console $ deno run examples/prompt/select.ts ``` ## Options The `Select` prompt has all [base options](https://cliffy.io/docs/v1.2.1/prompt/types/index.md) and the following prompt specific options. ### Select options With the `options` option you specify an array of options. An option can be either a string or an options object. Options can also be nested, see [child options](#child-options). #### Select option ##### Option value Value which will be returned as result. ##### Option name Name is displayed in the list. Defaults to `value`. ##### Disable option Disabled item. Can't be selected. ### Max rows The `maxRows` option specifies the number of options displayed per page. Defaults to `10`. ### List pointer With the `listPointer` you specify the list pointer icon. Default is `❯`. ### Enable search input You can enable a search/filter input with the `search` option. The `search` option is useful if you have a large list of options. You can change the search input label with the `searchLabel` option. The `searchMode` option controls how options are matched, ranked and highlighted while searching. Defaults to `"all"`. - `"substring"`: classic contiguous substring match. - `"fuzzy"`: match the typed characters in order, allowing gaps (e.g. `strubu` matches `structure-builder`). - `"typo"`: tolerate misspellings via edit distance (e.g. `stroberry` matches `strawberry`), without fuzzy subsequence matching. - `"all"`: combines `"substring"`, `"fuzzy"` and `"typo"`. > [!NOTE] > While searching, the single-letter navigation keys (`j`, `k`, `n`, `p`, etc.) > are typed into the search input instead of moving the selection. Use the arrow > keys, or the readline shortcuts `ctrl+n` (next) and `ctrl+p` (previous), to > move through the list without leaving the search input. ### Display usage info The `info` option enables the info bar which displays some usage information. ### Child options The `options` option allows you to group options together. It accepts an array of child options, of which you can nest as many as you want. ```ts import { Select } from "@cliffy/prompt/select"; const title = await Select.prompt({ message: "Pick a book", search: true, options: [ { name: "Harry Potter", options: [ "Harry Potter and the Philosopher's Stone", "Harry Potter and the Chamber of Secrets", "Harry Potter and the Prisoner of Azkaban", "Harry Potter and the Goblet of Fire", "Harry Potter and the Order of the Phoenix", "Harry Potter and the Half-Blood Prince", "Harry Potter and the Deathly Hallows", ], }, { name: "Middle-Earth", options: [ "The Hobbit", { name: "The Lord of the Rings", options: [ "The Fellowship of the Ring", "The Two Towers", "The Return of the King", ], }, "Silmarillion", ], }, ], }); console.log({ title }); ``` #### Max breadcrumb items The `maxBreadcrumbItems` option limits the maximum number of breadcrumb items which will be displayed. #### Breadcrumb separator With the `breadcrumbSeparator` option the breadcrumb separator can be changed. #### Back pointer With the `backPointer` option you can change the icon of the _back_ option to any string value. #### Group pointer With the `groupPointer` option you can change the pointer of _group_ options to any string value. #### Group icon With the `groupIcon` option you can change the icon of _group_ options to any string value. #### Group open icon With the `groupOpenIcon` option you can change the icon of _opened group_ options to any string value. --- # Checkbox The `Checkbox` prompt is a multi select prompt. ![](https://cliffy.io/docs/v1.2.1/prompt/assets/img/checkbox.gif) ```typescript import { Checkbox } from "@cliffy/prompt/checkbox"; const colors = await Checkbox.prompt({ message: "Pick a color", options: [ { name: "Red", value: "#ff0000" }, { name: "Green", value: "#00ff00", disabled: true }, { name: "Blue", value: "#0000ff" }, Checkbox.separator("--------"), { name: "White", value: "#ffffff" }, { name: "Black", value: "#000000" }, ], }); ``` ```console $ deno run examples/prompt/checkbox.ts ``` ## Options The `Checkbox` prompt has all [base options](https://cliffy.io/docs/v1.2.1/prompt/types/index.md) and the following prompt specific options. ### Checkbox options With the `options` option you specify an array of options. An option can be either a string or an options object. Options can also be nested, see [options](#child-options). #### Checkbox option ##### Option value Value which will be returned as result. ##### Option name Name is displayed in the list. Defaults to `value`. ##### Disable option Disabled item. Can't be selected. ##### Checked option Whether item is checked or not. Defaults to `false`. ##### Option icon Show or hide item icon. Defaults to `true`. ### Min options The `minOptions` option specifies the minimum amount of selectable options. Default is `0`. ### Max options The `maxOptions` option specifies the maximum amount of selectable options. Default is `infinity`. ### Max rows The `maxRows` option specifies the number of options displayed per page. Defaults to `10`. ### List pointer With the `listPointer` you specify the list pointer icon. Default is `❯`. ### Enable search input You can enable a search/filter input with the `search` option. The `search` option is useful if you have a large list of options. You can change the search input label with the `searchLabel` option. The `searchMode` option controls how options are matched, ranked and highlighted while searching. Defaults to `"all"`. - `"substring"`: classic contiguous substring match. - `"fuzzy"`: match the typed characters in order, allowing gaps (e.g. `strubu` matches `structure-builder`). - `"typo"`: tolerate misspellings via edit distance (e.g. `stroberry` matches `strawberry`), without fuzzy subsequence matching. - `"all"`: combines `"substring"`, `"fuzzy"` and `"typo"`. > [!NOTE] > While searching, the single-letter navigation keys (`j`, `k`, `n`, `p`, etc.) > are typed into the search input instead of moving the selection. Use the arrow > keys, or the readline shortcuts `ctrl+n` (next) and `ctrl+p` (previous), to > move through the list without leaving the search input. ### check icon With the `check` option you can change the icon of a selected option to any string value. ### uncheck icon With the `uncheck` option you can change the icon of an unselected option to any string value. ### Partial check icon With the `partialCheck` option you can change the icon of a partial selected group option to any string value. Change the uncheck icon. ### Confirm submit If `confirmSubmit` is enabled, the user needs to press enter twice to submit. Default is `true`. ### Display usage info The `info` option enables the info bar which displays some usage information. ### Child options The `options` option allows you to group options together. It accepts an array of child options, of which you can nest as many as you want. ```ts import { Checkbox } from "@cliffy/prompt/checkbox"; const title = await Checkbox.prompt({ message: "Pick some books", search: true, options: [ { name: "Harry Potter", options: [ "Harry Potter and the Philosopher's Stone", "Harry Potter and the Chamber of Secrets", "Harry Potter and the Prisoner of Azkaban", "Harry Potter and the Goblet of Fire", "Harry Potter and the Order of the Phoenix", "Harry Potter and the Half-Blood Prince", "Harry Potter and the Deathly Hallows", ], }, { name: "Middle-Earth", options: [ "The Hobbit", { name: "The Lord of the Rings", options: [ "The Fellowship of the Ring", "The Two Towers", "The Return of the King", ], }, "Silmarillion", ], }, ], }); console.log({ title }); ``` #### Max breadcrumb items The `maxBreadcrumbItems` option limits the maximum number of breadcrumb items which will be displayed. #### Breadcrumb separator With the `breadcrumbSeparator` option the breadcrumb separator can be changed. #### Back pointer With the `backPointer` option you can change the icon of the _back_ option to any string value. #### Group pointer With the `groupPointer` option you can change the pointer of _group_ options to any string value. #### Group icon With the `groupIcon` option you can change the icon of _group_ options to any string value. #### Group open icon With the `groupOpenIcon` option you can change the icon of _opened group_ options to any string value. --- # Dynamic prompts You can dynamically control the flow of the prompt list with the `before` and `after` callbacks which work like a middleware function. ```typescript import { Checkbox, Confirm, Number, prompt } from "@cliffy/prompt"; const result = await prompt([{ name: "animals", message: "Select some animals", type: Checkbox, options: ["dog", "cat", "snake"], }, { name: "like", message: "Do you like animals?", type: Confirm, after: async ({ like }, next) => { // executed after like prompt if (like) { await next(); // run age prompt } else { await next("like"); // run like prompt again } }, }, { name: "age", message: "How old are you?", type: Number, before: async ({ animals }, next) => { // executed before age prompt if (animals?.length === 3) { await next(); // run age prompt } else { await next("animals"); // begin from start } }, }]); console.log(result); ``` ```console $ deno run examples/prompt/dynamic_prompts.ts ``` ## Options Following options are available as global and/or prompt specific options. Global options will be passed as second argument to the `prompt()` method. ### Prompt name The `name` option is required for each prompt and is used as key for the results object where the answer of the prompt is stored. ### Prompt type The `type` option is required for each prompt and specifies the type of the prompt. ### Before and after hooks The `before` callback method is called before and the `after` callback method after the prompt is executed. It is available as global and prompt specific option. The first argument is the `result` object which contains all already available answers. The second argument is the `next()` method which executes the next prompt in the list (for the before callback it's the current prompt). To jump to a specific prompt you can pass the name or index of the prompt to the `next()` method. To skip this prompt you can pass `true` to the `next()` method. If `next()` isn't called all other prompts will be skipped. ### OS signals > [!WARNING] > The [cbreak](https://deno.land/api@v1.31.1?s=Deno.SetRawOptions#prop_cbreak) > option works currently only on Linux and macOS and is not supported with Bun! The `cbreak` option enables pass-through of os signals to deno, allowing you to register your own signal handler. It is available as global and prompt specific option. Read more about os signals [here](https://cliffy.io/docs/v1.2.1/prompt/os_signals.md). --- # Auto suggestions You can provide suggestions to the [input](https://cliffy.io/docs/v1.2.1/prompt/types/input.md), [number](https://cliffy.io/docs/v1.2.1/prompt/types/number.md) and [list](https://cliffy.io/docs/v1.2.1/prompt/types/list.md) prompt to enable tab-completions with the [suggestions](#suggestions) and/or [id](#local-storage-id) option. If an `id` is provided, the values will be saved to the local storage using the `id` as local storage key. With `suggestions` you can provide some default suggestions. Both options can be defined at the same time. ```shell deno install you/cli.ts --location https://example.com # or deno run you/cli.ts --location https://example.com ``` ## Options ### Suggestions The `suggestions` option specifies a list of default suggestions. ```typescript import { Input } from "@cliffy/prompt/input"; const color = await Input.prompt({ message: "Choose a color", suggestions: [ "Abbey", "Absolute Zero", "Acadia", "Acapulco", "Acid Green", "Aero", "Aero Blue", "Affair", "African Violet", "Air Force Blue", ], }); console.log({ color }); ``` ```console $ deno run examples/prompt/suggestions.ts ``` ![](https://cliffy.io/docs/v1.2.1/prompt/assets/img/suggestions.gif) ### Local storage id If the `id` option is provided, values are stored in the local storage using the id as local storage key. The stored values are used as suggestions the next time the prompt is used. > [!NOTE] > The `id` option requires deno >= `1.10` and the `--location` flag. Since deno > `1.16.0` the `--location` flag is optional. ### Path completions To enable path completions for relative and absolute files, set the `files` option to `true`. Path completions is only available for `Input` and `List` prompts. ### Suggestions list With the `list` option you can display a list of suggestions. Matched suggestions are highlighted in the list. Press `tab` to complete the highlighted suggestion, or `enter` to accept and submit it (see [Complete on submit](#complete-on-submit)). You can also display the info bar with the `info` option to show the number of available suggestions and usage information. ```typescript import { Input } from "@cliffy/prompt/input"; const color = await Input.prompt({ message: "Choose a color", list: true, info: true, suggestions: [ "Abbey", "Absolute Zero", "Acadia", "Acapulco", "Acid Green", "Aero", "Aero Blue", "Affair", "African Violet", "Air Force Blue", ], }); console.log({ color }); ``` ```console $ deno run examples/prompt/suggestions_list.ts ``` ![](https://cliffy.io/docs/v1.2.1/prompt/assets/img/suggestions_list.gif) #### Max suggestions With the `maxRows` option you specify the number of suggestions displayed per page. Defaults to `10`. ### Search mode The `searchMode` option controls the matching strategy used to filter, rank and highlight suggestions. Defaults to `"all"`. - `"substring"`: classic contiguous substring match. - `"fuzzy"`: match the typed characters in order, allowing gaps (e.g. `strubu` matches `structure-builder`). - `"typo"`: tolerate misspellings via edit distance (e.g. `stroberry` matches `strawberry`), without fuzzy subsequence matching. - `"all"`: combines `"substring"`, `"fuzzy"` and `"typo"`. Substring matches always rank above fuzzy matches, which always rank above typo-tolerant matches, regardless of their inner scores. ```ts ignore import { Input } from "@cliffy/prompt/input"; const color = await Input.prompt({ message: "Choose a color", list: true, searchMode: "substring", suggestions: ["Abbey", "Acadia", "Aero"], }); console.log({ color }); ``` ### Complete on submit The `completeOnSubmit` option controls whether the highlighted suggestion is accepted when the prompt is submitted. When enabled and a suggestion is highlighted, pressing `enter` completes the highlighted suggestion and submits it instead of the raw input value. If no suggestion matches the current input, the typed value is submitted. A custom `complete` handler and [path completions](#path-completions) are respected. The default depends on the `list` option: - With `list: true`, suggestions are shown as a highlighted list and `completeOnSubmit` defaults to `true`, so `enter` accepts the highlighted entry (menu-like behavior). - For inline suggestions (`list` disabled), it defaults to `false`, so `enter` submits the typed value, like fish/zsh autosuggestions. Set the option explicitly to override the per-mode default: ```ts ignore import { Input } from "@cliffy/prompt/input"; const color = await Input.prompt({ message: "Choose a color", list: true, suggestions: ["Abbey", "Acadia", "Aero"], completeOnSubmit: false, }); console.log({ color }); ``` To submit a value that is a prefix of a suggestion (for example `Ab` while `Abbey` is highlighted), press `escape` to dismiss the highlighted suggestion, then `enter` to submit the typed value. Typing or navigating the list re-activates suggestions. The dismiss key can be changed with the `deselect` keymap (`keys: { deselect: ["escape"] }`). --- # OS signals > [!WARNING] > The [cbreak](https://deno.land/api@v1.31.1?s=Deno.SetRawOptions#prop_cbreak) > option works currently only on Linux and macOS and is not supported with Bun! By default, cliffy will call `Deno.exit(0)` after the user presses `ctrl+c`. If you need to use a custom signal handler, you can enable the `cbreak` option on your prompt. This will enable pass-through of os signals to deno, allowing you to register your own signal handler. > When using prompts like `Select` or `Toggle` with the `cbreak` option enabled, > you have to show the cursor and clear the stdout before calling `Deno.exit()` > manually. Maybe this will be improved somehow in the future. ```typescript import { tty } from "@cliffy/ansi/tty"; import { Toggle } from "@cliffy/prompt/toggle"; Deno.addSignalListener("SIGINT", () => { tty.cursorLeft.eraseDown.cursorShow(); console.log("interrupted!"); Deno.exit(1); }); const confirmed = await Toggle.prompt({ message: "Please confirm", cbreak: true, }); console.log({ confirmed }); ``` ```console $ deno run examples/prompt/os_signals.ts ``` --- # Keycode ANSI key code parser. ## Installation ### Deno ```bash deno add jsr:@cliffy/keycode ``` ### Pnpm ```bash pnpm add jsr:@cliffy/keycode ``` or (using pnpm 10.8 or older): ```bash pnpm dlx jsr add @cliffy/keycode ``` ### Yarn ```bash yarn add jsr:@cliffy/keycode ``` or (using Yarn 4.8 or older): ```bash yarn dlx jsr add @cliffy/keycode ``` ### Vlt ```bash vlt install jsr:@cliffy/keycode ``` ### Npm ```bash npx jsr add @cliffy/keycode ``` ### Bun ```bash bunx jsr add @cliffy/keycode ``` ## Usage The `keycode` module exports a `parse()` method which accepts an ansi string and returns an array of [`KeyCode`](https://cliffy.io/docs/v1.2.1/keycode/keycode.md). ```typescript import { parse } from "@cliffy/keycode"; console.log( parse( "\x1b[A\x1b[B\x1b[C\x1b[D\x1b[E\x1b[F\x1b[H", ), ); ``` ```console $ deno run examples/keycode/example.ts ``` **Output:** ```json [ { name: "up", sequence: "\x1b[A", code: "[A", ctrl: false, meta: false, shift: false }, { name: "down", sequence: "\x1b[B", code: "[B", ctrl: false, meta: false, shift: false }, { name: "right", sequence: "\x1b[C", code: "[C", ctrl: false, meta: false, shift: false }, { name: "left", sequence: "\x1b[D", code: "[D", ctrl: false, meta: false, shift: false }, { name: "clear", sequence: "\x1b[E", code: "[E", ctrl: false, meta: false, shift: false }, { name: "end", sequence: "\x1b[F", code: "[F", ctrl: false, meta: false, shift: false }, { name: "home", sequence: "\x1b[H", code: "[H", ctrl: false, meta: false, shift: false } ] ``` ## Example ### Deno ```typescript import { KeyCode, parse } from "@cliffy/keycode"; async function* keypress(): AsyncGenerator { while (true) { const data = new Uint8Array(8); Deno.stdin.setRaw(true); const nread = await Deno.stdin.read(data); Deno.stdin.setRaw(false); if (nread === null) { return; } const keys: Array = parse(data.subarray(0, nread)); for (const key of keys) { yield key; } } } console.log("Hit ctrl + c to exit."); for await (const key of keypress()) { if (key.ctrl && key.name === "c") { console.log("exit"); break; } console.log(key); } ``` ### Deno ```console $ deno run examples/keycode/read_key.ts ``` --- # Keycode The `KeyCode` interface represents a parsed ansi sequence: ```json { "name": "up", "sequence": "\x1b[A", "code": "[A", "ctrl": false, "meta": false, "shift": false } ``` ### Name The `name` property defines the name of the key code. The type is `string | undefined`. ### Char The `char` property defines the pressed character of the key code. The type is `string | undefined`. ### Sequence The `sequence` property defines the ansi sequence of the key code. The type is `string | undefined`. ### Code The `code` property defines the ansi code of the key code. The type is `string | undefined`. ### Ctrl The `ctrl` property defines whether the ctrl key is pressed or not. The type is `boolean | undefined`. ### Meta The `meta` property defines whether the meta key is pressed or not. The type is `boolean | undefined`. ### Shift The `shift` property defines whether the shift key is pressed or not. The type is `boolean | undefined`. --- # Keypress Keypress module with promise, async iterator and event target API. ## Installation ### Deno ```bash deno add jsr:@cliffy/keypress ``` ### Pnpm ```bash pnpm add jsr:@cliffy/keypress ``` or (using pnpm 10.8 or older): ```bash pnpm dlx jsr add @cliffy/keypress ``` ### Yarn ```bash yarn add jsr:@cliffy/keypress ``` or (using Yarn 4.8 or older): ```bash yarn dlx jsr add @cliffy/keypress ``` ### Vlt ```bash vlt install jsr:@cliffy/keypress ``` ### Npm ```bash npx jsr add @cliffy/keypress ``` ### Bun ```bash bunx jsr add @cliffy/keypress ``` ## Usage There are two ways to use this module. You can use the `keypress()` method, which returns a global instance of the `KeyPress` class, or you can create a new instance of the `KeyPress` class. There is no difference between these two ways, except that the `keypress()` method always returns the same instance, unless the `.dispose()` method is called. In this case a new instance is returned. ### Promise The keypress module can be used as promise. It reads one chunk from stdin and returns a `KeyPressEvent` for the first parsed character. ```typescript import { keypress, KeyPressEvent } from "@cliffy/keypress"; const event: KeyPressEvent = await keypress(); console.log( "type: %s, key: %s, ctrl: %s, meta: %s, shift: %s, alt: %s, repeat: %s", event.type, event.key, event.ctrlKey, event.metaKey, event.shiftKey, event.altKey, event.repeat, ); ``` ```console $ deno run --reload examples/keypress/promise.ts ``` ### Async Iterator The keypress module can be used as async iterator to iterate over all keypress events. The async iterator reads chunk by chunk from stdin. On each step, it reads one chunk from stdin and emits for each character a keypress event. It pauses reading from stdin before emitting the events, so stdin is not blocked inside the for loop. ```typescript import { keypress, KeyPressEvent } from "@cliffy/keypress"; for await (const event of keypress()) { console.log( "type: %s, key: %s, ctrl: %s, meta: %s, shift: %s, alt: %s, repeat: %s", event.type, event.key, event.ctrlKey, event.metaKey, event.shiftKey, event.altKey, event.repeat, ); if (event.ctrlKey && event.key === "c") { console.log("exit"); break; } } ``` ```console $ deno run --reload examples/keypress/async_iterator.ts ``` ### Event Target The Keypress class extends the EventTarget class that provides a `.addEventListener()` method that can be used to register event listeners. The addEventListener method starts an event loop in the background that reads from stdin and emits an event for each input. > [!NOTE] > As long as the event loop is running, stdin is blocked for other resources. You can stop the event loop with `keypress().dispose()`. > [!NOTE] > The promise and async iterator based solution does not start an event loop in > the background. ```typescript import { keypress, KeyPressEvent } from "@cliffy/keypress"; keypress().addEventListener("keydown", (event: KeyPressEvent) => { console.log( "type: %s, key: %s, ctrl: %s, meta: %s, shift: %s, alt: %s, repeat: %s", event.type, event.key, event.ctrlKey, event.metaKey, event.shiftKey, event.altKey, event.repeat, ); if (event.ctrlKey && event.key === "c") { console.log("exit"); keypress().dispose(); } }); ``` ```console $ deno run --reload examples/keypress/event_target.ts ``` --- # KeyPressEvent The `KeyPressEvent` represents a keypress event and it inherits most properties from the [`KeyCode`](https://cliffy.io/docs/v1.2.1/keycode/keycode.md) interface. ```json { "name": "up", "sequence": "\x1b[A", "code": "[A", "ctrlKey": false, "metaKey": false, "shiftKey": false, "altKey": false } ``` ### key The `key` property defines the name of the key code. The type is `string | undefined`. ### char The `char` property defines the pressed character of the key code. The type is `string | undefined`. ### sequence The `sequence` property defines the ansi sequence of the key code. The type is `string | undefined`. ### code The `code` property defines the ansi code of the key code. The type is `string | undefined`. ### ctrlKey The `ctrlKey` property defines whether the ctrl key is pressed or not. The type is `boolean`. ### metaKey The `metaKey` property defines whether the meta key is pressed or not. The type is `boolean`. ### shiftKey The `shiftKey` property defines whether the shift key is pressed or not. The type is `boolean`. ### altKey The `altKey` property defines whether the alt key is pressed or not. The type is `boolean`. ### repeat The `repeat` property indicates how many times the key was pressed repeatedly. --- # Table Fast and customizable table module to render unicode tables on the command line. ## Installation ### Deno ```bash deno add jsr:@cliffy/table ``` ### Pnpm ```bash pnpm add jsr:@cliffy/table ``` or (using pnpm 10.8 or older): ```bash pnpm dlx jsr add @cliffy/table ``` ### Yarn ```bash yarn add jsr:@cliffy/table ``` or (using Yarn 4.8 or older): ```bash yarn dlx jsr add @cliffy/table ``` ### Vlt ```bash vlt install jsr:@cliffy/table ``` ### Npm ```bash npx jsr add @cliffy/table ``` ### Bun ```bash bunx jsr add @cliffy/table ``` ## Usage ### Basic Usage To create a table you can simply create an instance of the `Table` class and pass the rows as arguments to the constructor. The example below will output a simple table with three rows and without any styles. The only default option is `padding` which is set to `1`. ```ts import { Table } from "@cliffy/table"; const table: Table = new Table( ["Baxter Herman", "Oct 1, 2020", "Harderwijk", "Slovenia"], ["Jescie Wolfe", "Dec 4, 2020", "Alto Hospicio", "Japan"], ["Allegra Cleveland", "Apr 16, 2020", "Avernas-le-Bauduin", "Samoa"], ["Aretha Gamble", "Feb 22, 2021", "Honolulu", "Georgia"], ); console.log(table.toString()); ``` ```console $ deno run examples/table/basic_usage.ts ``` ![](https://cliffy.io/docs/v1.2.1/table/assets/img/basic_usage.gif) ### Using as Array Since the `Table` class is an `Array`, you can call all the methods of the array class like `.from()`, `.sort()`, `.push()`, `.unshift()` and friends. ```ts import { Table } from "@cliffy/table"; const table: Table = Table.from([ ["Baxter Herman", "Oct 1, 2020", "Harderwijk", "Slovenia"], ["Jescie Wolfe", "Dec 4, 2020", "Alto Hospicio", "Japan"], ["Allegra Cleveland", "Apr 16, 2020", "Avernas-le-Bauduin", "Samoa"], ]); table.push(["Aretha Gamble", "Feb 22, 2021", "Honolulu", "Georgia"]); table.sort(); table.render(); ``` ```console $ deno run examples/table/using_as_array.ts ``` ![](https://cliffy.io/docs/v1.2.1/table/assets/img/using_as_array.gif) --- # Table options To customize the table, the table class provides a few chainable option methods. To see a list of all available options go to the [Table](#table) API section. ```ts import { Table } from "@cliffy/table"; new Table() .header(["Name", "Date", "City", "Country"]) .body([ ["Baxter Herman", "Oct 1, 2020", "Harderwijk", "Slovenia"], ["Jescie Wolfe", "Dec 4, 2020", "Alto Hospicio", "Japan"], ["Allegra Cleveland", "Apr 16, 2020", "Avernas-le-Bauduin", "Samoa"], ["Aretha Gamble", "Feb 22, 2021", "Honolulu", "Georgia"], ]) .maxColWidth(10) .padding(1) .indent(2) .border() .render(); ``` ```console $ deno run examples/table/table_options.ts ``` ![](https://cliffy.io/docs/v1.2.1/table/assets/img/table_options.gif) ## Header and Body To define a table header you can use the `.header()` method. The header is not affected by any `Array` method like `.sort()` because it is stored as a separate property and not in the array stack. The `.body()` method adds an array of rows to the table and removes all existing rows. The first argument of the `.header()` method can be an `Array` of `string` and/or `Cell`. The first argument of the `.body()` can be an `Array` of rows and a row can be an `Array` of `string` and `Cell`. You can read more about rows and cells [here](https://cliffy.io/docs/v1.2.1/table/rows_and_cells.md). ```ts import { Table } from "@cliffy/table"; new Table() .header(["Name", "Date", "City", "Country"]) .body([ ["Baxter Herman", "Oct 1, 2020", "Harderwijk", "Slovenia"], ["Jescie Wolfe", "Dec 4, 2020", "Alto Hospicio", "Japan"], ["Allegra Cleveland", "Apr 16, 2020", "Avernas-le-Bauduin", "Samoa"], ["Aretha Gamble", "Feb 22, 2021", "Honolulu", "Georgia"], ]) .render(); ``` ```console $ deno run examples/table/header_and_body.ts ``` ![](https://cliffy.io/docs/v1.2.1/table/assets/img/header_and_body.gif) ## Columns The `.columns(columns)` method can be used to set column options of multiple columns. All available column options can be found [here](https://cliffy.io/docs/v1.2.1/table/columns.md). ```ts import { Column, Table } from "@cliffy/table"; new Table() .body([ ["Baxter Herman", "Oct 1, 2020", "Harderwijk", "Slovenia"], ["Jescie Wolfe", "Dec 4, 2020", "Alto Hospicio", "Japan"], ["Allegra Cleveland", "Apr 16, 2020", "Avernas-le-Bauduin", "Samoa"], ["Aretha Gamble", "Feb 22, 2021", "Honolulu", "Georgia"], ]) .columns([ { border: true }, new Column().align("right"), ]) .render(); ``` ## Column With the `.column(index, options)` method you can set options for a single column at a specific index. All available column options can be found [here](https://cliffy.io/docs/v1.2.1/table/columns.md). ```ts import { Column, Table } from "@cliffy/table"; new Table() .body([ ["Baxter Herman", "Oct 1, 2020", "Harderwijk", "Slovenia"], ["Jescie Wolfe", "Dec 4, 2020", "Alto Hospicio", "Japan"], ["Allegra Cleveland", "Apr 16, 2020", "Avernas-le-Bauduin", "Samoa"], ["Aretha Gamble", "Feb 22, 2021", "Honolulu", "Georgia"], ]) .column(0, { border: true, }) .column(1, new Column().align("right")) .render(); ``` ## Render The `.render()` method outputs the table to stdout. If you need the output as string you can use the `.toString()` method to return the table as string. ## Column width You can set the min/max width of columns with the `.minColWidth()` and `.maxColWidth()` methods. ## Max width The `.maxWidth(width)` method sets the maximum total width of the table in columns. This is required for `flexGrow`, `flexShrink`, and `flex` to work. Without a finite max width the layout has no target and flex is a no-op. ```ts import { Table } from "@cliffy/table"; new Table() .body([["foo", "bar"]]) .maxWidth(80) .flexShrink([0, 1]) .render(); ``` ## Responsive flex The `.flexGrow()`, `.flexShrink()`, and `.flex()` table-level methods set a default flex weight for all columns. They accept either a single number (applied to every column) or an array of per-column weights. > [!NOTE] > A finite `.maxWidth()` must be set on the table for flex to have any effect. ```ts import { Table } from "@cliffy/table"; // Grow the second column to fill available space, keep others rigid. new Table() .body([["Name", "Description", "Version"]]) .maxWidth(100) .flexGrow([0, 1, 0]) .render(); // Shrink only the second column on overflow. new Table() .body([["Name", "Description", "Version"]]) .maxWidth(60) .flexShrink([0, 1, 0]) .render(); // Shorthand: grow and shrink together. new Table() .body([["Name", "Description", "Version"]]) .maxWidth(80) .flex([0, 1, 0]) .render(); ``` For per-column control use the `Column` class — see [Flex grow](https://cliffy.io/docs/v1.2.1/table/columns.md#flex-grow), [Flex shrink](https://cliffy.io/docs/v1.2.1/table/columns.md#flex-shrink), and [Flex (shorthand)](https://cliffy.io/docs/v1.2.1/table/columns.md#flex-shorthand) in the column docs. ## Cell padding The `.padding()` method adds padding to all cells. ## Border You can enable border by using the `.border()` method. ### Border style With the `.chars()` method you can change the border style. Here is an example of the default border characters: ```json { "top": "─", "topMid": "┬", "topLeft": "┌", "topRight": "┐", "bottom": "─", "bottomMid": "┴", "bottomLeft": "└", "bottomRight": "┘", "left": "│", "leftMid": "├", "mid": "─", "midMid": "┼", "right": "│", "rightMid": "┤", "middle": "│" } ``` ## Align content The content can be aligned with the `.align()` method. The first argument is the direction. Possible values are: - `"left"` - `"right"` - `"center"` ## Table indent With the `.indent()` method you can add indentation to the table. ## Clone The `.clone()` method clones the entire table. --- # Column The `Column` class is used to set table column options. A `Column` class can be added to the `Table` with the [.columns()](https://cliffy.io/docs/v1.2.1/table/options.md) and [.column()](https://cliffy.io/docs/v1.2.1/table/options.md) method. ```ts import { Column, Table } from "@cliffy/table"; new Table() .body([ ["Baxter Herman", "Oct 1, 2020", "Harderwijk", "Slovenia"], ["Jescie Wolfe", "Dec 4, 2020", "Alto Hospicio", "Japan"], ["Allegra Cleveland", "Apr 16, 2020", "Avernas-le-Bauduin", "Samoa"], ["Aretha Gamble", "Feb 22, 2021", "Honolulu", "Georgia"], ]) .columns([ { border: true }, new Column().align("right"), ]) .render(); ``` ## Column options ### Border You can enable/disable the border by using the `.border()` method which accepts an optional boolean value. ### Align column content The content can be aligned with the `.align()` method. The first argument is the direction. Possible values are: - `"left"` - `"right"` - `"center"` ### Column width You can set the min and max width of columns with the `.minColWidth()` and `.maxColWidth()` methods. ### Cell padding The `.padding()` method adds padding to all cells in this column. ### Flex grow The `.flexGrow(weight)` method allows a column to expand into available slack. Follows CSS `flex-grow` semantics: available space is distributed proportionally by weight, so a column with weight `2` receives twice the extra space of one with weight `1`. The default is `0` (no grow). > [!NOTE] > Flex only takes effect when `maxWidth` is set to a finite value on the table. > Without it the layout has no target width and flex is a no-op. ```ts import { Column, Table } from "@cliffy/table"; new Table() .body([ ["Name", "Description"], ["foo", "A short value"], ["bar", "A much longer description that needs more space"], ]) .maxWidth(80) // required — flex is a no-op without a finite maxWidth .columns([ new Column().minWidth(10), new Column().flexGrow(1), // expand to fill up to 80 columns ]) .render(); ``` ### Flex shrink The `.flexShrink(weight)` method allows a column to give up space when the table is wider than `maxWidth`. Follows CSS `flex-shrink` semantics: each column's share of the reduction is proportional to `weight × width`, so a wider column or one with a higher weight absorbs more of the overflow. The default is `0` (rigid — never shrinks). > [!NOTE] > Flex only takes effect when `maxWidth` is set to a finite value on the table. > Without it the layout has no target width and flex is a no-op. ```ts import { Column, Table } from "@cliffy/table"; new Table() .body([ ["Name", "Description"], ["foo", "A short value"], ["bar", "A much longer description that can be compressed"], ]) .maxWidth(40) // required — triggers shrink when content exceeds this width .columns([ new Column().minWidth(10), new Column().flexShrink(1), // absorb overflow proportionally ]) .render(); ``` ### Flex (shorthand) The `.flex(weight)` method is a shorthand that sets both `.flexGrow` and `.flexShrink` to the same weight. The column will both expand into and contract out of available space. > [!NOTE] > Flex only takes effect when `maxWidth` is set to a finite value on the table. > Without it the layout has no target width and flex is a no-op. ```ts import { Column, Table } from "@cliffy/table"; new Table() .body([ ["Name", "Description"], ["foo", "A short value"], ["bar", "A much longer description"], ]) .maxWidth(80) // required — flex is a no-op without a finite maxWidth .columns([ new Column().minWidth(10), new Column().flex(1), // grow and shrink proportionally ]) .render(); ``` ### Set multiple options The `.options()` method allows you to set multiple options at once by passing an options bag to the `.options()` method. --- # Rows and cells It is also possible to customize single rows and cells. To do this you can use the `Row` and `Cell` class. The `Row` class is also an `Array` class like the `Table` class. ```ts import { Cell, Row, Table } from "@cliffy/table"; new Table() .header(Row.from(["Name", "Date", "City", "Country"]).border()) .body([ [ "Baxter Herman", new Cell("Oct 1, 2020").border(), "Row 1 Column 3", "Harderwijk", "Slovenia", ], new Row("Jescie Wolfe", "Dec 4, 2020", "Alto Hospicio", "Japan").border( true, ), ["Allegra Cleveland", "Apr 16, 2020", "Avernas-le-Bauduin", "Samoa"], ["Aretha Gamble", "Feb 22, 2021", "Honolulu", "Georgia"], ]) .render(); ``` ```console $ deno run examples/table/rows_and_cells.ts ``` ![](https://cliffy.io/docs/v1.2.1/table/assets/img/rows_and_cells.gif) ## Rows ### Row border To enable row border you can use the `.border()` method. ### Align row content The `.align()` method aligns the content of all cells in the row. The first argument is the direction. Possible values are: - `"left"` - `"right"` - `"center"` ### Clone row The `.clone()` method clones the entire row. ## Cells ### Cell border With the `.border()` method you can add border to a cell. ### Align cell content The `.align()` method aligns the content of the cell. The first argument is the direction. Possible values are: - `"left"` - `"right"` - `"center"` ### Colspan and rowspan `.colSpan()` and `.rowSpan()` allows a single table cell to span the width/height of more than one column and/or row. With `.colSpan()` and `.rowSpan()` the next or lower cell is moved to the right if the next or lower cell is not of type `undefined`. If it's of type `undefined` the cell is overridden. The following examples both have the same output. **Override undefined values** ```ts import { Cell, Table } from "@cliffy/table"; Table.from([ [ new Cell("Row 1 & 2 Column 1").rowSpan(2), "Row 1 Column 2", "Row 1 Column 3", ], [undefined, new Cell("Row 2 Column 2 & 3").colSpan(2), undefined], [ new Cell("Row 3 & 4 Column 1").rowSpan(2), "Row 3 Column 2", "Row 3 Column 3", ], [undefined, new Cell("Row 4 Column 2 & 3").colSpan(2), undefined], [ "Row 5 Column 1", new Cell("Row 5 & 6 Column 2 & 3").rowSpan(2).colSpan(2), undefined, ], ["Row 6 Column 1", undefined, undefined], ]) .border() .render(); ``` **Omit undefined values** ```ts import { Cell, Table } from "@cliffy/table"; Table.from([ [ new Cell("Row 1 & 2 Column 1").rowSpan(2), "Row 1 Column 2", "Row 1 Column 3", ], [new Cell("Row 2 Column 2 & 3").colSpan(2)], [ new Cell("Row 3 & 4 Column 1").rowSpan(2), "Row 3 Column 2", "Row 3 Column 3", ], [new Cell("Row 4 Column 2 & 3").colSpan(2)], ["Row 5 Column 1", new Cell("Row 5 & 6 Column 2 & 3").rowSpan(2).colSpan(2)], ["Row 6 Column 1"], ]) .border() .render(); ``` ```console $ deno run examples/table/colspan_and_rowspan.ts ``` ![](https://cliffy.io/docs/v1.2.1/table/assets/img/colspan_and_rowspan.gif) ### Clone cell To clone a single cell you can use the `.clone()` method. --- # Ansi Chainable ansi escape sequences. ![](https://cliffy.io/docs/v1.2.1/ansi/assets/img/demo.gif) ## Installation ### Deno ```bash deno add jsr:@cliffy/ansi ``` ### Pnpm ```bash pnpm add jsr:@cliffy/ansi ``` or (using pnpm 10.8 or older): ```bash pnpm dlx jsr add @cliffy/ansi ``` ### Yarn ```bash yarn add jsr:@cliffy/ansi ``` or (using Yarn 4.8 or older): ```bash yarn dlx jsr add @cliffy/ansi ``` ### Vlt ```bash vlt install jsr:@cliffy/ansi ``` ### Npm ```bash npx jsr add @cliffy/ansi ``` ### Bun ```bash bunx jsr add @cliffy/ansi ``` ## Usage ### Ansi escape sequences The [ansi](https://cliffy.io/docs/v1.2.1/ansi/ansi.md) and [tty](https://cliffy.io/docs/v1.2.1/ansi/tty.md) module can be used to generate or write ansi escape sequences to stdout. ```typescript import { tty } from "@cliffy/ansi/tty"; tty.cursorSave .cursorHide .cursorTo(0, 0) .eraseScreen(); ``` ### Colors The [colors](https://cliffy.io/docs/v1.2.1/ansi/colors.md) module is a simple and tiny chainable wrapper for [@std/fmt/colors](https://jsr.io/@std/fmt@1.0.3/doc/colors) module and works similarly to node's [chalk](https://github.com/chalk/chalk) module. ```typescript import { colors } from "@cliffy/ansi/colors"; console.log( colors.bold.underline.rgb24("Welcome to Deno.Land!", 0xff3333), ); ``` --- # Ansi The ansi module exports an `ansi` object with chainable methods and properties for generating ansi escape sequence strings. The last property must be invoked as a method to generate the ansi string. ```typescript import { ansi } from "@cliffy/ansi"; console.log( ansi.cursorUp.cursorLeft.eraseDown(), ); ``` ## Arguments If the last method takes some arguments, you have to invoke the `.toString()` method to generate the ansi string. ```typescript import { ansi } from "@cliffy/ansi"; console.log( ansi.cursorUp(2).cursorLeft.eraseDown(2).toString(), ); ``` ## Uint8Array Convert to `Uint8Array`: ```typescript import { ansi } from "@cliffy/ansi"; await Deno.stdout.write( ansi.cursorUp.cursorLeft.eraseDown.bytes(), ); ``` ## Functional You can also directly import the ansi escape methods from the `ansi_escapes.ts` module. ```typescript import { cursorTo, eraseDown, image, link } from "@cliffy/ansi/ansi-escapes"; const response = await fetch( "https://raw.githubusercontent.com/c4spar/deno-cliffy/main/logo.png", ); const imageBuffer: ArrayBuffer = await response.arrayBuffer(); console.log( cursorTo(0, 0) + eraseDown() + image(imageBuffer, { width: 29, preserveAspectRatio: true, }) + "\n " + link("Deno", "https://deno.com") + "\n", ); ``` --- # Tty The tty module exports a `tty` object which works almost the same way as the `ansi` module. The only difference is, the `tty` module writes the ansi escape sequences directly to stdout. ```typescript import { tty } from "@cliffy/ansi/tty"; tty.cursorSave .cursorHide .cursorTo(0, 0) .eraseScreen(); ``` Create a new instance. ```typescript import { tty } from "@cliffy/ansi/tty"; const myTty = tty(); myTty.cursorSave .cursorHide .cursorTo(0, 0) .eraseScreen(); ``` Create a new instance with custom writer and reader. ### Deno ```typescript import { tty } from "@cliffy/ansi/tty"; const myTty = tty({ writer: Deno.stdout, reader: Deno.stdin, }); myTty.cursorSave .cursorHide .cursorTo(0, 0) .eraseScreen(); ``` --- # Colors The colors module is a simple and tiny chainable wrapper around [@std/fmt/colors](https://jsr.io/@std/fmt@1.0.3/doc/colors) module and works similarly to node's [chalk](https://github.com/chalk/chalk) module. ```typescript import { colors } from "@cliffy/ansi/colors"; console.log( colors.bold.underline.rgb24("Welcome to Deno.Land!", 0xff3333), ); ``` ```console $ deno run examples/ansi/colors.ts ``` ## Themes You can create your own re-usable themes just by storing your styles into a variable. ```typescript import { colors } from "@cliffy/ansi/colors"; // Define theme colors. const error = colors.bold.red; const warn = colors.bold.yellow; const info = colors.bold.blue; // Use theme colors. console.log(error("[ERROR]"), "Some error!"); console.log(warn("[WARN]"), "Some warning!"); console.log(info("[INFO]"), "Some information!"); // Override theme colors. console.log(error.underline("[ERROR]"), "Some error!"); console.log(warn.underline("[WARN]"), "Some warning!"); console.log(info.underline("[INFO]"), "Some information!"); ``` ```console $ deno run examples/ansi/color_themes.ts ``` --- # Testing Experimental testing utilities for command line applications. > [!WARNING] > The testing module currently only supports Deno. ## Installation ### Deno ```bash deno add jsr:@cliffy/testing ``` ### Pnpm ```bash pnpm add jsr:@cliffy/testing ``` or (using pnpm 10.8 or older): ```bash pnpm dlx jsr add @cliffy/testing ``` ### Yarn ```bash yarn add jsr:@cliffy/testing ``` or (using Yarn 4.8 or older): ```bash yarn dlx jsr add @cliffy/testing ``` ### Vlt ```bash vlt install jsr:@cliffy/testing ``` ### Npm ```bash npx jsr add @cliffy/testing ``` ### Bun ```bash bunx jsr add @cliffy/testing ``` --- # Snapshot testing The `snapshotTest` method can be used to test `stdin`, `stdout` and `stderr` of a single test case. It injects data to stdin and snapshots the `stdout` and `stderr` output of each test case separately. ## Usage The `snapshotTest` method behaves like a combination of `Deno.test()` and the `assertSnapshot` method from the deno std library. The `name`, `meta` and `fn` options are required. ### Basic usage This example snapshots the output of `console.log` and `console.error`. ```ts import { snapshotTest } from "@cliffy/testing"; await snapshotTest({ name: "should log to stdout and stderr", meta: import.meta, async fn() { console.log("foo"); console.error("bar"); }, }); ``` To update the snapshots, run `deno test -- --update`. This creates a snapshot file at `__snapshots__/[filename].snap` with the following content: ```ts ignore export const snapshot = {}; snapshot[`should log to stdout and stderr 1`] = ` "stdout: foo stderr: bar " `; ``` If you now run `deno test` (without `-- --update`), it will check if your test function still has the same output as the snapshot file content has. ### Script arguments Arguments defined with the `args` option are injected into the test method as script args. You can simply use `Deno.args` as you normally would to get the script arguments. ```ts import { snapshotTest } from "@cliffy/testing"; await snapshotTest({ name: "should log Deno.args", meta: import.meta, args: ["--foo", "bar"], async fn() { console.log(Deno.args); }, }); ``` You can use this to create snapshot tests for commands. ```ts import { snapshotTest } from "@cliffy/testing"; import { Command } from "@cliffy/command"; await snapshotTest({ name: "should execute the command with the --foo option", meta: import.meta, args: ["--foo", "bar"], async fn() { await new Command() .name("example") .description("Example command.") .option("-f, --foo ", "Example option.") .action(({ foo }) => { console.log("foo: %s", foo); }) .parse(); }, }); ``` ### Stdin The `snapshotTest` method can inject data to the test function with the `stdin` option. You can simply read the data from `Deno.stdin` as you normally would when reading data from stdin. ```ts import { snapshotTest } from "@cliffy/testing"; await snapshotTest({ name: "should read cliffy from stdin", meta: import.meta, stdin: ["cliffy"], async fn() { let name = ""; const decoder = new TextDecoder(); for await (const chunk of Deno.stdin.readable) { name += decoder.decode(chunk); if (name === "cliffy") { break; } } console.log("name:", name); }, }); ``` You can use this to create snapshot tests for prompts. The `ansi` module can be used to generate escape sequences to control the prompt. ```ts import { snapshotTest } from "@cliffy/testing"; import { Select } from "@cliffy/prompt"; import { ansi } from "@cliffy/ansi"; await snapshotTest({ name: "should select a color", meta: import.meta, stdin: ansi .cursorDown .cursorDown .text("\n") .toArray(), async fn() { const name = await Select.prompt({ message: "Select a color", options: ["red", "green", "blue"], }); console.log("name:", name); }, }); ``` ### Test steps You can also add multiple steps to the test function. The `snapshotTest` method then calls the test function once for each step within a separate test step by calling `t.step()` from the test context. Each step can have separate options for `stdin`, `args`, and `env`. ```ts import { snapshotTest } from "@cliffy/testing"; await snapshotTest({ name: "should log to stdout and stderr", meta: import.meta, steps: { "step 1": { args: ["foo"], stdin: ["bar"] }, "step 2": { args: ["beep"], stdin: ["boop"] }, }, async fn() { console.log(Deno.args); }, }); ``` You can use the `env` option to inject environment variables into each step: ```ts import { snapshotTest } from "@cliffy/testing"; await snapshotTest({ name: "should use env vars per step", meta: import.meta, steps: { "step 1": { env: { MY_VAR: "hello" } }, "step 2": { env: { MY_VAR: "world" } }, }, async fn() { console.log(Deno.env.get("MY_VAR")); }, }); ``` You can also run only specific steps by setting `only: true` on a step. When any step has `only: true`, all other steps are skipped and the test suite is marked as failed (same semantics as `Deno.test`'s `only` option). ```ts import { snapshotTest } from "@cliffy/testing"; await snapshotTest({ name: "run only specific steps", meta: import.meta, steps: { "step 1": { args: ["foo"] }, "step 2": { args: ["bar"], only: true }, // only this step runs }, async fn() { console.log(Deno.args); }, }); ``` --- # Options ## name The name of the test. ## meta The `meta` option is required and needs to be set to `import.meta`. This is required for executing the snapshot tests. ## fn Test function that executes your test code. A snapshot is taken of the `stdout` and `stderr` outputs of this function and stored in the snapshot file. ## args Script arguments injected into the test function. Read them with `Deno.args` as you normally would. Can be set at the top level or on individual steps. (see [Script arguments](https://cliffy.io/docs/v1.2.1/testing/snapshot/index.md#script-arguments)) ## stdin Data injected into `Deno.stdin`. Read it from `Deno.stdin` as you normally would when reading from stdin. Useful for snapshotting prompts. Can be set at the top level or on individual steps. (see [Stdin](https://cliffy.io/docs/v1.2.1/testing/snapshot/index.md#stdin)) ## steps With the `steps` option you can add multiple steps to the test function. The `snapshotTest` method then calls the test function once for each step within a separate test step by calling `t.step()` from the test context. Each step can have separate options for `stdin`, `args`, and `env`. (see [Test steps](https://cliffy.io/docs/v1.2.1/testing/snapshot/index.md#test-steps)) ## denoArgs Arguments passed to the `deno test` command when executing the snapshot tests. Use this to grant your CLI the permissions it needs at runtime (e.g. `--allow-read`, `--allow-env`). `--allow-env=SNAPSHOT_TEST_NAME` is always added on top of whatever you pass. Adding `--quiet` keeps Deno's own output out of the snapshot. ## dir Snapshot output directory. Snapshot files will be written to this directory. This can be relative to the test directory or an absolute path. If both `dir` and `path` are specified, the `dir` option will be ignored and the `path` option will be handled as normal. ## path Snapshot output path. The snapshot will be written to this file. This can be a path relative to the test directory or an absolute path. If both `dir` and `path` are specified, the `dir` option will be ignored and the `path` option will be handled as normal. ## osSuffix Operating system snapshot suffix. This is useful when your test produces different output on different operating systems. `osSuffix` is an array of `typeof Deno.build.os`. ## colors Enable/disable colors. Default is `true`. ## timeout Timeout in milliseconds to wait until the input stream data is buffered before writing the next data to the stream. This ensures that each user input is rendered as separate line in the snapshot file. If your test gets flaky, try to increase the timeout. The default timeout is `600`. ## env Environment variables to inject into the test process. Can be set at the top-level test options or on individual steps. (see [Test steps](https://cliffy.io/docs/v1.2.1/testing/snapshot/index.md#test-steps)) ## ignore If truthy the current test step will be ignored. It is a quick way to skip over a step, but also can be used for conditional logic, like determining if an environment feature is present. ## only If at least one test has `only` set to `true`, only run tests that have `only` set to `true` and fail the test suite. The `only` option can be set at the top level or on individual steps inside the `steps` object. When set on a step, only that step runs and the others are skipped (the test suite is still marked as failing due to the `only` filter). (see [Test steps](https://cliffy.io/docs/v1.2.1/testing/snapshot/index.md#test-steps)) --- # Examples ## Prompt snapshot You can use the `ansi` module to generate some control sequences to control a prompt in your test. ```ts import { snapshotTest } from "@cliffy/testing"; import { Checkbox } from "@cliffy/prompt/checkbox"; import { ansi } from "@cliffy/ansi"; await snapshotTest({ name: "should check an option", meta: import.meta, stdin: ansi .cursorDown .cursorDown .text(" ") .text("\n") .toArray(), async fn() { await Checkbox.prompt({ message: "Select an option", options: [ { name: "Foo", value: "foo" }, { name: "Bar", value: "bar" }, { name: "Baz", value: "baz" }, ], }); }, }); ``` ## Command snapshot A simple example with two steps and different arguments to snapshot the output of a command. ```ts import { snapshotTest } from "@cliffy/testing"; import { Command } from "@cliffy/command"; await snapshotTest({ name: "command", meta: import.meta, ignore: Deno.build.os === "windows", colors: true, steps: { "should delete a file": { args: ["/foo/bar"], }, "should delete a directory recursively": { args: ["--recursive", "/foo/bar"], }, }, async fn() { await new Command() .version("1.0.0") .name("rm") .option("-r, --recursive", "Delete recursive.") .arguments("") .action(({ recursive }, path) => { if (recursive) { console.log("Delete recursive: %s", path); } else { console.log("Delete: %s", path); } }) .parse(); }, }); ``` ## Smoke test A single snapshot can guard your entire help and usage surface against accidental changes. Each step runs `--help` on a different command, so one test covers the root command and every subcommand at once. ```ts import { snapshotTest } from "@cliffy/testing"; import { Command } from "@cliffy/command"; const cli = new Command() .name("shop") .version("1.0.0") .description("Example shop CLI.") .globalOption("-v, --verbose", "Enable verbose output.") .command( "search", new Command() .description("Search for products.") .arguments("") .option("-l, --limit ", "Limit the number of results."), ) .command( "product", new Command() .description("Show a single product.") .arguments(""), ); await snapshotTest({ name: "smoke", meta: import.meta, colors: false, steps: { "--help": { args: ["--help"] }, "search --help": { args: ["search", "--help"] }, "product --help": { args: ["product", "--help"] }, }, async fn() { await cli.parse(Deno.args); }, }); ``` In a real project you would import the command from your entry file instead of defining it inline, and use `denoArgs` to grant your CLI the permissions it needs at runtime. ```ts ignore import { snapshotTest } from "@cliffy/testing"; import { cli } from "./main.ts"; await snapshotTest({ name: "smoke", meta: import.meta, denoArgs: ["--quiet", "--allow-env", "--allow-read"], steps: { "--help": { args: ["--help"] }, "search --help": { args: ["search", "--help"] }, "product --help": { args: ["product", "--help"] }, }, async fn() { await cli.parse(Deno.args); }, }); ```