Repository: sasstools/sass-lint
Pull request #503 (2016-02-05 07:14:33)

Gonzales 3.2 - Fix collectSuffixExtensions
Fixes the `collectSuffixExtensions` rule to work with the latest version of gonzales.

Note: Travis will fail as there are broken tests on gonzales-3-develop branch. Verify by checking `helpers - collectSuffixExtensions` rule test success.

DCO 1.1 Signed-off-by: Ben Rothman <bensrothman@gmail.com>

benthemonkey: This also fixes `bem-depth`, `class name format - scss`, `id name format - scss`, and `id name format - sass` except for `[convention: RegExp ^[_A-Z]+$], with convention-explanation` :+1: 
bgriffith: Nice work @benthemonkey - good to see more sass tests too.

Can you just confirm that it's fixes `id name format - sass` except for `[convention: RegExp ^[_A-Z]+$]`? 

Looking at the travis report, they all the sass tests appear to be failing.
benthemonkey: @bgriffith You're right, I forgot that I purposely didn't commit a change to `tests/sass/id-name-format.sass`. I'll handle further fixes to `id name format` in a separate PR.

Patch:
diff --git a/lib/helpers.js b/lib/helpers.js
index 71f2e46..cda8d39 100644
--- a/lib/helpers.js
+++ b/lib/helpers.js
@@ -4,16 +4,7 @@ var util = require('util'),
     fs = require('fs'),
     path = require('path'),
     yaml = require('js-yaml'),
-    merge = require('merge');
-
-/**
- * Easy access to the 'merge' library's cloning functionality
- * @param   {object} obj Object to clone
- * @returns {object}     Clone of obj
- */
-var clone = function (obj) {
-  return merge(true, obj);
-};
+    gonzales = require('gonzales-pe');
 
 var helpers = {};
 
