express-compiless
Advanced tools
Comparing version 0.0.4 to 0.0.5
var Path = require('path'), | ||
less = require('less'); | ||
crypto = require('crypto'), | ||
fs = require('fs'), | ||
less = require('less'), | ||
seq = require('seq'), | ||
passError = require('passerror'), | ||
importRegExp = /(@import(?:\s*'.*?'|\s*".*?"|\s+url\((?:[^'"]*?|'[^']*'|"[^"]*")\));)/, | ||
importRegExpWithCaptures = /@import(?:\s*(['"])(.*?)\1|\s+url\((['"]|)(.*?)\3\));/; | ||
require('express-hijackresponse'); | ||
// Returns flattened less text, accumulates seen files in the isSeenByFileName object provided by the caller. | ||
function resolveImports(lessText, baseDir, isSeenByFileName, cb) { | ||
seq(lessText.split(importRegExp)) | ||
.parMap(function (chunk) { | ||
var cb = this, | ||
matchImport = chunk.match(importRegExpWithCaptures); | ||
if (matchImport) { | ||
var importedFileName = Path.resolve(baseDir.replace(/\/*$/, '/'), matchImport[2] || matchImport[4]); | ||
isSeenByFileName[importedFileName] = true; | ||
fs.readFile(importedFileName, 'utf-8', passError(cb, function (importedLessText) { | ||
resolveImports(importedLessText, Path.dirname(importedFileName), isSeenByFileName, passError(cb, function (lessText) { | ||
cb(null, lessText); | ||
})); | ||
})); | ||
} else { | ||
this(null, chunk); | ||
} | ||
}) | ||
.seq(function () { | ||
cb(null, this.stack.join('')); | ||
}); | ||
} | ||
module.exports = function compiless(options) { | ||
@@ -32,12 +62,2 @@ if (!options || !options.root) { | ||
// won't reply with a false positive 304 on the same URL after compiless has been enabled or disabled: | ||
var oldETag = res.getHeader('ETag'); | ||
if (oldETag) { | ||
var newETag = '"' + oldETag.replace(/^"|"$/g, '') + '-compiless"'; | ||
res.setHeader('ETag', newETag); | ||
if (ifNoneMatch && ifNoneMatch.indexOf(newETag) !== -1) { | ||
return res.send(304); | ||
} | ||
} | ||
var chunks = []; | ||
@@ -54,13 +74,45 @@ res.on('error', function () { | ||
var lessText = Buffer.concat(chunks).toString('utf-8'), // No other charsets are really relevant, right? | ||
baseDir = Path.resolve(options.root, req.url.replace(/\/[^\/]*(?:\?.*)?$/, '/').substr(1)); | ||
baseDir = Path.resolve(options.root, req.url.replace(/\/[^\/]*(?:\?.*)?$/, '/').substr(1)), | ||
isSeenByFileName = {}; | ||
// Unfortunately less.render throws errors in async code instead of passing it to our callback. | ||
// Hopefully the solution for https://github.com/cloudhead/less.js/issues/462 will remedy this. | ||
less.render(lessText, {paths: [baseDir]}, function (err, cssText) { | ||
if (err) { | ||
return res.send(500); | ||
} | ||
res.setHeader('Content-Type', 'text/css'); | ||
res.setHeader('Content-Length', Buffer.byteLength(cssText)); | ||
res.end(cssText); | ||
resolveImports(lessText, baseDir, isSeenByFileName, function (err, flattenedLessText) { | ||
seq(Object.keys(isSeenByFileName)) | ||
.parEach(function (fileName) { | ||
fs.stat(fileName, this.into(fileName)); | ||
}) | ||
.unflatten() | ||
.seq(function (fileNames) { | ||
var oldETag = res.getHeader('ETag'); | ||
if (oldETag) { | ||
var etagFragments = [oldETag.replace(/^"|"$/g, '')]; | ||
if (fileNames.length) { | ||
var importedFileStats = []; | ||
fileNames.sort().forEach(function (fileName) { | ||
var stats = this.vars[fileName]; | ||
importedFileStats.push(fileName, String(stats.mtime.getTime()), String(stats.size)); | ||
}, this); | ||
etagFragments.push(crypto.createHash('md5').update(importedFileStats.join('-')).digest('hex').substr(0, 16)); | ||
} | ||
var newETag = '"' + etagFragments.join('-') + '-compiless"'; | ||
res.setHeader('ETag', newETag); | ||
if (ifNoneMatch && ifNoneMatch.indexOf(newETag) !== -1) { | ||
return res.send(304); | ||
} | ||
} | ||
// Unfortunately less.render throws errors in async code instead of passing it to our callback. | ||
// Hopefully the solution for https://github.com/cloudhead/less.js/issues/462 will remedy this. | ||
less.render(lessText, {paths: [baseDir]}, function (err, cssText) { | ||
if (err) { | ||
return res.send(500); | ||
} | ||
cssText = fileNames.map(function (fileName) { | ||
return "/*@IMPORTED:" + Path.relative(options.root, fileName) + "*/\n"; | ||
}).join("") + cssText; | ||
res.setHeader('Content-Type', 'text/css'); | ||
res.setHeader('Content-Length', Buffer.byteLength(cssText)); | ||
res.end(cssText); | ||
}); | ||
}); | ||
}); | ||
@@ -67,0 +119,0 @@ }); |
{ | ||
"name": "express-compiless", | ||
"version": "0.0.4", | ||
"version": "0.0.5", | ||
"description": "Express middleware that compiles less files to css on the way out.", | ||
@@ -25,3 +25,4 @@ "main": "lib/index.js", | ||
"less": "=1.3.0", | ||
"passerror": "=0.0.1" | ||
"passerror": "=0.0.1", | ||
"seq": "=0.3.5" | ||
}, | ||
@@ -28,0 +29,0 @@ "devDependencies": { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
12431
170
4
1
+ Addedseq@=0.3.5