7

I've got a Node.js CLI I've been building using the Commander Library. Here's the code from the main execution file.

#!/usr/bin/env node

var program = require('commander');
var scmStash = require('./lib/hdqc/scmStash');

var command = {};

program
    .version('0.0.1')
    .option('-P, --Projects', 'List Projects')
    .option('-R, --Repositories', 'List All Repositories on Server.')
    .parse(process.argv);

function listProjects() {
    scmStash.getProjectListing(function (data) {
        for (var i = 0; i < data.size; i++) {
            var project = data.values[i];
            console.log('    ' + i + ' ' + project.name + ' @ ' + project.link.url);
        }
    })
}

if (program.Projects) {
    console.log('  - Projects');
    listProjects();
}

I've been building this in WebStorm, and when I call the command using the node.js running everything works perfectly. for example, if I run the WebStorm runner executing the ./strack -P command to output the project, the output looks like this...

node strack -P
  - Projects
    0 Business Insights @ /projects/BI
    1 Platform @ /projects/HDP
    2 H @ /projects/H
    3 QC Application Code @ /projects/QCCODE
    4 QC Design @ /projects/QCDESIGN
    5 QC Reports @ /projects/QCREP
    6 Sandbox @ /projects/SAN
    7 Systemic Automation Tools @ /projects/SAT
    8 The Swamp @ /projects/SWAMP

However when I run the same 'strack' command from the standard bash (inside WebStorm or outside of WebStorm in iTerm or such) then the following output is displayed.

23:11 $ node strack -P
  - Projects

As I wrote up this question, I - like so often happens when typing up a stackoverflow question - realized the dillemma. The other call that prints out the projects themselves, is an asynchronous call, the actual app shoots off that call and then executes the remaining lines of code and finishes. Before the projects are even returned and can be printed to the console. I'm not sure what WebStorm is doing to keep the console attached to the running process but I'd love to have that work for my CLI. Any ideas, thoughts, or suggestions on how I should redesign this application to actually print out the projects to the command line?

All of the code is available on the github repo here.

Adron
  • 1,698
  • 3
  • 19
  • 40
  • Better create a minimal example: https://stackoverflow.com/help/mcve. We are not here to read a thousand lines of code and a wall of text. (Usually these questions are just left to rot). – 4ae1e1 Nov 14 '15 at 19:49
  • First thing I'd try: put a console.readline at the end of the file and make all your file access synchronous. – jcollum Nov 14 '15 at 20:08
  • (I'm assuming that scmstash is pulling from a file path) – jcollum Nov 14 '15 at 20:17
  • @jcollum scmstash is just using http to do an asynchronous API call. – Adron Nov 14 '15 at 20:19
  • @4ae1e1 removed all extra code and pulled it down to just a very minimal, inline code sample. Does that help? – Adron Nov 14 '15 at 20:20
  • OH SNAP. `data.size`? That's undefined. I think you meant `data.length`. – jcollum Nov 14 '15 at 20:32
  • data isn't actually an array, it's an object (it is what stash returns) and size is a base object count of how many items are in data.values.length, where values in this situation is the actual array. It *should* return ok, but either way, that code actually executes in a debugger, I just lose it in the terminal/bash prompt. :( – Adron Nov 14 '15 at 22:13

2 Answers2

2

I think the issue is with your array boundaries in your while loop. data.size is probably something you're remembering from one of the 8 other languages you know, lol. BUT it's not in js, you're looking for data.length. Try this, it's trimmed down and has a mock for the scmStash object but I think you'll see what I mean:

var command, listProjects, program, scmStash;

program = require('commander');

scmStash = {
  getRepositories: function(cb) {
    return cb([
      {
        name: 'a',
        cloneUrl: 'b'
      }, {
        name: 'c',
        cloneUrl: 'd'
      }
    ]);
  },
  getProjectListing: function(cb) {
    return cb([
      {
        name: "proj1",
        link: {
          url: "http://blah"
        }
      }, {
        name: "proj2",
        link: {
          url: "http://bluh"
        }
      }
    ]);
  }
};

command = {};

listProjects = function() {
  return scmStash.getProjectListing(function(projects) {
    var i, j, len, project, results;
    results = [];
    for (i = j = 0, len = projects.length; j < len; i = ++j) {
      project = projects[i];
      results.push(console.log('    ' + i + ' ' + project.name + ' @ ' + project.link.url));
    }
    return results;
  });
};

Output:

$ node .temp/adron.js -P
  - Projects
    0 proj1 @ http://blah
    1 proj2 @ http://bluh

Also, there's an optimization to be had by storing the length of the array in a variable before iterating it but it's very minor. Worry about that when you have a million elements in an array.

Community
  • 1
  • 1
jcollum
  • 43,623
  • 55
  • 191
  • 321
  • Iterating through the object actually works fine, it is that the console.log for any of the asynchronous calls (the scmStash) are exceuted after the program.parse(process.argv); line executes. So in that regard the node server thinks the code is done running and releases the terminal, so the terminal never receives anymore console.log messages. :( – Adron Nov 14 '15 at 22:11
  • You're right, I was assuming you had an array coming back but it could be something else. In that case you should rewrite it to be more of an interactive command line, like yeoman. E.g. don't pull from your asynch source until _after_ the user has selected an option. The basic version is readline: https://nodejs.org/api/readline.html – jcollum Nov 15 '15 at 00:01
2

There is an ES7 recommendation for async/await syntax.

In the meanwhile, there are plenty of flow control packages available. You might consider the 'async' NPM package.

Joe Davis
  • 233
  • 3
  • 11