Running scripts with npm

npm-script

Most people are aware that is is possible to define scripts in package.json which can be run with npm start or npm test, but npm scripts can do a lot more than simply start servers and run tests.

Here is a typical package.json configuration.

start, actually defaults to node server.js, so the above declaration is redundant. In order for the test command to work with mocha, I also need to include it in the devDependencies section (it works in the dependencies section also, but since it is not needed in production it is better to declare it here).

The reason the above test command, mocha --reporter spec test, works is because npm looks for binaries inside node_modules/.bin and when mocha was installed it installed mocha into this directory.

The code that describes what will be installed into the bin directory is defined in mocha's package.json and it looks like this:

As we can see in the above declaration, mocha has two binaries, mocha and _mocha.

Many packages have a bin section, declaring scripts that can be called from npm similar to mocha. To find out what binaries we have in our project we can run ls node_modules/.bin

Invoking Commands

Both start and test are special values and can be invoked directly.

All other values will have to be invoked by npm run. npm run is actually a shortcut of npm run-script.

The above code must be invoked with npm run watch-test, npm watch-test will fail.

Running Binaries Directly

All the above examples consists of running scripts that are declared in package.json but this is not required. Any of the commands in node_modules/.bin can be invoked with npm run. This means that I can invoke mocha by running npm run mocha directly instead of running it with mocha test.

Code Completion

With a lot of modules providing commands it can be difficult to remember what all of them are. Wouldn't it be nice if we could have some command completion to help us out? It turns out we can! npm follows the superb practice of providing its own command completion. By running the command npm completion we get a completion script that we can source to get completion for all the normal npm commands including completion for npm run. Awesome!

I usually put each of my completion script into their own file which I invoke from .bashrc.

Pretty cool!

Combining Commands

The above features gets us a long way but sometimes we want to do more than one thing at a time. It turns out that npm supports this too. npm runs the scripts by passing the line to sh. This allows us to combine commands just as we can do on the command line.

Piping

Lets say that I want to use browserify to pack my Javascript files into a bundle and then I want to minify the bundle with uglifyjs. I can do this by piping (|) the output from browserify into uglifyjs. Simple as pie!

Anding

Another use case for running commands is to run a command only if the previous command is successful. This is easily done with and (&&). Or (||), naturally, also works.

Here I run two scripts declared in my package.json in combination with the command build. Running scripts from other scripts is different from running binaries, they have to prefixed with npm run.

Concurrent

Sometimes it is also nice to be able to run multiple commands at the concurrently. This is easily done by using & to run them as background jobs.

The above scripts contain a few interesting things. First of all watch uses & to run three watch jobs concurrently. When the command is killed, by pressing Ctrl-C, all the jobs are killed, since they are all run with the same parent process.

watchify is a way to run browserify in watch mode. watch-server uses nodemon in the standard way and restarts the server whenever a relevant file has changed.

watch-less users nodemon in a less well-known way. It runs a script when any of the less-files changes and compiles them into CSS by running npm run build-less. Please note that the option --ext less is required for this to work. --exec is the option that allows nodemon to run external commands.

Complex Scripts

For more complex scripts I prefer to write them in Bash, but I usually include a declaration in package.json to run the command. Here, for example, is a small script that deploys the compiled assets to Heroku by adding them to a deploy branch and pushing that branch to Heroku.

Add the script to package.json so that it can be run with npm run deploy.

Conclusion

npm is a lot more than a package manager for Node. By configuring it properly I can handle most of my scripting needs.

Configuring start and test also sets me up for integration with SaaS providers such as Heroku and TravisCI. Another good reason to do it.

30 Comments

  1. Henrik Ravn

    Also, if you specify a “postinstall” script, this will be run automatically when you’ve run ‘npm install’.

    Useful for running bower install, for example.

  2. George Ivanov

    Great post with great examples. Very informative!

  3. Anders Janmyr

    @George, thanks for letting me know, I’m glad you liked it. :)

  4. shruthi

    Hello Anders,

    Is it possible to run scripts from a public url

    something like
    “start”: “node http://mysitedomain/index.js
    },

    The nodejs is installed in a different server. I need to run the indes.js from a different server

    Thanks in advance

    • Anders Janmyr

      Hi shruthi,

      You can run an external script by curling it (assuming curl is installed) and piping it into node.
      Something like this:

      This should work as an npm script.

      This will only work for a standalone script since it cannot download any dependencies.

  5. shruthi

    Hello Anders,

    I installed curl using ” npm install curl” and then tried with
    “start”: “curl http://117.240.88.103/myfolder/index.js | node”
    },

    I am getting error-“Failed to exec start script”.

    Is there anything wrong here

    Thanks
    Shruthi

    • Anders Janmyr

      @shrutni, you need to install curl as a system command.
      Depending on your operating system it may already be installed.
      Try running curl --version at the command line.

      If it works you will see something like

      Otherwise install with apt-get on Linux and brew on OSX.

      Anders

  6. Ed

    Excellent article, thank you so much for taking the time to write this up. I won’t be using Grunt, Gulp, or any of that nonsense anytime soon!

  7. Ashwini Kumar

    Nice insight of how the script tag work with NPM and presented in an way easy to understand. Thanks.

  8. Kevin

    Awesome! I didn’t know a lot of this stuff was possible with NPM. You’ve just greatly simplified my understanding of how to deploy node apps! Great concise article. Thanks very much! :)

  9. Greg

    Very helpful article, thank you!

  10. Excellent article. This is better than using gulp or grunt. I should have seen this before :)

  11. arieljake

    great article. thanks.

  12. Daniel

    Great tutorial.

    I have problems to following the example in Code Complete.
    After I run $ . <(npm completion), the only thing I see through $ npm run TAB is listed directories.

    What did I miss?

    • Anders Janmyr

      It seems that the completion only works with the scripts in package.json and not the bin
      files anymore. Try to add a few scripts and you will get completion.

  13. Bo Chen

    Haven’t seen good instructions like this for a while!!!

  14. ky lee

    Good job !! your article is so helpful to me.

    I have soem question about “Running Binaries Directly”
    When i run “npm run mocha” directly, it just produce ” missing script: ~~~~”
    does it work??

  15. leroy

    Great article and very informative

  16. Sean

    Still a very useful article, many thanks for sharing!

  17. Javier

    It is because of articles like these that actually made me create a tool that works like npm-scripts (kind of), but tries to address its cons :D
    https://www.npmjs.com/package/makfy
    Still in its infancy, but I’m open to suggestions.

  18. Eric

    I had hoped that the “bin” option, defined in a submodule, would make install a local command in my main module. Apparently, bin only works for modules that are installed globally. Is that true?

    • Anders Janmyr

      Eric, I usually add ./node_modules/.bin to my PATH, then it works for locally installed modules too.

  19. Alex

    Thanks! I’m learning how to use the “scripts” section of package.json as a new user to Node.js and now I understand why I can run “npm test” directly but not other scripts I add into package.json.

  20. Tamanna

    Thanks a Lot……. i ws searching this frm so long

Leave a Reply