I'm a big fan of having an "automated build step" as part of the development workflow for a project. Figured I'd share what I'm currently doing with my Node.js projects.
What is an "automated build step"? For me, this mean some "shell" code that gets run when I save files while developing. It may not actually be "building" anything - often I'm just running tests. But the idea is that as I save files in my editor, things are run against the code that I just saved.
So there's basically two concepts here:
- file watchers - watching for updates to files
- code runners - when files are updated, run some code
file watchers
I have some history with file watchers - wr, jbuild, cakex. The problem with file watching is that it's not easy. The file watching bits in jbuild and cakex are also bundled with other build-y things, which is good and bad - basically bad because you have to buy in to using jbuild and cakex exclusively. wr is a little better in that it's just a file watcher/runner, but I ended finding a replacement for that with nodemon. The advantage to using nodemon is that it's already an accepted tool, and I don't have to support it. :-) Thanks Remy!
code runners
There are many options for code runners; make, cake, grunt, gulp, npm scripts, etc. I'm currently using npm scripts, because they're the easiest thing to do, for non-complex "build steps".
For example, here are the scripts
defined in the package.json
of the
nsolid-statsd package.
"scripts": {
"start": "node daemon",
"utest": "node test/index.js | tap-spec",
"test": "npm run utest && standard",
"watch": "nodemon --exec 'npm test' --ext js,json"
}
Everything's kicked off by the watch
script. It watches for file changes -
in this case, changes to .js
and .json
files, and runs npm test
when
file changes occur. npm test
is defined to run the test
script, and for
this package, that means running npm run utest
and then standard
. utest
is the final leg of the scripts here, which runs my tape
tests rooted at
test/index.js
, piping them through tap-spec
for colorization, etc.
So the basic flow will be this, when running the watch
script:
- run
node test/index.js
, piping throughtap-spec
- if that passes, run
standard
- wait for a
.js
or.json
file to change - when one of those files changes, start over from the beginning
Just run the watch
script in a dedicated terminal window, perhaps beside
your editor, and start editing and saving files.
Note that nodemon
, standard
and tap-spec
are available in the local
node_modules
directory, because they are devDependencies
in the package
itself (tape
is used when running test/index.js
):
"devDependencies": {
"nodemon": "~1.8.1",
"tape": "~4.2.0",
"tap-spec": "~4.1.1",
"standard": "~5.4.1"
}
As such, I can use their 'binaries' directly in the npm scripts.
command line usage
You would then kick this all off by running npm run watch
. But since it turns
out I've been adding a watch
script to projects for a long time, all of which
do basically the same thing (maybe with different tools), I instead wrote a
bash script a while back called watch
:
#!/bin/sh
npm run watch
So now I just always run watch
in the project directory to start my
automated build step.
Here's what the complete workflow looks like:
- open a terminal in the project directory
- open my editor (Atom)
- run
watch
at the command-line - see the tests runs successfully
- make a change by adding a blank line that
standard
will provide a diagnostic for - save file
- see tests run, see message from standard
- remove blank line, save file
- see tests run, no message from standard
It's basically an IDE using the command-line and your flavorite editor, and I love it.
As projects get more complex, I'll start using some additional tooling like
make, cake, etc, or just invoke stand-alone bash or node scripts available
in a tools
directory. Those would just get added as new npm scripts, and
injected into the script flow with nodemon. For instance, you might do
something like this:
"scripts": {
"start": "node daemon",
"utest": "node test/index.js | tap-spec",
"test": "npm run utest && standard",
"build": "tools/build.sh",
"build-test": "npm run build && npm test",
"watch": "nodemon --exec 'npm run build-test' --ext js,json"
}
Now when I watch
, tools/build.sh
will be run before the tests. You can
also run each of these steps individually, via npm run build
, npm test
,
npm run utest
, etc.