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

lib/container.d.ts

7

lib/baseArg.d.ts

@@ -0,1 +1,2 @@

import { InferredOptionType, Options, PositionalOptions } from 'yargs';
import { ArgumentOptions } from './argument';

@@ -6,2 +7,8 @@ import { OptionOptions } from './option';

}
export declare type InferArgType<O extends Options | PositionalOptions, D = unknown> = O extends {
variadic: true;
type: 'number';
} ? Array<number> : O extends {
variadic: true;
} ? Array<string> : unknown extends InferredOptionType<O> ? D : InferredOptionType<O>;
export declare class BaseArg {

@@ -8,0 +15,0 @@ protected name: string;

29

lib/command.d.ts

@@ -1,26 +0,20 @@

import { Argv, InferredOptionType, Arguments as BaseArguments, Options, PositionalOptions } from 'yargs';
import { Argv, Arguments as BaseArguments } from 'yargs';
import { Argument, ArgumentOptions } from './argument';
import { Option, OptionOptions } from './option';
declare type InferT<O extends Options | PositionalOptions, D = unknown> = O extends {
variadic: true;
type: 'number';
} ? Array<number> : O extends {
variadic: true;
type: 'string';
} ? Array<string> : unknown extends InferredOptionType<O> ? D : InferredOptionType<O>;
import { InferArgType } from './baseArg';
export declare type Arguments<T = {}> = T & BaseArguments<T> & {
__promise?: Promise<any>;
};
export interface HandlerFn<T = {}> {
export interface HandlerFn<T> {
(args: Omit<T, '_' | '$0'>): Promise<any> | any;
}
export declare function command<T = {}>(command: string, description?: string): Command<T>;
export declare function command<T = {}>(command?: string, description?: string): Command<T>;
export declare class Command<T = {}> {
private command;
private command?;
private description?;
private args;
private handler?;
constructor(command: string, description?: string);
argument<K extends string, O extends ArgumentOptions>(name: K, descriptionOrOptions?: string | O, options?: O): Command<T & { [key in K]: InferT<O, string>; }>;
option<K extends string, O extends OptionOptions>(name: K, descriptionOrOptions?: string | O, options?: O): Command<T & { [key in K]: InferT<O, unknown>; }>;
constructor(command?: string, description?: string);
argument<K extends string, O extends ArgumentOptions>(name: K, descriptionOrOptions?: string | O, options?: O): Command<T & { [key in K]: InferArgType<O, string>; }>;
option<K extends string, O extends OptionOptions>(name: K, descriptionOrOptions?: string | O, options?: O): Command<T & { [key in K]: InferArgType<O, unknown>; }>;
/**

@@ -33,5 +27,5 @@ * This is the base method for adding arguments and options, but it doesn't provide

action(fn: HandlerFn<T>): this;
getArguments(): Argument[];
getOptions(): Option[];
getCommands(): Command<{}>[];
private getArguments;
private getOptions;
private getCommands;
/**

@@ -61,2 +55,1 @@ * Calls the command() method on the passed in yargs instance and returns it.

}
export {};

@@ -128,2 +128,5 @@ "use strict";

getCommand() {
if (!this.command) {
throw new Error('Command name must be set');
}
const args = this.getArguments()

@@ -160,3 +163,3 @@ .map(arg => arg.toCommand())

}
chain = chain.then(args => {
chain = chain.then(async (args) => {
if (!this.handler) {

@@ -163,0 +166,0 @@ throw new Error('No handler defined for this command.');

import { Argv } from 'yargs';
import { Command } from '.';
import { Command } from './command';
import { Arguments } from './command';
import { Runner } from './runner';
export declare function program(description?: string): Program;
declare type FailFn = (msg: string, err: Error, yargs: Argv) => void;
declare type FailFn = (msg: string, err: Error, args: Arguments, usage?: string) => void;
export declare class Program {

@@ -10,2 +12,3 @@ private yargs;

private replInstance?;
private runnerInstance?;
constructor(description?: string);

@@ -19,6 +22,10 @@ add<T>(command: Command<T>): this;

/**
* If invoked with a command, this is used instead of process.argv.
* Evaluate command (or process.argv) and return runner instance.
*/
run(command?: string | ReadonlyArray<string>): Promise<unknown>;
eval(command?: string | ReadonlyArray<string>): Runner<unknown>;
/**
* Run a command (or process.argv) and print output.
*/
run(command?: string | ReadonlyArray<string>): Runner<unknown>;
/**
* Run event loop which reads command from stdin.

@@ -32,3 +39,4 @@ */

private failHandler;
private defaultFailFn;
}
export {};

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

const ansi_colors_1 = require("ansi-colors");
const command_1 = require("./command");
const repl_1 = require("./repl");
const command_1 = require("./command");
const utils_1 = require("./utils");
const runner_1 = require("./runner");
function program(description) {

@@ -18,3 +19,13 @@ return new Program(description);

constructor(description) {
this.yargs = yargs_1.default(process.argv.slice(2));
this.yargs = yargs_1.default();
this.defaultFailFn = (msg, err, args, usage) => {
if (msg) {
console.error(ansi_colors_1.yellow(msg));
}
if (usage) {
console.error('');
console.error(usage);
}
process.exit(1);
};
if (description) {

@@ -58,16 +69,25 @@ this.yargs.usage(description);

/**
* If invoked with a command, this is used instead of process.argv.
* Evaluate command (or process.argv) and return runner instance.
*/
run(command) {
eval(command) {
const cmd = command || process.argv.slice(2);
// Return promise resolving to the return value of the command handler.
return new Promise((resolve, reject) => {
// Set executor to promise resolving to the return value of the command
// handler.
this.runnerInstance = runner_1.runner((resolve, reject) => {
this.yargs.parse(cmd, {}, (err, argv, output) => {
// Output is a string for built-in commands like --version and --help
if (output) {
console.log(output);
}
// TODO When is err defined?
if (err) {
console.error(err);
}
if (utils_1.isPromise(argv.__promise)) {
argv.__promise.then(resolve);
// Delegate resolve/reject to promise returned from handler
argv.__promise.then(resolve).catch(reject);
}
else {
// Resolve with void if promise is not available, which is the case
// e.g. with --version and --help
resolve();

@@ -77,8 +97,15 @@ }

});
return this.runnerInstance.eval();
}
/**
* Run a command (or process.argv) and print output.
*/
run(command) {
return this.eval(command).print();
}
/**
* Run event loop which reads command from stdin.
*/
repl() {
this.replInstance = new repl_1.Repl(this, this.promptPrefix);
this.replInstance = repl_1.repl(this, this.promptPrefix);
// Don't exit on errors.

@@ -99,24 +126,26 @@ this.yargs.exitProcess(false);

failHandler(msg, err, yargs) {
var _a, _b;
if (this.failFn) {
this.failFn(msg, err, yargs);
var _a;
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)));
}
else if (this.replInstance) {
if (msg) {
this.replInstance.setError(msg);
}
else if (err) {
this.replInstance.setError((_a = err.stack, (_a !== null && _a !== void 0 ? _a : err.message)));
}
}
else {
if (msg) {
console.error(ansi_colors_1.red(msg));
}
else if (err) {
console.error(ansi_colors_1.red((_b = err.stack, (_b !== null && _b !== void 0 ? _b : err.message))));
}
console.error('');
console.error(yargs.help());
process.exit(1);
const args = yargs.argv;
const usage = yargs.help();
const cb = () => {
if (this.failFn) {
// Call custom fail function.
this.failFn(msg, err, args, usage);
}
else {
// Call default fail function.
this.defaultFailFn(msg, err, args, usage);
}
};
// We call the fail function in the runner chain if available, to give
// async printer a chance to complete first.
this.runnerInstance
? this.runnerInstance.then(cb)
: Promise.resolve().then(cb);
}

@@ -123,0 +152,0 @@ }

import { Program } from './program';
export declare function repl(program: Program, prefix?: string): Repl;
export declare class Repl {

@@ -3,0 +4,0 @@ private program;

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

const ansi_colors_1 = require("ansi-colors");
function repl(program, prefix = '>') {
return new Repl(program, prefix);
}
exports.repl = repl;
class Repl {

@@ -7,0 +11,0 @@ constructor(program, prefix = '>') {

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

@@ -28,8 +28,17 @@ "files": [

"@types/node": "^12.12.17",
"doctoc": "^1.4.0",
"husky": "^3.1.0",
"jest": "^24.9.0",
"mock-argv": "^1.1.2",
"prettier": "^1.19.1",
"promise.prototype.finally": "^3.1.2",
"ts-jest": "^24.2.0",
"ts-node": "^8.5.4",
"typescript": "^3.7.3"
},
"husky": {
"hooks": {
"pre-commit": "yarn doctoc . && git add README.md"
}
}
}

@@ -11,11 +11,11 @@ # bandersnatch

**🚧 but not quite yet**
**🚧 alpha version**
## Features
- Built-in REPL
- Prompts for missing arguments
- Autocompletes arguments, options and values
- Fully typed
- Uses the power of `yargs` & `inquirer`
- ➰ Built-in REPL
- 💬 Prompts for missing arguments
- ➡ Autocompletes arguments, options and values
- 🤯 Fully typed
- ⚡ Uses the power of `yargs` & `inquirer`

@@ -29,2 +29,42 @@ It's built in TypeScript and while it's of course possible to write your app

## Table of contents
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Getting started](#getting-started)
- [API](#api)
- [`program(description)`](#programdescription)
- [`program.add(command)`](#programaddcommand)
- [`program.default(command)`](#programdefaultcommand)
- [`program.prompt(prompt)`](#programpromptprompt)
- [`program.withHelp()`](#programwithhelp)
- [`program.withVersion()`](#programwithversion)
- [`program.fail(function)`](#programfailfunction)
- [`program.eval(command)`](#programevalcommand)
- [`program.run(command)`](#programruncommand)
- [`program.repl()`](#programrepl)
- [`program.yargsInstance()`](#programyargsinstance)
- [`command(name, description)`](#commandname-description)
- [`command.argument(name, description, options)`](#commandargumentname-description-options)
- [`command.option(name, description, options)`](#commandoptionname-description-options)
- [`command.command(command)`](#commandcommandcommand)
- [`command.default()`](#commanddefault)
- [`command.action(function)`](#commandactionfunction)
- [`runner`](#runner)
- [`runner.then(function)`](#runnerthenfunction)
- [`runner.catch(function)`](#runnercatchfunction)
- [`runner.print(printer)`](#runnerprintprinter)
- [`printer`](#printer)
- [`printer.write(string)`](#printerwritestring)
- [`printer.error(Error)`](#printererrorerror)
- [`ArgumentOptions`](#argumentoptions)
- [`OptionOptions`](#optionoptions)
- [Bundle](#bundle)
- [Contributing](#contributing)
- [License](#license)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Getting started

@@ -44,5 +84,3 @@

.argument('words', 'Say some kind words', { variadic: true })
.action(function(args) {
console.log(args.words.join(' '))
})
.action(args => args.words.join(' '))

@@ -54,7 +92,327 @@ program()

More examples in https://github.com/hongaar/bandersnatch/tree/master/examples
And run with:
## Development
```bash
ts-node echo.ts
```
_👆 Assuming you have `ts-node` installed._
ℹ More examples in the [examples](https://github.com/hongaar/bandersnatch/tree/alpha/examples) directory.
## API
All methods are chainable unless the docs mention otherwise.
### `program(description)`
Creates a new program.
- Description (string, optional) is used in --help output.
#### `program.add(command)`
Adds a command to the program.
```ts
program().add(command(...))
```
#### `program.default(command)`
Adds a default command to the program. Shorthand for:
```ts
program().add(command(...).default())
```
#### `program.prompt(prompt)`
Use this prompt prefix (string, required) when in REPL mode.
#### `program.withHelp()`
Adds `help` and `--help` to program which displays program usage information.
#### `program.withVersion()`
Adds `version` and `--version` to program which displays program version from
package.json.
#### `program.fail(function)`
Use custom error handler. Function will be called with 4 arguments:
- Message (string) will contain internal message about e.g. missing arguments
- Error (Error) is only set when an error was explicitly thrown
- Args (array) contains program arguments
- Usage (string) contains usage information from --help
#### `program.eval(command)`
Uses process.argv or passed in command (string, optional) to match and execute
command. Returns runner instance.
```ts
program()
.add(command(...))
.eval()
```
#### `program.run(command)`
Shorthand for `eval().print()`.
```ts
program()
.add(command(...))
.run()
```
#### `program.repl()`
Start a read-eval-print loop.
#### `program.yargsInstance()`
Returns internal `yargs` instance. Use with caution.
### `command(name, description)`
Creates a new command.
- 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.
#### `command.argument(name, description, options)`
Adds a positional argument to the command.
- Name (string, required) is used to identify the argument.
- Description (string, optional) is used in --help output.
- Options (ArgumentOptions) can be provided to change the behaviour of the
argument.
#### `command.option(name, description, options)`
Adds an option to the command.
- Name (string, required) is used to identify the option.
- Description (string, optional) is used in --help output.
- Options (OptionOptions) can be provided to change the behaviour of the
option.
#### `command.command(command)`
Adds a sub-command to the command.
#### `command.default()`
Mark command as default. Default commands are executed immediately and don't require a name.
#### `command.action(function)`
Function to execute when command is invoked. Is called with one argument: an
object containing key/value pairs of parsed arguments and options.
### `runner`
Returned from `program().eval()`, can't be invoked directly.
#### `runner.then(function)`
Function is invoked when command handler resolves.
#### `runner.catch(function)`
Function is invoked when command handler rejects.
#### `runner.print(printer)`
Attaches a printer to the runner. Uses a default printer unless called with a
custom printer argument.
### `printer`
Used by runner, can't be invoked directly.
#### `printer.write(string)`
Handles output. Prints to stdout by default.
#### `printer.error(Error)`
Handles errors. Prints stack trace to stderr by default.
### `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
There are many options to bundle your application for distribution. We'll
discuss a common pattern.
ℹ An example can be found in the [examples/bundle](https://github.com/hongaar/bandersnatch/tree/alpha/examples/bundle) directory.
Init a `package.json` if needed:
```bash
mkdir echo && cd echo
yarn init
```
Install dependencies:
```bash
yarn add bandersnatch
yarn add typescript pkg --dev
```
And create an example app in `src/cli.ts`:
```ts
import { program, command } from 'bandersnatch'
export default program()
.withHelp()
.default(
command('echo', 'Echo something in the terminal')
.argument('words', 'Say some kind words', { variadic: true })
.action(console.log)
)
```
Building your app with TypeScript is very powerful, but runtime compilation is
slow so we compile the code ahead of time.
Add a `tsconfig.json`, similar to:
```json
{
"include": ["./src"],
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"lib": ["es2017"],
"declaration": true,
"outDir": "lib",
"rootDir": "src",
"strict": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"moduleResolution": "node"
}
}
```
Add these scripts to your `package.json`:
```diff
{
"name": "echo",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
+ "scripts": {
+ "prepublishOnly": "yarn build",
+ "build": "tsc",
+ },
"dependencies": {
"bandersnatch": "^1.0.0-alpha.2"
},
"devDependencies": {
"pkg": "^4.4.2",
"typescript": "^3.7.3"
}
}
```
And compile now by running `yarn build`.
Next, we need to create a simple entrypoint `echo.js`, which can be run with
node:
```bash
#!/usr/bin/env node
require('./lib/cli').default.run()
```
To run your app, users may want to run `yarn global add echo`. For this to
work, we need to make a small adjustment to `package.json`:
```diff
{
"name": "echo",
"version": "1.0.0",
- "main": "index.js",
+ "bin": "echo.js",
+ "files": [
+ "lib"
+ ],
"license": "MIT",
"scripts": {
"prepublishOnly": "yarn build",
"build": "tsc",
},
"dependencies": {
"bandersnatch": "^1.0.0-alpha.2"
},
"devDependencies": {
"pkg": "^4.4.2",
"typescript": "^3.7.3"
}
}
```
You can now `npm publish`.
To create a binary (your app with Node.js bundled), add this script to
`package.json`:
```diff
{
"name": "echo",
"version": "1.0.0",
"bin": "echo.js",
"files": [
"lib"
],
"license": "MIT",
"scripts": {
"prepublishOnly": "yarn build",
"build": "tsc",
+ "bundle": "yarn build && pkg -t host ."
},
"dependencies": {
"bandersnatch": "^1.0.0-alpha.2"
},
"devDependencies": {
"pkg": "^4.4.2",
"typescript": "^3.7.3"
}
}
```
_👆 Omit `-t host` to create binaries for all platforms._
Run `yarn bundle` and then `./echo --help`. 💪
Optionally deploy to GitHub, S3, etc. using your preferred CD method if needed.
## Contributing
```bash
# Clone and install

@@ -69,13 +427,8 @@ git clone git@github.com:hongaar/bandersnatch.git

## API
## License
_Work in progress_
Copyright (c) 2019 Joram van den Boezem. Licensed under the MIT license.
## Todo
- [ ] Autocomplete in repl mode
- [ ] Remove \$0 from help when in repl mode
---
Inspired by Vorpal
Inspired by [vorpal](https://vorpal.js.org/)
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