Nodejitsu

Save time managing and deploying your node.js app. Code faster with jitsu and npm

More Than Web: Six Node.js CLI Apps

About the author

Name
Location
Worldwide
nodejitsu nodejitsu

Node.js is best known, of course, as a tool for building and serving awesome webapps. After all, that's what Nodejitsu is all about!

However, if you talk to Node.JS users you may hear something along the lines of, "Javascript is actually pretty nice for writing command line apps." As it turns out, node.js is a great platform for writing command line applications, and with npm they are as easy to install---nay, easier---than similar programs written in ruby or python and installed with rubygems or pip. In addition, node.js has all sorts of helpful libraries for writing command line apps, such as prompt,
optimist, and cliff, that make writing command line interfaces easy!

Here, I hope to showcase a few of the command line apps written in node.js. Many of them are still written with web development in mind. They are all written in javascript, and they are all used from the command line.


forever

Forever, written by Nodejitsu's Charlie Robbins, is a process supervisor written in node.js. More important, however, is that forever is robust and simple to use.

There are many process supervision frameworks out there. The problem with a lot of them is that they're often written with system init scenarios in mind and have their own DSLs. Other times, they're not written in a particularly user-friendly manner, or they aren't very robust. Sometimes, all of these things are true. Because of this (and other problems), myself and others before me have written their own ad-hoc process supervisors, using some unholy combination of nohup, screen and/or bash.

Forever is a much better solution. In fact, forever might be the best solution. It's certainly one of the best I've seen.

For an example, suppose you're a grad student (as I was in a past life) and you have a simulation you need to run that, due to buggy software, might as well look like this:

#!/usr/bin/env node

console.log("=================Super Number Cruncher 5000=================");
console.log("--Running important simulations for your thesis since now!--");

var i = 0;
setInterval(function() {
    if (Math.random() < 0.05) {
        throw Error('FLAGRANT ERROR');
    } else {
        //Generate some output data
        console.log((i++)+','+(273.15+60*Math.random()));
    }
}, 500);

The output of this program looks something like this:

josh@pidgey:~/broken_science_simulation$ ./simulation 
==================Super Number Cruncher 5000==================
---Running important simulations for your thesis since now!---
0,274.32085393052546
1,273.85998188573865
2,332.435783926025
3,312.9851095430553
4,323.98653633948413
5,298.41442882139233
6,314.28931980449704
7,291.39460802134124
8,297.300358219631
9,309.1636345820501
10,281.5037131268531
11,292.7776571927592
12,332.8452686598524
13,275.4756304487586

/home/josh/broken_science_simulation/simulation:9
        throw Error('FLAGRANT ERROR');
              ^
Error: FLAGRANT ERROR
    at Error (unknown source)
    at Timer.callback (/home/josh/broken_science_simulation/simulation:9:15)

What? Flagrant error?

(Of course, given that it's academia, it's probably written in java and MATLAB instead, but you get the idea. Also, ignore the implications of buggy science software. It's better if you don't think about it too much.)

Now, supposing that we can't track down this error because it's buried inside a proprietary software suite, we can just use forever to make it restart every time it crashes:

josh@pidgey:~/broken_science_simulation$ forever -o output.log -e err.log simulation
warn: Forever detected script exited with code: 1
warn: Forever restarting script for 1 time
warn: Forever detected script exited with code: 1
warn: Forever restarting script for 2 time
warn: Forever detected script exited with code: 1

This command puts your simulation's output into output.log, and the errors into err.log. As you can see, Forever restarts your process like a boss. Here's some example text from output.log:

51,275.67675554249433
52,323.125968520157
53,299.9657142587006
54,285.97948576379565
==================Super Number Cruncher 5000==================
---Running important simulations for your thesis since now!---
0,317.5622826308012
1,311.84217803701756
2,330.1117456337437
3,331.6873355587944
==================Super Number Cruncher 5000==================
---Running important simulations for your thesis since now!---
0,298.9083861099556
1,288.8923557434231
2,300.0416149344295
3,312.97375301420686

and here's some example text from err.log:

Error: FLAGRANT ERROR
    at Error (unknown source)
    at Timer.callback (/home/josh/broken_science_simulation/simulation:9:15)

/home/josh/broken_science_simulation/simulation:9
        throw Error('FLAGRANT ERROR');
              ^
Error: FLAGRANT ERROR
    at Error (unknown source)
    at Timer.callback (/home/josh/broken_science_simulation/simulation:9:15)

/home/josh/broken_science_simulation/simulation:9
        throw Error('FLAGRANT ERROR');
              ^

Clearly, errors are still a problem. However, with forever you can keep the bad news from your advisor. Just restart the process, and rewrite it to serialize output and start where it left off!


http-server

http-server, written by Nodejitsu's Marak Squires, is a zero-configuration http-server. This means that all you have to do to serve the files in a folder is to run http-server from the command line, in that folder, without having to set any options. It's as easy as file serving gets!

For example, here's the content of a pure client-side web page that I wrote, and normally serve using github pages:

josh@pidgey:~/virtual-window$ ls
favicon.ico  frame.png  index.html  jquery.js  README.md  wall.jpg  window.css

Of course, the smart thing to do is to test the project locally before pushing changes to github. Plus, github pages is mildly annoying to work with anyway, right? With http-server, testing it from my computer is trivial:

josh@pidgey:~/virtual-window/$ http-server
Starting up http-server, serving . on port: 8080
http-server successfully started: localhost:8080
Hit CTRL-C to stop the server

Now all I have to do is go to localhost:8080, and there it is! Just like it is on github pages, without having to do any configuration at all.

http-server is also nice for quickly sharing material from my shell at a moment's notice, and for (legally, mind you) sharing games at LAN parties (for those of us that have time for such things).


