Socket
Socket
Sign inDemoInstall

bandersnatch

Package Overview
Dependencies
Maintainers
1
Versions
74
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bandersnatch - npm Package Compare versions

Comparing version 1.0.0-alpha.9 to 1.0.0

lib/prompter.d.ts

2

lib/argument.d.ts
import { Argv, PositionalOptions } from 'yargs';
import { BaseArg, BaseArgOptions } from './baseArg';
declare type IgnoreOptions = 'desc' | 'describe' | 'conflicts' | 'implies';
declare type IgnoreOptions = 'array' | 'conflicts' | 'demandOption' | 'desc' | 'describe' | 'implies' | 'normalize';
export interface ArgumentOptions extends Omit<PositionalOptions, IgnoreOptions>, BaseArgOptions {

@@ -5,0 +5,0 @@ optional?: true;

@@ -0,0 +0,0 @@ "use strict";

@@ -0,0 +0,0 @@ import { Program } from './program';

@@ -0,0 +0,0 @@ "use strict";

@@ -1,4 +0,4 @@

import { InferredOptionType, Options, PositionalOptions } from 'yargs';
import { ArgumentOptions } from './argument';
import { OptionOptions } from './option';
import type { InferredOptionType, Options, PositionalOptions } from 'yargs';
import type { ArgumentOptions } from './argument';
import type { OptionOptions } from './option';
export interface BaseArgOptions {

@@ -18,2 +18,8 @@ prompt?: true | string;

choices: ReadonlyArray<infer C>;
type: 'array';
} ? C[] : O extends {
choices: ReadonlyArray<infer C>;
default: ReadonlyArray<string>;
} ? C[] : O extends {
choices: ReadonlyArray<infer C>;
} ? C : unknown extends InferredOptionType<O> ? F : InferredOptionType<O>;

@@ -37,2 +43,14 @@ export declare class BaseArg {

/**
* Get default value, if specified.
*/
getDefault(): any;
/**
* Get possible values, is specified.
*/
getChoices(): import("yargs").Choices | undefined;
/**
* Get type, is specified.
*/
getType(): "string" | "number" | "boolean" | "array" | "count" | undefined;
/**
* Returns the argument/option identifier.

@@ -39,0 +57,0 @@ */

@@ -33,2 +33,20 @@ "use strict";

/**
* Get default value, if specified.
*/
getDefault() {
return this.options.default;
}
/**
* Get possible values, is specified.
*/
getChoices() {
return this.options.choices;
}
/**
* Get type, is specified.
*/
getType() {
return this.options.type;
}
/**
* Returns the argument/option identifier.

@@ -35,0 +53,0 @@ */

@@ -99,13 +99,3 @@ import { Argv, Arguments as BaseArguments } from 'yargs';

private getHandler;
/**
* Returns an array of arguments and options which should be prompted, because
* they are promptable (`isPromptable()` returned true) and they are not
* provided in the args passed in to this function.
*/
private getQuestions;
/**
* Ask questions and merge with passed in args.
*/
private prompt;
}
export {};

@@ -15,5 +15,5 @@ "use strict";

exports.Command = exports.command = void 0;
const inquirer_1 = require("inquirer");
const argument_1 = require("./argument");
const option_1 = require("./option");
const prompter_1 = require("./prompter");
function isArgument(obj) {

@@ -40,8 +40,2 @@ return obj.constructor.name === 'Argument';

this.args = [];
/**
* Ask questions and merge with passed in args.
*/
this.prompt = (questions) => (args) => {
return inquirer_1.prompt(questions).then((answers) => (Object.assign(Object.assign({}, args), answers)));
};
}

@@ -201,13 +195,11 @@ /**

getHandler(commandRunner) {
return (argv) => {
return async (argv) => {
const { _, $0 } = argv, rest = __rest(argv, ["_", "$0"]);
const questions = this.getQuestions(rest);
let chain = Promise.resolve(rest);
if (questions.length) {
chain = chain.then(this.prompt(questions));
}
chain = chain.then((args) => {
const prompterInstance = prompter_1.prompter([...this.getArguments(), ...this.getOptions()], rest);
let promise = prompterInstance.prompt();
promise = promise.then((args) => {
if (this.handler) {
return this.handler(args);
}
// Display help this command contains sub-commands
if (this.getCommands().length) {

@@ -220,27 +212,7 @@ return commandRunner(`${this.getFqn()} --help`);

// callback.
argv.__promise = chain;
return chain;
argv.__promise = promise;
return promise;
};
}
/**
* Returns an array of arguments and options which should be prompted, because
* they are promptable (`isPromptable()` returned true) and they are not
* provided in the args passed in to this function.
*/
getQuestions(args) {
// If we need to prompt for things, fill questions array
return [...this.getArguments(), ...this.getOptions()].reduce((questions, arg) => {
const name = arg.getName();
const presentInArgs = Object.constructor.hasOwnProperty.call(args, name);
// @todo How can we force prompting when default was used?
if (!presentInArgs && arg.isPromptable()) {
questions.push({
name,
message: arg.getPrompt(),
});
}
return questions;
}, []);
}
}
exports.Command = Command;

