LearnHow to Use npm as a Task Runner

Andrew Chalkley
writes on August 10, 2015

A task is something you need to do. If you want to perform that task over and over again, as you do in development, you’ll save yourself a lot of time if you automate the process. Common web development tasks include running test suites, compiling Sass, TypeScript and CoffeeScript files, starting a web server or a worker process that goes through a queue of jobs like sending out emails or push notifications.

Popular build systems like Grunt and gulp are on the rise and are great for complex project. But it may be daunting to undertake learning something new and you don’t always need their power or even want to bother setting them up in your project. As your project evolves you can always add them later.

For the things you want to do, you may not need them at all. You may not know this, but npm has a way for you to run tasks for you!

There are two types of tasks with npm, default or built-in tasks and arbitrary or your own tasks. Built-in tasks are those that are common to most projects. An example of a built-in task is test.

Built-in Tasks

There are a number of built-in tasks that npm can run for you. The most common is test.

You’re often encouraged to install test frameworks and build systems globally with the -g flag. But that can be a little overkill if you’re working on different projects with potentially different versions.

You could install any globally recommended package as a devDependancy by using the --save-dev flag.

npm install mocha --save-dev

Normally you’d be able to run mocha from anywhere when it’s installed globally by running the mocha command like this:

mocha

But if it’s a development dependency how do you run it? In your node_modules folder there is a hidden directory .bin. In this folder there are command line executables. You run the following command to run it:

node_modules/.bin/mocha 

However, you can run executables from npm without typing in lengthy node_modules/.bin/<whatever your executable is called>.

Here’s a pretty standard package.json. npm’s tasks are called “scripts”, and they’re added to the “scripts” property of a project’s  package.json file.

{
  "name": "test_project",
  "version": "1.0.0",
  "description": "",
  "main": "example.js",
  "dependencies": {
    "treehouse_profile": "^1.0.0"
  },
  "devDependencies": {
    "mocha": "^2.2.5"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Inside the “script” property there’s a “test” key.  If you don’t specify your tests when you initialise your package.json file it adds:

"echo \"Error: no test specified\" && exit 1"
echo

echo is a Unix command that prints things to the screen. This prints out "Error: no test specified" and then the Unix exit command gets ran with the number as an argument.  Computer programs that successfully run exit with the code of 0. It exists with 1. That means something has gone wrong: we have no tests. Note that you can run any Unix or Windows command line app from the task.

We can update the “test” with the “mocha” command.

{
  "name": "test_project",
  "version": "1.0.0",
  "description": "",
  "main": "example.js",
  "dependencies": {
    "treehouse_profile": "^1.0.0"
  },
  "devDependencies": {
    "mocha": "^2.2.5"
  },
  "scripts": {
    "test": "mocha"
  },
  "author": "",
  "license": "ISC"
}

What we’ve just done is created a test task. This task will run any mocha tests in the project.
To run a task you use npm’s run command, like this:

npm run test

Or for any built-in task you can run:

npm test

npm will run any binary that’s a devDependency, you don’t need to specify the full path!

You can ditch the run command because test is a default test. It’s also a common thing you’ll do in many projects.

Your Own Tasks

When you want to go outside of the built-in tasks you can create your own. You can name these tasks whatever you want, but you must use the run command to set them going.

Imagine you have some TypeScript or CoffeeScript in a source (src) folder. You’d run something like this:

tsc src/*.ts --out dist/app.js

This will compile all TypeScript files into an app.js file. Remembering all this and typing it out every time could be troublesome and prone to human error. We can put this into a task. We can name it "build".

{
  "name": "test_project",
  "version": "1.0.0",
  "description": "",
  "main": "example.js",
  "dependencies": {
    "treehouse_profile": "^1.0.0"
  },
  "devDependencies": {
    "mocha": "^2.2.5",
    "typescript": "^1.5.0-beta"
  },
  "scripts": {
    "test": "mocha",
    "build": "tsc src/*.ts --out dist/app.js"
  },
  "author": "",
  "license": "ISC"
}

Now you only need to remember the task name build:

npm run build

What if we want to run the app.js file? There’s another default task, start. Just include node, the path to the app.js file and any parameters your application needs:

{
  "name": "test_project",
  "version": "1.0.0",
  "description": "",
  "main": "example.js",
  "dependencies": {
    "treehouse_profile": "^1.0.0"
  },
  "devDependencies": {
    "mocha": "^2.2.5",
    "typescript": "^1.5.0-beta"
  },
  "scripts": {
    "test": "mocha",
    "build": "tsc src/*.ts --out dist/app.js",
    "start": "node dist/app.js --port 8080"
  },
  "author": "",
  "license": "ISC"
}

I’ve added a port number for illustrative purposes. Now I can run both of the tasks by issuing the following commands:

npm run build
npm start

But you may not want to run two commands every time before you start your application. You can combine them like this:

{
  "name": "test_project",
  "version": "1.0.0",
  "description": "",
  "main": "example.js",
  "dependencies": {
    "treehouse_profile": "^1.0.0"
  },
  "devDependencies": {
    "mocha": "^2.2.5",
    "typescript": "^1.5.0-beta"
  },
  "scripts": {
    "test": "mocha",
    "build": "tsc src/*.ts --out dist/app.js",
    "start": "npm run build && node dist/app.js --port 8080"
  },
  "author": "",
  "license": "ISC"
}

See in the “start” task I’ve included npm run build? If you use the double ampersand (&&) the two commands will run one after the other. The build first and then the starting of the server.

Conclusion

There are several other default tasks that you can see when you type npm help scripts. There’s an interesting one that you could use in this scenario above. You could move the build process into the prestart task. Ultimately you can run any command in these scripts from a single javascript file that creates or deletes directories, concatenates files and minifies them. The choice is up to you and you can program it your way and not have to deal with the overhead of a build system like gulp or Grunt!

If you’re new to npm you should check out my new npm Basics course and stay tuned for other accelerated Treehouse Workshops on npm!

5 Responses to “How to Use npm as a Task Runner”

  1. Have totally embraced what have learned here,Kudos Guys

  2. Gracias mi pana, tremenda lectura, muy fácil de entender.

  3. I was using grunt since the beginning of time and recently tried to test different ideas. I was fighting with Webpack hardships, when I came upon your post. I love the idea and setting everything up was just a breeze. Thanks :-*

  4. Nice writing Andrew. I have also taken your npm course, really enjoyed . I am really excited about learning how to use mocha or just javascript tests now. Wouldn’t that be a good topic for a treehouse course/workshop?

Leave a Reply

You must be logged in to post a comment.

Want to learn more about Javascript?

Learn how to use JavaScript to add interactivity to websites.

Learn more