Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

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.3 to 1.0.0-alpha.4

lib/autocompleter.d.ts

1

lib/baseArg.d.ts

@@ -22,2 +22,3 @@ import { InferredOptionType, Options, PositionalOptions } from 'yargs';

getDescription(): string | undefined;
getOptions(): OptionOptions | ArgumentOptions;
}

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

}
getOptions() {
return this.options;
}
}
exports.BaseArg = BaseArg;

5

lib/command.d.ts

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

}
export declare function command<T = {}>(command?: string, description?: string): Command<T>;
export declare function command<T = {}>(command?: string | string[], description?: string): Command<T>;
export declare class Command<T = {}> {

@@ -18,3 +18,3 @@ private command?;

private handler?;
constructor(command?: string, description?: string);
constructor(command?: string | string[] | undefined, description?: string | undefined);
argument<K extends string, O extends ArgumentOptions>(name: K, descriptionOrOptions?: string | O, options?: O): Command<T & { [key in K]: InferArgType<O, string>; }>;

@@ -32,2 +32,3 @@ option<K extends string, O extends OptionOptions>(name: K, descriptionOrOptions?: string | O, options?: O): Command<T & { [key in K]: InferArgType<O, unknown>; }>;

private getCommands;
private toModule;
/**

@@ -34,0 +35,0 @@ * Calls the command() method on the passed in yargs instance and returns it.

@@ -32,2 +32,4 @@ "use strict";

constructor(command, description) {
this.command = command;
this.description = description;
this.args = [];

@@ -40,4 +42,2 @@ /**

};
this.command = command;
this.description = description;
}

@@ -111,17 +111,20 @@ /*

}
/**
* Calls the command() method on the passed in yargs instance and returns it.
* See https://github.com/yargs/yargs/blob/master/docs/advanced.md#providing-a-command-module
*/
toYargs(yargs) {
toModule() {
const module = {
command: this.getCommand(),
aliases: [],
describe: this.description,
describe: this.description || '',
builder: this.getBuilder(),
handler: this.getHandler()
};
return yargs.command(module);
return module;
}
/**
* Calls the command() method on the passed in yargs instance and returns it.
* See https://github.com/yargs/yargs/blob/master/docs/advanced.md#providing-a-command-module
*/
toYargs(yargs) {
return yargs.command(this.toModule());
}
/**
* Returns a formatted command which can be used in the command() function

@@ -138,3 +141,5 @@ * of yargs.

if (args !== '') {
return `${this.command} ${args}`;
return Array.isArray(this.command)
? [`${this.command[0]} ${args}`, ...this.command.slice(1)]
: `${this.command} ${args}`;
}

@@ -141,0 +146,0 @@ return this.command;

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

export * from './program';
export * from './argument';
export * from './autocompleter';
export * from './command';
export * from './argument';
export * from './option';
export * from './printer';
export * from './program';
export * from './repl';
export * from './runner';
export * from './utils';

@@ -6,5 +6,10 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
__export(require("./program"));
__export(require("./argument"));
__export(require("./autocompleter"));
__export(require("./command"));
__export(require("./argument"));
__export(require("./option"));
__export(require("./printer"));
__export(require("./program"));
__export(require("./repl"));
__export(require("./runner"));
__export(require("./utils"));

@@ -5,11 +5,22 @@ import { Argv } from 'yargs';

import { Runner } from './runner';
export declare function program(description?: string): Program;
declare type FailFn = (msg: string, err: Error, args: Arguments, usage?: string) => void;
declare type ProgramOptions = {
help?: boolean;
version?: boolean;
fail?: FailFn;
prompt?: string;
exitOnError?: boolean;
};
export declare function program(description?: string, options?: ProgramOptions): Program;
export declare class Program {
private yargs;
private promptPrefix;
private failFn?;
private description?;
private options;
private commands;
private replInstance?;
private runnerInstance?;
constructor(description?: string);
constructor(description?: string | undefined, options?: ProgramOptions);
/**
* Create a new yargs instance. Not intended for public use.
*/
createYargsInstance(): Argv<{}>;
add<T>(command: Command<T>): this;

@@ -33,6 +44,2 @@ default<T>(command: Command<T>): this;

repl(): void;
/**
* Allow tweaking the underlaying yargs instance.
*/
yargsInstance(): Argv<{}>;
private failHandler;

@@ -39,0 +46,0 @@ private defaultFailFn;

@@ -12,9 +12,11 @@ "use strict";

const runner_1 = require("./runner");
function program(description) {
return new Program(description);
function program(description, options = {}) {
return new Program(description, options);
}
exports.program = program;
class Program {
constructor(description) {
this.yargs = yargs_1.default();
constructor(description, options = {}) {
this.description = description;
this.options = options;
this.commands = [];
this.defaultFailFn = (msg, err, args, usage) => {

@@ -30,37 +32,52 @@ if (msg) {

};
if (description) {
this.yargs.usage(description);
}
// Some defaults
this.yargs.help(false);
this.yargs.version(false);
this.yargs.recommendCommands();
this.yargs.strict();
this.yargs.demandCommand();
}
/**
* Create a new yargs instance. Not intended for public use.
*/
createYargsInstance() {
const yargs = yargs_1.default();
this.description && yargs.usage(this.description);
// Help accepts boolean
yargs.help(!!this.options.help);
// Version must be false or undefined
!!this.options.version ? yargs.version() : yargs.version(false);
// Non-configurable options
yargs.recommendCommands();
yargs.strict();
yargs.demandCommand();
// Hidden completion command
yargs.completion('completion', false);
// Custom fail function.
// TODO current yargs types doesn't include the third parameter.
this.yargs.fail(this.failHandler.bind(this));
yargs.fail(this.failHandler.bind(this));
// Exit on errors?
yargs.exitProcess(!!this.options.exitOnError);
// Add commands
this.commands.forEach(command => {
command.toYargs(yargs);
});
return yargs;
}
add(command) {
command.toYargs(this.yargs);
this.commands.push(command);
return this;
}
default(command) {
command.default().toYargs(this.yargs);
this.commands.push(command.default());
return this;
}
prompt(prompt) {
this.promptPrefix = prompt;
this.options.prompt = prompt;
return this;
}
withHelp() {
this.yargs.help(true);
this.options.help = true;
return this;
}
withVersion() {
this.yargs.version();
this.options.version = true;
return this;
}
fail(fn) {
this.failFn = fn;
this.options.fail = fn;
return this;

@@ -76,3 +93,3 @@ }

this.runnerInstance = runner_1.runner((resolve, reject) => {
this.yargs.parse(cmd, {}, (err, argv, output) => {
this.createYargsInstance().parse(cmd, {}, (err, argv, output) => {
// Output is a string for built-in commands like --version and --help

@@ -82,3 +99,3 @@ if (output) {

}
// TODO When is err defined?
// TODO when is err defined?
if (err) {

@@ -93,3 +110,3 @@ console.error(err);

// Resolve with void if promise is not available, which is the case
// e.g. with --version and --help
// with e.g. --version and --help
resolve();

@@ -111,5 +128,5 @@ }

repl() {
this.replInstance = repl_1.repl(this, this.promptPrefix);
this.replInstance = repl_1.repl(this, this.options.prompt);
// Don't exit on errors.
this.yargs.exitProcess(false);
this.options.exitOnError = false;
// Add exit command

@@ -119,16 +136,11 @@ this.add(command_1.command('exit', 'Exit the application').action(() => {

}));
this.replInstance.loop();
this.replInstance.start();
}
/**
* Allow tweaking the underlaying yargs instance.
*/
yargsInstance() {
return this.yargs;
}
failHandler(msg, err, yargs) {
var _a;
// TODO needs more use-cases: only do something when msg is set, and have
// errors always handled in the runner?
if (this.replInstance) {
// In case we're in a REPL session, we don't want to exit the process
// when an error occurs.
this.replInstance.setError((_a = (msg !== null && msg !== void 0 ? msg : err.stack), (_a !== null && _a !== void 0 ? _a : err.message)));
// In case we're in a REPL session, forward the message which may
// originate from yargs. Errors are handled in the runner.
msg && this.replInstance.setError(msg);
}

@@ -139,5 +151,5 @@ else {

const cb = () => {
if (this.failFn) {
if (this.options.fail) {
// Call custom fail function.
this.failFn(msg, err, args, usage);
this.options.fail(msg, err, args, usage);
}

@@ -144,0 +156,0 @@ else {

@@ -5,15 +5,11 @@ import { Program } from './program';

private program;
private prefix;
private prompt;
private server?;
private lastError;
constructor(program: Program, prefix?: string);
loop(): Promise<void>;
private autocompleter;
constructor(program: Program, prompt?: string);
start(): Promise<void>;
setError(err: string): void;
private tick;
/**
* Prompt the user for a command.
*/
private read;
private completer;
private eval;
private print;
}
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const inquirer_1 = require("inquirer");
const repl_1 = __importDefault(require("repl"));
const string_argv_1 = require("string-argv");
const ansi_colors_1 = require("ansi-colors");
function repl(program, prefix = '>') {
const autocompleter_1 = require("./autocompleter");
function repl(program, prefix = '> ') {
return new Repl(program, prefix);

@@ -10,48 +15,38 @@ }

class Repl {
constructor(program, prefix = '>') {
constructor(program, prompt = '> ') {
this.program = program;
this.prompt = prompt;
this.lastError = null;
this.program = program;
this.prefix = prefix;
this.prompt = inquirer_1.createPromptModule();
this.autocompleter = autocompleter_1.autocompleter(program);
}
async loop() {
await this.tick();
await this.loop();
async start() {
this.server = repl_1.default.start({
prompt: this.prompt,
eval: this.eval.bind(this),
completer: this.completer.bind(this),
ignoreUndefined: true
});
}
setError(err) {
// Only display one error per tick
if (!this.lastError) {
this.lastError = err;
console.error(ansi_colors_1.red(err));
}
this.lastError = err;
}
async tick() {
const stdin = await this.read();
async completer(line, cb) {
const argv = string_argv_1.parseArgsStringToArgv(line);
const current = argv.slice(-1).toString();
const completions = await this.autocompleter.completions(argv);
let hits = completions.filter(completion => completion.startsWith(current));
// Add trailing space to each hit
hits = hits.map(hit => `${hit} `);
// Show all completions if none found
cb(null, [hits.length ? hits : completions, current]);
}
async eval(line, context, file, cb) {
this.lastError = null;
await this.eval(stdin);
const result = await this.program.run(line.trim());
if (this.lastError) {
console.error(ansi_colors_1.red(this.lastError));
}
cb(null, result);
}
/**
* Prompt the user for a command.
*/
async read() {
// Inquirers default behaviour is to prefix the message with a space.
// See https://github.com/SBoudrias/Inquirer.js/issues/677
const answers = await this.prompt([
{
type: 'input',
name: 'stdin',
message: this.prefix,
prefix: '',
suffix: ''
}
]);
return answers.stdin;
}
async eval(stdin) {
return this.program.run(stdin);
}
async print() {
// Here just for completeness.
}
}
exports.Repl = Repl;

@@ -19,3 +19,3 @@ import { Printer } from './printer';

*/
private promise;
promise(): Promise<any>;
private onfulfilled;

@@ -22,0 +22,0 @@ private onrejected;

{
"name": "bandersnatch",
"description": "",
"version": "1.0.0-alpha.3",
"version": "1.0.0-alpha.4",
"main": "lib/index.js",

@@ -23,2 +23,3 @@ "files": [

"inquirer": "^7.0.0",
"string-argv": "^0.3.1",
"yargs": "^15.0.2"

@@ -25,0 +26,0 @@ },

@@ -35,2 +35,6 @@ # bandersnatch

- [Getting started](#getting-started)
- [Installation](#installation)
- [Simple](#simple)
- [REPL](#repl)
- [Prompt](#prompt)
- [API](#api)

@@ -47,3 +51,2 @@ - [`program(description)`](#programdescription)

- [`program.repl()`](#programrepl)
- [`program.yargsInstance()`](#programyargsinstance)
- [`command(name, description)`](#commandname-description)

@@ -56,11 +59,11 @@ - [`command.argument(name, description, options)`](#commandargumentname-description-options)

- [`runner`](#runner)
- [`runner.print(printer)`](#runnerprintprinter)
- [`runner.then(function)`](#runnerthenfunction)
- [`runner.catch(function)`](#runnercatchfunction)
- [`runner.print(printer)`](#runnerprintprinter)
- [`runner.print(printer)`](#runnerprintprinter-1)
- [`printer`](#printer)
- [`printer.write(string)`](#printerwritestring)
- [`printer.error(Error)`](#printererrorerror)
- [`ArgumentOptions`](#argumentoptions)
- [`OptionOptions`](#optionoptions)
- [Bundle](#bundle)
- [Todo](#todo)
- [Contributing](#contributing)

@@ -73,2 +76,4 @@ - [License](#license)

### Installation
```bash

@@ -79,2 +84,4 @@ # Add dependency

### Simple
Now create a simple app `echo.ts`:

@@ -87,6 +94,6 @@

.argument('words', 'Say some kind words', { variadic: true })
.action(args => args.words.join(' '))
.action(args => args.words.map(word => `${word}!`).join(' '))
program()
.add(echo)
.default(echo)
.run()

@@ -98,3 +105,4 @@ ```

```bash
ts-node echo.ts
$ ts-node echo.ts Hello world
Hello! world!
```

@@ -104,2 +112,77 @@

### REPL
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
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' })
.action(async args => {
const json = JSON.parse(args.json)
args.color
? console.dir(json)
: console.log(JSON.stringify(json, undefined, 4))
})
)
process.argv.slice(2).length ? app.run() : app.repl()
```
And run with:
```bash
$ ts-node pretty.ts
> [0,1,1,2,3,5]
[
0,
1,
1,
2,
3,
5
]
```
Now, try typing `[0,1,1,2,3,5] --c` and then hit `TAB`. 😊
### Prompt
Bandersnatch can also ask a user for input if arguments were not provided on the
command line:
```ts
import { program, command } from 'bandersnatch'
const cmd = command()
.argument('name', "What's your name?", {
prompt: true
})
.argument('question', "What's your question?", {
prompt: true
})
.action(args => `Hi ${args.name}, the answer to "${args.question}" is 42.`)
program('Ask me anything')
.default(cmd)
.run()
```
And run with:
```bash
$ ts-node ama.ts --name Joram
? What's your question? What is everything in ASCII?
Hi Joram, the answer to "What is everything in ASCII?" is 42.
```
When you omit the `--name` part, the program will also prompt for it.
---
ℹ More examples in the [examples](https://github.com/hongaar/bandersnatch/tree/alpha/examples) directory.

@@ -180,6 +263,8 @@

#### `program.yargsInstance()`
```ts
program()
.add(command(...))
.repl()
```
Returns internal `yargs` instance. Use with caution.
### `command(name, description)`

@@ -189,4 +274,4 @@

- Name (string, optional) is used to invoke a command. When
not used as default command, name is required.
- Name (string, optional) is used to invoke a command. When not used as default
command, name is required.
- Description (string, optional) is used in --help output.

@@ -198,6 +283,11 @@

- Name (string, required) is used to identify the argument.
- 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.
- Description (string, optional) is used in --help output.
- Options (ArgumentOptions) can be provided to change the behaviour of the
argument.
- Options can be provided to change the behaviour of the
argument. Object with any of these keys:
- `optional` (boolean) makes this argument optional.
- `variadic` (boolean) eagerly take all remaining arguments and parse as array.
Only valid for last argument.
- ...

@@ -211,3 +301,5 @@ #### `command.option(name, description, options)`

- Options (OptionOptions) can be provided to change the behaviour of the
option.
option. Object with any of these keys:
- `alias` (string or array of strings) alias(es) for the option key.
- ...

@@ -231,2 +323,26 @@ #### `command.command(command)`

#### `runner.print(printer)`
Prints resolved and rejected command executions to the terminal. Uses the
built-in printer if invoked without arguments.
```ts
const runner = program()
.default(
command().action(() => {
throw new Error('Test customer printer')
})
)
.eval()
runner.print({
write(str: any) {
str && console.log(str)
},
error(error: any) {
console.error(`${red('‼')} ${bgRed(error)}`)
}
})
```
#### `runner.then(function)`

@@ -257,16 +373,2 @@

### `ArgumentOptions`
Object with any of these keys:
- `optional` (boolean) makes this argument optional.
- `variadic` (boolean) eagerly take all remaining arguments and parse as array.
Only valid for last argument.
- ...
### `OptionOptions`
- `alias` (string or array of strings) alias(es) for the option key.
- ...
## Bundle

@@ -425,4 +527,12 @@

## Todo
- [ ] Better code coverage
- [ ] Choices autocompletion in REPL mode
## Contributing
Contributions are very welcome. Please note this project is in a very early
stage and the roadmap is a bit foggy still...
```bash

@@ -438,2 +548,4 @@ # Clone and install

Please use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/).
## License

@@ -440,0 +552,0 @@

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