var path       = require('path');
var less       = require('less');
var fs         = require('fs');
var crypto     = require('crypto');
var chokidar   = require('chokidar');
var browserify = require('browserify');
var UglifyJS   = require('uglify-js');
var debug      = require('debug')('Noumena:setup');
var app;

function buildCss(next, ensureDirectory) {
    var code = fs.readFileSync(process.cwd() + '/all.less', {encoding: 'utf8'});
    var options = {
        paths        : [process.cwd()],
        filename     : "all.less",
        compress     : true,
        ieCompat     : false
    };
    less.render(code, options, function(error, output) {
        ensureDirectory();

        if (error) { return less.writeError(error, options); }

        app.set('css', {
            md5:  crypto.createHash('md5').update(output.css).digest('hex'),
            code: output.css
        });

        next();
    });
}

function buildJs(next, ensureDirectory) {
    var b = browserify();
    b.add('./main.js');
    b.bundle(function(err, buf) {
        ensureDirectory();

        if (err) {
            var js = 'alert("JS build error!\n\n"+' + JSON.stringify(String(err)) + ');';
        } else {
            var js = buf.toString();
        }

        var options = {
            fromString : true,
            mangle     : {screw_ie8: true},
            compress   : {
                booleans      : true,
                cascade       : true,
                comparisons   : true,
                conditionals  : true,
                dead_code     : true,
                drop_console  : true,
                drop_debugger : true,
                evaluate      : true,
                hoist_funs    : true,
                if_return     : true,
                join_vars     : true,
                loops         : true,
                negate_iife   : true,
                properties    : true,
                screw_ie8     : true,
                sequences     : true,
                unused        : true
            }
        };

        var minified = UglifyJS.minify(js, options).code;

        app.set('js', {
            md5:  crypto.createHash('md5').update(minified).digest('hex'),
            code: minified
        });

        next();
    });
}

// Auto-compilation abstraction. You give it a directory path, and a compilation routine.
// The routine will be executed in the directory specified, and this directory will be
// watched for changes, running this compile step again when changes are detected.
// If the routine needs to be async, we'll pass it a "next" argument.
function autoCompile(directory, routine) {
    var absolute = path.resolve(__dirname, directory);
    function compile() {
        var originalDirectory = process.cwd();
        var ensureDirectory = process.chdir.bind(process, absolute);
        var next = process.chdir.bind(process, originalDirectory);
        try {
            ensureDirectory();
            routine(next, ensureDirectory);
        } catch (e) {
            debug('Compile routine error for ' + directory + ': \n' + e);
            next();
        }
        next();
    }
    compile();
    var watcher = chokidar.watch(absolute);
    var timeout = null;
    watcher.on('all', function change() {
        if (timeout) {
            clearTimeout(timeout);
            timeout = null;
        }
        timeout = setTimeout(function() {
            compile();
            timeout = null;
        }, 150);
    });
    // It's not like Chokidar is particularly helpful when it flops over and dies, but...
    watcher.on('error', function(error){ debug('Chokidar error: ' + error); });
}

// This setup mechanism is invoked before starting the server.
function setup(instance) {
    app = instance;
    autoCompile('../stylesheets', buildCss);
    autoCompile('../scripting', buildJs);
}

module.exports = setup;

@@ -4,24 +4,26 @@
  path       
+  
 = require('p
@@ -40,24 +40,26 @@
  less       
+  
 = require('l
@@ -72,32 +72,34 @@
 %0Avar fs         
+  
 = require('fs');
@@ -110,24 +110,26 @@
  crypto     
+  
 = require('c
@@ -152,16 +152,18 @@
 kidar   
+  
 = requir
@@ -192,16 +192,18 @@
 wserify 
+  
 = requir
@@ -234,16 +234,18 @@
 ifyJS   
+  
 = requir
@@ -256,24 +256,77 @@
 glify-js');%0A
+var LessCleanCSS = require('less-plugin-clean-css');%0A
 var debug   
@@ -324,24 +324,26 @@
  debug      
+  
 = require('d
@@ -412,32 +412,141 @@
 ureDirectory) %7B%0A
+    var lessCleanCSS = new LessCleanCSS(%7B%0A        advanced: true,%0A        keepSpecialComments: false%0A    %7D);%0A
     var code = f
@@ -647,20 +647,16 @@
 aths    
-    
 : %5Bproce
@@ -683,20 +683,16 @@
 ilename 
-    
 : %22all.l
@@ -714,20 +714,16 @@
 ompress 
-    
 : true,%0A
@@ -743,19 +743,50 @@
 pat 
-    
 : false
+,%0A        plugins  : %5BlessCleanCSS%5D
 %0A   

Configure less to use clean-css via its plugin.