But when you put it together with the vast possibilities in package.json you get another gear. Then npm can actually become a build tool - driving every aspect of your development. Without getting unwieldy and out of control.
In this post I wanted to examine one of the lesser known features of scripting with npm and package.json;
post hooks. In doing so I will most likely brush on a lot of other subjects too. We’ll see how it goes.
[UPDATED] This, and other posts on npm scripting, has drawn a lot of attention. I wanted you to know that I have created a PluralSight course on this topic, published in late October 2015. You can find it here.
Also, don’t miss the other posts on this blog on npm scripting:
- npm scripting: git, version and deploy
- npm scripting: configs and arguments… and some more tricks
- Pre and Post hooks for npm scripting
If you liked this post I know you will love the course! Thank you for reading this.
I’ll keep my code here
Pretty soon after starting to use the package.json file we start to use the
scripts-node. In fact when you use
npm init to set up your
package.json file a test script is created for you. Often it looks something like this:
That’s not much to look at (and you should probably change it to something that actually does something useful, like running your tests), but it can still teach us some about the
So, a command is defined for me. In this case an
echo reminding me to add some test facilities to my application / package.
How can I run this?
All scripts under
scripts can be executed with
npm run-script [script name]. Like this, to run our test command:
This command with simply just run whatever code found under the
test-key of the
Luckily there’s a short form too and it’s more commonly used:
This is awesome because that means that we now can have scripts in our
package.json that we can use
npm to execute. A first, vital, step towards a build tool.
test and start
There are two scripts that
npm supports out of the box;
start. When I write supports it’s more like; there’s very convenient, well-known short cuts for them.
So instead of
npm run start we can go
npm start. Testing is even cooler; we can choose from these options:
But the really useful thing about these “well known” scripts is that tools and infrastructure in the Node world knows that they exists and can make use of them. For example, when you push code to Heroku it will launch your application with
npm start. Or you continuous integration platform might run test, by convention by simply executing
Hooks; pre and post
Before I get completely side tracked here let’s go back to the theme of the this post; pre and post hooks.
This is one of the lesser known and used features of npm scripting I think. And it’s a bit funny that this is “lesser” known and used, since it’s actually the first thing that they write about in the documentation.
Ok anyway; all scripts supports
post hooks or scripts. They are automatically run by
npm before (
pre) and after (
post) their respectively script / command. Aka JustWhatIThought (tm)
The pre/post scripts are for all intents and purposes just another script in your
package.json file. They are also defined under the
For example; the script you’ve defined (if any) under
pretest is run before the
test script. And subsequently the
posttest script is run after.
Here’s an example;, you can find all my code here:
In this contrived example my tests creates some log files and I need to remove them, and recreate the folder, before (and after) I run my test. Easy to accomplish with the
post hooks. If you haven’t seen it
&& is a simple way of running a few commands in a row.
Note that you should probably clean up test-data in your test code but it works as an example
Hooks out the box
There’s quite a few hooks that
npm knows about out of the box, and hence will run for you, if they are defined:
- and finally
uninstallalso have pre and post hooks
test, that we talked about
For all of these there’s respectively
post hook. This comes very handy to manage the life cycle of your application.
npm more useful, and what actually makes it a viable build tool, is that you can define your own, custom scripts.
Remember that you
npm test actually was a short-cut for
npm run test (which in turn is a short-cut for
npm run-script test)?
Well there’s nothing stopping you from defining your own script and run them. Here’s a script that I’ve defined to run jslint over my code. The “tool that makes developer cry”. I’m one of those guys I guess…
I can now run this by simply going
npm run lint and it will lint all my files in the root directory.
Yes, I’ve could have defined the script as:
to run it on all my files, but I grew tired trying to fix the lint issues with mocha. Sorry.
Dependencies and devDependencies
Now, there’s a dependency problem here - that
jslint command? What is that? Psst… a Node package.
And now everyone has to have it installed globally? No. This is a development time dependency for my package. It only needs to be installed if you’re going to run
npm run jslint.
This is what
devDependencies in the
package.json is for. You can install and save the dependency like this
npm install jslint --save-dev.
We don’t have to give the path to the
./node_modules/jslint/bin/jslint) since npm will look for packages first the local
node_modules and then continue upwards.
Pre and post hooks for custom script
Now, while that
npm run lint is cool, I still have to remember to run it from time to time. If only I could run it just before testing, failing if the linting didn’t pass 100%. What to do? What to do?
pretest! Yes, folks - this is a perfect scenario to run my custom
lint script. Let’s add it to the
Now then I, or my continuous integration server or what have you, go
npm test to test my application the following happens:
pretestscript is executed
- it’s first command
npm run lintis run, and halt execution if
- we then clean the
- and recreate it for good measures
- it’s first command
- Now the
pretestscript is fully executed and the
testscript is run
Refactor the script
package.json is doing what I want, but starting to look messy. For example the
pretest script is a bit ugly to read now, don’t you think? Also the exact same script is used for
posttest Let’s fix it by refactoring a new script to clean logs. Like this:
There - that’s better. Now I simply call into my custom
cleanlogs script from
pretest script calls two scripts after each other.
I played around with actually installing the
jslint as a
prelint task. It works but I don’t think this is a good idea:
- The dependencies that is need to develop should be listed under
devDependencies. This adds clarity and understanding.
- Updating the
package.jsonfeels … dirty me thinks
That said - it’s totally possible. Here’s how:
But I’d advice against it.
The ‘npm run’ command
Let me leave you with a nifty little feature more of
npm. If you only go
npm run, without any parameters,
npm will list all scripts in your
package.json. Like a poor-mans-help. Here’s what comes out if you go
npm run on the file we’ve created:
As you can see the
post hooks are very powerful allies if you want to use
package.json as a build tool.
You can find my code here
I’ll write a post more about how we can integrate git, pushing and deploying into such script next.
I hope you liked this post.
Published byon Last updated