[NodeJS] Command line tool


With NodeJS, it's fairly simple to create a command-line tool. First, you need to add bin field into package.json file.

# File: package.json
"bin": {
    "command1": "path/to/command1/implementation",
    "command2": "path/to/command2/implementation",
    "command3": "path/to/command3/implementation"

You can have as many commands as you want. I like to store all the executable files in bin/ directory. Let say I want to create a command named node-greet to simply print out a greeting message. Here's the bin field of my package.json

"bin": {
    "node-greet": "bin/greeting.js"

Note that the command name and the name of js file can be totally different. Now, let's create the bin/greeting.js file to implement an overly simplified greeting function.

#!/usr/bin/env node
console.log('Hello,', process.argv[2], '!');

That's it! The only difference between this file and a normal js file is the shebang in the very first line. Now you can install this package globally and use the command anywhere in the terminal.

$ node-greet Khanh
Hello, Khanh!

or you can install it as a dependency or devDependency in other package and use the command in the scripts field.

"scripts": {
    "say-hi": "node-greet name"

However, things get a bit more complicated when you want to support new features of ES6, which requires --harmony.


Things get complicated when you need --harmony because shebang's arguments are interpreted differently in different operating systems. In Mac OS, you can just add --harmony to the end of the shebang above and it will work just fine. However, the same thing does not work on Linux. Fortunately, I found a solution by Olov Lassus on github. He wrote a shell script wrapper to execute js script with --harmony option and other input arguments. You can find the solution here. I simply copy it here, break it in several lines and add some comments to make it easier to read and understand.

In the bin/ directory, add a file named node-greet (no extension) and fill it with the following content.

rdlkf() { 
  # if command is symlink
  [ -L "$1" ] && \
  ( \
    # resolve the link
    local lk="$(readlink "$1")"; \
    # get directory name
    local d="$(dirname "$1")"; \
    # go to the directory
    cd "$d"; \
    # recursively call rdlkf on the path
    local l="$(rdlkf "$lk")"; \
    # if l is absolute path
    ([[ "$l" = /* ]] && \
    # then return l
    echo "$l" || \
    # otherwise, return d/l
    echo "$d/$l") \
  ) || \
  # otherwise, return the command
  echo "$1";
# run rdlkf on the script and get the directory name
DIR="$(dirname "$(rdlkf "$0")")"
# execute the js file
/usr/bin/env node --harmony "$DIR/greeting.js" "$@"

Now, if you run bin/node-greet, greeting.js will be executed with harmony option.

No comments:

Post a Comment