Repository: sasstools/sass-lint
Pull request #640 (2016-04-22 14:03:03)

Release/1.6.2


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6cddc7e..29867ed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
 # Sass Lint Changelog
 
-## v1.6.1
+## v1.6.2
 
 **April 22, 2016**
 
@@ -10,6 +10,11 @@
 * `no-misspelled-properties` now correctly ignores vendor prefixes [#606](https://github.com/sasstools/sass-lint/issues/606)
 * Now correctly strips double-barreled vendor prefixes such as `-moz-osx-`
 
+## v.1.6.1
+
+The update that time forgot
+
+* Issues publishing to npm
 
 ## v1.6.0
 
diff --git a/lib/helpers.js b/lib/helpers.js
index a41ed98..8183ba0 100644
--- a/lib/helpers.js
+++ b/lib/helpers.js
@@ -207,10 +207,20 @@ helpers.stripQuotes = function (str) {
   return str.substring(1, str.length - 1);
 };
 
+/**
+ * Strips vendor prefixes from a string
+ *
+ * @param {string} str - The string we wish to remove vendor prefixes from
+ * @returns {string} The string without vendor prefixes
+ */
 helpers.stripPrefix = function (str) {
   var modProperty = str.slice(1),
       prefixLength = modProperty.indexOf('-');
 
+  // check for the -osx- vendor prefix too as well as the original -moz- etc
+  if (modProperty.slice(prefixLength + 1).slice(0, 3) === 'osx') {
+    return modProperty.slice(prefixLength + 5);
+  }
   return modProperty.slice(prefixLength + 1);
 };
 
diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js
index af1d627..11eaa88 100644
--- a/lib/rules/brace-style.js
+++ b/lib/rules/brace-style.js
@@ -30,8 +30,20 @@ var getPreviousNode = function (node) {
     return previousParent && previousParent.contains('atkeyword') ? previousParent.last('atkeyword') : false;
   }
 
-  // Functions, Mixins, Loops
-  if (node.is('atrule') || node.is('mixin') || node.is('loop')) {
+  // Loops
+  if (node.is('loop')) {
+    return node.contains('atkeyword') ? node.last('atkeyword') : false;
+  }
+
+  // Mixins and atrules (functions etc)
+  if (node.is('mixin') || node.is('atrule')) {
+    if (node.contains('function')) {
+      return node.last('function');
+    }
+
+    if (node.contains('arguments')) {
+      return node.last('arguments');
+    }
     return node.contains('atkeyword') ? node.last('atkeyword') : false;
   }
 
@@ -107,11 +119,18 @@ var isClosingBraceOnNewLine = function (node) {
  * @returns {bool|null} True or false if relevant else null
  */
 var isConditionOnNewLine = function (node, parentNode, j) {
+  var currentNode = node;
+
+  // Check node is part of an `else if` and if it is, use the else node instead
+  if (node.first('ident').content === 'if') {
+    var initialNode = parentNode.get(j);
+    currentNode = initialNode.contains('atkeyword') ? initialNode.first('atkeyword') : false;
+  }
+
   // Only check if it's an @else condition
-  if (node.contains('ident') && node.first('ident').content === 'else') {
+  if (currentNode && currentNode.contains('ident') && currentNode.first('ident').content === 'else') {
     // Reverse back up tree
     var previousChild = parentNode.get(--j) || false;
-
     if (previousChild) {
       // Determine if we have a leading new line
       if (previousChild.is('space') && helpers.hasEOL(previousChild.content)) {
@@ -156,6 +175,24 @@ var runRuleChecks = function (node, currentNode, previousNode, parentNode, index
   return checks;
 };
 
+/**
+ * Filter at-rules
+ *
+ * @param {Object} node - The node to filter
+ * @param {Array} accepted - The array of accepted at-rule types
+ * @returns {bool} true if we should ignore, false to continue
+ */
+var filterAtrule = function (node, accepted) {
+  if (node.is('atrule')) {
+    if (node.contains('atkeyword') && node.first('atkeyword').contains('ident')) {
+      if (accepted.indexOf(node.first('atkeyword').first('ident').content) === -1) {
+        return true;
+      }
+    }
+  }
+  return false;
+};
+
 /**
  * Create an issue using the supplied information
  *
@@ -167,8 +204,8 @@ var runRuleChecks = function (node, currentNode, previousNode, parentNode, index
 var createIssue = function (parser, node, message) {
   return {
     'ruleId': parser.rule.name,
-    'line': node.end.line,
-    'column': node.end.column,
+    'line': node.line,
+    'column': node.column,
     'message': message,
     'severity': parser.severity
   };
@@ -181,7 +218,12 @@ module.exports = {
     'allow-single-line': true
   },
   'detect': function (ast, parser) {
-    var result = [];
+    var result = [],
+        acceptedAtrules = [
+          'function',
+          'if',
+          'else'
+        ];
 
     ast.traverseByTypes(['conditionalStatement', 'atrule', 'ruleset', 'mixin', 'loop'], function (node, i, parent) {
       var currentNode = false,
@@ -206,6 +248,11 @@ module.exports = {
         return false;
       }
 
+      // Filter at-rule types
+      if (filterAtrule(node, acceptedAtrules)) {
+        return false;
+      }
+
       // Assign current & previous nodes based on node type
       currentNode = getCurrentNode(node);
       previousNode = getPreviousNode(node);
@@ -218,12 +265,18 @@ module.exports = {
 
         // Build single-line statement results
         if (checks.singleLineStatement === false && checks.closingBraceOnNewLine === false) {
-          result = helpers.addUnique(result, createIssue(parser, currentNode, messages[5]));
+          result = helpers.addUnique(result, createIssue(parser, {
+            line: currentNode.end.line,
+            column: currentNode.end.column
+          }, messages[5]));
         }
 
         if (checks.singleLineStatement === true) {
           if (parser.options['allow-single-line'] === false) {
-            result = helpers.addUnique(result, createIssue(parser, node, messages[0]));
+            result = helpers.addUnique(result, createIssue(parser, {
+              line: node.start.line,
+              column: node.start.column
+            }, messages[0]));
           }
           return false;
         }
@@ -232,28 +285,46 @@ module.exports = {
         if (previousNode && currentNode) {
           if (parser.options.style === '1tbs') {
             if (checks.openingBraceOnNewLine === false) {
-              result = helpers.addUnique(result, createIssue(parser, currentNode, messages[1]));
+              result = helpers.addUnique(result, createIssue(parser, {
+                line: currentNode.start.line,
+                column: currentNode.start.column
+              }, messages[1]));
             }
             if (checks.conditionOnNewLine === true) {
-              result = helpers.addUnique(result, createIssue(parser, previousNode, messages[3]));
+              result = helpers.addUnique(result, createIssue(parser, {
+                line: previousNode.start.line,
+                column: previousNode.start.column
+              }, messages[3]));
             }
           }
 
           if (parser.options.style === 'stroustrup') {
             if (checks.openingBraceOnNewLine === false) {
-              result = helpers.addUnique(result, createIssue(parser, currentNode, messages[1]));
+              result = helpers.addUnique(result, createIssue(parser, {
+                line: currentNode.start.line,
+                column: currentNode.start.column
+              }, messages[1]));
             }
             if (checks.conditionOnNewLine === false) {
-              result = helpers.addUnique(result, createIssue(parser, previousNode, messages[4]));
+              result = helpers.addUnique(result, createIssue(parser, {
+                line: previousNode.start.line,
+                column: previousNode.start.column
+              }, messages[4]));
             }
           }
 
           if (parser.options.style === 'allman') {
             if (checks.openingBraceOnNewLine === true) {
-              result = helpers.addUnique(result, createIssue(parser, currentNode, messages[2]));
+              result = helpers.addUnique(result, createIssue(parser, {
+                line: currentNode.end.line,
+                column: currentNode.end.column
+              }, messages[2]));
             }
             if (checks.conditionOnNewLine === false) {
-              result = helpers.addUnique(result, createIssue(parser, previousNode, messages[4]));
+              result = helpers.addUnique(result, createIssue(parser, {
+                line: previousNode.start.line,
+                column: previousNode.start.column
+              }, messages[4]));
             }
           }
         }
diff --git a/lib/rules/extends-before-declarations.js b/lib/rules/extends-before-declarations.js
index c920b74..028122e 100644
--- a/lib/rules/extends-before-declarations.js
+++ b/lib/rules/extends-before-declarations.js
@@ -13,20 +13,25 @@ module.exports = {
       var lastDeclaration = null;
 
       block.forEach(function (item, j) {
-        // TODO: Remove tempory fix - atrule type is work around for issue:
-        // https://github.com/tonyganch/gonzales-pe/issues/147
-        if ((item.is('include') || item.is('extend') || item.is('atrule')) &&
-            item.first('atkeyword')) {
-          if (item.first('atkeyword').first('ident').content === 'extend') {
-            if (j > lastDeclaration && lastDeclaration !== null) {
-              error = {
-                'ruleId': parser.rule.name,
-                'line': item.start.line,
-                'column': item.start.column,
-                'message': 'Extends should come before declarations',
-                'severity': parser.severity
-              };
-              result = helpers.addUnique(result, error);
+        if (item.is('include') || item.is('extend')) {
+          if (item.contains('atkeyword')) {
+            var atkeyword = item.first('atkeyword');
+
+            if (atkeyword.contains('ident')) {
+              var ident = atkeyword.first('ident');
+
+              if (ident.content === 'extend') {
+                if (j > lastDeclaration && lastDeclaration !== null) {
+                  error = {
+                    'ruleId': parser.rule.name,
+                    'line': item.start.line,
+                    'column': item.start.column,
+                    'message': 'Extends should come before declarations',
+                    'severity': parser.severity
+                  };
+                  result = helpers.addUnique(result, error);
+                }
+              }
             }
           }
         }
diff --git a/lib/rules/extends-before-mixins.js b/lib/rules/extends-before-mixins.js
index 6aff259..87ec0db 100644
--- a/lib/rules/extends-before-mixins.js
+++ b/lib/rules/extends-before-mixins.js
@@ -12,9 +12,7 @@ module.exports = {
       var lastMixin = null;
 
       block.forEach(function (item, j) {
-        // TODO: Remove tempory fix - atrule type is work around for issue:
-        // https://github.com/tonyganch/gonzales-pe/issues/147
-        if (item.is('include') || item.is('extend') || item.is('atrule')) {
+        if (item.is('include') || item.is('extend')) {
           if (item.contains('atkeyword')) {
             var atkeyword = item.first('atkeyword');
 
diff --git a/lib/rules/no-misspelled-properties.js b/lib/rules/no-misspelled-properties.js
index 2bf3a04..00988a5 100644
--- a/lib/rules/no-misspelled-properties.js
+++ b/lib/rules/no-misspelled-properties.js
@@ -21,10 +21,9 @@ module.exports = {
 
         if (curProperty.charAt(0) === '-') {
           curProperty = helpers.stripPrefix(curProperty);
-
         }
 
-        if (properties.indexOf(curProperty) === -1 && parser.options['extra-properties'].indexOf(curProperty) === -1) {
+        if (curProperty.length > 0 && properties.indexOf(curProperty) === -1 && parser.options['extra-properties'].indexOf(curProperty) === -1) {
           result = helpers.addUnique(result, {
             'ruleId': parser.rule.name,
             'line': node.start.line,
@@ -32,7 +31,6 @@ module.exports = {
             'message': 'Property `' + curProperty + '` appears to be spelled incorrectly',
             'severity': parser.severity
           });
-
         }
       }
     });
diff --git a/package.json b/package.json
index 703e36c..ea95500 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "sass-lint",
-  "version": "1.6.1",
+  "version": "1.6.2",
   "description": "All Node Sass linter!",
   "main": "index.js",
   "scripts": {


Test patch:
diff --git a/tests/helpers.js b/tests/helpers.js
index aeee4e4..126d10c 100644
--- a/tests/helpers.js
+++ b/tests/helpers.js
@@ -995,6 +995,14 @@ describe('helpers', function () {
     done();
   });
 
+  it('stripPrefix - [-moz-osx-font-smoothing - font-smoothing]', function (done) {
+
+    var result = helpers.stripPrefix('-moz-osx-font-smoothing');
+
+    assert.equal('font-smoothing', result);
+    done();
+  });
+
   //////////////////////////////
   // stripLastSpace
   //////////////////////////////
diff --git a/tests/rules/brace-style.js b/tests/rules/brace-style.js
index ac2a86a..065b3a6 100644
--- a/tests/rules/brace-style.js
+++ b/tests/rules/brace-style.js
@@ -12,7 +12,7 @@ describe('brace style - scss', function () {
     lint.test(file, {
       'brace-style': 1
     }, function (data) {
-      lint.assert.equal(36, data.warningCount);
+      lint.assert.equal(40, data.warningCount);
       done();
     });
   });
@@ -27,7 +27,7 @@ describe('brace style - scss', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(58, data.warningCount);
+      lint.assert.equal(62, data.warningCount);
       done();
     });
   });
@@ -42,7 +42,7 @@ describe('brace style - scss', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(40, data.warningCount);
+      lint.assert.equal(44, data.warningCount);
       done();
     });
   });
@@ -57,7 +57,7 @@ describe('brace style - scss', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(62, data.warningCount);
+      lint.assert.equal(66, data.warningCount);
       done();
     });
   });
@@ -72,7 +72,7 @@ describe('brace style - scss', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(77, data.warningCount);
+      lint.assert.equal(81, data.warningCount);
       done();
     });
   });
@@ -87,7 +87,7 @@ describe('brace style - scss', function () {
         }
       ]
     }, function (data) {
-      lint.assert.equal(99, data.warningCount);
+      lint.assert.equal(103, data.warningCount);
       done();
     });
   });
diff --git a/tests/sass/brace-style.scss b/tests/sass/brace-style.scss
index 2ab8d1f..3f8d127 100644
--- a/tests/sass/brace-style.scss
+++ b/tests/sass/brace-style.scss
@@ -471,3 +471,70 @@ $total: 4;
 
 @mixin bar() {
   content: 'bar'; }
+
+
+
+// =======================
+//  Multi-line parameters
+// =======================
+
+@function foo(
+  $bar
+) {
+  content: $bar;
+}
+
+
+@function foo(
+  $bar
+)
+{
+  content: $bar;
+}
+
+
+
+@function bar(
+  $bar,
+  $baz
+) {
+  content: $bar;
+}
+
+@function bar(
+  $bar,
+  $baz
+)
+{
+  content: $bar;
+}
+
+
+@mixin foo(
+  $bar
+) {
+  content: 'foo bar';
+}
+
+@mixin foo(
+  $bar
+)
+{
+  content: 'foo bar';
+}
+
+
+@mixin bar(
+  $bar,
+  $baz
+) {
+  content: 'foo bar';
+}
+
+@mixin bar(
+  $bar,
+  $baz
+)
+{
+  content: 'foo bar';
+}
diff --git a/tests/sass/no-misspelled-properties.sass b/tests/sass/no-misspelled-properties.sass
index a86c71f..b9e454c 100644
--- a/tests/sass/no-misspelled-properties.sass
+++ b/tests/sass/no-misspelled-properties.sass
@@ -14,7 +14,9 @@ $red: #ff0000
   color: $red
   transition: width 2s
 
-// FIXME Currently a parse error within gonzales-pe
-// .test-interp
-//-webkit-#{$property}: $value
-//
+.test-interp
+  -webkit-#{$property}: $value
+
+// https://github.com/sasstools/sass-lint/issues/606
+.test-vendor
+  -moz-osx-font-smoothing: auto
diff --git a/tests/sass/no-misspelled-properties.scss b/tests/sass/no-misspelled-properties.scss
index 1557143..ac1b99c 100644
--- a/tests/sass/no-misspelled-properties.scss
+++ b/tests/sass/no-misspelled-properties.scss
@@ -18,7 +18,11 @@ $red: #ff0000;
 
 }
 
-// FIXME Currently a parse error within gonzales-pe
-// .test-interp {
-//-webkit-#{$property}: $value;
-// }
+.test-interp {
+  -webkit-#{$property}: $value;
+}
+
+// https://github.com/sasstools/sass-lint/issues/606
+.test-vendor {
+  -moz-osx-font-smoothing: auto;
+}