@@ -0,0 +0,0 @@ export * from './argument';

@@ -0,0 +0,0 @@ "use strict";

import { Options as BaseOptions, Argv } from 'yargs';
import { BaseArgOptions, BaseArg } from './baseArg';
declare type IgnoreOptions = 'require' | 'required' | 'desc' | 'describe' | 'conflicts' | 'implies' | 'demand';
declare type IgnoreOptions = 'array' | 'boolean' | 'conflicts' | 'config' | 'configParser' | 'count' | 'defaultDescription' | 'demand' | 'demandOption' | 'desc' | 'describe' | 'global' | 'group' | 'hidden' | 'implies' | 'nargs' | 'normalize' | 'number' | 'require' | 'required' | 'requiresArg' | 'skipValidation' | 'string';
export interface OptionOptions extends Omit<BaseOptions, IgnoreOptions>, BaseArgOptions {

@@ -5,0 +5,0 @@ }

@@ -0,0 +0,0 @@ "use strict";

@@ -0,0 +0,0 @@ import TypedEventEmitter from 'typed-emitter';

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

this.createYargsInstance().parse(cmd, {}, (err, argv, output) => {
console.log('program.run');
/**

@@ -99,0 +100,0 @@ * From the yargs docs:

@@ -15,3 +15,6 @@ import { Program } from './program';

/**
* Start the REPL server.
* Start the REPL server. This method may change at any time, not
* intended for public use.
*
* @private
*/

@@ -18,0 +21,0 @@ start(): Promise<void>;

@@ -27,3 +27,6 @@ "use strict";

/**
* Start the REPL server.
* Start the REPL server. This method may change at any time, not
* intended for public use.
*
* @private
*/

@@ -30,0 +33,0 @@ async start() {

export declare function isPromise<T = any>(obj: any): obj is Promise<T>;

@@ -0,0 +0,0 @@ "use strict";

{
"name": "bandersnatch",
"description": "Simple TypeScript CLI / REPL framework",
"version": "1.0.0-alpha.9",
"version": "1.0.0",
"repository": {

@@ -28,2 +28,3 @@ "type": "git",

"build": "tsc",
"watch": "tsc --watch",
"lint": "prettier --write \"src/**/*\"",

@@ -46,4 +47,4 @@ "test": "jest",

"devDependencies": {
"@types/jest": "26.0.3",
"@types/node": "14.0.14",
"@types/jest": "26.0.4",
"@types/node": "14.0.18",
"doctoc": "1.4.0",

@@ -53,3 +54,3 @@ "husky": "4.2.5",

"leasot": "11.1.0",
"mock-argv": "1.1.6",
"mock-argv": "1.1.7",
"prettier": "2.0.5",

@@ -56,0 +57,0 @@ "promise.prototype.finally": "3.1.2",

@@ -9,6 +9,4 @@ # bandersnatch

> Super lightweight and friendly CLI framework for Node.js.
> Super lightweight and friendly CLI scaffolding for Node.js programs.
**🚧 alpha version**
## Features

@@ -19,11 +17,11 @@

- 💬 Prompts for missing arguments
- ➡ Autocompletes arguments, options and values
- 🔜 Autocompletes arguments
- 🤯 Fully typed
- ⚡ Uses the power of `yargs` and `inquirer`
It's built-in TypeScript to provide you with some very handy type hints.
It's built in TypeScript and command arguments are fully typed.
Bandersnatch is not designed to be used as a full CLI framework like oclif,
and tries to minimize the assumptions made about your program to make
bandersnatch easy and intuitive to work with.
Bandersnatch is not designed to be used as a full CLI framework like oclif, and
tries to minimize the assumptions made about your program to make it easy and
intuitive to work with.

@@ -38,7 +36,5 @@ ## Table of contents

- [Installation](#installation)
- [Simple](#simple)
- [REPL](#repl)
- [Simple example](#simple-example)
- [REPL example](#repl-example)
- [Prompt](#prompt)
- [Principles](#principles)
- [Output](#output)
- [API](#api)

@@ -53,2 +49,4 @@ - [`program(options)`](#programoptions)

- [`program.runOrRepl()`](#programrunorrepl)
- [`program.isRepl()`](#programisrepl)
- [`program.on(event, listener)`](#programonevent-listener)
- [`command(name, options)`](#commandname-options)

@@ -60,2 +58,5 @@ - [`command.argument(name, options)`](#commandargumentname-options)

- [`command.action(function)`](#commandactionfunction)
- [Design principles](#design-principles)
- [Errors](#errors)
- [Output](#output)
- [Bundle](#bundle)

@@ -74,8 +75,8 @@ - [Todo](#todo)

# Add dependency
yarn add bandersnatch
yarn|npm add bandersnatch
```
### Simple
### Simple example
Now create a simple app `concat.ts`:
Let's create a simple program `foo.ts`:

@@ -85,59 +86,100 @@ ```ts

const concat = command('concat', 'Concatenate input')
.argument('input', 'List of inputs to concatenate', { variadic: true })
.action((args) => console.log(args.input.join(', '))
const foo = command('foo')
.description('Outputs "bar".')
.action(() => console.log('bar'))
program().default(concat).run()
program().default(foo).run()
```
And run with:
This creates a new program, adds a default command which logs "bar" to the
stdout, and runs the program.
```bash
$ ts-node concat.ts Hello world
Hello, world
Now try your program by running it:
```
$ ts-node foo.ts
bar
```
_👆 Assuming you have `ts-node` installed._
_ℹ Assuming you have `ts-node` installed._
### REPL
Try running `ts-node foo.ts help` to see the auto-generated help output:
Let's dive right into some more features. This simple app has a single default
command which pretty prints JSON input. When invoked without input, it'll show
an interactive prompt:
```
$ ts-node foo.ts help
bin.js
Outputs "bar".
Commands:
bin.js Outputs "bar". [default]
Options:
--help Show help [boolean]
--version Show version number [boolean]
```
_ℹ You see `bin.js` here instead of `foo.ts` because we're running the program
with `ts-node`._
### REPL example
A program can also show an interactive
[REPL](https://en.wikipedia.org/wiki/Read–eval–print_loop) to make interacting
with more complex programs easier and to enable autocompleting of commands and
arguments.
Let's create a new program `dice.ts` with a command to roll a dice:
```ts
import { program, command } from 'bandersnatch'
const app = program('JSON pretty printer').default(
command()
.argument('json', 'Raw JSON input as string')
.option('color', 'Enables colorized output', { type: 'boolean' })
async function rng(bounds: [number, number]) {
const [min, max] = bounds
return Math.floor(Math.random() * (max - min + 1)) + min
}
const dice = program().add(
command('roll')
.option('min', { default: 1 })
.option('max', { default: 6 })
.action(async (args) => {
const json = JSON.parse(args.json)
args.color
? console.dir(json)
: console.log(JSON.stringify(json, undefined, 4))
console.log(await rng([args.min, args.max]))
})
)
app.runOrRepl()
dice.repl()
```
And run with:
This code defines a program `dice` and a command `roll` with two options, both
of which will inherit a default value. When the command is executed, it calls
an async random number generator (async only for illustrative purposes) and
writes its results to stdout.
```bash
$ ts-node pretty.ts
> [0,1,1,2,3,5]
[
0,
1,
1,
2,
3,
5
]
The last line in our code runs the program as a interactive REPL, which means
it won't accept any arguments on the command line, but render a prompt instead.
This prompt will read any user input, parse it, and execute matching commands.
Try rolling the dice:
```
$ ts-node dice.ts
> roll
5
```
Now, try typing `[0,1,1,2,3,5] --c` and then hit `TAB`. 😊
The REPL can autocomplete commands, arguments and options. Try typing only the
letter `r` and then hit _TAB_. This works for options as well:
```
$ ts-node dice.ts
> r
[TAB]
> roll -
[TAB]
> roll --m
[TAB] [TAB]
--min --max
```
### Prompt

@@ -148,2 +190,4 @@

Let's say we want to write a program `pizza.ts` which takes pizza orders:
```ts

@@ -153,49 +197,80 @@ import { program, command } from 'bandersnatch'

const cmd = command()
.argument('name', "What's your name?", {
.argument('address', {
prompt: 'Your address',
})
.argument('name', {
description: 'Your name',
default: 'anonymous',
prompt: true,
})
.argument('question', "What's your question?", {
.option('size', {
description: 'Choose pizza size',
choices: ['small', 'medium', 'large'],
default: 'medium',
prompt: true,
})
.option('toppings', {
description: 'Pick some toppings',
choices: ['mozarella', 'pepperoni', 'veggies'],
default: ['mozarella'],
prompt: true,
})
.option('confirmed', {
description: 'Order pizza?',
default: true,
prompt: true,
})
.action((args) => {
console.log(`Hi ${args.name}, the answer to "${args.question}" is 42.`)
console.log(args)
})
program('Ask me anything').default(cmd).run()
program().description('Order a pizza').default(cmd).run()
```
And run with:
And run it:
```bash
$ ts-node ama.ts --name Joram
? What's your question? What is the meaning of life?
Hi Joram, the answer to "What is the meaning of life?" is 42.
```
$ ts-node pizza.ts
? Your address The Netherlands
? Your name Joram
? Choose pizza size small
? Pick some toppings veggies
? Order pizza? Yes
{
name: 'Joram',
size: 'small',
toppings: [ 'veggies' ],
confirmed: true,
address: 'The Netherlands'
}
```
When you omit the `--name` part, the program will also prompt for it.
You can choose to specify parameters on the command line, in which case you
won't get a prompt for these options:
---
```
$ ts-node pizza.ts "The Netherlands" --name Joram --confirmed
? Choose pizza size small
? Pick some toppings veggies
? Order pizza? Yes
{
name: 'Joram',
size: 'small',
toppings: [ 'veggies' ],
confirmed: true,
address: 'The Netherlands'
}
```
ℹ More examples in the [examples](https://github.com/hongaar/bandersnatch/tree/alpha/examples) directory.
⚠ Please note that even though `--confirmed` was specified on the command line,
it was still being prompted. This is a known issue. In this case, the default
value was the same as the input, in which case bandersnatch doesn't know whether
a value was explicitly passed in or inherited from the default value.
## Principles
---
In general, bandersnatch is designed to create [twelve-factor apps](https://12factor.net/).
ℹ More examples in the
[examples](https://github.com/hongaar/bandersnatch/tree/master/examples)
directory.
### Output
Programs are encouraged to use the following conventions with regards to output,
based on the [POSIX standard](https://pubs.opengroup.org/onlinepubs/9699919799/functions/stdin.html).
- When a program is designed to be used in a scripting environment and its
output should be available as stdin for other programs, use stdout for
printing output and stderr for diagnostic output (e.g. progress and/or error
messages).
- When a program is designed to be used as a service (twelve-factor app), use
stdout/stderr as a logging mechanism for informative messages/error and
diagnostic messages.
Bandersnatch has no built-in method for writing to stdout/stderr. Node.js
provides [everything you need](https://nodejs.org/api/console.html).
## API

@@ -211,4 +286,6 @@

- `prompt` (string, default: `>`) use this prompt prefix when in REPL mode.
- `help` (boolean, default: true) adds `help` and `--help` to the program which displays program usage information.
- `version` (boolean, default: true) adds `version` and `--version` to the program which displays program version from package.json.
- `help` (boolean, default: true) adds `help` and `--help` to the program which
displays program usage information.
- `version` (boolean, default: true) adds `version` and `--version` to the
program which displays program version from package.json.

@@ -252,3 +329,3 @@ #### `program.description(description)`

Start a read-eval-print loop.
Start a read-eval-print loop. Returns promise-like repl instance.

@@ -263,3 +340,4 @@ ```ts

Invokes `run()` if process.argv is set, `repl()` otherwise.
Invokes `run()` if process.argv is set, `repl()` otherwise. Returns promise or
promise-like repl instance.

@@ -272,2 +350,16 @@ ```ts

#### `program.isRepl()`
Returns `true` if program is running a repl loop, `false` otherwise.
#### `program.on(event, listener)`
Attaches a listener function for the event. Currently, these events are
supported:
```ts
// Fired before a command action is invoked
program().on('run', (cmd) => logger.debug(`Running ${cmd}`))
```
### `command(name, options)`

@@ -286,8 +378,19 @@

- Name (string, required) is used to identify the argument. Can also be an array of strings, in which case subsequent items will be treated as command aliases.
- Options can be provided to change the behavior of the argument. Object with any of these keys:
- `description` (string, optional) is used in --help output.
- Name (string, required) is used to identify the argument.
- Options can be provided to change the behavior of the argument. Object with
any of these keys:
- `description` (string) is used in --help output.
- `optional` (boolean) makes this argument optional.
- `variadic` (boolean) eagerly take all remaining arguments and parse as an array. Only valid for the last argument.
- ...
- `variadic` (boolean) eagerly take all remaining arguments and parse as an
array. Only valid for the last argument.
- `type` (string) one of `"boolean"|"number"|"string"` which determines the
runtime type of the argument.
- `default` (any) default value for the argument.
- `choices` (array) any input value should be included in the array, or it
will be rejected.
- `prompt` (boolean|string) prompts for missing arguments. If it is true, it
will use the arguments description or name as the question text. If it is a
string, it will be used as the question text.
- `alias` (string|array) alias or aliases for the argument.
- `coerce` (function) transform function for this argument value (untyped).

@@ -299,6 +402,16 @@ #### `command.option(name, options)`

- Name (string, required) is used to identify the option.
- Options (object, optional) can be provided to change the behavior of the option. Object with any of these keys:
- Options (object, optional) can be provided to change the behavior of the
option. Object with any of these keys:
- `description` (string, optional) is used in --help output.
- `alias` (string or array of strings) alias(es) for the option key.
- ...
- `type` (string) one of `"array"|"boolean"|"count"|"number"|"string"` which
determines the runtime type of the argument. Use count for the number of
times an option was provided (e.g. verbosity levels).
- `default` (any) default value for the argument.
- `choices` (array) any input value should be included in the array, or it
will be rejected.
- `prompt` (boolean|string) prompts for missing arguments. If it is true, it
will use the arguments description or name as the question text. If it is a
string, it will be used as the question text.
- `alias` (string|array) alias or aliases for the option.
- `coerce` (function) transform function for this option value (untyped).

@@ -311,11 +424,62 @@ #### `command.command(command)`

Mark command as default. Default commands are executed immediately and don't require a name.
Mark command as default. Default commands are executed immediately and don't
require a name.
#### `command.action(function)`
Function which executes when the command is invoked. Is called with these arguments:
Function which executes when the command is invoked. Is called with these
arguments:
1. Args (object) is an object containing key/value pairs of parsed arguments and options.
2. Command runner (function) can be invoked with one (string) parameter to execute another command.
1. Args (object) is an object containing key/value pairs of parsed arguments and
options.
2. Command runner (function) can be invoked with one (string) parameter to
execute another command.
## Design principles
In general, bandersnatch is designed to create
[twelve-factor apps](https://12factor.net/).
### Errors
The bandersnatch API allows to catch errors in a promise-like way. The `run` and
`repl` program methods return either a promise or promise-like object which can
be used to handle program errors:
```ts
program()
.default(
command()
.description('This command will always fail')
.action(function () {
throw new Error('Whoops')
})
)
.runOrRepl()
.catch((error: any) => {
console.error('[failed]', String(error))
if (!app.isRepl()) {
process.exit(1)
}
})
```
### Output
Programs are encouraged to use the following conventions with regards to output,
based on the
[POSIX standard](https://pubs.opengroup.org/onlinepubs/9699919799/functions/stdin.html).
- When a program is designed to be used in a scripting environment and its
output should be available as stdin for other programs, use stdout for
printing output and stderr for diagnostic output (e.g. progress and/or error
messages).
- When a program is designed to be used as a service (twelve-factor app), use
stdout/stderr as a logging mechanism for informative messages/error and
diagnostic messages.
Bandersnatch has no built-in method for writing to stdout/stderr. Node.js
provides [everything you need](https://nodejs.org/api/console.html).
## Bundle

@@ -326,7 +490,9 @@

ℹ An example can be found in the [examples/bundle](https://github.com/hongaar/bandersnatch/tree/alpha/examples/bundle) directory.
ℹ An example can be found in the
[examples/bundle](https://github.com/hongaar/bandersnatch/tree/master/examples/bundle)
directory.
Init a `package.json` if needed:
```bash
```
mkdir echo && cd echo

@@ -338,3 +504,3 @@ yarn init

```bash
```
yarn add bandersnatch

@@ -394,3 +560,3 @@ yarn add typescript pkg --dev

"dependencies": {
"bandersnatch": "^1.0.0-alpha.2"
"bandersnatch": "^1.0.0"
},

@@ -433,3 +599,3 @@ "devDependencies": {

"dependencies": {
"bandersnatch": "^1.0.0-alpha.2"
"bandersnatch": "^1.0.0"
},

@@ -463,3 +629,3 @@ "devDependencies": {

"dependencies": {
"bandersnatch": "^1.0.0-alpha.2"
"bandersnatch": "^1.0.0"
},

@@ -473,3 +639,3 @@ "devDependencies": {

_👆 Omit `-t host` to create binaries for all platforms._
_ℹ Omit `-t host` to create binaries for all platforms._

@@ -483,8 +649,7 @@ Run `yarn bundle` and then `./echo --help`. 💪

- [ ] Better code coverage
- [ ] Choices autocompletion in REPL mode
- [ ] Choices autocompletion in REPL mode (open upstream PR in yargs)
## Contributing
Contributions are very welcome. Please note this project is in a very early
stage and the roadmap is a bit foggy still...
Contributions are very welcome.

@@ -498,6 +663,7 @@ ```bash

# Run an example
yarn start examples/simple.ts
yarn start examples/foo.ts
```
Please use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/).
Please use
[conventional commits](https://www.conventionalcommits.org/en/v1.0.0/).

@@ -504,0 +670,0 @@ ## License

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc