Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

node-sass-middleware

Package Overview
Dependencies
Maintainers
2
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

node-sass-middleware - npm Package Compare versions

Comparing version 0.5.0 to 0.6.0

.gitattributes

214

middleware.js

@@ -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' });
});
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc