https://github.com/alwaysai/alwayscli
A framework for building command-line interfaces in Node.js
https://github.com/alwaysai/alwayscli
alwaysai cli nodejs typescript
Last synced: 4 months ago
JSON representation
A framework for building command-line interfaces in Node.js
- Host: GitHub
- URL: https://github.com/alwaysai/alwayscli
- Owner: alwaysai
- License: other
- Created: 2019-02-27T23:51:16.000Z (over 7 years ago)
- Default Branch: main
- Last Pushed: 2024-05-06T21:21:50.000Z (about 2 years ago)
- Last Synced: 2025-06-22T08:47:05.670Z (about 1 year ago)
- Topics: alwaysai, cli, nodejs, typescript
- Language: TypeScript
- Homepage:
- Size: 542 KB
- Stars: 6
- Watchers: 9
- Forks: 2
- Open Issues: 3
-
Metadata Files:
- Readme: readme.md
- License: LICENSE
Awesome Lists containing this project
README
# `alwaysCLI` [](https://travis-ci.com/alwaysai/alwayscli)
A framework for building command-line interfaces (CLIs) in Node.js. This package includes runtime JavaScript files suitable for Node.js >=8 as well as the corresponding TypeScript type declarations.
## Usage
```
npm install @alwaysai/alwayscli
```
In an `alwaysCLI` program, commands are organized into a tree. Each "leaf" represents an action whereas "branches" collect and organize leaves. For example, in the command `alwaysai user login`, `alwaysai` is the "root" command (a branch), `user` is a branch of commands related to user authentication and `login` is the action leaf. Your CLI need not have branches. Here is a simple CLI whose root is a leaf:
```ts
// readme.ts
import {
CliLeaf,
CliFlagInput,
CliNumberArrayInput,
runCliAndExit,
} from '@alwaysai/alwayscli';
export const root = CliLeaf({
name: 'multiply',
description: 'Multiply numbers and print the result',
positionalInput: CliNumberArrayInput({ required: true }),
namedInputs: {
squared: CliFlagInput({
description: 'Square the result before printing it',
}),
},
action(args, { squared }) {
const multiplied = args.reduce((a, b) => a * b, 1);
if (squared) {
return multiplied * multiplied;
}
return multiplied;
},
});
if (require.main === module) {
runCliAndExit(root);
}
```
Here's how that behaves as a CLI.
```
$ ts-node readme.ts
Usage: multiply [...] []
Multiply numbers and print the result
Options:
[--squared] : Square the result before printing it
Error: " [...]": Value is required
```
With arguments:
```
$ ts-node readme.ts 1 2 3
6
$ ts-node readme.ts 1 2 3 --squared
36
```
The `if (require.main === module)` conditional is Node.js for "if this file is the entry point", which is `true` when you do `ts-node readme.ts`, but not when you do `import { root } from './readme.ts'` for unit tests for example.
More generally the usage of an `alwaysCLI` CLI is:
```
--name --
```
To invoke an action the user provides (in order):
- zero or more branch names
- a leaf name
- zero or more positional args
- zero or more "options" (inputs of the form `--foo bar`)
## API
### Input
TODO
### CliLeaf({name, description?, args?, options?, action, hidden?, version?})
A factory for creating "action" commands. Returns the newly-created `leaf`.
#### name
If this "leaf" is a subcommand, `name` is the string that the user will pass as the "subcommand" argument to invoke this action. If this "leaf" is the root command, `name` should be the CLI's name.
#### description
(Optional) A string that will be included in `Usage:` if present.
#### positionalInput
(Optional) An `Input` for
#### namedInputs
(Optional) An object of named `Input`s, for example:
```ts
const options = {
path: createStringInput({
description: 'An absolute or relative path',
}),
}
```
The `args` and `options` properties define how the command-line arguments get parsed and transformed before being passed into the `action` function.
#### action(args, options)
The function that defines your command logic. `action` can return a value synchronously like in the "multiply" example above, or it can be an `async` function that returns a `Promise`. If `action` returns/resolves a value, that value is `console.log`ged before the CLI exits. If `action` throws/rejects, the exception is `console.log`ged before the CLI exits. That means that if you don't want the user to see a stack trace, your `action` should throw a `string` instead of an `Error` object. The type of the `args` argument received by `action` is derived by the `args` property of the leaf. Similarly, the `options` argument type is derived from `leaf.options`.
#### hidden
(Optional) `boolean`
#### version
(Optional) `string`. If provided, this string will be printed when the user does `cli --version` or `cli -v`. If this value is not provided, alwaysCLI will attempt to find a version string in your package.json file.
### CliBranch({name, description, subcommands, hidden?})
A factory function similar to `CliLeaf`. Returns the newly-created `Branch` object.
#### name
If this "branch" is not the root command, `name` is the string that the user will pass as the "subcommand" argument to invoke actions in this part of the command tree. If this "branch" command is the root command, `name` should be the CLI's name.
#### description
(Optional) A string that will be included in `Usage:` if present.
#### subcommands
An array of `Branch` and/or `Leaf` objects.
#### hidden
(Optional) `boolean`
### createCli(root)
Returns a function of the form `(...args: string[]) => Promise` that can be invoked as e.g. `cli('foo', 'bar')` for unit tests or as `cli(process.argv.slice(2))` in an executable CLI script.
#### root
A `Leaf` or `Branch`
### ArgvInterface
`cli` is a function that takes command-line arguments (strings) as input and returns a `Promise` representing the execution of the arguments. We export `cli` so that we can unit test it [like so](src/examples/__tests__/readme.test.ts).
## More information
This library has a couple dozen unit tests with >90% coverage. If you want to see more examples of how things works, check out the `.test.ts` files in the [src](src) directory. Also check out [src/examples](src/examples). If you encounter any bugs or have any questions or feature requests, please don't hesitate to file an issue or submit a pull request on this project's repository on GitHub.
## Release procedure
To release the package to [npmjs.org](https://www.npmjs.com/package/@alwaysai/alwayscli) follow the steps:
- publish new version: npm run publish:
- check the [github pipeline](https://github.com/alwaysai/alwayscli/actions) running, if successful a new version will be created and published to npmjs.org
- to get the auto-generated commit and tags, simply pull: git pull
## Pipeline is lintng, unit testing and building package on:
- [x] Ubuntu latest / Node.js: 16.x, 18.x, 20.x, 22.x
- [x] MacOS latest / Node.js: 16.x, 18.x, 20.x, 22.x
- [x] Windows latest / Node.js: 16.x, 18.x, 20.x, 22.x