Comparing version 1.4.0 to 1.5.0
@@ -10,3 +10,4 @@ #!/usr/bin/env node | ||
ignores, | ||
configOptions = {}; | ||
configOptions = {}, | ||
exitCode = 0; | ||
@@ -22,2 +23,6 @@ var detectPattern = function (pattern) { | ||
if (lint.errorCount(detects).count) { | ||
exitCode = 1; | ||
} | ||
if (program.exit) { | ||
@@ -91,1 +96,5 @@ lint.failOnError(detects); | ||
} | ||
process.on('exit', function () { | ||
process.exit(exitCode); // eslint-disable-line no-process-exit | ||
}); |
# Sass Lint Changelog | ||
## v1.5.0 | ||
**January 28, 2016** | ||
New year blues | ||
**Changes** | ||
* AST parse errors will now be returned to the user as `Fatal` lint errors this prevents un-handled errors breaking builds [#459](https://github.com/sasstools/sass-lint/pull/459) | ||
* Sass-lint plugin for Brackets added to the README [#470](https://github.com/sasstools/sass-lint/issues/470) | ||
* Sass-lint plugin for IntelliJ IDEA, RubyMine, WebStorm, PhpStorm, PyCharm, added to the README [#484](https://github.com/sasstools/sass-lint/issues/484) | ||
**CLI** | ||
* Updated error codes, whenever errors are present even when cli is using the `--no-exit` flag a error code of 1 will be output [#221](https://github.com/sasstools/sass-lint/issues/221) | ||
**Fixes** | ||
* Fixed an issue where an error of `next is undefined` would be thrown in the `space-after-colon` rule [#468](https://github.com/sasstools/sass-lint/issues/468) | ||
* Fixed an issue with negative z-index values in the `space-around-operator` rule [#454](https://github.com/sasstools/sass-lint/issues/454) | ||
* Fixed another minor issue with `space-around-operator` to prevent a possible crash [#483](https://github.com/sasstools/sass-lint/issues/483) | ||
## v1.4.0 | ||
@@ -42,2 +61,3 @@ **December 10, 2015** | ||
* [Don Abrams](https://github.com/donabrams) | ||
* [Andrew Hays](https://github.com/Dru89) | ||
* [Kaelig](https://github.com/kaelig) | ||
@@ -44,0 +64,0 @@ |
148
index.js
@@ -9,6 +9,4 @@ 'use strict'; | ||
path = require('path'), | ||
jsonFormatter = require('eslint/lib/formatters/json'), | ||
fs = require('fs-extra'); | ||
var sassLint = function (config) { | ||
@@ -19,2 +17,10 @@ config = require('./lib/config')(config); | ||
/** | ||
* Takes any user specified options and a configPath | ||
* which returns a compiled config object | ||
* | ||
* @param {object} config user specified rules/options passed in | ||
* @param {string} configPath path to a config file | ||
* @returns {object} the compiled config object | ||
*/ | ||
sassLint.getConfig = function (config, configPath) { | ||
@@ -24,17 +30,74 @@ return slConfig(config, configPath); | ||
sassLint.resultCount = function (results) { | ||
var flagCount = 0, | ||
jsonResults = JSON.parse(jsonFormatter(results)); | ||
/** | ||
* Parses our results object to count errors and return | ||
* paths to files with detected errors. | ||
* | ||
* @param {object} results our results object | ||
* @returns {object} errors object containing the error count and paths for files incl. errors | ||
*/ | ||
sassLint.errorCount = function (results) { | ||
var errors = { | ||
count: 0, | ||
files: [] | ||
}; | ||
results.forEach(function (result) { | ||
if (result.errorCount) { | ||
errors.count += result.errorCount; | ||
errors.files.push(result.filePath); | ||
} | ||
}); | ||
for (var i = 0; i < jsonResults.length; i++) { | ||
flagCount += (jsonResults[i].warningCount + jsonResults[i].errorCount); | ||
} | ||
return errors; | ||
}; | ||
return flagCount; | ||
/** | ||
* Parses our results object to count warnings and return | ||
* paths to files with detected warnings. | ||
* | ||
* @param {object} results our results object | ||
* @returns {object} warnings object containing the error count and paths for files incl. warnings | ||
*/ | ||
sassLint.warningCount = function (results) { | ||
var warnings = { | ||
count: 0, | ||
files: [] | ||
}; | ||
results.forEach(function (result) { | ||
if (result.warningCount) { | ||
warnings.count += result.warningCount; | ||
warnings.files.push(result.filePath); | ||
} | ||
}); | ||
return warnings; | ||
}; | ||
/** | ||
* Parses our results object to count warnings and errors and return | ||
* a cumulative count of both | ||
* | ||
* @param {object} results our results object | ||
* @returns {int} the cumulative count of errors and warnings detected | ||
*/ | ||
sassLint.resultCount = function (results) { | ||
var warnings = this.warningCount(results), | ||
errors = this.errorCount(results); | ||
return warnings.count + errors.count; | ||
}; | ||
/** | ||
* Runs each rule against our AST tree and returns our main object of detected | ||
* errors, warnings, messages and filenames. | ||
* | ||
* @param {object} file file object from fs.readFileSync | ||
* @param {object} options user specified rules/options passed in | ||
* @param {string} configPath path to a config file | ||
* @returns {object} an object containing error & warning counts plus lint messages for each parsed file | ||
*/ | ||
sassLint.lintText = function (file, options, configPath) { | ||
var rules = slRules(this.getConfig(options, configPath)), | ||
ast = groot(file.text, file.format, file.filename), | ||
ast = {}, | ||
detects, | ||
@@ -45,3 +108,19 @@ results = [], | ||
if (ast.content.length > 0) { | ||
try { | ||
ast = groot(file.text, file.format, file.filename); | ||
} | ||
catch (e) { | ||
var line = e.line || 1; | ||
errors++; | ||
results = [{ | ||
ruleId: 'Fatal', | ||
line: line, | ||
column: 1, | ||
message: e.message, | ||
severity: 2 | ||
}]; | ||
} | ||
if (ast.content && ast.content.length > 0) { | ||
rules.forEach(function (rule) { | ||
@@ -61,3 +140,2 @@ detects = rule.rule.detect(ast, rule); | ||
results.sort(helpers.sortDetects); | ||
@@ -73,2 +151,12 @@ | ||
/** | ||
* Takes a glob pattern or target string and creates an array of files as targets for | ||
* linting taking into account any user specified ignores. For each resulting file sassLint.lintText | ||
* is called which returns an object of results for that file which we push to our results object. | ||
* | ||
* @param {string} files a glob pattern or single file path as a lint target | ||
* @param {object} options user specified rules/options passed in | ||
* @param {string} configPath path to a config file | ||
* @returns {object} results object containing all results | ||
*/ | ||
sassLint.lintFiles = function (files, options, configPath) { | ||
@@ -108,3 +196,10 @@ var that = this, | ||
/** | ||
* Handles formatting of results using EsLint formatters | ||
* | ||
* @param {object} results our results object | ||
* @param {object} options user specified rules/options passed in | ||
* @param {string} configPath path to a config file | ||
* @returns {object} results our results object in the user specified format | ||
*/ | ||
sassLint.format = function (results, options, configPath) { | ||
@@ -119,2 +214,11 @@ var config = this.getConfig(options, configPath), | ||
/** | ||
* Handles outputting results whether this be straight to the console/stdout or to a file. | ||
* Passes results to the format function to ensure results are output in the chosen format | ||
* | ||
* @param {object} results our results object | ||
* @param {object} options user specified rules/options passed in | ||
* @param {string} configPath path to a config file | ||
* @returns {object} results our results object | ||
*/ | ||
sassLint.outputResults = function (results, options, configPath) { | ||
@@ -143,12 +247,14 @@ var config = this.getConfig(options, configPath); | ||
/** | ||
* Throws an error if there are any errors detected. The error includes a count of all errors | ||
* and a list of all files that include errors. | ||
* | ||
* @param {object} results our results object | ||
* @returns {void} | ||
*/ | ||
sassLint.failOnError = function (results) { | ||
var result, | ||
i; | ||
var errorCount = this.errorCount(results); | ||
for (i = 0; i < results.length; i++) { | ||
result = results[i]; | ||
if (result.errorCount > 0) { | ||
throw new Error(result.errorCount + ' errors detected in ' + result.filePath); | ||
} | ||
if (errorCount.count > 0) { | ||
throw new Error(errorCount.count + ' errors were detected in \n- ' + errorCount.files.join('\n- ')); | ||
} | ||
@@ -155,0 +261,0 @@ }; |
@@ -9,4 +9,3 @@ ////////////////////////////// | ||
module.exports = function (text, syntax, filename) { | ||
var fileInfo = filename ? ' at ' + filename : '', | ||
tree; | ||
var tree; | ||
@@ -22,7 +21,16 @@ // Run `.toString()` to allow Buffers to be passed in | ||
catch (e) { | ||
throw new Error('Parsing error' + fileInfo + ': ' + e.message); | ||
throw { | ||
message: e.message, | ||
file: filename, | ||
line: e.line | ||
}; | ||
} | ||
if (typeof tree === 'undefined') { | ||
throw new Error('Undefined tree' + fileInfo + ': ' + text.toString() + ' => ' + tree.toString()); | ||
throw { | ||
message: 'Undefined tree', | ||
file: filename, | ||
text: text.toString(), | ||
tree: tree.toString() | ||
}; | ||
} | ||
@@ -29,0 +37,0 @@ |
@@ -30,6 +30,17 @@ 'use strict'; | ||
loadRule, | ||
severity, | ||
options, | ||
ruleSearch; | ||
if (typeof fullRule === 'number') { | ||
severity = fullRule; | ||
options = {}; | ||
} | ||
else { | ||
severity = fullRule[0]; | ||
options = fullRule[1]; | ||
} | ||
// Only seek out rules that are enabled | ||
if ((typeof fullRule === 'number' && fullRule !== 0) || (typeof fullRule === 'object' && fullRule[0] !== 0)) { | ||
if (severity !== 0) { | ||
ruleSearch = searchArray(rules, rule); | ||
@@ -39,4 +50,2 @@ if (ruleSearch >= 0) { | ||
options = typeof fullRule === 'object' ? fullRule[1] : {}; | ||
options = merge.recursive(true, loadRule.defaults, options); | ||
@@ -46,3 +55,3 @@ | ||
'rule': loadRule, | ||
'severity': typeof fullRule === 'number' ? fullRule : fullRule[0], | ||
'severity': severity, | ||
'options': options | ||
@@ -49,0 +58,0 @@ }); |
@@ -16,3 +16,4 @@ 'use strict'; | ||
block.forEach(function (item, j) { | ||
if (item.type === 'include' || item.type === 'extend') { | ||
if ((item.type === 'include' || item.type === 'extend') && | ||
item.first('atkeyword')) { | ||
if (item.first('atkeyword').first('ident').content === 'extend') { | ||
@@ -19,0 +20,0 @@ if (j > lastDeclaration && lastDeclaration !== null) { |
@@ -17,3 +17,3 @@ 'use strict'; | ||
if (next.is('space')) { | ||
if (next && next.is('space')) { | ||
if (!parser.options.include) { | ||
@@ -20,0 +20,0 @@ result = helpers.addUnique(result, { |
@@ -47,2 +47,3 @@ 'use strict'; | ||
if (parent && operator && next) { | ||
// The parent ofthe exceptions are always going to be of type value | ||
@@ -59,3 +60,2 @@ if (parent.is('value')) { | ||
if (previous) { | ||
// The first value is the font-size and must have a unit, therefore | ||
@@ -70,2 +70,8 @@ // a node of type dimension | ||
} | ||
// 3. If there is nothing leading the minus, it's a negative value so we | ||
// shouldn't have a following space | ||
if (!previous && operator === '-' && !next.is('space')) { | ||
return false; | ||
} | ||
} | ||
@@ -92,4 +98,4 @@ } | ||
if ( | ||
((previous.end.line >= previous.start.line) && (previous.end.column > previous.start.column)) | ||
|| (next.end.line >= next.start.line) && (next.end.column > next.start.column) | ||
(previous && (previous.end.line >= previous.start.line) && (previous.end.column > previous.start.column)) | ||
|| (next && (next.end.line >= next.start.line) && (next.end.column > next.start.column)) | ||
) { | ||
@@ -96,0 +102,0 @@ result = helpers.addUnique(result, { |
{ | ||
"name": "sass-lint", | ||
"version": "1.4.0", | ||
"version": "1.5.0", | ||
"description": "All Node Sass linter!", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -57,1 +57,3 @@ # Sass Lint [![npm version](https://badge.fury.io/js/sass-lint.svg)](http://badge.fury.io/js/sass-lint) [![Build Status](https://travis-ci.org/sasstools/sass-lint.svg)](https://travis-ci.org/sasstools/sass-lint) [![Coverage Status](https://coveralls.io/repos/sasstools/sass-lint/badge.svg?branch=develop&service=github)](https://coveralls.io/github/sasstools/sass-lint?branch=develop) [![Dependency Status](https://david-dm.org/sasstools/sass-lint.svg)](https://david-dm.org/sasstools/sass-lint#info=dependencies&view=list) [![Dev Dependency Status](https://david-dm.org/sasstools/sass-lint/dev-status.svg)](https://david-dm.org/sasstools/sass-lint#info=devDependencies&view=list) | ||
* [Sublime Text](https://github.com/skovhus/SublimeLinter-contrib-sass-lint) | ||
* [Brackets](https://github.com/petetnt/brackets-sass-lint) | ||
* [IntelliJ IDEA, RubyMine, WebStorm, PhpStorm, PyCharm](https://github.com/idok/sass-lint-plugin) |
@@ -310,2 +310,67 @@ var assert = require('assert'), | ||
}); | ||
it('should exit with exit code 1 when quiet', function (done) { | ||
var command = 'sass-lint -c tests/yml/.error-output.yml tests/sass/cli-error.scss --verbose --no-exit'; | ||
childProcess.exec(command, function (err) { | ||
if (err.code === 1) { | ||
return done(); | ||
} | ||
return done(new Error('Error code not 1')); | ||
}); | ||
}); | ||
/** | ||
* We disabled eslints handle callback err rule here as we are deliberately throwing errors that we don't care about | ||
*/ | ||
it('parse errors should report as a lint error', function (done) { | ||
var command = 'sass-lint --config tests/yml/.stylish-output.yml tests/sass/parse.scss --verbose --no-exit --format json'; | ||
childProcess.exec(command, function (err, stdout) { // eslint-disable-line handle-callback-err | ||
var result = JSON.parse(stdout)[0]; | ||
assert.equal(1, result.errorCount); | ||
done(); | ||
}); | ||
}); | ||
it('parse errors should report as severity 2', function (done) { | ||
var command = 'sass-lint --config tests/yml/.stylish-output.yml tests/sass/parse.scss --verbose --no-exit --format json'; | ||
childProcess.exec(command, function (err, stdout) { // eslint-disable-line handle-callback-err | ||
var result = JSON.parse(stdout)[0], | ||
messages = result.messages[0], | ||
severity = 2; | ||
assert.equal(severity, messages.severity); | ||
done(); | ||
}); | ||
}); | ||
it('parse errors should report the correct message', function (done) { | ||
var command = 'sass-lint --config tests/yml/.stylish-output.yml tests/sass/parse.scss --verbose --no-exit --format json'; | ||
childProcess.exec(command, function (err, stdout) { // eslint-disable-line handle-callback-err | ||
var result = JSON.parse(stdout)[0], | ||
message = result.messages[0].message, | ||
expected = 'Please check validity of the block starting from line #5'; | ||
assert.equal(expected, message); | ||
done(); | ||
}); | ||
}); | ||
it('parse errors rule Id should be \'Fatal\'', function (done) { | ||
var command = 'sass-lint --config tests/yml/.stylish-output.yml tests/sass/parse.scss --verbose --no-exit --format json'; | ||
childProcess.exec(command, function (err, stdout) { // eslint-disable-line handle-callback-err | ||
var result = JSON.parse(stdout)[0], | ||
messages = result.messages[0], | ||
ruleId = 'Fatal'; | ||
assert.equal(ruleId, messages.ruleId); | ||
done(); | ||
}); | ||
}); | ||
}); |
@@ -15,2 +15,44 @@ 'use strict'; | ||
var resultsObj = [{ | ||
filePath: 'app/scss/echo-base/defaults/utilities/_mixins.scss', | ||
warningCount: 3, | ||
errorCount: 0, | ||
messages: [{ | ||
ruleId: 'no-vendor-prefixes', | ||
line: 120, | ||
column: 8, | ||
message: 'Vendor prefixes should not be used', | ||
severity: 1 | ||
}, { | ||
ruleId: 'no-vendor-prefixes', | ||
line: 130, | ||
column: 8, | ||
message: 'Vendor prefixes should not be used', | ||
severity: 1 | ||
}, { | ||
ruleId: 'no-vendor-prefixes', | ||
line: 140, | ||
column: 8, | ||
message: 'Vendor prefixes should not be used', | ||
severity: 1 | ||
}] | ||
}, { | ||
filePath: 'app/scss/main.scss', | ||
warningCount: 0, | ||
errorCount: 2, | ||
messages: [{ | ||
ruleId: 'no-ids', | ||
line: 52, | ||
column: 1, | ||
message: 'ID selectors not allowed', | ||
severity: 2 | ||
}, { | ||
ruleId: 'no-ids', | ||
line: 57, | ||
column: 1, | ||
message: 'ID selectors not allowed', | ||
severity: 2 | ||
}] | ||
}]; | ||
describe('sass lint', function () { | ||
@@ -28,2 +70,80 @@ ////////////////////////////// | ||
}); | ||
////////////////////////////// | ||
// Parse Errors should return as lint errors | ||
////////////////////////////// | ||
it('Parse Errors should return as lint errors', function (done) { | ||
lintFile('parse.scss', function (data) { | ||
assert.equal(1, data.errorCount); | ||
done(); | ||
}); | ||
}); | ||
it('Parse Errors should not include warnings too', function (done) { | ||
lintFile('parse.scss', function (data) { | ||
assert.equal(0, data.warningCount); | ||
done(); | ||
}); | ||
}); | ||
it('Parse Errors should return as severity 2', function (done) { | ||
lintFile('parse.scss', function (data) { | ||
var severity = data.messages[0].severity; | ||
assert.equal(2, severity); | ||
done(); | ||
}); | ||
}); | ||
it('Parse Errors should return the correct error message', function (done) { | ||
lintFile('parse.scss', function (data) { | ||
var message = data.messages[0].message, | ||
expected = 'Please check validity of the block starting from line #5'; | ||
assert.equal(expected, message); | ||
done(); | ||
}); | ||
}); | ||
it('Parse Errors should return the rule ID \'Fatal\'', function (done) { | ||
lintFile('parse.scss', function (data) { | ||
var ruleId = data.messages[0].ruleId, | ||
expected = 'Fatal'; | ||
assert.equal(expected, ruleId); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('sassLint detect counts', function () { | ||
////////////////////////////// | ||
// Error count | ||
////////////////////////////// | ||
it('should equal 2 errors', function (done) { | ||
var result = lint.errorCount(resultsObj); | ||
assert.equal(2, result.count); | ||
done(); | ||
}); | ||
////////////////////////////// | ||
// Warning count | ||
////////////////////////////// | ||
it('should equal 3 warnings', function (done) { | ||
var result = lint.warningCount(resultsObj); | ||
assert.equal(3, result.count); | ||
done(); | ||
}); | ||
////////////////////////////// | ||
// Result count | ||
////////////////////////////// | ||
it('should equal 5 overall detects', function (done) { | ||
var result = lint.resultCount(resultsObj); | ||
assert.equal(5, result); | ||
done(); | ||
}); | ||
}); |
@@ -15,3 +15,3 @@ 'use strict'; | ||
}, function (data) { | ||
lint.assert.equal(83, data.warningCount); | ||
lint.assert.equal(84, data.warningCount); | ||
done(); | ||
@@ -30,3 +30,3 @@ }); | ||
}, function (data) { | ||
lint.assert.equal(77, data.warningCount); | ||
lint.assert.equal(78, data.warningCount); | ||
done(); | ||
@@ -47,3 +47,3 @@ }); | ||
}, function (data) { | ||
lint.assert.equal(83, data.warningCount); | ||
lint.assert.equal(84, data.warningCount); | ||
done(); | ||
@@ -62,3 +62,3 @@ }); | ||
}, function (data) { | ||
lint.assert.equal(77, data.warningCount); | ||
lint.assert.equal(78, data.warningCount); | ||
done(); | ||
@@ -65,0 +65,0 @@ }); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
521462
354
10851
59