var less = require('less');
var fs = require('fs');
var url = require('url');
var path = require('path');
var async = require('async');
var ap = require('ap');

exports = module.exports = lessmagic;

function lessmagic(basepath, opts) {

argument overloading magic

    if (typeof basepath != 'string') {
        opts = basepath;
        basepath = null;

ensure opts is an object

    opts = opts || {};

make sure opts.paths is an array

    if (!opts.paths) opts.paths = [];

if we got a path, add it

    if (path) opts.paths.unshift(basepath);

make ourselves a parser with our options

    var parser = new less.Parser(opts);

give back the middleware

    return function lessmagicMiddleware(req, res, next) {

figure out the filepath from the URL

        var pathname = url.parse(req.url).pathname.slice(1);

check if it's a CSS request

        var match = pathname.match(/^(.*)\.css$/);

nope? this req is not my problem.

        if (!match) return next();

turn .css name into .less name

        var lessname = match[1] + '.less';

make a list of .less paths to check

        var searchnames =, lessname));

go find the right one

        async.detect(searchnames, fs.exists || path.exists, function(lesspath) {

not found? this request is not my problem.

            if (!lesspath) return next();

read the file and pass it on

            fs.readFile(lesspath, 'utf8', parseLess);

        function parseLess(err, data) {

failure? pass it on to the client

            if (err) return error(err, 'reading LESS file');

parse the file!

            parser.parse(data, sendLess);

        function sendLess(err, tree) {

failure? pass it on.

            if (err) return error(err, 'parsing LESS file');

            var css;
            try {

try generating the CSS

                css = tree.toCSS();
            catch (e) {

failure? pass it on.

                return error(e, 'creating CSS');

set the right content type

            res.setHeader('Content-Type', 'text/css');

write the response


and we're done!


        function error(err, explanation) {

aww, failure. that's a 500.

            res.statusCode = 500;

our response is plain text

            res.setHeader('Content-Type', 'text/plain');

and it's the explanation of the error

            res.write('error ' + explanation + '\n');

and perhaps the exception

            if (err) res.write((err.stack || JSON.stringify(err, null, 4)) + '\n');

and done :(


let us be attachable as Broadway plugin for Flatiron

lessmagic.attach = function attach(opts) {

wire ourselves up to the router for .css requests

    this.router.get(/\.css$/, lessmagic(opts));

lessmagic.detach is not implemented

lessmagic.detach = null;

lessmagic.init = function(cb) {

we don't need to do any initialisation, so return right away

    return cb();