19 March 2016

Make your Node.js module.exports more reusable as a standalone program

Likely we’ve all written something that is structurally like this when writing helper functions in JavaScript & CoffeeScript (I’ll be using JavaScript syntax in this article, but the principle is the same in CoffeeScript):

module.exports = function(args) {
  //something something here
}

And you’re all set to reuse your something something here anonymous functionalty elsewhere in your application’s codebase.

Indeed, putting code that you want to reuse into module.exports is a great way to structure your applications and you need a pretty good reason not to do so where applicable when building applications with Node.js.

That said, just chucking your logic into a module.exports anonymous function and leaving it at that reduces the reusability of your reusable code, which is why you’re using this method in the first place.

However, over in Python land, the following program structure is widely used to make files act as reusable modules, and as standalone programs. Consider this program that invokes the main() function if it is run standalone on the command line (it just prints whatever argument you feed it):

import sys

def main(args):
    print(args)

if __name__ == '__main__':
    main(sys.argv)

When invoked, you get the expected output of your command line arguments as a list:

jamon@vagrant: /tmp>>
 0 % python args.py arg1 arg2 argn
['args.py', 'arg1', 'arg2', 'argn']

The same can be done with just a little bit of extra code in JavaScript and CoffeeScript. That if __name__ == '__main__': method that is used in Python programs looks like this in JavaScript land: if (module.parent === null)

Hopefully you see where this is going now - the same method of invoking the main() function can be used in JavaScript & CoffeeScript.

Just use a named function like main() here, and export it using module.exports = main. You can of course call it whatever you like, it’s the structure that we’re after here.

The reason that this works is that the module.parent object does not exist when invoking args.js on the command line. However, when args is imported as a module, the parent is marked as such, and the module.exports section of args.js is invoked.

Accordingly, here’s a JavaScript version of the Python program above. It can be used on the command line on its own, or as a module in another Node.js program:

var main = function(args) {
  console.log(args)
}

module.exports = main

if (module.parent === null) {
  main(process.argv)
}

Invoking does what you’d expect, just like with the Python version:

jamon@vagrant: /tmp>>
 0 % node args.js arg1 arg2 argn
[ '/usr/local/bin/node',
  '/tmp/args.js',
  'arg1',
  'arg2',
  'argn' ]

The handy part in all this is that now the args.js file can be used on its own and as you’d usually use it with module.exports in a more general program like this:

var main = require('./args');

main(process.argv)

There is one thing to keep in mind when using this technique. Make sure that the arguments that you pass on the command line match those that you pass when invoking the exported function in an application. In some cases you might need some additional if else clauses to handle different arguments. But in most cases, as long as you know the parameters that your function will be handling, you won’t need to do anything beyond what I’ve outlined here.

In part two of this two part article, I’ll show how I use this technique to make hubot scripts work as standalone tools, and as part of a hubot instance.