Loom, a JavaScript build tool

I’ve tried using grunt before (I mentioned it before) and haven’t been able to wrap my head around it and why it’s useful. GNU Make is still my preferred build tool and Makefiles for JavaScript projects are usually short, at least relative to some bigger C/C++ projects that I’ve seen.

The reasoning behind grunt files seems to be that you’re specifying a set of tasks and actions to take and then you’re providing settings/options for the task. This feels like an inversion of Make where you have a set of rules that are matched and then tasks and actions are taken. In grunt, I would have given the “less” task a list of input files and the output directory, whereas in Make I would setup a rule for the output files or name it something like “style” and then setup the shell command to call. Grunt abstracts the shell from you and lets you write JavaScript code for the tasks you want to execute. I recognize the advantage of having a build tool written in a language you’re using daily (Makefiles can be obscure) and the advantage of having several reusable modules that provide access to common commands such as testing frameworks, minifiers and compilers, etc.

So I thought I would take a crack at writing my own JavaScript build tool that combines Makefiles with some of the power of Grunt. I’m calling it Loom and you can check out the code here.

In your project, you create a Loomfile.json where you define a list of two dictionaries/hash-tables: one is the set of targets, the rules that you want to execute tasks for; the other hash-table is a set of tasks. The values for the targets are the list of dependencies, for example if you want to compile a style.less file, it might depend on three or four files. The values for the tasks are a list of commands to execute in order.

Here’s an example of a Loomfile.json (we’re assuming that style.less imports reset.less so that we only have to compile style.less instead of both files):

[{
  "style.css": ["reset.less", "style.less"],
  "ie7.css": "ie7.less"
},{
  "style.css": "lessc style.css style.less",
  "ie7.css": "lessc ie7.less ie7.css" 
}]

When you run loom, it will find that there are two targets, style.css and ie7.css, and then discover the tasks. Right now, it won’t automatically do anything, but the goal is that it will check to see if style.css exists and to see if its dependencies (reset.less and style.less) have been updated recently (either their checksum has changed or I might do a naive mtime check). Right now you will have to run “loom style.css ie7.css” to have the tasks executed.

So far it looks like a Makefile squished into a JSON file with a separation between the rules/targets and the tasks/commands. Might as well use a Makefile right? Wrong! Because you can also use built-in tasks that are written in JavaScript or custom tasks (which are in a Loomtasks.js file). So all the great reusable modules can be added to your projects and imported into Loomtasks.js or, if they’re commonly used, will find their way into the built-in tasks. Right now, I took a look at the preprocess node module and saw a grunt plugin that wraps it and wrapped it for use in loom; the difference is huge, because loom doesn’t have an API like grunt has for writing new tasks. Just write whatever you want in Loomtasks.js and the arguments to the command will be passed in and you can do whatever you want with them.

What I’ve enjoyed so far is that the code is compact. Loom may not offer as much as Grunt or Make, but I like the direction I’m going in and it’s yet another good way for me to become comfortable with NodeJS.

Some more work needs to be done to be able to use loom:

  • if a target has dependencies listed, assume the target is a file (or set of files) and check to see if it exists; if it doesn’t exist or if the dependencies have been updated since last running loom, run the corresponding task
  • -w, –watch option that watches the files of the target(s) for any changes and executes the corresponding task(s)
  • being able to install loom using npm and use it as standalone tool (“loom -w styles” versus “node ./node_modules/loom/loom.js -w styles.js”)
  • run the tasks in a synchronous way, I had trouble with node’s child_process.exec (which is asynchronous) and when I tried child_process.spawn I had some weird errors happening (the nodejs docs don’t include very good examples to work from)

This is only an experiment and I do not recommend anyone use the tool in production. It’s mostly for my own enjoyment and whenever I’m working on a front-end project I won’t be pushing to use loom, I’ll stick to grunt (if that’s already in use) or Makefiles or shell scripts or whatever.

Comments are Closed