Search code examples
node.jsnode-commander

Node.js commander with optional+variadic arguments


Please help me get node's commander module to parse arguments the way I want.

I'm looking to upload a list of files to a named database. There is a default database name, so the user shouldn't need to include a database parameter.

I'd like this command to work as following:

>>> ./upload.js --db ReallyCoolDB /files/uploadMe1.txt /files/uploadMe2.txt
(uploads "uploadMe1.txt" and "uploadMe2.txt" to database "ReallyCoolDB")

>>> ./upload.js /files/uploadMe1.txt /files/uploadMe2.txt
(uploads "uploadMe1.txt" and "uploadMe2.txt" to the default database)

>>> ./upload.js --db ReallyCoolDB
(returns an error; no files provided)

How can I implement this with commander? I've tried a number of things already, currently I'm stuck with this code which doesn't work:

// upload.js:

#!/usr/bin/env node

var program = require('commander');
program
  .version('0.1.0')
  .description('Upload files to a database')
  .command('<path1> [morePaths...]')
  .option('-d, --db [dbName]', 'Optional name of db', null)
  .action(function(path1, morePaths) {
    
    // At this point I simply want:
    // 1) a String "dbName" var
    // 2) an Array "paths" containing all the paths the user provided
    var dbName = program.db || getDefaultDBName();
    var paths = [ path1 ].concat(morePaths || []);
    console.log(dbName, paths);
    
    // ... do the upload ...
    
  })
  .parse(process.argv);

When I try to run ./upload.js, I get no output!

How can I use commander to accept a single optional parameter, and a non-empty list of strings??

EDIT: Thanks to Rob Raisch's answer I've solved my problem! The solution is to use usage instead of action, do all work after the program commands (instead of within an action function), work with program.db and program.args, and manually ensure that program.args is non-empty:

var program = require('commander');

program
    .version('0.1.0')
    .description('Upload files to a database')
    .usage('[options] <path1> [morePaths ...]') // This improves "--help" output
    .option('-d, --db [dbName]', 'Optional name of db', null)
    .parse(process.argv);

var dbName = program.db || getDefaultDBName();
var paths = program.args;

if (!paths.length) {
    console.log('Need to provide at least one path.');
    process.exit(1);
}

// Do the upload!

Solution

  • The README.md file for the commander command line processing module answers your use case in its second paragraph:

    "Options with commander are defined with the .option() method, also serving as documentation for the options. The example below parses args and options from process.argv, leaving remaining args as the program.args array which were not consumed by options."