node-sass-middleware
Advanced tools
Comparing version 0.5.0 to 0.6.0
@@ -0,1 +1,3 @@ | ||
"use strict"; | ||
var sass = require('node-sass'), | ||
@@ -15,9 +17,12 @@ fs = require('fs'), | ||
* | ||
* `src` Source directory used to find .scss files | ||
* `dest` Destination directory used to output .css files | ||
* when undefined defaults to `src` | ||
* `root` A base path for both source and destination directories | ||
* `outputStyle` Sass output style (nested or compressed), nested by default | ||
* `force` Always re-compile | ||
* `debug` Output debugging information | ||
* `src` Source directory used to find .scss files | ||
* `dest` Destination directory used to output .css files | ||
* when undefined defaults to `src`. | ||
* `outputStyle` Sass output style (nested,expanded, compact or compressed) | ||
* `response` Always write output directly to response | ||
* `response` True (default) to write output directly to response | ||
* instead of to a file | ||
* `error` A function to be called when something goes wrong | ||
* | ||
@@ -32,9 +37,9 @@ * Examples: | ||
* | ||
* var server = connect.createServer( | ||
* sass.middleware({ | ||
* src: __dirname | ||
* , dest: __dirname + '/public' | ||
* }) | ||
* , connect.static(__dirname + '/public') | ||
* ); | ||
* var server = connect.createServer( | ||
* sass.middleware({ | ||
* src: __dirname, | ||
* dest: __dirname + '/public' | ||
* }), | ||
* connect.static(__dirname + '/public') | ||
* ); | ||
* | ||
@@ -46,27 +51,29 @@ * @param {Object} options | ||
module.exports = function(options){ | ||
module.exports = function(options) { | ||
options = options || {}; | ||
// Accept src/dest dir | ||
if ('string' == typeof options) { | ||
// Accept single src/dest dir | ||
if (typeof options == 'string') { | ||
options = { src: options }; | ||
} | ||
// Force compilation | ||
var force = options.force || options.response; | ||
// This function will be called if something goes wrong | ||
var error = options.error || function() { }; | ||
// Debug option | ||
var debug = options.debug; | ||
// Source dir required | ||
var src = options.src; | ||
if (!src) { throw new Error('sass.middleware() requires "src" directory'); } | ||
// Default dest dir to source | ||
// Source directory (required) | ||
var src = options.src || function() { | ||
throw new Error('sass.middleware() requires "src" directory.'); | ||
}(); | ||
// Destination directory (source by default) | ||
var dest = options.dest || src; | ||
// Optional base path for src and dest | ||
var root = options.root || null; | ||
// Force compilation everytime | ||
var force = options.force || options.response; | ||
// Enable debug output | ||
var debug = options.debug; | ||
// Default compile callback | ||
options.compile = options.compile || function(){ | ||
options.compile = options.compile || function() { | ||
return sass; | ||
@@ -76,4 +83,7 @@ }; | ||
// Middleware | ||
return function sass(req, res, next){ | ||
if ('GET' != req.method && 'HEAD' != req.method) { return next(); } | ||
return function sass(req, res, next) { | ||
if (req.method != 'GET' && req.method != 'HEAD') { | ||
return next(); | ||
} | ||
var path = url.parse(req.url).pathname; | ||
@@ -83,12 +93,15 @@ if (options.prefix && 0 === path.indexOf(options.prefix)) { | ||
} | ||
if (/\.css$/.test(path)) { | ||
if (!/\.css$/.test(path)) { | ||
next(); | ||
} else { | ||
var cssPath = join(dest, path), | ||
sassPath = join(src, path.replace('.css', '.scss')), | ||
sassPath = join(src, path.replace(/\.css$/, '.scss')), | ||
sassDir = dirname(sassPath); | ||
if (root) { | ||
cssPath = join(root, dest, path.replace(dest, '')); | ||
cssPath = join(root, dest, path.replace(new RegExp('^' + dest), '')); | ||
sassPath = join(root, src, path | ||
.replace(dest, '') | ||
.replace('.css', '.scss')); | ||
.replace(new RegExp('^' + dest), '') | ||
.replace(/\.css$/, '.scss')); | ||
sassDir = dirname(sassPath); | ||
@@ -102,43 +115,64 @@ } | ||
// Ignore ENOENT to fall through as 404 | ||
var error = function(err) { | ||
next('ENOENT' == err.code | ||
? null | ||
: err); | ||
}; | ||
// When render is done, respond to the request accordingly | ||
var done = function(err, result) { | ||
var data; | ||
if (err) { | ||
var file = sassPath; | ||
if (err.file && err.file != 'stdin') { | ||
file = err.file; | ||
} | ||
var fileLineColumn = file + ':' + err.line + ':' + err.column; | ||
data = err.message.replace(/^ +/, '') + '\n\nin ' + fileLineColumn; | ||
if (debug) logError(data); | ||
error(err); | ||
return next(err); | ||
} else { | ||
data = result.css; | ||
if (debug) { log('render', options.response ? '<response>' : sassPath); } | ||
imports[sassPath] = result.stats.includedFiles; | ||
// If response is falsey, also write to file | ||
if (!options.response) { | ||
mkdirp(dirname(cssPath), '0700', function(err) { | ||
if (err) { | ||
return error(err); | ||
} | ||
fs.writeFile(cssPath, data, 'utf8', function(err) { | ||
if (err) { | ||
return error(err); | ||
} | ||
}); | ||
}); | ||
} | ||
} | ||
res.writeHead(200, { | ||
'Content-Type': 'text/css', | ||
'Cache-Control': 'max-age=0' | ||
}); | ||
res.end(data); | ||
} | ||
// Compile to cssPath | ||
var compile = function() { | ||
if (debug) { log('read', cssPath); } | ||
fs.readFile(sassPath, 'utf8', function(err, str){ | ||
if (err) { return error(err); } | ||
fs.readFile(sassPath, 'utf8', function(err, str) { | ||
if (err) { | ||
error(err); | ||
return next(); | ||
} | ||
var style = options.compile(); | ||
var paths = []; | ||
delete imports[sassPath]; | ||
style.render({ | ||
data: str, | ||
success: function(result){ | ||
if (debug) { log('render', options.response ? '<response>' : sassPath); } | ||
imports[sassPath] = paths; | ||
// If response is falsey, also write to file | ||
if (!options.response) { | ||
mkdirp(dirname(cssPath), 0700, function(err){ | ||
if (err) return error(err); | ||
fs.writeFile(cssPath, result.css, 'utf8', function(err) { | ||
if (err) return error(err); | ||
}); | ||
}); | ||
} | ||
res.writeHead(200, { | ||
'Content-Type': 'text/css', | ||
'Cache-Control': 'max-age=0' | ||
}); | ||
res.end(result.css); | ||
}, | ||
includePaths: [ sassDir ].concat(options.include_paths || options.includePaths || []), | ||
imagePath: options.image_path || options.imagePath, | ||
outputStyle: options.output_style || options.outputStyle | ||
}); | ||
includePaths: [sassDir].concat(options.includePaths || []), | ||
outputStyle: options.outputStyle | ||
}, done); | ||
}); | ||
@@ -148,11 +182,18 @@ }; | ||
// Force | ||
if (force) { return compile(); } | ||
if (force){ | ||
return compile(); | ||
} | ||
// Re-compile on server restart, disregarding | ||
// mtimes since we need to map imports | ||
if (!imports[sassPath]) { return compile(); } | ||
if (!imports[sassPath]) { | ||
return compile(); | ||
} | ||
// Compare mtimes | ||
fs.stat(sassPath, function(err, sassStats){ | ||
if (err) { return error(err); } | ||
if (err) { | ||
error(err); | ||
return next(); | ||
} | ||
fs.stat(cssPath, function(err, cssStats){ | ||
@@ -174,3 +215,3 @@ // CSS has not been compiled, compile it! | ||
} else { | ||
checkImports(sassPath, function(changed){ | ||
checkImports(sassPath, cssStats.mtime, function(changed) { | ||
if (debug && changed && changed.length) { | ||
@@ -187,4 +228,2 @@ changed.forEach(function(path) { | ||
}); | ||
} else { | ||
next(); | ||
} | ||
@@ -202,6 +241,7 @@ }; | ||
function checkImports(path, fn) { | ||
function checkImports(path, time, fn) { | ||
var nodes = imports[path]; | ||
if (!nodes) { return fn(); } | ||
if (!nodes.length) { return fn(); } | ||
if (!nodes || !nodes.length) { | ||
return fn(); | ||
} | ||
@@ -211,8 +251,10 @@ var pending = nodes.length, | ||
nodes.forEach(function(imported){ | ||
fs.stat(imported.path, function(err, stat){ | ||
// examine the imported files (nodes) for each parent sass (path) | ||
nodes.forEach(function(imported) { | ||
fs.stat(imported, function(err, stat) { | ||
// error or newer mtime | ||
if (err || !imported.mtime || stat.mtime > imported.mtime) { | ||
changed.push(imported.path); | ||
if (err || stat.mtime >= time) { | ||
changed.push(imported); | ||
} | ||
// decrease pending, if 0 call fn with the changed imports | ||
--pending || fn(changed); | ||
@@ -230,3 +272,13 @@ }); | ||
function log(key, val) { | ||
console.error(' \033[90m%s :\033[0m \033[36m%s\033[0m', key, val); | ||
console.error(' \x1B[90m%s:\x1B[0m \x1B[36m%s\x1B[0m', key, val); | ||
} | ||
/** | ||
* Log an error message. | ||
* | ||
* @api private | ||
*/ | ||
function logError(message) { | ||
log('error', '\x07\x1B[31m' + message + '\x1B[91m'); | ||
} |
@@ -5,3 +5,3 @@ { | ||
"description": "Connect middleware for node-sass", | ||
"version": "0.5.0", | ||
"version": "0.6.0", | ||
"scripts": { | ||
@@ -34,11 +34,11 @@ "test": "./node_modules/mocha/bin/mocha" | ||
"dependencies": { | ||
"mkdirp": "0.5.x", | ||
"node-sass": "^2.0.1" | ||
"mkdirp": "^0.5.0", | ||
"node-sass": "^3.1.0" | ||
}, | ||
"devDependencies": { | ||
"connect": "^3.3.4", | ||
"mocha": "^2.1.0", | ||
"should": "^5.0.0", | ||
"supertest": "^0.15.0" | ||
"connect": "^3.3.5", | ||
"mocha": "^2.2.4", | ||
"should": "^6.0.1", | ||
"supertest": "^1.0.1" | ||
} | ||
} |
@@ -9,4 +9,7 @@ 'use strict'; | ||
middleware = require('../middleware'), | ||
cssfile = path.join(__dirname, '/test.css'), | ||
scssfile = path.join(__dirname, '/test.scss'); | ||
cssFile = path.join(__dirname, '/test.css'), | ||
scssFile = path.join(__dirname, '/test.scss'), | ||
cssIndexFile = path.join(__dirname, '/index.css'), | ||
scssDependentFile = path.join(__dirname, '/test.scss'), | ||
scssIndexFile = path.join(__dirname, '/index.scss'); | ||
@@ -34,12 +37,22 @@ describe('Creating middleware', function () { | ||
dest: __dirname | ||
})); | ||
})) | ||
.use(function(err, req, res, next) { | ||
res.statusCode = 500; | ||
res.end(err.message); | ||
}); | ||
beforeEach(function (done) { | ||
fs.exists(cssfile, function (exists) { | ||
fs.exists(cssFile, function (exists) { | ||
if (exists) { | ||
fs.unlink(cssfile, done); | ||
} else { | ||
done(); | ||
fs.unlink(cssFile); | ||
} | ||
}); | ||
fs.exists(cssIndexFile, function (exists) { | ||
if (exists) { | ||
fs.unlink(cssIndexFile); | ||
} | ||
}); | ||
done(); | ||
}); | ||
@@ -58,7 +71,7 @@ | ||
it('serves the compiled contents of the relative scss file', function (done) { | ||
var filesrc = fs.readFileSync(scssfile), | ||
var filesrc = fs.readFileSync(scssFile), | ||
result = sass.renderSync({ data: filesrc.toString() }); | ||
request(server) | ||
.get('/test.css') | ||
.expect(result.css) | ||
.expect(result.css.toString()) | ||
.expect(200, done); | ||
@@ -68,7 +81,7 @@ }); | ||
it('writes the file contents out to the expected file', function (done) { | ||
var filesrc = fs.readFileSync(scssfile), | ||
var filesrc = fs.readFileSync(scssFile), | ||
result = sass.renderSync({ data: filesrc.toString() }); | ||
request(server) | ||
.get('/test.css') | ||
.expect(result.css) | ||
.expect(result.css.toString()) | ||
.expect(200, function (err) { | ||
@@ -79,4 +92,4 @@ if (err) { | ||
(function checkFile() { | ||
if (fs.existsSync(cssfile)) { | ||
fs.readFileSync(cssfile).toString().should.equal(result.css); | ||
if (fs.existsSync(cssFile)) { | ||
fs.readFileSync(cssFile).toString().should.equal(result.css.toString()); | ||
done(); | ||
@@ -104,2 +117,100 @@ } else { | ||
describe('compiling files with dependencies (source file contains includes)', function() { | ||
it('serves the expected result', function (done) { | ||
var filesrc = fs.readFileSync(scssIndexFile), | ||
result = sass.renderSync({ data: filesrc.toString() }); | ||
request(server) | ||
.get('/index.css') | ||
.expect('Content-Type', /css/) | ||
.expect(result.css.toString()) | ||
.expect(200, function (err) { | ||
if (err) { | ||
done(err); | ||
} else { | ||
(function checkFile() { | ||
if (fs.existsSync(cssIndexFile)) { | ||
fs.readFileSync(cssIndexFile).toString().should.equal(result.css.toString()); | ||
done(); | ||
} else { | ||
setTimeout(checkFile, 25); | ||
} | ||
}()); | ||
} | ||
}); | ||
}); | ||
it('any change in a dependent file, force recompiling', function(done) { | ||
request(server) | ||
.get('/index.css') | ||
.expect(200, function() { | ||
(function checkInitialFile() { | ||
fs.stat(cssIndexFile, function(err, initialDate) { | ||
if (initialDate != undefined) { | ||
fs.appendFile(scssDependentFile, '\nbody { background: red; }', function(err, data) { | ||
if (err) throw err; | ||
var filesrc = fs.readFileSync(scssIndexFile), | ||
result = sass.renderSync({ data: filesrc.toString() }); | ||
request(server) | ||
.get('/index.css') | ||
.expect(200, function() { | ||
(function checkRecompiledFile() { | ||
var cont = fs.readFileSync(cssIndexFile).toString(); | ||
if (cont === result.css.toString()) { | ||
done(); | ||
} else { | ||
setTimeout(checkRecompiledFile, 10); | ||
} | ||
}()); | ||
}); | ||
}); | ||
} else { | ||
setTimeout(checkInitialFile, 10); | ||
} | ||
}); | ||
}()); | ||
}); | ||
// clean | ||
after(function(){ | ||
var reset = fs.readFileSync(scssDependentFile).toString().replace('\nbody { background: red; }', ''); | ||
fs.writeFileSync(scssDependentFile, reset, { flag: 'w' }); | ||
}); | ||
}); | ||
}); | ||
describe('compiling files with errors moves to next middleware with err', function() { | ||
// alter | ||
before(function(){ | ||
fs.appendFileSync(scssDependentFile, '\nbody { background;: red; }'); | ||
}) | ||
it('if error is in the main file', function(done) { | ||
request(server) | ||
.get('/test.css') | ||
.expect('property "background" must be followed by a \':\'') | ||
.expect(500, done); | ||
}); | ||
it('if error is in imported file', function(done) { | ||
request(server) | ||
.get('/index.css') | ||
.expect('property "background" must be followed by a \':\'') | ||
.expect(500, done); | ||
}); | ||
// clean | ||
after(function(){ | ||
var reset = fs.readFileSync(scssDependentFile).toString().replace('\nbody { background;: red; }', ''); | ||
fs.writeFileSync(scssDependentFile, reset, { flag: 'w' }); | ||
}); | ||
}); | ||
}); |
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
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
19888
11
413
+ Addedamdefine@1.0.1(transitive)
+ Addedansi-regex@2.1.1(transitive)
+ Addedansi-styles@2.2.1(transitive)
+ Addedaproba@1.2.0(transitive)
+ Addedare-we-there-yet@1.1.7(transitive)
+ Addedasync-foreach@0.1.3(transitive)
+ Addedcall-bind@1.0.7(transitive)
+ Addedcamelcase@3.0.0(transitive)
+ Addedchalk@1.1.3(transitive)
+ Addedcliui@3.2.0(transitive)
+ Addedcode-point-at@1.1.0(transitive)
+ Addedconsole-control-strings@1.1.0(transitive)
+ Addedcross-spawn@3.0.1(transitive)
+ Addeddefine-data-property@1.1.4(transitive)
+ Addeddefine-properties@1.2.1(transitive)
+ Addedes-define-property@1.0.0(transitive)
+ Addedes-errors@1.3.0(transitive)
+ Addedfs.realpath@1.0.0(transitive)
+ Addedgauge@2.7.4(transitive)
+ Addedgaze@1.1.3(transitive)
+ Addedget-caller-file@1.0.3(transitive)
+ Addedget-intrinsic@1.2.4(transitive)
+ Addedglob@7.1.77.2.3(transitive)
+ Addedglobule@1.3.4(transitive)
+ Addedgopd@1.0.1(transitive)
+ Addedhas-ansi@2.0.0(transitive)
+ Addedhas-property-descriptors@1.0.2(transitive)
+ Addedhas-proto@1.0.3(transitive)
+ Addedhas-symbols@1.0.3(transitive)
+ Addedhas-unicode@2.0.1(transitive)
+ Addedin-publish@2.0.1(transitive)
+ Addedinvert-kv@1.0.0(transitive)
+ Addedis-fullwidth-code-point@1.0.0(transitive)
+ Addedisexe@2.0.0(transitive)
+ Addedjs-base64@2.6.4(transitive)
+ Addedlcid@1.0.0(transitive)
+ Addedlodash@4.17.21(transitive)
+ Addedlodash.assign@4.2.0(transitive)
+ Addedlodash.clonedeep@4.5.0(transitive)
+ Addedlru-cache@4.1.5(transitive)
+ Addedminimatch@3.0.83.1.2(transitive)
+ Addednan@2.22.0(transitive)
+ Addednode-gyp@3.8.0(transitive)
+ Addednode-sass@3.13.1(transitive)
+ Addednpmlog@4.1.2(transitive)
+ Addednumber-is-nan@1.0.1(transitive)
+ Addedobject-keys@1.1.1(transitive)
+ Addedobject.assign@4.1.5(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedos-locale@1.4.0(transitive)
+ Addedpath-is-absolute@1.0.1(transitive)
+ Addedpseudomap@1.0.2(transitive)
+ Addedrequire-directory@2.1.1(transitive)
+ Addedrequire-main-filename@1.0.1(transitive)
+ Addedrimraf@2.7.1(transitive)
+ Addedsass-graph@2.2.6(transitive)
+ Addedscss-tokenizer@0.2.3(transitive)
+ Addedsemver@5.3.0(transitive)
+ Addedset-blocking@2.0.0(transitive)
+ Addedset-function-length@1.2.2(transitive)
+ Addedsource-map@0.4.4(transitive)
+ Addedstring-width@1.0.2(transitive)
+ Addedstrip-ansi@3.0.1(transitive)
+ Addedsupports-color@2.0.0(transitive)
+ Addedtar@2.2.2(transitive)
+ Addedwhich@1.3.1(transitive)
+ Addedwhich-module@1.0.0(transitive)
+ Addedwide-align@1.1.5(transitive)
+ Addedwrap-ansi@2.1.0(transitive)
+ Addedy18n@3.2.2(transitive)
+ Addedyallist@2.1.2(transitive)
+ Addedyargs@7.1.2(transitive)
+ Addedyargs-parser@5.0.1(transitive)
- Removedansi@0.3.1(transitive)
- Removedansi-regex@0.2.1(transitive)
- Removedansi-styles@1.1.0(transitive)
- Removedare-we-there-yet@1.0.6(transitive)
- Removedasn1@0.1.11(transitive)
- Removedassert-plus@0.1.5(transitive)
- Removedasync@0.9.2(transitive)
- Removedaws-sign2@0.5.0(transitive)
- Removedbl@0.9.5(transitive)
- Removedboom@0.4.2(transitive)
- Removedcaseless@0.8.0(transitive)
- Removedchalk@0.5.1(transitive)
- Removedcombined-stream@0.0.7(transitive)
- Removedcommander@0.6.12.20.32.3.0(transitive)
- Removedconfig-chain@1.1.13(transitive)
- Removedcross-spawn@0.2.9(transitive)
- Removedcryptiles@0.2.2(transitive)
- Removedctype@0.5.3(transitive)
- Removeddebug@2.2.0(transitive)
- Removeddelayed-stream@0.0.5(transitive)
- Removeddiff@1.4.0(transitive)
- Removedescape-string-regexp@1.0.2(transitive)
- Removedforever-agent@0.5.2(transitive)
- Removedform-data@0.2.0(transitive)
- Removedgauge@1.0.2(transitive)
- Removedgaze@0.5.2(transitive)
- Removedglob@3.1.213.2.114.3.54.5.3(transitive)
- Removedglobule@0.1.0(transitive)
- Removedgraceful-fs@1.2.33.0.12(transitive)
- Removedgrowl@1.9.2(transitive)
- Removedhas-ansi@0.1.0(transitive)
- Removedhas-unicode@1.0.1(transitive)
- Removedhawk@1.1.1(transitive)
- Removedhoek@0.9.1(transitive)
- Removedhttp-signature@0.10.1(transitive)
- Removedinherits@1.0.2(transitive)
- Removedini@1.3.8(transitive)
- Removedisarray@0.0.1(transitive)
- Removedjade@0.26.3(transitive)
- Removedlodash@1.0.22.4.2(transitive)
- Removedlru-cache@2.7.3(transitive)
- Removedmime-db@1.12.0(transitive)
- Removedmime-types@1.0.22.0.14(transitive)
- Removedminimatch@0.2.140.3.02.0.10(transitive)
- Removedminimist@0.0.8(transitive)
- Removedmkdirp@0.3.00.5.1(transitive)
- Removedmocha@2.5.3(transitive)
- Removedms@0.7.1(transitive)
- Removednan@1.9.0(transitive)
- Removednatives@1.1.6(transitive)
- Removednode-sass@2.1.1(transitive)
- Removednode-uuid@1.4.8(transitive)
- Removednpmconf@2.1.3(transitive)
- Removednpmlog@1.0.0(transitive)
- Removedoauth-sign@0.5.0(transitive)
- Removedobject-assign@2.1.1(transitive)
- Removedonce@1.3.3(transitive)
- Removedpangyp@2.3.3(transitive)
- Removedproto-list@1.2.4(transitive)
- Removedqs@2.3.3(transitive)
- Removedreadable-stream@1.0.34(transitive)
- Removedreplace-ext@0.0.1(transitive)
- Removedrequest@2.51.0(transitive)
- Removedrimraf@2.2.8(transitive)
- Removedsafe-buffer@5.2.1(transitive)
- Removedsass-graph@1.3.0(transitive)
- Removedsemver@4.3.6(transitive)
- Removedshelljs@0.3.0(transitive)
- Removedsigmund@1.0.1(transitive)
- Removedsntp@0.2.4(transitive)
- Removedstring_decoder@0.10.31(transitive)
- Removedstringstream@0.0.6(transitive)
- Removedstrip-ansi@0.3.0(transitive)
- Removedsupports-color@0.2.01.2.0(transitive)
- Removedtar@1.0.3(transitive)
- Removedtldts@6.1.61(transitive)
- Removedtldts-core@6.1.61(transitive)
- Removedto-iso-string@0.0.2(transitive)
- Removedtough-cookie@5.0.0(transitive)
- Removedtunnel-agent@0.4.3(transitive)
- Removeduid-number@0.0.5(transitive)
- Removedwhich@1.0.9(transitive)
Updatedmkdirp@^0.5.0
Updatednode-sass@^3.1.0