New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

cilly

Package Overview
Dependencies
Maintainers
1
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cilly - npm Package Compare versions

Comparing version

to
1.0.9

1

dist/cli-command.d.ts

@@ -62,2 +62,3 @@ export declare type ArgumentValue = any;

name: string;
version?: string;
description?: string;

@@ -64,0 +65,0 @@ opts: OptionDefinition[];

@@ -122,2 +122,3 @@ "use strict";

name: this.name,
version: this.version,
description: this.description,

@@ -124,0 +125,0 @@ opts: Object.values(this.opts).map(o => this.dumpOption(o)),

2

package.json
{
"name": "cilly",
"version": "1.0.8",
"version": "1.0.9",
"description": "The last library you'll ever need for building intuitive, robust and flexible CLI tools with Node.js and TypeScript.",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

@@ -13,7 +13,12 @@ # Cilly

- [Documentation](#documentation)
- [Commands](#commands)
- [parse()](#parse)
- [process()](#process)
- [Subcommands](#subcommands)
- [Option inheritance](#option-inheritance)
- [Arguments](#arguments)
- [Variadic arguments](#variadic-arguments)
- [Options](#options)
- [Commands](#commands)
- [Subcommands](#subcommands)
- [Option inheritance](#option-inheritance)
- [Option arguments](#option-arguments)
- [Negating flags](#negating-flags)
- [Validators](#validators)

@@ -25,3 +30,3 @@ - [Hooks](#hooks)

- [Custom help handlers](#custom-help-handlers)
- [Custom exception handling](#custom-exception-handling)
- [Exception handling](#exception-handling)

@@ -34,3 +39,3 @@ # Installation

# Basic usage
With the file `build.ts`:
With a file called `build.ts`:
```typescript

@@ -142,2 +147,114 @@ #!/usr/local/bin/ts-node

## Commands
Commands are represented by `CliCommand` class instances.
The command constructor has the following signature:
```
CliCommand(name: string, opts?: { inheritOpts?: boolean, consumeUnknownOpts?: boolean })
```
The `CliCommand` API looks as follows:
```typescript
new CliCommand('command')
.withVersion() // Set the version
.withDescription() // Set the description
.withHandler() // Set the function to run when command is invoked
.withArguments() // Register arguments
.withOptions() // Register options
.withSubCommands() // Register subcommands
.withHelpHandler() // Custom handling of the --help flag
.parse() // Generate { args, opts, extra } from process.argv, run onParse() hooks
.process() // Run parse(), hooks, and call command handler
.help() // Call the helpHandler
.dump() // Dump the command description to an object (useful for documentation)
```
### parse()
The `parse()` method takes the command line arguments (`process.argv`) and parses it to produce the `{args, opts, extra}` objects passed to handlers.
The `parse()` method does not call any hooks and does not invoke command handlers, and thus does not require a command handler to be defined.
```typescript
const cmd = new CliCommand('build')
.withArguments({ name: 'address' })
.withOptions({ name: ['-g', '--garage'], negatable: true })
const { args, opts, extra } = cmd.parse(process.argv)
```
#### Extra
All arguments that cannot be parsed are put in the `extra` argument to command handlers.
If desired, a command can choose to to treat unknown options similarly by setting the `consumeUnknownOpts` flag:
```typescript
const cmd = new CliCommand('build', { consumeUnknownOpts: true })
const { args, opts, extra } = cmd.parse(process.argv)
console.log(extra)
```
With the input:
```
build --an --option --that --isnt --defined
```
The above would print `['--an', '--option', '--that', '--isnt', '--defined']`
### process()
The `process()` method (asynchronous) calls `parse()`, runs argument and options hooks, validators, and invokes the appropriate command handler with the output of `parse()`. The result of `await process()` is whatever the command handler returns.
```typescript
const cmd = new CliCommand('build')
.withArguments({ name: 'address' })
.withOptions({ name: ['-g', '--garage'], negatable: true })
// The args, opts, extra comes from .parse()
.withHandler((args, opts, extra) => {
return new House(args.address, opts.garage)
})
const house = await cmd.process(process.argv)
```
### Subcommands
Commands can have an arbitrary number of subcommands, allowing developers to decouple their command handling logic.
These are registered with the `withSubCommands()` method:
```typescript
new CliCommand('build')
.withSubCommands(
new CliCommand('house')...,
new CliCommand('apartment')...,
)
```
A command **cannot** have both arguments and subcommands. This is because subcommands are invoked be essentially passing command names as arguments, and there would be no good way to tell the two apart.
Subcommands are displayed in the help text:
```
Usage: build [options]
Options:
...
Commands:
house <address> [state] [options]
apartment <address> [options]
```
#### Option inheritance
Contrary to `commander.js`, subcommands can share options and arguments in the parent command(s).
By setting the `inheritOpts` flag to true when constructing the command, the command inherits all options from the parent command:
```typescript
new CliCommand('build')
.withOptions({ name: ['-vb', '--verbose'] })
.withSubCommands(
new CliCommand('house', { inheritOpts: true })
.withOptions({ name: ['-r', '--rooms'] })
)
```
The `opts` object in the `house` command handler will contain both `verbose` and `rooms`:
```typescript
opts: {
verbose: ...,
rooms: ...
}
```
## Arguments

@@ -305,12 +422,198 @@ Arguments are provided to a command with the `withArguments()` chain method.

## Commands
### Subcommands
### Option inheritance
## Validators
Options and arguments can be assigned validators that are called on `.process()`.
A validator has the following signature:
```typescript
type Validator = (value: any, parsed: { args, opts, extra }) => string | boolean | Promise<string | boolean>
```
1. The `value` argument is the value assigned to the option or argument.
2. The `parsed` argument is the result of `.parsed()`; the result of parsing the command line arguments.
If a validator returns `true`, the value is interpreted as valid. Otherwise, if the validator returns `false`, a `ValidationError` is thrown with a default error message.
- If the validator returns a string, that string is used as the error message.
```typescript
new CliCommand('build')
.withArguments({ name: 'address', validator: (value, parsed) => {
if (!validators.isValidAddress(value)) {
return `The address ${value} is not a valid address.`
}
return true
}})
```
## Hooks
It can be useful to intercept an option or argument before it's passed to the command handler.
To do this, we can use `onParse()` and `onProcess()` hooks on both options and arguments.
### onParse()
When registered on an option or argument, an `onParse()` hook is called immediately when that argument or option is parsed from the command line.
This is useful for implementing interrupting flags such as `--help`, `--version`, and so on.
An `OnParseHook` has the following signature:
```typescript
type OnParseHook = (value: any, parsed: { args, opts, extra }) => void
```
1. The `value` argument is the value assigned to the option or argument.
2. The `parsed` argument is what the `.parse()` method has parsed so far. Note that this object may not be complete when the hook is invoked.
```typescript
new CliCommand('build')
.withOptions({ name: ['-v', '--version'], onParse: (value, parsed) => {
console.log(version)
process.exit()
}})
```
### onProcess()
Contrary to `onParse()` hooks, `onProcess()` hooks are run after `parse()` has finished.
Hooks also allow you to change the value of an option or argument at processing time, before the command handler is invoked.
This can be very useful for designing "user-proof" CLIs that prompt the users for the information they need in a nice looking and robust manner.
An `OnProcessHook` has the following signature:
```typescript
type OnProcessHook = (value: any, parsed: { args, opts, extra }, assign: (value: any) => Promise<void>) => void | Promise<void>
```
1. The `value` argument is the value assigned to the option or argument
2. The `parsed` argument is the result of `parse()`
3. The `assign` argument is a function that, when called with a new value:
1. Runs the value through the option/argument validator (if one exists)
2. Assigns the value to the option/argument
```typescript
new CliCommadn('build')
.withArguments({ name: 'address', onProcess: async (value, parsed, assign) => {
if (value === undefined) {
const address = await prompts.Prompt('Please enter your address')
await assign(address) // Validate and assign
}
}})
```
## Generating documentation
The `CliCommand.dump()` method dumps the entire command (and its subcommands) to an easily readable object of type `CommandDefinition`. This is useful for generating documentation.
A `CommandDefinition` has the following signature:
```typescript
export type CommandDefinition = {
name: string,
version: string,
description?: string,
opts: OptionDefinition[],
args: ArgumentDefinition[],
subCommands: CommandDefinition[]
}
type OptionDefinition = {
name: [string, string],
args: ArgumentDefinition[],
description?: string,
required?: boolean,
negatable?: boolean,
defaultValue?: any
}
type ArgumentDefinition = {
name: string,
description?: string,
required?: boolean,
defaultValue?: any,
variadic?: boolean
}
```
When printing the `help` text, this is done completely from the `CommandDefinition` objects.
While out of scope for this specific package, one could dream of a package that could take a `CommandDefinition` object and generate a nice looking documentation page :eyes:
Here's an example of a command dump:
```typescript
const cmd = new CliCommand('build')
.withDescription('Build a home')
.withArguments({ name: 'address', required: true })
.withOptions(
{ name: ['-r', '--residents'], required: false, args: [ {name: 'residents', variadic: true} ] },
{ name: ['-d', '--doors'], defaultValue: 1 }
)
console.log(cmd.dump())
```
Produces:
```typescript
{
"name": "build",
"description": "Build a home",
"opts": [
{
"name": [
"-h",
"--help"
],
"description": "Display help for command",
"args": []
},
{
"name": [
"-r",
"--residents"
],
"required": false,
"args": [
{
"name": "residents",
"variadic": true
}
]
},
{
"name": [
"-d",
"--doors"
],
"args": [],
"defaultValue": 1
}
],
"args": [
{
"name": "address",
"required": true
}
],
"subCommands": []
}
```
## Custom help handlers
## Custom exception handling
You can use the `withHelpHandler()` method to override the default `help` text.
```typescript
new CliCommand('build')
.withHelpHandler((command: CommandDefinition) => {
console.log(`This is the documentation for ${command.name} (${command.definition})`)
...
process.exit()
})
```
## Exception handling
All exceptions thrown by `cilly` extend the `CillyException` class. If you want to catch each exception and handle them individually, here's the full list of exceptions thrown by `cilly`:
```typescript
class CillyException extends Error
class UnknownOptionException extends CillyException
class UnknownSubcommandException extends CillyException
class InvalidNumOptionNamesException extends CillyException
class InvalidShortOptionNameException extends CillyException
class InvalidLongOptionNameException extends CillyException
class InvalidCommandNameException extends CillyException
class InvalidArgumentNameException extends CillyException
class ExpectedButGotException extends CillyException
class NoCommandHandlerException extends CillyException
class DuplicateArgumentException extends CillyException
class DuplicateOptionException extends CillyException
class DuplicateCommandNameException extends CillyException
class NoArgsAndSubCommandsException extends CillyException
class ValidationError extends CillyException
```