Documentation
Quick Startβ
Get to it.
- npx
- npm
- yarn
npx @rotini/cli generate my-cli
npm install -g @rotini/cli
rotini generate my-cli
yarn global add @rotini/cli
rotini generate my-cli
cd my-cli
npm run setup
template --help
π
Conceptsβ
Rotini is an opinionated CLI framework that aims to remove the code overhead required to create a Node.JS CLI program. Instead of building your own program, commands, arguments, and flags, rotini has a declarative definition object structure that defines your programβthe only code that you write is for your command operations. When your program is executed, rotini matches the argv parameters passed to your program against your program definition and maps commands, arguments, and flags accordingly. The resulting parsed commands, arguments, and flags are handed to the last matched command operation, allowing you to expect argument and flag values and to perform your program actions.
Rotini is built on a structure of commands, arguments, and flags. The structure and concepts are very similar to the powerful Go CLI framework Cobra.
Commands represent verbs
(actions), Arguments represent nouns
(things) and Flags represent adjectives
(modifiers) for actions.
At the core of rotini is a desire to be declarative, because the best program configurations read in ways that humans are able to easily reason about. Similarly, the best applications read like sentences when used, and as a result users intuitively know how to interact with them.
The pattern to follow is PROGRAM_NAME VERB NOUN --ADJECTIVE
or PROGRAM_NAME COMMAND ARGUMENT --FLAG
.
In the example below we are using git to clone (verb) the repository at the URL (noun) with bare (adjective) behavior.
git clone URL --bare
In the next example we are using kubectl to get (verb) pods (noun) with all-namespaces (adjective) behavior.
kubectl get pods --all-namespaces
Installationβ
- npm
- yarn
npm install rotini
yarn add rotini
Usageβ
Rotini requires a program definition and optionally accepts a program configuration to alter how framework will behave during program building and parsing.
- JavaScript
- TypeScript
#!/usr/bin/env node
const { rotini } = require('rotini');
const definition = {
// see definition below...
};
const configuration = {
// see configuration below...
};
(async () => {
const { results } = await rotini({ definition, configuration });
// log results...
})();
#!/usr/bin/env node
import { rotini, Definition, Configuration } from 'rotini';
const definition: Definition = {
// see definition below...
};
const configuration: Configuration = {
// see configuration below...
};
void (async (): Promise<void> => {
const { results } = await rotini({ definition, configuration });
// log results...
})();
Examplesβ
A few rotini example programs can be found here. These examples can be useful when setting up a rotini cli for the first time or when looking for an example of rotini functionality.
All examples exist as typescript and javascript as well as in commonjs and module format.
Program Definitionβ
Defines a rotini program. See the Definition interface for more information.
Property | Type | Required |
---|---|---|
name | string | true |
description | string | true |
version | string | true |
documentation | string | false |
configuration_files | ConfigurationFile[] | false |
commands | Command[] | false |
positional_flags | PositionalFlag[] | false |
global_flags | GlobalFlag[] | false |
examples | Example[] | false |
usage | string | false |
help | string | false |
Nameβ
Program definition property "name" must be defined, of type "string", and cannot contain spaces.
The program name is used in the auto-generated program help output.
Descriptionβ
Program definition property "description" must be defined and of type "string".
The program description is used in the auto-generated program help output.
Versionβ
Program definition property "version" must be defined and of type "string".
The program version is used in the auto-generated program help output. Additionally, when the check_for_npm_update
configuration property is set to true
the version is used the the NPM registry version comparison.
Documentationβ
Program definition property "documentation" must be of type "string".
The program documentation is used in the auto-generated program help output.
Configuration Filesβ
Program definition property "configuration_files" is the optional array of configuration objects used to setup program configuration files. See the ConfigurationFile interface for more information. When defined it must be an array objects that each contain "id", "directory", and "file" properties. Configuration files must be JSON files.
configuration_file.idβ
Program definition property "configuration_file.id" must be defined, of type "string", and cannot contain spaces.
The configuration file id is used to control which configuration file handlers are returned when getConfigurationFile
is called from inside a command operation. When an unknown id is passed to the getConfigurationFile
function, a configuration error will be thrown. When a known id is passed, the getConfigurationFile
function will return getContent
and setContent
helper functions for getting and setting file contents.
configuration_file.directoryβ
Program definition property "configuration_file.directory" must be defined and of type "string".
The configuration file directory is used as the directory location where rotini will attempt to write the configuration file.
configuration_file.fileβ
Program definition property "configuration_file.file" must be defined and of type "string".
The configuration file file is used as the filename that is written at the provided directory location. The file must have a .json
file extension.
Commandsβ
Program definition property "commands" must be of type "array". Additionally, the same command "name" cannot exist at the same level in the definition. For example, if a "get" command has been registered in an array of commands, then a second "get" command cannot exist within that command array. However, a "get" command could be registered in the commands array of the first "get" command as a subcommand. See the Command interface for more information.
command.nameβ
Command property "name" must be defined, of type "string", and cannot contain spaces.
The command name is used in the final resulting parse object that is handed to the last parsed command. Additionally, the command name is used in the auto-generated help output that is built for each command.
command.descriptionβ
Command property "description" must be defined and of type "string".
The command description is used in the auto-generated help output that is built for each command.
command.aliasesβ
Command property "aliases" must be of type "array", can only contain indexes of type "string", and cannot contain indexes with spaces.
Command aliases have two purposes. First, to control breaking name migration changes for your cli program. If you want to move away from "view" and to "list" for a command, you can alias "list" in releases as a reminder to users of the breaking change until you fully switch to that name. Rotini will output a warning message when the old command name is used if the deprecated
command property is set to true
. The second purpose is simply to have multiple matched names, often used when the command name is too long to fully type out (environment vs env). In this case, you would not want to set the deprecated
property to true
so that you are not indicating a future change to your program.
command.deprecatedβ
Command property "deprecated" must be of type "boolean".
The command deprecated property is used to signify to users that a command will be removed or that a move from one command name to another in a future release will take place. Rotini will output warnings when a command is executed with the command name when deprecated
is set as true
for the command. The warning will not be output if the alias is used. Additionally, deprecated commands will have an additional message in the command help informing users of the deprecation.
command.argumentsβ
Command property "arguments" must of type "array". Additionally, the same argument "name" cannot be used in the same arguments array, only one variadic argument "variant" can be supplied to a command, and if a variadic argument "variant" is registered on a command it must be the last argument in the array.
argument.nameβ
Argument property "name" must be defined, of type "string", and cannot contain spaces.
The argument name is used in the command parse object that is handed to each command operation. The name acts as the key, with the value being the resulting parsed value.
argument.descriptionβ
Argument property "description" must be defined and of type "string".
The argument description is used in the auto-generated command help output that is built for each command.
argument.variantβ
Argument property "variant" must be defined, of type "string", and set as "boolean", "value" or "variadic". Defaults to value
.
Variant | Description |
---|---|
boolean | The boolean variant is used when true or false is expected as the argument value |
value | The value variant is used when a string or number value is expected as the argument value |
variadic | The variadic variant is used when many string or number values are expected as the argument value |
Only one variadic argument can be defined for each command, and the variadic argument must be the last argument defined in the arguments array.
argument.typeβ
Argument property "type" must be defined, of type "string", and set as "string", "number", or "boolean". Defaults to string
.
argument.valuesβ
Argument property "values" must be of type "array" and can only contain indexes of type "argument.type".
argument.validatorβ
Argument property "validator" must be of type "function". The "validator" function is provided the parsed value found for the argument so that additional validation can be performed beyond providing an allowed value set. If a boolean is returned from the "validator" function, true
will result in a noop and false
will throw a default rotini parse error. To provide additional control over the resulting error output, explicitly throwing an error with a custom message will override the default parse error message.
argument.parserβ
Argument property "parser" must be of type "function". The "parser" function is provided the original value and the type-coerced value found for the flag and can be used to additionally manipulate how the value is parsed for the flag.
command.flagsβ
Command property "flags" must of type "array". Additionally, the same flag "name", "short_key", or "long_key" cannot exist at the same level in the definition. However, subcommands with flags can register a previously defined flag "name", "short_key", or "long_key". See Flags below for more information.
flag.nameβ
Flag property "name" must be defined, of type "string", and cannot contain spaces.
flag.descriptionβ
Flag property "description" must be defined and of type "string".
flag.variantβ
Flag property "variant" must be defined, of type "string", and set as "boolean" or "value". Defaults to value
.
flag.typeβ
Flag property "type" must be defined, of type "string", and set as "string", "number", or "boolean". Defaults to string
unless flag.variant is set as "boolean", in which case the value is set as boolean
if not explicitly defined.
flag.short_keyβ
Flag property "short_key" must be of type "string" and cannot contain spaces. A flag must have property "short_key", "long_key", or both "short_key" and "long_key" set, and if both "short_key" and "long_key" are set they cannot be the same value.
flag.long_keyβ
Flag property "long_key" must be of type "string" and cannot contain spaces. A flag must have property "short_key", "long_key", or both "short_key" and "long_key" set, and if both "short_key" and "long_key" are set they cannot be the same value.
flag.valuesβ
Flag property "values" must be of type "array" and can only contain indexes of type "flag.type".
flag.defaultβ
Flag property "default" must be of type "string", "number", or "boolean". The type of the value defined for the flag "default" must match the flag.type, and if an allowed values array is supplied the value must be found in that array.
flag.requiredβ
Flag property "required" must be of type "boolean". If a flag is marked as required, if it is not found by the rotini parser an error will be thrown.
flag.validatorβ
Flag property "validator" must be of type "function". The "validator" function is provided the parsed value found for the flag so that additional validation can be performed beyond providing an allowed value set. If a boolean is returned from the "validator" function, true
will result in a noop and false
will throw a default rotini parse error. To provide additional control over the resulting error output, explicitly throwing an error with a custom message will override the default parse error message.
flag.parserβ
Flag property "parser" must be of type "function". The "parser" function is provided the original value and the type-coerced value found for the flag and can be used to additionally manipulate how the value is parsed for the flag.
A few "reserved" flags names exist for rotini commands:
If a help
flag name is defined in the program definition for a given command, the flag will be set as a boolean "variant" and "type", "values", "validator", and "parser" properties will be ignored. The benefit of defining a help flag on a command rather than letting rotini add a flag for the command would be to control the "short_key" and "long_key" if the default "-h" and "--help" keys are not preferred.
If a force
flag name is defined in the program definition for a given command, the flag will be set as a boolean "variant" and "type", "values", "validator", and "parser" properties will be ignored. When a force flag is defined for a command, the operation will prompt before continuing execution (y/N) unless the flag is passed to override the prompt. Force flags can be beneficial when command operations are destructive.
command.commandsβ
Command property "commands" must of type "array". Additionally, the same command "name" cannot exist at the same level in the definition. For example, if a "get" command has been registered in an array of commands, then a second "get" command cannot exist within that command array. However, a "get" command could be registered in the commands array of the first "get" command as a subcommand.
command.examplesβ
Command property "examples" must be of type "array".
Examples are used in the auto-generated command help output to provide users with example command usages.
example.descriptionβ
Command property "example.description" must be defined and of type "string".
The example description will appear above the example usage in the auto-generated command help output.
example.usageβ
Command property "example.usage" must be defined and of type "string".
The example usage will below above the example description in the auto-generated command help output.
command.operationβ
Command property "operation" must be of type "object". See the Operation interface for more information.
operation.timeoutβ
Operation property "timeout" must be of type "number".
The operation timeout can be used for handlers that resolve a promise as a way to control how long the operation should be allowed to run before the program exists with a timeout error. The timeout should be specified in milliseconds. Defaults to 300000
milliseconds or five minutes.
operation.beforeHandlerβ
Operation property "beforeHandler" must be of type "function".
The operation beforeHandler is called before the handler and the return of the beforeHandler is provided to the handler function. When similar commands use the same arguments and flags, it may be useful to use the beforeHandler as the "input shaping" function that the handler and afterHandler can use without re-formatting.
operation.handlerβ
Operation property "handler" must be of type "function".
The operation handler is called after the beforeHandler and before the afterHandler. The return of the beforeHandler is provided to the handler function. Commands that do not define a handler will output their help when called.
operation.afterHandlerβ
Operation property "afterHandler" must be of type "function".
The operation afterHandler is called after the handler. The return of the beforeHandler and the handler are both provided to the afterHandler function.
operation.onHandlerSuccessβ
Operation property "onHandlerSuccess" must be of type "function".
The operation onHandlerSuccess is called after a successful beforeHandler, handler, and afterHandler have executed. Only a handler needs to be defined and executed for the onHandlerSuccess function to be executed.
operation.onHandlerFailureβ
Operation property "onHandlerFailure" must be of type "function".
The operation onHandlerFailure is called after a failure in the beforeHandler, handler, or afterHandler execution.
operation.onHandlerTimeoutβ
Operation property "onHandlerTimeout" must be of type "function".
The operation onHandlerTimeout is only called if the handler returns the result of a promise, and only if the execution time of the handler exceeds the timeout threshold.
command.usageβ
Command property "usage" must be of type "string". This property will override the generated command usage if set.
The command usage is used in the auto-generated command help output.
command.helpβ
Command property "help" must be of type "string". This property will override the generated command help output if set.
The command help value is output to console when the command help is triggered from passed flag or command error.
Positional Flagsβ
Program definition property "positional_flags" must be of type "array". Additionally, the same flag "name", "short_key", or "long_key" cannot exist in the array.
positional_flag.nameβ
Positional flag property "name" must be defined, of type "string", and cannot contain spaces.
positional_flag.descriptionβ
Positional flag property "description" must be defined and of type "string".
positional_flag.variantβ
Positional flag property "variant" must be defined, of type "string", and set as "boolean" or "value". Defaults to value
.
positional_flag.typeβ
Positional flag property "type" must be defined, of type "string", and set as "string", "number", or "boolean". Defaults to string
unless flag.variant is set as "boolean", in which case the value is set as boolean
if not explicitly defined.
positional_flag.short_keyβ
Positional flag property "short_key" must be of type "string" and cannot contain spaces. A flag must have property "short_key", "long_key", or both "short_key" and "long_key" set, and if both "short_key" and "long_key" are set they cannot be the same value.
positional_flag.long_keyβ
Positional flag property "long_key" must be of type "string" and cannot contain spaces. A flag must have property "short_key", "long_key", or both "short_key" and "long_key" set, and if both "short_key" and "long_key" are set they cannot be the same value.
positional_flag.valuesβ
Positional flag property "values" must be of type "array" and can only contain indexes of type "flag.type".
positional_flag.defaultβ
Positional flag property "default" must be of type "string", "number", or "boolean". The type of the value defined for the flag "default" must match the flag.type, and if an allowed values array is supplied the value must be found in that array.
positional_flag.requiredβ
Positional flag property "required" must be of type "boolean". If a flag is marked as required, if it is not found by the rotini parser an error will be thrown.
positional_flag.validatorβ
Positional flag property "validator" must be of type "function". The "validator" function is provided the parsed value found for the flag so that additional validation can be performed beyond providing an allowed value set. If a boolean is returned from the "validator" function, true
will result in a noop and false
will throw a default rotini parse error. To provide additional control over the resulting error output, explicitly throwing an error with a custom message will override the default parse error message.
positional_flag.parserβ
Positional flag property "parser" must be of type "function". The "parser" function is provided the original value and the type-coerced value found for the flag and can be used to additionally manipulate how the value is parsed for the flag.
positional_flag.operationβ
Positional flag property "operation" must be of type "function". The "operation" function is called when the positional flag is passed and found by the parser. If the defined flag is of type "value", it will be handed the remaining passed arguments.
A few default positional flags exist for rotini programs:
The help
flag defined for programs is defined as a positional flag. If a help flag is seen in the first argv parameter position, it will output the program help and exit. If a positional flag with the name of help
is defined in the positional flag array, it will override the help flag defaults. This may be useful if a different short_key or long_key is desired, or if additional actions are desired over the default operation behavior.
The version
flag defined for programs is defined as a positional flag. If a version flag is seen in the first argv parameter position, it will output the program version and exit. If a positional flag with the name of version
is defined in the positional flag array, it will override the version flag defaults. This may be useful if a different short_key or long_key is desired, or if additional actions are desired over the default operation behavior.
When the check_for_npm_update
property is set as true
, an update
flag is defined for programs is defined as a positional flag. If an update flag is seen in the first argv parameter position, it will attempt to update the cli to latest npm version of the package. If a positional flag with the name of update
is defined in the positional flag array, it will override the update flag defaults. This may be useful if a different short_key or long_key is desired, or if different functionality is desired over the default operation behavior.
Global Flagsβ
Program definition property "global_flags" must be of type "array". Additionally, the same flag "name", "short_key", or "long_key" cannot exist in the array.
global_flag.nameβ
Global flag property "name" must be defined, of type "string", and cannot contain spaces.
global_flag.descriptionβ
Global flag property "description" must be defined and of type "string".
global_flag.variantβ
Global flag property "variant" must be defined, of type "string", and set as "boolean" or "value". Defaults to value
.
global_flag.typeβ
Global flag property "type" must be defined, of type "string", and set as "string", "number", or "boolean". Defaults to string
unless flag.variant is set as "boolean", in which case the value is set as boolean
if not explicitly defined.
global_flag.short_keyβ
Global flag property "short_key" must be of type "string" and cannot contain spaces. A flag must have property "short_key", "long_key", or both "short_key" and "long_key" set, and if both "short_key" and "long_key" are set they cannot be the same value.
global_flag.long_keyβ
Global flag property "long_key" must be of type "string" and cannot contain spaces. A flag must have property "short_key", "long_key", or both "short_key" and "long_key" set, and if both "short_key" and "long_key" are set they cannot be the same value.
global_flag.valuesβ
Global flag property "values" must be of type "array" and can only contain indexes of type "flag.type".
global_flag.defaultβ
Global flag property "default" must be of type "string", "number", or "boolean". The type of the value defined for the flag "default" must match the flag.type, and if an allowed values array is supplied the value must be found in that array.
global_flag.requiredβ
Global flag property "required" must be of type "boolean". If a flag is marked as required, if it is not found by the rotini parser an error will be thrown.
global_flag.validatorβ
Global flag property "validator" must be of type "function". The "validator" function is provided the parsed value found for the flag so that additional validation can be performed beyond providing an allowed value set. If a boolean is returned from the "validator" function, true
will result in a noop and false
will throw a default rotini parse error. To provide additional control over the resulting error output, explicitly throwing an error with a custom message will override the default parse error message.
global_flag.parserβ
Global flag property "parser" must be of type "function". The "parser" function is provided the original value and the type-coerced value found for the flag and can be used to additionally manipulate how the value is parsed for the flag.
Examplesβ
Program definition property "examples" must be of type "array".
Examples are used in the auto-generated program help output to provide users with example program command usages.
example.descriptionβ
Program definition property "example.description" must be defined and of type "string".
The example description will appear above the example usage in the auto-generated command help output.
example.usageβ
Program definition property "example.usage" must be defined and of type "string".
The example usage will below above the example description in the auto-generated command help output.
Usageβ
Program definition property "usage" must be of type "string". This property will override the generated program usage if set.
The program usage is used in the auto-generated program help output.
Helpβ
Program definition property "help" must be of type "string". This property will override the generated program help output if set.
The program help value is output to console when the program help is triggered from passed flag or command error.
Program Configurationβ
The optional configuration object used to control the rotini framework behavior. See the Configuration interface for more information.
Property | Type | Default |
---|---|---|
strict_commands | boolean | true |
strict_flags | boolean | true |
strict_help | boolean | false |
strict_mode | boolean | false |
check_for_npm_update | boolean | false |
Strict Commandsβ
Program configuration property "strict_commands" must be of type "boolean". This property controls whether or not rotini will ignore parameters that it parses as commands when they cannot be mapped according to the provided program definition. When the property is set to true
, rotini will output a parse error message to the console. Defaults to true
.
Strict Flagsβ
Program configuration property "strict_flags" must be of type "boolean". This property controls whether or not rotini will ignore parameters that it parses as flags when they cannot be mapped according to the provided program definition. When the property is set to true
, rotini will output a parse error message to the console. Defaults to true
.
Strict Helpβ
Program configuration property "strict_help" must be of type "boolean". This property controls whether or not rotini will output help when suggested commands (Did you mean one of these commands?) are output on parse error. When set to true
, the program or command help will always be output on error. Defaults to false
.
Strict Modeβ
Program configuration property "strict_mode" must be of type "boolean". This property controls whether or not rotini will perform additional checks against the passed program definition. Strict error output can be helpful when developing a rotini cli program, but these additional checks result in additional overhead that a correctly configured rotini cli program would benefit from skipping to speed up build and parse times. Defaults to false
.
Check NPM Registry Versionβ
Program configuration property "check_for_npm_update" must be of type "boolean". This property controls whether or not rotini will intermittently check a registry for package updates. The NPM registry is used by default, but a different registry can be used by setting the NPM_CONFIG_REGISTRY
environment variable. Defaults to false
.
- JavaScript
- TypeScript
const configuration = {
strict_commands: true,
strict_flags: true,
strict_help: false,
strict_mode: false,
check_for_npm_update: false
};
const configuration: Configuration = {
strict_commands: true,
strict_flags: true,
strict_help: false,
strict_mode: false,
check_for_npm_update: false
};
Releaseβ
Ensure your program is executable:
chmod +x index.js
Add a bin
section to your project package.json
file so that you can map a command name for your project to your project entry point:
{
"bin": {
"my-cli": "./index.js"
}
}
To test your program with your command name locally, you can create a symlink for your project folder:
npm link
Now you should be able to test your program:
$ my-cli hello-world
$ Hello World!
After testing your program, be sure to unlink:
npm unlink
Publish your project to the npm registry, and then install it globally on your machine:
$ npm publish
$ npm install -g my-cli
$ my-cli hello-world
$ Hello World!
Once your package is published to the npm registry, anyone can download and use your program as long as they have npm and node setup on their machine. If you wish to create an executable that bundles the required runtime with your program, you can use a library like pkg to make executables for Windows, MacOS, and Linux.