Goodbye Grunt, Hello Tend

June 9, 2014

Recently decided to give Grunt a try, which caused me to write my own node.js build system.


For the longest time I had refused to move away from Makefiles for Grunt or some other node.js build system. But I finally gave in and decided to take an afternoon to give Grunt a go. Initially it seemed promising, Grunt had a plugin for everything and ultimately it supporting watching files and directories (the one feature I really wanted for my make build setup).

I tried to move over a fairly simplistic Makefile that I already had written into a Gruntfile. However, after about an hour (or more) of trying to get grunt setup with grunt-cli and all the other plugins installed and configured to do the right thing I realized that Grunt wasn’t for me. I took a simple 10 (ish) line Makefile and turned it into a 40+ line Gruntfile and it still didn’t seem to do exactly what I wanted. What I had to reflect on was why should I spend all this time trying to learn how to configure some convoluted plugins when I already known the correct commands to execute? Then I realized what I really wanted wasn’t a new build system but simply watch for a Makefile

I have attempted to get some form of watch working with a Makefile in the past, but it usually involves using inotify and I’ve never gotten it working exactly like how I wanted. So, I decided to start writing my own system, because, why not spend more time on perfecting my build system. My requirements were fairly simple, I wanted a way to watch a directory/files for changes and when they do simply run a single command (ultimately make <target>), I wanted the ability to also run long running processing like node server.js and restart them if certain files have changed, and lastly unlike other watch based systems I have seen I wanted a way to run a command as soon as I start up the watch program (so you dont have to start the watching, then go open/save a newline change to a file to get it to build for the first time).

What I came up with was tend. Which solves mostly all of my needs, which was simply “watch for make”. So how do you use it?

Installation

npm install -g tend

Usage

Usage:
  tend
  tend <action>
  tend [--restart] [--start] [--ignoreHidden] [--filter <filter>] [<dir> <command>]
  tend (--help | --version)

Options:
  -h --help             Show this help text
  -v --version          Show tend version information
  -r --restart          If <command> is still running when there is a change, stop and re-run it
  -i --ignoreHidden     Ignore changes to files which start with "."
  -f --filter <filter>  Use <filter> regular expression to filter which files trigger the command
  -s --start            Run <command> as soon as tend executes

Example CLI Usage

The following will watch for changes to any js files in the directory ./src/ when any of them change or are added it will run uglifyjs to combine them into a single file.

tend --ignoreHidden --filter "*.js" ./src "uglifyjs -o ./public/main.min.js ./src/*.js"

The following will run a long running process, starting it as soon as tend starts and restarting the program whenever files in ./routes/ has changed.

tend --restart --start --filter "*.js" ./routes "node server.js"

Config File

Instead of running tend commands singly from the command line you can provide tend with a .tendrc file of multiple directories/files to watch with commands to run.

The following .tendrc file are setup to run the same commands as shown above.

; global settings
ignoreHidden=true

[js]
filter=*.js
directory=./src
command=uglifyjs -o ./public/main.min.js ./src/*.js

[app]
filter=*.js
directory=./routes
command=node ./app/server.js
restart=true
start=true

You can then simply run tend without any arguments to have tend watch for all changes configured in your .tendrc file.

Running:

tend

Will basically execute:

tend --ignoreHidden --filter "*.js" ./src "uglifyjs -o ./public/main.min.js ./src/*.js" \
  & tend --restart --start --filter "*.js" ./routes "node server.js"

Along with running multiple targets at once, you can run specific targets from a .tendrc file as well, tend <target>.

tend js

Will run the js target once.

tend --ignoreHidden --filter "*.js" ./src "uglifyjs -o ./public/main.min.js ./src/*.js"

With Make

If I haven’t beaten a dead horse enough, I am a Makefile kind of person and that is exactly what I wanted to use this new tool with. So below is an example of a Makefile and it’s corresponding .tendrc file.

js:
    uglifyjs -o ./public/main.min.js ./src/*.js

app:
    node server.js

.PHONY: js app
ignoreHidden=true

[js]
filter=*.js
directory=./src
command=make js

[app]
filter=*.js
directory=./routes
command=make app
restart=true
start=true

Conclusion

So that is mostly it. Nothing overly exciting and nothing really new here, just another watch/build system written in node to add to the list. For the most part this tool does exactly what I want for now, but if anyone has any ideas on how to make this better or even any other better/easier tools which do similar things please let me know, I am more than willing to continue maintaining this tool.

comments powered by Disqus