Using npm-scripts has become a popular way of maintaining the various build tasks needed to develop Node.js modules. People like npm-scripts because it's simple! This is a common refrain:
Don't bother with grunt, gulp, or broccoli, just add a little script to your package.json and run it with
npm run name:of:script
Indeed, this is much simpler, but it can quickly become a mess. Take a look at what happened to our testdouble.js library's package.json. Using npm-scripts for everything is simple to start with, but it can't hope to guard against the complexity that naturally accumulates over the life of a project.
We wrote scripty to help us extract our npm scripts—particularly the gnarly ones—into their own files without changing the command we use to run them. To see how to do this yourself, read on!
$ npm install --save-dev scripty
- From your module's root, create a
scripts
directory - If you want to define an npm script named "foo:bar", write an executable
file at
scripts/foo/bar
- Feel a liberating breeze roll over your knuckles as your script is free to roam within its own file, beyond the stuffy confines of a quote-escaped string inside a pile of JSON
- Declare your
"foo:bar"
script in"scripts"
in yourpackage.json
:
"scripts": {
"foo:bar": "scripty"
}
From this point on, you can run npm run foo:bar
and scripty will use npm's
built-in npm_lifecycle_event
environment variable to look up
scripts/foo/bar
and execute it for you.
This pattern is great for extracting
scripts that are starting to become unwieldy inside your package.json
, while
still explicitly calling out the scripts that your package supports (though
where to take that aspect from here is up for
debate).
Ready to take things to the next level? Check this stuff out:
To pass command-line args when you're running an npm script, set them after
--
and npm will forward them to your script (and scripty will do its part by
forwarding them along).
For example, if you had a script in scripts/echo/hello
:
#!/usr/bin/env sh
echo Hello, "$1"!
Then you can run npm run echo:hello -- WORLD
and see your script print
"Hello, WORLD!"
.
Let's say you have two test tasks in scripts/test/unit
and
scripts/test/integration
:
"scripts": {
"test:unit": "scripty",
"test:integration": "scripty"
}
And you want npm test
to simply run all of them, regardless of order. In that
case, just add a "test"
entry to your package.json
like so:
"scripts": {
"test:unit": "scripty",
"test:integration": "scripty",
"test": "scripty"
}
And from then on, running npm test
will result in scripty running all the
executable files it can find in scripts/test/*
.
Suppose in the example above, it becomes important for us to run our scripts in
a particular order. Or, perhaps, when running npm test
we need to do some other
custom scripting as well. Fear, not!
Without changing the JSON from the previous example:
"scripts": {
"test:unit": "scripty",
"test:integration": "scripty",
"test": "scripty"
}
Defining a script named scripts/test/index
will cause scripty to only run that
index
script, as opposed to globbing for all the scripts it finds in
scripts/test/*
.
Windows support is provided by Scripty in two ways:
- If everything in your
scripts
directory can be safely executed by Windows, no action is needed (this is only likely if you don't have collaborators on Unix-like platforms) - If your project needs to run scripts in both Windows & Unix, then you may
define a
scripts-win/
directory with a symmetrical set of scripts to whatever Unix scripts might be found inscripts/
To illustrate the above, suppose you have this bash script configured as
"test/unit"
in your package.json file and this bash script defined in
scripts/test/unit
:
#!/usr/bin/env bash
teenytest --helper test/unit-helper.js "lib/**/*.test.js"
In order to add Windows support, you could define scripts-win/test/unit.cmd
with this script:
@ECHO OFF
teenytest --helper test\unit-helper.js "lib\**\*.test.js"
With a configuration like the above, if npm run test:unit
is run from a Unix
platform, the initial bash script in scripts/
will run. If the same CLI
command is run from Windows, however, the batch script in scripts-win/
will be
run.
- Is this black magic? - Nope! For once, instilling some convention didn't
require any clever metaprogramming, just environment variables npm already sets;
try running
printenv
from a script some time! - Why isn't my script executing? - If your script isn't executing, make sure
it's executable! In UNIX, this can be accomplished by running
chmod +x scripts/path/to/my/script