Skip to content

Instantly share code, notes, and snippets.

@undoZen
Last active December 18, 2015 15:49
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save undoZen/5806774 to your computer and use it in GitHub Desktop.
Save undoZen/5806774 to your computer and use it in GitHub Desktop.
a simple less middleware with source map, usage: app.use(lessMiddleware(path.join(__dirname))); -- only works for __dirname right now.
var less = require('less');
var fs = require('fs');
var path = require('path');
var SourceMapGenerator = require('source-map').SourceMapGenerator
function errfn(callback) {
return function (err) {
if (err) callback.call(this, err);
else callback.apply(this, [].slice.apply(arguments).slice(1));
}
}
function compileLess(filePath, callback) {
fs.readFile(filePath, 'utf8', errfn(parse));
function parse(str) {
var parser = new (less.Parser)({
paths: [path.dirname(filePath)],
filename: filePath,
dumpLineNumbers: 'comments'
})
parser.parse(str, errfn(gencss));
}
function gencss(tree) {
try {
var css = tree.toCSS();
callback(null, css);
} catch(parseError) {
callback(parseError);
}
}
}
module.exports = function (root) {
function removeRoot(filePath) {
if (filePath.indexOf(root) === 0) {
filePath = filePath.substring(root.length);
if (filePath[0] != '/') filePath = '/' + filePath;
}
return filePath;
}
return function (req, res, next) {
var isMap = false;
if (req.url.match(/\.css\.map(\?|$)/i)) isMap = true;
var fileName = req.url.replace(/\.css(\.map)?(\?|$)/i, '.less');
if (!fileName.match(/\.less$/)) return next();
var mapPath = fileName.replace(/\.less$/i, '.css.map');
var cssName = fileName.replace(/\.less$/i, '.css');
var filePath = path.join(root, fileName);
compileLess(filePath, function (err, css) {
var lines = css.split('\n');
if (err) return next(err);
if (isMap) {
var smg = new SourceMapGenerator({ file: cssName });
for (var i = 0; i < lines.length; i++) {
var match = lines[i].match(/\/\*\sline\s(\d+), ([^\*]+)\s\*\//);
if (match) {
smg.addMapping({
generated:{line:i+1,column:0},
original:{line:parseInt(match[1], 10),column:0},
source:removeRoot(match[2])
});
}
}
res.end(smg.toString());
} else {
res.end(css + '\n/*@ sourceMappingURL=' + mapPath + ' */');
}
})
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment