Skip to content

Creating a volo command

jrburke edited this page Jun 7, 2012 · 3 revisions

A new volo command is just a package that can be installed via npm that exports a certain API.

There is a command template that can make it easy to bootstrap the structure to develop a command:

> volo create yourcommand volojs/create-command-template
> cd yourcommand

yourcommand/index.js is where the command is implemented.

The command template's index.js implements all of the command API to show docs, declare flags, validate arguments and finally run, so it gives a good flavor for the extent of the command API.

Here is an annotated version:

var fs = require('fs'),
    path = require('path');

module.exports = {
    summary: 'A help summary.',

    //This is a longer form version of the help. markdown
    //is the suggested file format to use.
    doc: fs.readFileSync(path.join(__dirname, 'doc.md'), 'utf8'),

    //Declare flags that are accepted. This one accepts
    //a -u flag, and it gets converted to
    //namedArgs.upper = true for the validate() and
    //run() calls below.
    flags: {
        'u': 'upper'
    },

    //Optional, validates the arguments passed to the command.
    //volo commands can take name=value arguments (stored in
    //the namedArgs as namedArgs.name = value), and positional 
    //arguments, passed after namedArgs
    //in the order they were received. Flags show up as namedArgs.
    validate: function (namedArgs, arg1, arg2) {
        if (!arg1) {
            return new Error('Please enter a greeting');
        }
        if (!arg2) {
            return new Error('Please enter a name.');
        }
    },

    //Runs the command. More info on the d and v
    //arguments below.
    run: function (d, v, namedArgs, arg1, arg2) {
        var message = arg1 + ' ' + arg2 + '!';
        if (namedArgs.upper) {
            message = message.toUpperCase();
        }

        d.resolve(message);
    }
};

The d argument to run() is a deferred to call when the command is done. Since node and network APIs can be async, commands can complete asynchronously. Call d.resolve() with a string that will be printed to indicate success, and d.reject() with an Error object if there is a failure.

It is important that you call d.resolve() or d.reject() to indicate the command has finished. Otherwise volo may hang waiting for the output, or not properly move to the next task.

The v argument to run() is an object that offers some utility methods. Its API is still in flux (but should maintain backwards compatible for a while, with newer versions giving a deprecation warning if they go away). It needs more docs, but you can look at the implementation of v.js for more information.

If you need to do some nontrivial control async control flow, you can use the version of q's promises that volo bundles by calling v.require('q').

However, volo commands can also just be shell-like strings, or an array of shell-like strings that will be executed in order. See the Creating a volofile page.

See create-command-template's README.md for instructions on how to create a symlink to your command so you can develop it and test it with volo.

Once it is done, publish it to npm.