Yoke - a drop-in, quick and dirty alternative to Sprockets

In local development, I tend to create separate pieces of JavaScript for every controller, model, view, etc. I loved the idea of Sprockets — grab all these files and stitch them together. While it worked well for a while (on the command-line), I got fed up with how slow it runs, especially on Ruby under Cygwin.
So, what to do? Node.js of course. Why? It’s fast, it’s super-easy to install and writing a script on top of it is a breeze. Meet Yoke:

#!/usr/bin/env node

var fs = require("fs"),
  path = require("path"),
  options = {
    verbose: false,
    directories: ["."],
  };

var includeFollowing = false;
process.argv.slice(2).forEach(function (option) {
  if (option === "-v" || option === "--verbose") {
    options.verbose = true;
  } else if (option === "-I" || option === "--include") {
    includeFollowing = true;
  } else if (option.indexOf("-I") === 0) {
    options.directories.push(option.substring(2));
  } else if (includeFollowing) {
    options.directories.push(option);
    includeFollowing = false;
  }
});

function findOne(file, directories) {
  var resolved = null;
  directories.forEach(function (directory) {
    if (resolved) {
      return;
    }
    var target = path.join(directory, file.replace(/\.js$/, "") + ".js");
    if (fs.existsSync(target)) {
      resolved = target;
    }
  });
  return resolved;
}

function pad(level) {
  return Array(level * 2 + 1).join(" ");
}

function processOne(file, level) {
  var lines = fs.readFileSync(file, "utf8");
  level || (level = 0);
  if (options.verbose) {
    console.error(pad(level) + "> " + file + "\n");
  }
  lines = lines.replace(
    /^\s*\/\/=\s*require\s+(["<])([^">]+).$/gm,
    function (match, type, location) {
      var resolved = findOne(
        location,
        type === "<" ? options.directories : [path.dirname(file)],
      );
      if (resolved) {
        return processOne(resolved, level + 1);
      }
      throw new Error(
        "Cannot resolve require:\n\n    " + match + "\n\n in file " + file,
      );
    },
  );
  return lines;
}

process.stdout.write(processOne(process.argv.pop()));

To use, copy the script above on $PATH and then:

yoke [-I path[ -I path]...] input.js > output.js

In most cases, you would just need to replace your existing sprocketize command with yoke. Both <file> and "file" requires are supported.