(O)
<M
o <M
/| ...... /:M\------------------------------------------------,,,,,,
(O)[ VORPAL ]::@+}==========================================------------>
\| ^^^^^^ \:W/------------------------------------------------''''''
o <W
<W
(O)
Vorpal is Node's first framework for building immersive CLI applications. With a simple and powerful API, Vorpal opens the door to a new breed of rich, interactive CLI environments like wat and vantage.
Inspired by and based on commander.js, Vorpal is a framework for building immersive CLI applications built on an interactive prompt provided by inquirer.js. Vorpal launches Node into an isolated CLI environment and provides a suite of API commands and functionality including:
Commander.js-flavored command creation, including optional, required and variadic commands, arguments and aliases
// Create a new instance of vorpal.
var vorpal = require("vorpal")();
// Add the command "foo", which logs "bar".
vorpal
.command("foo")
.description("Outputs 'bar'.")
.action(function(args, callback) {
this.log("bar");
callback();
});
// Name your prompt delimiter
// "myapp
quot; and show the Vorpal prompt.
vorpal
.delimiter("myappquot;)
.show();
Run your project file. You Node app has become a CLI.
$ node server.js
myapp~$
Try out your "foo" command.
myapp~$ foo
bar
myapp~$
Now type "help" to see Vorpal's built in commands in addition to "foo":
myapp~$ help
Commands
help [command] Provides help for a given command.
exit [options] Exits the app.
foo Outputs "bar".
myapp~$
That's the basic idea. Once you get the hang of it, read on to learn some of the fancier things Vorpal can do.
API
.command(command, [description])
Adds a new command to your command line API. Returns a Command object, with the following chainable functions:
Entering "farm" or "farm --help" would then drill down on the commands:
myapp~$ farm
Commands:
farm animals Lists all animals in the farm.
farm tools Lists all tools in the farm.
farm feed [animal] Feeds a given animal.
Command Groups:
farm with * 1 sub-command.
.command.description(string)
If you don't pass a description into vorpal.command(...) above, you can use the description function as an alternative.
Registers a custom tabbed autocompletion for this command.
If a user has typed part of a registered command, the default auto-completion will fill in the rest of the command:
node~$ co
node~$ cook
However, after the user has fully typed the command cook, you can now implement command-specific auto-completion:
node~$ bake coo # tab is pressed
node~$ bake cookies # tab is pressed again
cake cookies pie
node~$ bake cookies
This is implemented as follows:
vorpal
.command("bake", "Bakes a meal.")
.autocompletion(function(text, iteration, cb) {
// The meals are all of the possible actions.
var meals = ["cookies", "pie", "cake"];
// The iteration is the count of how many times
// the `tab` key was pressed in a row. You can
// make multiple presses return all of the options
// for the user's convenience.
if (iteration > 1) {
// By returning an array of values, Vorpal
// will format them in a pretty fashion, as
// in the example above.
cb(void 0, meals);
} else {
// `this.match` is a helper function that will
// return the closest auto-completion match.
// Just makin' your job easier.
var match = this.match(text, meals);
if (match) {
// If there is a good autocomplete, return
// it in the callback (first param is reserved
// for errors).
cb(void 0, meals);
} else {
// If you don't want to do anything, just
// return undefined.
cb(void 0, void 0);
}
}
})
.action(...);
.command.action(function)
This is the action execution function of a given command. It passes in an arguments object and callback.
Actions are executed async and must either call the passed callback upon completion or return a Promise.
// As a callback:
command(...).action(function(args, cb){
var self = this;
doSomethingAsync(function(results){
self.log(results);
// If this is not called, Vorpal will not
// return its CLI prompt after command completion.
cb();
});
});
// As a newly created Promise:
command(...).action(function(args, cb){
return new Promise(function(resolve, reject) {
if (skiesAlignTonight) {
resolve();
} else {
reject("Better luck next time");
}
});
});
// Or as a pre-packaged promise of your app:
command(...).action(function(args, cb){
return app.promisedAction(args.action);
});
Action Arguments
Given the following command:
vorpal
.command('order pizza [type] [otherThings...]', 'Orders a type of food.')
.option('-s, --size <size>', 'Size of pizza.')
.option('-a, --anchovies', 'Include anchovies.')
.option('-p, --pineapple', 'Include pineapple.')
.option('-o', 'Include olives.')
.option('-d, --delivery', 'Pizza should be delivered')
.action(function(args, cb){
this.log(args);
cb();
});
The this variable in a command.action function is exposed to a special "Session" context. This context has a few functions to make use of:
session.log(string)
Any and all logging in command.action should be done through this.log, which behaves exactly like console.log. This ensures all output for your given Vorpal session is piped back properly to your TTY, and so that logging does not interrupt what the user is typing in their prompt.
vorpal
.command("foo", "Outputs 'bar'.")
.action(function(args, callback) {
// This will pipe back to your terminal.
this.log("bar");
// This will only log on the remote terminal,
// and you will not see it on your local TTY.
console.log("bar");
callback();
});
session.prompt(object, [callback])
Vorpal supports mid-command prompting. You can make full use of inquirer.js's prompt function, which is exposed through this.prompt.
vorpal.command("destroy database").action(function(args, cb){
var self = this;
this.prompt({
type: "confirm",
name: "continue",
default: false,
message: "That sounds like a really bad idea. Continue?",
}, function(result){
if (!result.continue) {
self.log("Good move.");
cb();
} else {
self.log("Time to dust off that resume.");
app.destroyDatabase(cb);
}
});
});
dbsvr~$ destroy database
? That sounds like a really bad idea. Continue? y/N: N
Good move.
dbsvr~$
session.delimiter(string)
You can change the prompt delimiter mid command through this.delimiter.
Mode is a special type of command that brings the user into a given mode, wherein regular Vorpal commands are ignored and the full command strings are interpreted literally by the mode.action function. This will continue until the user exits the mode by typing exit.
vorpal
.mode("repl")
.description("Enters the user into a REPL session.")
.delimiter("repl:")
.action(function(command, callback) {
this.log(eval(command));
});
mode's syntax is a duplicate of command's, with the following additional / altered commands:
.delimiter(string): Tacks on an additional prompt delimiter for orientation.
.init(function): Same as command's .action, called once on entering the mode.
.action(function): Called on each command submission while in the mode.
.mode.delimiter(string)
This will add on an additional delimiter string to one's Vorpal prompt upon entering the mode, so the user can differentiate what state he is in.
vorpal
.mode('repl')
.delimiter('you are in repl>')
.action(function(command, callback) {
this.log(eval(command));
});
node~$
node~$ repl
node~$ you are in repl>
.mode.init(function)
Behaves exactly like command.action, where the function passed in is fired once when the user enters the given mode. Passed the same parameters as command.action: args and callback. init is helpful when one needs to set up the mode or inform the user of what is happening.
vorpal
.mode('sql')
.delimiter('sql:')
.init(function(args, callback){
this.log('Welcome to SQL mode.\nYou can now directly enter arbitrary SQL commands. To exit, type `exit`.');
callback();
})
.action(function(command, callback) {
var self = this;
app.query(command, function(res){
self.log(res);
callback();
});
});
node~$
node~$ sql
Welcome to SQL mode.
You can now directly enter arbitrary SQL commands. To exit, type `exit`.
node~$ sql:
node~$ sql: select first_name, last_name from persons where first_name = 'George';
first_name last_name
---------------- ----------------
George Clooney
George Smith
George Stevens
node~$ sql:
node~$ sql: exit
node~$
.mode.action(function)
Similar to command.action, mode.action differs in that it is repeatedly called on each command the user types until the mode is exited. Instead of args passed as the first argument, the full command string the user typed is passed and it is expected that mode.action appropriately handles the command. Example given above.
.parse(argv)
Parses the process's process.argv arguments and executes the matching command.