description and source-codeless-middleware = function (source, options){
// Source dir is required.
if (!source) {
throw new Error('less.middleware() requires `source` directory');
}
// Override the defaults for the middleware.
options = extend(true, {
cacheFile: null,
debug: false,
dest: source,
force: false,
once: false,
pathRoot: null,
postprocess: {
css: function(css, req) { return css; },
sourcemap: function(sourcemap, req) { return sourcemap; }
},
preprocess: {
less: function(src, req) { return src; },
path: function(pathname, req) { return pathname; },
importPaths: function(paths, req) { return paths; }
},
render: {
compress: 'auto',
yuicompress: false,
paths: []
},
storeCss: function(pathname, css, req, next) {
mkdirp(path.dirname(pathname), 511 /* 0777 */, function(err){
if (err) return next(err);
fs.writeFile(pathname, css, 'utf8', next);
});
},
storeSourcemap: function(pathname, sourcemap, req) {
mkdirp(path.dirname(pathname), 511 /* 0777 */, function(err){
if (err) {
utilities.lessError(err);
return;
}
fs.writeFile(pathname, sourcemap, 'utf8');
});
}
}, options || {});
// The log function is determined by the debug option.
var log = (options.debug ? utilities.logDebug : utilities.log);
if (options.cacheFile && !cacheFileInitialized) {
initCacheFile(options.cacheFile, log);
}
// Expose for testing.
less.middleware._saveCacheToFile = _saveCacheToFile;
// Actual middleware.
return function(req, res, next) {
if ('GET' != req.method.toUpperCase() && 'HEAD' != req.method.toUpperCase()) { return next(); }
var pathname = url.parse(req.url).pathname;
// Only handle the matching files in this middleware.
if (utilities.isValidPath(pathname)) {
var isSourceMap = utilities.isSourceMap(pathname);
// Translate source maps to a normal .css request which will update the associated source-map.
if( isSourceMap ){
pathname = pathname.replace( /\.map$/, '' );
}
var lessPath = path.join(source, utilities.maybeCompressedSource(pathname));
var cssPath = path.join(options.dest, pathname);
if (options.pathRoot) {
pathname = pathname.replace(options.dest, '');
cssPath = path.join(options.pathRoot, options.dest, pathname);
lessPath = path.join(options.pathRoot, source, utilities.maybeCompressedSource(pathname));
}
var sourcemapPath = cssPath + '.map';
// Allow for preprocessing the source filename.
lessPath = options.preprocess.path(lessPath, req);
log('pathname', pathname);
log('source', lessPath);
log('destination', cssPath);
// Ignore ENOENT to fall through as 404.
var error = function(err) {
return next('ENOENT' == err.code ? null : err);
};
var compile = function() {
fs.readFile(lessPath, 'utf8', function(err, lessSrc){
if (err) {
return error(err);
}
delete lessFiles[lessPath];
try {
var renderOptions = extend(true, {}, options.render, {
filename: lessPath,
paths: options.preprocess.importPaths(options.render.paths, req)
});
lessSrc = options.preprocess.less(lessSrc, req);
less.render(lessSrc, renderOptions, function(err, output){
if (err) {
utilities.lessError(err);
return next(err);
}
// Determine the imports used and check modified times.
var imports = [];
output.imports.forEach(function(imported) {
var currentImport = {
path: imported,
mtime: null
};
imports.push(currentImport);
// Update the mtime of the import async.
fs.stat(imported, function(err, lessStats){
if (err) { ...