Skip to content

Instantly share code, notes, and snippets.

@Warry Warry/stylus-shell.js
Last active Aug 29, 2015

What would you like to do?
sbt-we plugin workflow

We'll take the stylus plugin as an exemple.

Making it work with Node.js

To make our plugin to work with node, we are going to use Christopher's js-transpiler. It allows to develop a shell script that will work independtly on both node and the jvm. You can call a shell script from the cli like this:

    node my-shell.js file.extension '{"flag":true}'

You can use this to debug your processor until it works!

Anatomy of a jstranspiler shell script

You will find in attached, stylus-shell.js which will be our example. There are 4 parts in the file:


Nothing hard here. You can use npm (package.json) to define dependencies if you want. Note that sbt-web works natively with package.json, but cannot ensure that your code will work on Rhino, and node_modules/ files won't be accessible from other projects (such as the ones who will have the currently developed plugin). So a better practice is to use npm only the early stages of the developement workflow, then use webjars.


This is the code the logic of the build. We use when.js promises here to manage the callbacks. What you have to do is to define a function that will take in argument an input and an output file locations, and that returns a when promise.

The processor uses node apis such as mkdirp or fs.readFile, those apis are readable from Rhino, so you don't have to worry about this.

jstranspiler call

One line:

    jst.process({processor: processor, inExt: ".styl", outExt: (args.options.compress? ".min.css" : ".css")}, args);

You send the processor and some informations about the extentions to watch and to output. The last parametter is the arguments sent from sbt or the cli, basically the files locations.

error parsing

The biggest advantage of using sbt-web, especially with Playframework, is to get nice error reporting (in the browser or the IDE). In order to do that, you need to transform errors from your processor to this json object structure:

    message: "ERROR MESSAGE",
    severity: "error",
    lineNumber: parseInt(lineNumber),
    characterOffset: 0,
    lineContent: "x + z...",
    source: input

Making it work with sbt-web


/*global process, require */
var fs = require("fs"),
jst = require("jstranspiler"),
nodefn = require("when/node"),
mkdirp = require("mkdirp"),
path = require("path"),
stylus = require("stylus"),
nib = require("nib");
var promised = {
mkdirp: nodefn.lift(mkdirp),
readFile: nodefn.lift(fs.readFile),
writeFile: nodefn.lift(fs.writeFile)
var args = jst.args(process.argv);
function processor(input, output) {
return promised.readFile(input, "utf8").then(function(contents) {
var result = null;
var options = args.options;
options.filename = input;
var style = stylus(contents, options)
if (options.useNib) style.use(nib());
style.render(function (err, css) {
if (err) {
throw parseError(input, contents, err);
} else {
result = {
css: css,
style: style
return result;
}).then(function(result) {
return promised.mkdirp(path.dirname(output)).yield(result);
}).then(function(result) {
return promised.writeFile(output, result.css, "utf8").yield(result);
}).then(function(result) {
return {
source: input,
result: {
filesRead: [input].concat(,
filesWritten: [output]
}).catch(function(e) {
if (jst.isProblem(e)) return e; else throw e;
jst.process({processor: processor, inExt: ".styl", outExt: (args.options.compress? ".min.css" : ".css")}, args);
* Utility to take a stylus error object and coerce it into a Problem object.
function parseError(input, contents, err) {
var errLines = err.message.split("\n");
var lineNumber = (errLines.length > 0? errLines[0].substring(errLines[0].indexOf(":") + 1) : 0);
var lines = contents.split("\n", lineNumber);
return {
message: + ": " + (errLines.length > 2? errLines[errLines.length - 2] : err.message),
severity: "error",
lineNumber: parseInt(lineNumber),
characterOffset: 0,
lineContent: (lineNumber > 0 && lines.length >= lineNumber? lines[lineNumber - 1] : "Unknown line"),
source: input
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.