browserify

You may have heard of browserify already. Browserify began life as a node.js module for making client-side javascript more awesome by:

  • Giving you a node.js-compatible, commonjs-style module system for your client-side code, helping to keep everything clean and organized.
  • Allowing you to grab node.js packages from npm and use them client-side, not only enabling simple code reuse, but also allowing one to run code intended for the server in the browser in a "it's so crazy it just might work" fashion. For example, the author of browserify has used it to run pure javascript stack traces in the browser.

However, not all has been peachy in the world of browserify. Many people complained about having to run this "heavyweight" solution, requiring a node server to be running in order to serve these bundles instead of, say, bundling the javascript in a "build" step and serving the results with Apache. Other solutions along a similar vein, such as ender.js, allow you to do this, so why not Browserify?

Well, the author listened, and browserify recently gained a command line interface.

Here's a quick example: Suppose I would like to bundle the hashish library for manipulating some objects on the client side, and that I would also like to include some es5-shim action with the shimify browserify middleware, just in case Mom is insisting on using IE7. Here it is:

josh@pidgey:~$ browserify --require hashish --plugin shimify -o bundle.js

Like that, assuming traverse and shimify are both installed, browserify bundles traverse with es5-shim into a file called bundle.js. Awesome!


uglifyjs

Suppose you built the bundle from the browserify example, and you checked its size:

josh@pidgey:~$ wc -c bundle.js
59390 bundle.js

60 kilobytes?!

Okay, that's not too bad, but certainly we can do better, right? After all, the source from that output isn't minified or anything. For example, here's a short snippet from bundle.js:

// shallow copy
Hash.copy = function (ref) {
    var hash = { __proto__ : ref.__proto__ };
    Object.keys(ref).forEach(function (key) {
        hash[key] = ref[key];
    });
    return hash;
};

Goodness, that is way too readable!

Programmatically, browserify supports many more features than it does at the command line, including using minifiers such as uglifyjs as source filters. Uglifyjs is a high-quality, speedy javascript minifier that's a favorite amongst node.js developers.

Lucky for us, uglifyjs comes with a command line application! Check it out:

josh@pidgey:~$ browserify --require hashish --plugin shimify | uglifyjs | wc -c
21243

That's much better.


ngist

Ngist is a command line github gist tool, similar to Chris Wanstrath's gist from ruby. Its use is pretty similar.

Here's a really simple example of its use, after the initial git configuration step:

josh@pidgey:~$ npm search " cli " > /tmp/cli.npm.txt
npm info it worked if it ends with ok
npm info using npm@1.0.18
npm info using node@v0.4.9
npm info ok
josh@pidgey:~$ ngist /tmp/cli.npm.txt
Gist: https://gist.github.com/1115238
josh@pidgey:~$ 

Here, I searched npm for all modules with the word "cli" in their descriptions, then used ngist to gist the output. It worked great, as you can see for yourself.

Ngist can also use the contents of the clipboard, post multiple files at once (or a combination of files and clipboard contents), allow you to set the gist description, and even use someone else's github username and token.

Finally, ngist isn't only available as a command line app; you can also use it programmatically! This seems to be a common pattern in node.js cli apps, and it's one I really like.


jshint

jshint is a linting tool for javascript. While a fork of the more well-known jslint, its stated goals are to be more flexible and less opinionated than Doug Crockford's tool. The project, in addition to the web-based linter, also has a handy-dandy node-based cli tool for linting your projects!

For example, I decided to run it against a project of mine to see what would happen:

josh@pidgey:~$ jshint bf.js
bf.js/bf.js: line 16, col 22, Bad line breaking before ','.
bf.js/bf.js: line 21, col 21, Bad line breaking before ','.
bf.js/bf.js: line 24, col 43, Bad line breaking before '?'.
bf.js/bf.js: line 28, col 21, Bad line breaking before ','.
bf.js/bf.js: line 31, col 43, Bad line breaking before '?'.
bf.js/bf.js: line 35, col 21, Bad line breaking before ','.
bf.js/bf.js: line 40, col 21, Bad line breaking before ','.
bf.js/bf.js: line 47, col 21, Bad line breaking before ','.
bf.js/bf.js: line 57, col 21, Bad line breaking before ','.
bf.js/bf.js: line 82, col 22, Unnecessary semicolon.
bf.js/bf.js: line 91, col 21, Bad line breaking before ','.
bf.js/bf.js: line 92, col 21, Bad line breaking before ','.
bf.js/bf.js: line 93, col 19, Bad line breaking before ','.
bf.js/bf.js: line 96, col 2, Missing semicolon.
bf.js/test/tests.js: line 8, col 2, Missing semicolon.

15 errors
josh@pidgey:~$ 

Oh my! It seems that jshint does not like how I break my lines or place my semicolons! Because, whatever, I do what I want, I'll just dump the following into ~/.jshintrc so I can continue to write code that makes people want to stab their eyes out:

{
  "asi": true,
  "laxbreak": true
}

This tells jshint to allow automatic semicolon insertion and "lax" linebreaks. Now, when I run jshint:

josh@pidgey:~$ jshint bf.js
bf.js/bf.js: line 82, col 22, Unnecessary semicolon.

1 error

There, that's not so bad! The offending code on line 82 is for loop shenanigans I picked up from doing too much code golf, and I should probably rewrite it. Thanks, jshint!


Wrapping up

Forever, http-server, browserify, uglify, ngist and jshint are just a few of the command line applications written in node.js and available on npm! They are easy to install, and are crazy useful. In another blog post, I will discuss how to build your own command line applications with node.js, using options parsers like optimist, command line formatting tools like colors and cliff, and prompt, an easy to use module for prompting the command line for input. Stay tuned!