npm Blog (Archive)

The npm blog has been discontinued.

Updates from the npm team are now published on the GitHub Blog and the GitHub Changelog.

Testing and deploying with ordered npm run scripts

Many thanks to Kenneth Ormandy of Surge for this guest post. Have a post you’d like to share with the npm community? drop us a line.

If there’s a package.json file in your project, you have the opportunity to include time-saving scripts for your development process. npm test, the most common example, is actually included by default when you initialize a new package.json file. Most Node.js based projects make use of this pattern, and it’s increasingly common for front-end projects, too.

Knowing you can run npm install; npm test within a repository you don’t know much about yet is reassuring. This is an increasingly common first step after cloning a project. Beyond this, the script section of your package.json could cover,

everything from how the package should be tested to what code should get run after the application is terminated.

—K.Adam White, A Facade for Tooling with NPM Package Scripts

We use these scripts heavily while working on Surge. npm’s pre- and post- scripts, however, are employed much less often. They help you automatically run some tasks before or after others, and they can be used to make your project much friendlier to new developers—whether they are new to JavaScript, npm, or just to your project.

Starting with tests

Many projects will include a test script that looks something like this in the package.json file:

// …
"scripts": {
  "test": "node test/my-tests.js"
}

Let’s imagine you’d like to add code linting next, using Standard, to improve consistency. First, add Standard to your project by running the following command in your terminal:

npm install --save-dev standard

Now, the latest stable version of Standard has been added to your devDependencies in your package.json. The Standard README recommends you add it to your package.json test script, like so:

"scripts": {
  "test": "standard && node test/my-tests.js"
}

This script works well at first: you are now linting your JavaScript code and then running your tests. This should help improve code consistency by making it a common action. But this isn’t particularly friendly to someone new to your project who’s just looking to run the test: as soon as they start contributing, they could have the linter vocalize concerns over their use of semicolons or extra whitespace.

Now, you’ll need to worry less about nitpicking inconsistencies from contributors. Unfortunately, a linter yelling at a potential contributor when they are just trying to dig into the code a little is an effective way to deter them from contributing at all. It doesn’t matter if they are a new developer or just new to your project.

Splitting scripts

For greater flexibility, let’s make each step a separate script:

"scripts": {
  "lint": "standard",
  "test": "node test/my-tests.js"
}

Now, you can run your linter whenever you’d like, independently of your tests:

npm run lint

—but there is something convenient about having it run automatically as part of your tests. Luckily, npm’s pre- and post-run scripts can take care of that.

Run scripts after testing

Rather than running your linting as part of your test script, consider running it as a subsequent step, only if the tests pass:

"scripts": {
  "lint": "standard",
  "test": "node test/my-tests.js",
    "posttest": "npm run lint"
}

Potential contributors will be spared the syntax warnings until their changes make the tests pass, and they might actually be ready to prepare a pull request.

Run scripts before deploying

Your code is tested and linted, so there’s no excuse not to deploy it! Surge helps front-end developers publish any directory of static files. Pass a folder to surge, and it can be published for free, with a subdomain or custom domain. First, install Surge as a development dependency:

npm install --save-dev surge

Then, add a deployment run script to your package.json:

"deploy": "surge ./path/to/dist"

Using the command npm run deploy in your terminal will start the publishing process.

Running the tests before each deploy shouldn’t be something you need to think about, however. Just as you added linting after your tests with a post-run script, why not run the tests before deploying with a pre-run script?¹

"scripts": {
  "lint": "standard",
  "test": "node test/my-tests.js",
    "posttest": "npm run lint",
  "predeploy": "npm test",
  "deploy": "surge ./path/to/dist"
}

Prepending pre or post to any run script will automatically run it before or after the root task. In this case, npm run predeploy will automatically run the tests before deploying the project to Surge.

These run scripts are also available in an example repository on GitHub.

Build tool compatibility

This approach doesn’t require the project to actually take advantage of npm run scripts proper; Grunt, Gulp, or any other build tool can still be used while aliasing the most common commands to npm run scripts:

"scripts": {
  "start": "gulp",
  "test": "gulp test",
  "posttest": "gulp lint", 
  "deploy": "gulp deploy"
}

Use your best judgement on how many of these to include, but including some basics can make getting started with your project a lot clearer without someone needing to read your Gulp- or Gruntfile.js.

npm run conclusion

Run scripts help summarise common tasks within your project, and pre- and post-run scripts can order those tasks in a more friendly manner. Try adding one to the next package.json file you find yourself editing.

Further reading

¹ Update 2015-09-01: An earlier revision of this post incorrectly read

"posttest": "npm run test",

here instead of

"posttest": "npm run lint",

We’ve fixed it. Thanks to commenters including Joe Zimmerman for making the catch!