Skip to content

Instantly share code, notes, and snippets.

@yulanggong
Created March 15, 2012 02:36
Show Gist options
  • Save yulanggong/2041387 to your computer and use it in GitHub Desktop.
Save yulanggong/2041387 to your computer and use it in GitHub Desktop.
Less compiler with --watch and --smartpath support
/*
* Less compiler with --watch and --smartpath support
*
* Based on lessc of less 1.3.0
*
* Added options:
*
* -w, --watch Watch files (include @import files) for changes and re-compile
*
* -m, --smartpath output css file like this:
* if a css folder exists
* folder/less/main.less -> folder/css/main.css
* if not
* folder/main.less -> folder/main.css
*/
var path = require('path'),
fs = require('fs'),
sys = require('util'),
less = require('less');
var options = {
compress: false,
yuicompress: false,
optimization: 1,
silent: false,
paths: [],
color: true,
smartpath: false,
watch: false,
strictImports: true
}
var args = process.argv.slice(1);
args = args.filter(function (arg) {
var match;
if (match = arg.match(/^-I(.+)$/)) {
options.paths.push(match[1]);
return false;
}
if (match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=([^\s]+))?$/i)) { arg = match[1] }
else { return arg }
switch (arg) {
case 'v':
case 'version':
sys.puts("lessc " + less.version.join('.') + " (LESS Compiler) [JavaScript]");
process.exit(0);
case 'verbose':
options.verbose = true;
break;
case 's':
case 'silent':
options.silent = true;
break;
case 'strict-imports':
options.strictImports = true;
break;
case 'm' :
case 'smartpath' :
options.smartpath = true;
break;
case 'h':
case 'help':
sys.puts("usage: lessc source [destination]");
process.exit(0);
case 'x':
case 'compress':
options.compress = true;
break;
case 'w':
case 'watch':
options.watch = true;
break;
case 'yui-compress':
options.yuicompress = true;
break;
case 'no-color':
options.color = false;
break;
case 'include-path':
options.paths = match[2].split(os.type().match(/Windows/) ? ';' : ':')
.map(function(p) {
if (p) {
return path.resolve(process.cwd(), p);
}
});
break;
break;
case 'O0': options.optimization = 0; break;
case 'O1': options.optimization = 1; break;
case 'O2': options.optimization = 2; break;
}
});
var input = args[1];
if (input && input != '-') {
input = path.resolve(process.cwd(), input);
}
var output = args[2];
if (output) {
output = path.resolve(process.cwd(), output);
}
var css, fd, tree, watchList = [];
if (! input) {
sys.puts("lessc: no input files");
process.exit(1);
}
if (options.smartpath) {
var basename = path.basename(input, '.less'),
rootPath = path.resolve(input, '..','..'),
cssPath = path.resolve(rootPath, 'css');
path.exists(cssPath, function (exists){
if (exists) {
output = path.resolve(cssPath, basename + ".css");
} else {
output = path.resolve(input, '..', basename + ".css");
}
});
}
var currentTime = function (){
return (new Date()).toTimeString().substr(0,9);
}
var parseLessFile = function (e, data) {
if (e) {
sys.puts("lessc: " + e.message);
if (!options.watch) process.exit(1);
}
var parser = new (less.Parser)({
paths: [path.dirname(input)].concat(options.paths),
optimization: options.optimization,
filename: input,
strictImports: options.strictImports
});
parser.parse(data, function (err, tree) {
if (options.watch) {
if(!watchList.length) {
watchList.push(input);
setTimeout(function () {
watchLess(input);
},0);
}
for (i in parser.imports.files) {
var filename = path.resolve(input,"..", i);
if (watchList.indexOf(filename) == -1){
watchList.push(filename);
(function (filename) {
setTimeout(function () {
watchLess(filename);
},0);
})(filename);
}
}
}
if (err) {
sys.puts('Error -- ' + currentTime());
less.writeError(err, options);
if (!options.watch) process.exit(1);
} else {
try {
css = tree.toCSS({
compress: options.compress,
yuicompress: options.yuicompress
});
if (output) {
fd = fs.openSync(output, "w");
fs.writeSync(fd, css, 0, "utf8");
sys.puts(output + ' -- ' + currentTime());
} else {
sys.print(css);
}
} catch (e) {
sys.puts('Error -- ' + currentTime());
less.writeError(e, options);
if (!options.watch) process.exit(2);
}
}
});
};
var lastChange = 0;
var watchLess = function (filename){
path.exists(filename,function (exists){
if (exists) {
fs.watch(filename, function (event){
if(event === "change"){
var time = + new Date();
if (time - lastChange > 100) {
fs.readFile(input, 'utf-8', parseLessFile);
}
lastChange = time;
}
})
} else {
sys.puts('File not found: ' + filename);
}
})
};
if (input != '-') {
fs.readFile(input, 'utf-8', parseLessFile);
} else {
process.stdin.resume();
process.stdin.setEncoding('utf8');
var buffer = '';
process.stdin.on('data', function(data) {
buffer += data;
});
process.stdin.on('end', function() {
parseLessFile(false, buffer);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment