Create CLI applications with glee π
If I should maintain this repo, please βοΈ
DM me on Twitter if you have questions or suggestions.
Clee is a library for creating CLI applications with TypeScript. It is designed to be easy to use, and easy to test. It is also easy to break commands into modules, for reuse as an API or CLI.
- β¨ TypeScript native
- π¦ Enables extreme modularity
- π§ͺ Easy to test
- π§ Reusable - call as an API, or parse arguments as a CLI
- π€ Optional user prompting for missing arguments
- Includes parsers for 11 common types
- Includes formatters in 5 common patterns
- Includes configurable figlet titles
- Automatically load version from package.json
Clee is heavily inspired by commander. The base unit of Clee is a Command. A Command is a function that takes arguments and options, and returns a result:
import clee from "clee";
const command = clee("my-command")
.argument("[name]", "Your name")
.action((name) => {
return `Hello, ${name}!`;
});
Commands are reusable as both programs that you can run from the command line, and as functions that you can call from anywhere in your code:
// Call as a function
command("Bob");
// Parse arguments from the command line
command.parse();
Commands have several stages, and each stage can be customized:
- Parser - Converts a raw string to a parsed value
- Action - Takes parsed arguments and options, and returns a result
- Formatter - Takes the result of an action, and formats it for output to the console
When a Command is called as a function, if follows the following steps:
[function input]
-> [parser]
-> [action]
-> [function return]
When a command is parsed from the command line, the result is also formatted and then output to the console:
[CLI input]
-> [parser]
-> [action]
-> [formatter]
-> [CLI output]
Each command can have a name, title, and description. These are used when generating the help screen:
import clee, { pathParser } from "clee";
clee("my-program")
.title({ font: "Slant" }) // Settings for creating a figlet title
.description("Description") // Custom description to display on the help screen
Example help screen:
____ ___ __ __ ____ _________ ____ __________ _____ ___
/ __ `__ \/ / / /_____/ __ \/ ___/ __ \/ __ `/ ___/ __ `/ __ `__ \
/ / / / / / /_/ /_____/ /_/ / / / /_/ / /_/ / / / /_/ / / / / / /
/_/ /_/ /_/\__, / / .___/_/ \____/\__, /_/ \__,_/_/ /_/ /_/
/____/ /_/ /____/
Description
Usage: my-program
Options:
-h, --help Display help for command
Commands can have arguments and options. Either can be required or optional.
Required arguments are denoted using square brackets, while optional arguments are denoted using angle brackets.
"<required>"
"[optional]"
Additionally, three dots can be used to denote a variadic argument. However, only the last argument can be variadic:
"<required_variadic...>"
"[optional_variadic...]"
Arguments can be required or optional, and can have a description and a custom parser.
import clee from "clee";
clee("my-program")
.argument("<name>") // An required argument with no description, using the default string parser
.argument("[path]", "Description", pathParser) // An optional argument using a custom parser
Options can have both a short and long flag, or a long flag only. They can also have a description, and a custom parser.
import clee, { stringParser } from "clee";
clee("my-program")
.option("--flag") // An option with no description, using the default boolean parser
.option("--required", "<value>") // A required option
.option("--header", "[value...]") // An optional variadic option
.option("-o", "--other", "Description", stringParser) // An option using a custom parser
Values can be provided to options with either a space or an equals sign.
--flag value
--flag=value
Additionally, compound flags can be used to group multiple short flags together:
-abc
Variadic options can be provided by listing multiple values, or by repeating the flag multiple times:
--header value1 value2
--header value1 --header value2
Arguments and Options can both supply custom parsers. Several are provided with clee:
import {
parseString,
parseBoolean,
parseNumber,
parseInt,
parseFloat,
parseJSON,
parseDate,
parseURL,
parsePath,
parseFile,
parseDirectory,
parseCSV
} from "clee";
Additionally, a few prompt parsers are provided. If no value is provided, these will prompt the user for input, using Enquirer:
import {
promptString,
promptBoolean,
promptNumber,
promptInt,
promptFloat
} from "clee";
A command's action is called when the command is parsed from the command line. The action can be async, and can return a value, which will be passed to the formatter.
import clee from "clee";
clee("clee").action(() => {
console.log("Hello from clee");
});
Commands can also have formatters. Formatters are bypassed if a command is called directly, but are used when the command is parsed from the command line:
import clee from "clee";
clee("clee")
.action(() => {
return ["line 1", "line 2"];
})
.format((result) => {
return result.join("\n");
});
Several built-in formatters are also included with clee:
import {
formatDefault,
formatBuffer,
formatJSON,
formatJSONPretty,
formatStringsToLines
} from "clee";
By default, parse will use process.argv.slice(2)
, but a custom array can also be provided:
// Read from `process.argv.slice(2)`:
cmd.parse();
// Provide custom arguments:
cmd.parse(["one", "two", "three"]);
Commands can also be nested, and have their own arguments, options, actions, and sub-commands.
import clee from "clee";
const sub = clee("sub")
.action(() => {
console.log("Hello from sub command");
});
clee("my-program")
.command(sub);
Run allows you to run a subcommand from a parent command:
import clee from "clee";
const sub = clee("sub")
.action(() => {
console.log("Hello from sub command");
});
const cmd = clee("my-program")
.command(sub);
cmd.run("sub"); // Hello from sub command
The help flag can be customized with a short flag, long flag, and description:
import clee from "clee";
clee("my-program")
.help("-e", "--example", "Custom help description");
By default, -h
and --help
are used.
A version flag can be added by either hardcoding the value, or by passing a path to a file in project with a package.json:
import clee from "clee";
clee("my-program")
.version("1.0.0") // Hardcoded version
clee("my-program-2")
.version(import.meta.url) // Version from package.json
If an absolute path is provided, the nearest package.json's version will be used.
Additionally, the version flags and description can also be customized:
import clee from "clee";
clee("my-program")
.version("1.0.0", "-e", "--example", "Custom version description")
By default, -v
and --version
are used.
Types are also provided for deriving various types from a Command
:
import {
CommandName,
CommandInput,
CommandArguments,
CommandOptions,
CommandResult,
CommandSubCommands
} from "clee";
type Name = CommandName<typeof command>;
type Input = CommandInput<typeof command>;
type Arguments = CommandArguments<typeof command>;
type Options = CommandOptions<typeof command>;
type Result = CommandResult<typeof command>;
type SubCommands = CommandSubCommands<typeof command>;
- ls - A simple ls program
- enquirer: Stylish, intuitive and user-friendly prompt system. Fast and lightweight enough for small projects, powerful and extensible enough for the most advanced use cases.
- figlet: Creates ASCII Art from text. A full implementation of the FIGfont spec.
- get-module-pkg: Get your module's package.json without importing it
- read-boolean: A simple utility for parsing boolean values from strings.
- typed-case: Convert between typesafe string casings
- types-json: Type checking for JSON objects
- @types/figlet: TypeScript definitions for figlet
- @types/node: TypeScript definitions for Node.js
- autorepo: Autorepo abstracts away your dev dependencies, providing a single command to run all of your scripts.
- chalk: Terminal string styling done right
MIT - The MIT License