Gathering detailed insights and metrics for @why-ts/cli
Gathering detailed insights and metrics for @why-ts/cli
npm install @why-ts/cli
Typescript
Module System
Node Version
NPM Version
71.9
Supply Chain
98.5
Quality
80.1
Maintenance
100
Vulnerability
98.9
License
Total Downloads
683
Last Day
1
Last Week
3
Last Month
19
Last Year
683
Minified
Minified + Gzipped
Latest Version
0.0.1
Package Id
@why-ts/cli@0.0.1
Unpacked Size
71.83 kB
Size
17.50 kB
File Count
77
NPM Version
10.2.3
Node Version
18.19.0
Publised On
11 Aug 2024
Cumulative downloads
Total Downloads
Last day
0%
1
Compared to previous day
Last week
-76.9%
3
Compared to previous week
Last month
58.3%
19
Compared to previous month
Last year
0%
683
Compared to previous year
Building robust and maintainable command-line interfaces (CLIs) in TypeScript.
Commands are constructed with the command()
call and added to a Program with a string name.
Command handler is registered by providing a callback to the .handle()
function.
1import { program, command, option as o } from '@why-ts/cli'; 2const output = await program() 3 .command( 4 'ls', 5 command().handle(() => console.log('Running list')) 6 ) 7 .run(process.argv.slice(2));
Command objects are immutable. Any function that returns a Command
instance is always a new instance.
1const command1 = command(); 2const command2 = command1.option('foo', o.string()); 3const command3 = command2.handle(console.log); 4console.log(command1 === command2); // false 5console.log(command2 === command3); // false
A command hander is a function that gets invoked when user specifies to run the command
(first entry in the string array passed to .run()
is the command name).
The handler function will receive the following arguments:
args
: Parsed type-safe argsargv
: Raw args passed to program.run()
logger
: Logger
instance (see more in the Configuration section below)prompter
: Prompter
instance (see more in the Configuration section below)Since Commands are immutable, it is possible redefine/override a handler and invoke the "parent" one.
This is usually useful for implementing options and logic that are shared by multiple commands.
Basic:
1const command = command().handle({args, argv}) => { 2 console.log(args); // typed args 3 console.log(argv); // raw argv string array 4}
Share options and logic between multiple commands:
1const common = command() 2 .option('working-directory', o.string()) 3 .handle({args} => { 4 if(args.workingDirectory) 5 process.chdir(args.workingDirectory); 6}); 7 8const foo = common 9 .options('foo', o.string()) 10 .handle(({args}, _super) => { 11 await _super(); // change directory logic is implemented in base command 12 console.log(args.foo); 13 });
Note that in the following example, the first handler is shadowed
because it is not explicitly called via the _super
pattern in the second handler
1command() 2 .handle(() => console.log('foo')) 3 .handle(() => console.log('bar'));
Metadata for a command (e.g. description) can be specified in the command()
constructor call, or overridden via the .meta()
call
1const c1 = command({ description: 'Foo' }); 2const c2 = c1.meta({ description: 'Bar' }); 3const c3 = c1.meta((base) => ({ ...base, description: `${base.description} & Bar` }));
Options are are constructed with the option.<type>()
call and added to a Command with a string name.
The options can be accessed in the handler via the args
field.
The following types are currently supported:
o.string()
: single string option, e.g. --foo=orange
=> {foo: 'orange'}
o.strings()
: multiple string options, e.g. --foo=orange --foo=apple
=> {foo: ['orange', 'apple']}
o.number()
: single number option, e.g. --foo=42
=> {foo: 42}
o.numbers()
: multiple number options, e.g. --foo=42 --foo=100
=> {foo: [42, 100]}
o.boolean()
: boolean option, e.g. --foo
=> {foo: true}
(The default Parser
will also interpret --no-<name>
, i.e. --no-foo
=> {foo: false}
)o.choice(['orange', 'apple'])
: only allow the specified string values, e.g. --foo=lemon
will throw an error1import { command, option as o } from '@why-ts/cli'; 2command() 3 .option('foo', o.string()) 4 .option('bar', o.number()) 5 .option('baz', o.boolean()) 6 .handle(({ args }) => console.log(args)); // type of `args`: {foo?:string, bar?:number, baz?:boolean}
Basic usage
1> ts-node index.ts --foo=orange --bar=7 --baz 2# {foo: 'orange', bar: 7, baz: true}
Boolean fields will produce a false value for 0
, n
& false
(TODO: allow customisation)
1> ts-node index.ts --baz=false 2# {baz: false}
Boolean false values can also be specified with the --no-<name>
option (TODO: allow disabling this feature)
1> ts-node index.ts --no-baz 2# {baz: false}
Option aliases can be specified with an object in the .option()
function.
1command() 2 .option({ name: 'foo', aliases: ['f'] }, o.string()) 3 .handle(({ args }) => console.log(args)); // type of `args`: {foo?:string}
Basic usage
1> ts-node index.ts -f=orange 2# {foo: 'orange'}
Options can be marked as required
.
If user did not specify the option via command line, an error will be thrown.
1import { command, option as o } from '@why-ts/cli'; 2command() 3 .option('foo', o.string({ required: true })) 4 .option('bar', o.number({ required: true })) 5 .option('baz', o.boolean({ required: true })) 6 .handle(({ args }) => console.log(args)); // type of `args`: {foo:string, bar:string, baz:boolean}
(TODO: auto prompt for missing options if {required: 'prompt'}
)
1> ts-node index.ts --bar=7 --baz 2# Error: --foo is required
Only allow a specified list of strings. When user provides values other than the specified ones, a UsageError
will be thrown.
1import { command, option as o } from '@why-ts/cli'; 2command() 3 .option('foo', o.choice(['apple', 'orange'])) 4 .handle(({ args }) => console.log(args)); // type of `args`: {foo:'apple'|'orange'}
Allow user to specify a option more than once. Values are represented as an array at runtime.
1import { command, option as o } from '@why-ts/cli'; 2command() 3 .option('foo', o.strings()) 4 .option('bar', o.numbers()) 5 .handle(({ args }) => console.log(args)); // type of `args`: {foo?:string[], bar?:string[]}
Example:
1> ts-node index.ts --foo=orange --foo=apple --bar=7 --bar=42 2# {foo: ['orange', 'apple'], bar: [7, 42]}
An option can fallback to an environment variable if not specified via command line:
1import { command, option as o } from '@why-ts/cli'; 2command() 3 .option('foo', o.string({ env: 'MY_FOO' })) 4 .option('bar', o.number({ env: 'MY_BAR' })) 5 .handle(({ args }) => console.log(args));
By default, enviroment variables will be read from process.env
and basic transformation is applied.
Provide a custom Env
implementation to customize the behavior. (See more in Configuration section)
Basic
1> MY_FOO=apple MY_BAR=42 ts-node index.ts 2# {foo: 'apple', bar: 42}
While this library provides basic validations on user input (e.g. make sure a number value is provided to a number option), custom validations can be added over that.
1import { command, option as o } from '@why-ts/cli'; 2command() 3 // the validate function supports other return types for more control including 4 // custom error message and value transformation, see inline code documentation 5 .option('bar', o.number({ validate: (v) => v > 10 })) 6 .handle(({ args }) => console.log(args));
Basic
1> ts-node index.ts --bar=5 2# Error: --bar is invalid
There are various runtime configurations to customise program behaviour. Specific configurable functionality is listed in the following sub-sections. They can be provided/overridden at multiple places, listed below with lower-priority first:
At Program definition:
1program({logger: ...})
At Command definition:
1command({logger: ...})
At execution:
1program().run(argv, {logger:...})
If none is provided anywhere, it will fallback to an internal default.
Represented by the Parser
interface.
Controls how shell arguments (string array) are parsed into typed values
The default implementation is based on the minimist
package.
Represented by the Env
interface.
Controls how environment variables are retrieved and interpreted.
The default implementation reads values from process.env
.
Then the value is transformed with parseFloat
for numbers and splitting at comma(,
) for array values.
Represented by the Logger
interface.
Controls how output is logged to screen.
The default implementation is console.log
and console.error
Represented by the Prompter
interface.
Controls how input is captured from user.
The default implementation is based on Node.js readline
module.
Controls how the help text is formatted.
--
handlingNo vulnerabilities found.
No security vulnerabilities found.