@@ -407,7 +398,7 @@ helpers.attemptTraversal = function (node, traversalPath) {
  *                                       (without '.', '#', etc) resulting from suffix extensions
  */
 helpers.collectSuffixExtensions = function (ruleset, selectorType) {
-  var parentSelectors = helpers.attemptTraversal(ruleset, ['selector', 'simpleSelector', selectorType, 'ident']),
+  var parentSelectors = helpers.attemptTraversal(ruleset, ['selector', selectorType, 'ident']),
       childSuffixes = helpers.attemptTraversal(ruleset, ['block', 'ruleset']),
       selectorList = [];
 
@@ -419,25 +410,18 @@ helpers.collectSuffixExtensions = function (ruleset, selectorType) {
   // extended, so lots of looping is required.
   var processChildSuffix = function (child, parents) {
     var currentParents = [],
-        selectors = helpers.attemptTraversal(child, ['selector', 'simpleSelector']),
+        selectors = helpers.attemptTraversal(child, ['selector', 'parentSelectorExtension', 'ident']),
         nestedChildSuffixes = helpers.attemptTraversal(child, ['block', 'ruleset']);
 
     selectors.forEach(function (childSuffixNode) {
-      var extendedNode;
-
-      if (childSuffixNode.length >= 2 &&
-        childSuffixNode.contains('parentSelector') &&
-        childSuffixNode.contains('ident')) {
-
-        // append suffix extension to all parent selectors
-        parents.forEach(function (parent) {
-          // clone so we don't modify the actual AST
-          extendedNode = clone(childSuffixNode.first('ident'));
-          extendedNode.content = parent.content + extendedNode.content;
-
-          currentParents.push(extendedNode);
-        });
-      }
+      // append suffix extension to all parent selectors
+      parents.forEach(function (parent) {
+        // clone so we don't modify the actual AST
+        var clonedChildSuffixNode = gonzales.createNode(childSuffixNode);
+        clonedChildSuffixNode.content = parent.content + clonedChildSuffixNode.content;
+
+        currentParents.push(clonedChildSuffixNode);
+      });
     });
 
     selectorList = selectorList.concat(currentParents);


Test patch:
diff --git a/tests/helpers.js b/tests/helpers.js
index 885ba04..8861465 100644
--- a/tests/helpers.js
+++ b/tests/helpers.js
@@ -1576,7 +1576,7 @@ describe('helpers', function () {
   //////////////////////////////
   // attemptTraversal
   //////////////////////////////
-  it('attemptTraversal - collect all nodes', function () {
+  it('attemptTraversal - SCSS - collect all nodes', function () {
     var stylesheet = gonzales.parse(['',
       '.a {',
       '  .b {',
@@ -1599,7 +1599,7 @@ describe('helpers', function () {
     );
   });
 
-  it('attemptTraversal - empty array when traversal fails', function () {
+  it('attemptTraversal - SCSS - empty array when traversal fails', function () {
     var stylesheet = gonzales.parse(['',
       '.a {',
       '  color: red;',
@@ -1611,10 +1611,42 @@ describe('helpers', function () {
     );
   });
 
+  it('attemptTraversal - Sass - collect all nodes', function () {
+    var stylesheet = gonzales.parse(['',
+      '.a',
+      '  .b',
+      '    color: red',
+      '  .c',
+      '    color: blue',
+      '  .d',
+      '    color: green',
+      ''].join('\n'), { syntax: 'sass' });
+
+    assert.deepEqual(
+      helpers.attemptTraversal(stylesheet, ['ruleset', 'block', 'ruleset', 'block', 'declaration', 'property', 'ident'])
+        .map(function (node) {
+          return node.content;
+        }),
+      ['color', 'color', 'color']
+    );
+  });
+
+  it('attemptTraversal - Sass - empty array when traversal fails', function () {
+    var stylesheet = gonzales.parse(['',
+      '.a',
+      '  color: red',
+      ''].join('\n'), { syntax: 'sass' });
+
+    assert.equal(
+      helpers.attemptTraversal(stylesheet, ['ruleset', 'block', 'ruleset', 'block']).length,
+      0
+    );
+  });
+
   //////////////////////////////
   // collectSuffixExtensions
   //////////////////////////////
-  it('collectSuffixExtensions - no extensions', function () {
+  it('collectSuffixExtensions - SCSS - no extensions', function () {
     var ruleset = gonzales.parse(['',
       '.a {',
       '  .b {',
@@ -1633,7 +1665,7 @@ describe('helpers', function () {
     );
   });
 
-  it('collectSuffixExtensions - BEM example', function () {
+  it('collectSuffixExtensions - SCSS - BEM example', function () {
     var ruleset = gonzales.parse(['',
       '.block {',
       '  &__element {',
@@ -1652,7 +1684,7 @@ describe('helpers', function () {
     );
   });
 
-  it('collectSuffixExtensions - many parents and children', function () {
+  it('collectSuffixExtensions - SCSS - many parents and children', function () {
     var ruleset = gonzales.parse(['',
       '.a,',
       '.b {',
@@ -1673,4 +1705,55 @@ describe('helpers', function () {
       ['a', 'b', 'ac', 'bc', 'ad', 'bd', 'ace', 'bce', 'ade', 'bde', 'acf', 'bcf', 'adf', 'bdf']
     );
   });
+
+  it('collectSuffixExtensions - Sass - no extensions', function () {
+    var ruleset = gonzales.parse(['',
+      '.a',
+      '  .b',
+      '    .c',
+      '      width: 2px',
+      ''].join('\n'), { syntax: 'sass' })
+      .first('ruleset');
+
+    assert.deepEqual(
+      helpers.collectSuffixExtensions(ruleset, 'class').map(function (node) {
+        return node.content;
+      }),
+      ['a']
+    );
+  });
+
+  it('collectSuffixExtensions - Sass - BEM example', function () {
+    var ruleset = gonzales.parse(['',
+      '.block',
+      '  &__element',
+      '    &--modifier',
+      '      width: 2px',
+      ''].join('\n'), { syntax: 'sass' })
+      .first('ruleset');
+
+    assert.deepEqual(
+      helpers.collectSuffixExtensions(ruleset, 'class').map(function (node) {
+        return node.content;
+      }),
+      ['block', 'block__element', 'block__element--modifier']
+    );
+  });
+
+  it('collectSuffixExtensions - Sass - many parents and children', function () {
+    var ruleset = gonzales.parse(['',
+      '.a, .b',
+      '  &c, &d',
+      '    &e, &f',
+      '      width: 2px',
+      ''].join('\n'), { syntax: 'sass' })
+      .first('ruleset');
+
+    assert.deepEqual(
+      helpers.collectSuffixExtensions(ruleset, 'class').map(function (node) {
+        return node.content;
+      }),
+      ['a', 'b', 'ac', 'bc', 'ad', 'bd', 'ace', 'bce', 'ade', 'bde', 'acf', 'bcf', 'adf', 'bdf']
+    );
+  });
 });