connect-less.js | |
---|---|
Connect-less is a simple | |
Main documentationModule dependencies | var async = require('async')
, fs = require('fs')
, less = require('less')
, path = require('path')
, url = require('url'); |
File extensions | var ext = '.less'
, match = /\.css$/;
module.exports = function (options) {
options = options || {}; |
Options
| var srcDir = path.normalize(options.src || process.cwd())
, dstDir = path.normalize(options.dst || srcDir)
, dstRoot = path.normalize(options.dstRoot || dstDir)
, dstSub = new RegExp('^' + dstDir.replace(dstRoot, ''))
, compress = options.compress || false
, debug = options.debug || false
, force = options.force || false
, deps = {}; |
Middleware | return function (req, res, next) {
if ('GET' != req.method) return next();
var pathname = url.parse(req.url).pathname;
if (!match.test(pathname)) return next();
var relPath = pathname.replace(dstSub, '')
, src = path.join(srcDir, relPath).replace(match, ext)
, dst = path.join(dstDir, relPath);
if (debug) log('request', dst + ' -> ' + src + (force ? ' (forced)' : ''));
if (force) return compile();
fs.stat(src, function (err, srcStats) {
if (err && 'ENOENT' == err.code) return next();
if (err) return next(err);
fs.stat(dst, function (err, dstStats) {
if (err && 'ENOENT' == err.code) return compile();
if (err) return next(err);
if (srcStats.mtime > dstStats.mtime) {
if (debug) log('modified', src);
delete deps[src];
return compile();
} else {
statDeps(src, deps, function (err, stats) {
if (err) return next(err);
for (var file in stats) {
if (stats[file].mtime > dstStats.mtime) {
if (debug) log('modified', file + ' (dependency)');
delete deps[src];
return compile();
}
}
if (debug) log('not modified', src);
next();
});
}
});
});
function compile() {
var opts = { compress: compress, paths: [ path.dirname(src) ]};
if (debug) log('rendering', dst);
fs.readFile(src, 'utf8', function (err, str) {
if (err) return next(err);
less.render(str, opts, function (err, str) {
if (err) return next(err);
if (debug) log('rendered', dst);
fs.writeFile(dst, str, 'utf8', next);
});
});
}
};
function statDeps(file, deps, cb) {
var dir = path.dirname(file)
, stats = {};
findDeps(dir, file, deps, function (err, deps) {
if (err) cb(err);
if (!deps.length) cb(null, {}); |
Stat each file in parallel | async.forEach(deps, function (file, done) {
fs.stat(file, function (err, s) {
if (err) return done(err);
stats[file] = s;
done();
});
}, function (err) {
cb(err, stats);
});
});
}
function findDeps(dir, file, deps, cb) { |
Dependencies are cached | if (deps[file]) return cb(null, deps[file]);
fs.readFile(file, 'utf8', function (err, str) {
if (err) return cb(err);
if (debug) log('finding dependencies', file); |
Parse and extract dependencies recursively | var parser = new less.Parser({ paths: [ dir ]});
parser.parse(str, function (err, root) {
if (err) return cb(err);
deps[file] = [];
walkRoot(dir, root, deps[file]);
return cb(null, deps[file]);
});
});
}
function walkRoot(dir, root, deps) {
var file, rule;
for (var i = 0; i < root.rules.length; i++) {
rule = root.rules[i];
if (rule.path && rule.root) {
if (rule.path.charAt(0) == '/') {
file = rule.path;
} else {
file = path.normalize(dir + '/' + rule.path);
}
deps.push(file);
walkRoot(path.dirname(file), rule.root, deps);
}
}
return deps;
}
} |
Is there a standard for console debugging messages? | function log(message, arg) {
console.info('[connect-less] %s: %s', message, arg);
}
|