Repository: sasstools/sass-lint (js)

Issue:
New rule: spacing around bangs
Allow us to lint the spacing around a bang (before and after). 

Scsslint currently do this with one rule, [bangFormat](https://github.com/brigade/scss-lint/blob/master/lib/scss_lint/linter/README.md#bangformat), but I feel we should continue in the current style of whitespace rules and have two, `space-before-bang` and `space-after-bang`. Thoughts?

Happy to do this either way  if it gets the thumbs up.

Pull request description:
Add Space Before Bang and Space After Bang rules
This pull request adds 2 rules. One to manage the whitespace before a bang and another to manage the whitespace after a bang.

Defaults are to allow whitespace before but not after a bang.

Closes #26 (request for rules)


DCO 1.1 Signed-off-by: Ben Griffith <gt11687@gmail.com>

Patch:
diff --git a/lib/config/sass-lint.yml b/lib/config/sass-lint.yml
index 7e0307a..b008c18 100644
--- a/lib/config/sass-lint.yml
+++ b/lib/config/sass-lint.yml
@@ -27,5 +27,8 @@ rules:
 
   space-before-brace: 1
 
+  space-before-bang: 1
+  space-after-bang: 1
+
   space-between-parens: 1
   trailing-semicolon: 1
diff --git a/lib/rules/space-after-bang.js b/lib/rules/space-after-bang.js
new file mode 100644
index 0000000..82a11af
--- /dev/null
+++ b/lib/rules/space-after-bang.js
@@ -0,0 +1,40 @@
+'use strict';
+
+var helpers = require('../helpers.js');
+
+module.exports = {
+  'name': 'space-after-bang',
+  'defaults': {
+    'include': false
+  },
+  'detect': function (ast, parser) {
+    var result = [];
+
+    ast.traverseByTypes(['important'], function (block) {
+      block.traverse(function (item) {
+        if (item.type === 'space') {
+          if (parser.options.include) {
+            result = helpers.addUnique(result, {
+              'ruleId': parser.rule.name,
+              'line': block.start.line,
+              'column': block.start.column + 1,
+              'message': 'Bangs (!) should be followed by a space',
+              'severity': parser.severity
+            });
+          }
+          else {
+            result = helpers.addUnique(result, {
+              'ruleId': parser.rule.name,
+              'line': block.start.line,
+              'column': block.start.column,
+              'message': 'Bangs (!) should not be followed by a space',
+              'severity': parser.severity
+            });
+          }
+        }
+      });
+    });
+
+    return result;
+  }
+};
diff --git a/lib/rules/space-before-bang.js b/lib/rules/space-before-bang.js
new file mode 100644
index 0000000..e30ab9f
--- /dev/null
+++ b/lib/rules/space-before-bang.js
@@ -0,0 +1,42 @@
+'use strict';
+
+var helpers = require('../helpers.js');
+
+module.exports = {
+  'name': 'space-before-bang',
+  'defaults': {
+    'include': true
+  },
+  'detect': function (ast, parser) {
+    var result = [];
+
+    ast.traverseByTypes(['important'], function (block, i, parent) {
+      var previous = parent.content[i - 1];
+
+      if (!previous.is('space')) {
+        if (parser.options.include) {
+          result = helpers.addUnique(result, {
+            'ruleId': parser.rule.name,
+            'line': block.start.line,
+            'column': block.start.column,
+            'message': 'Whitespace required before !important',
+            'severity': parser.severity
+          });
+        }
+      }
+      else {
+        if (!parser.options.include) {
+          result = helpers.addUnique(result, {
+            'ruleId': parser.rule.name,
+            'line': previous.start.line,
+            'column': previous.start.column,
+            'message': 'Whitespace not allowed before !important',
+            'severity': parser.severity
+          });
+        }
+      }
+    });
+
+    return result;
+  }
+};


Test patch:
diff --git a/tests/main.js b/tests/main.js
index 8616b90..1523d1b 100644
--- a/tests/main.js
+++ b/tests/main.js
@@ -262,9 +262,47 @@ describe('rules', function () {
   // No Important
   //////////////////////////////
   it('no important', function (done) {
-    lintFile('no-important.scss', function (data) {
+    lintFile('no-important.scss', {
+      'rules': {
+        'no-important': 1,
+        'space-before-bang': 0,
+        'space-after-bang': 0
+      }
+    }, function (data) {
       assert.equal(1, data.warningCount);
       done();
     });
   });
+
+  //////////////////////////////
+  // Space Before Bang
+  //////////////////////////////
+  it('space before bang', function (done) {
+    lintFile('space-before-bang.scss', {
+      'rules': {
+        'space-before-bang': 1,
+        'space-after-bang': 0,
+        'no-important': 0
+      }
+    }, function (data) {
+      assert.equal(2, data.warningCount);
+      done();
+    });
+  });
+
+  //////////////////////////////
+  // Space After Bang
+  //////////////////////////////
+  it('space after bang', function (done) {
+    lintFile('space-after-bang.scss', {
+      'rules': {
+        'space-after-bang': 1,
+        'space-before-bang': 0,
+        'no-important': 0
+      }
+    }, function (data) {
+      assert.equal(2, data.warningCount);
+      done();
+    });
+  });
 });
diff --git a/tests/sass/space-after-bang.scss b/tests/sass/space-after-bang.scss
new file mode 100644
index 0000000..a5e2c3a
--- /dev/null
+++ b/tests/sass/space-after-bang.scss
@@ -0,0 +1,15 @@
+.foo {
+  color: orange !important;
+}
+
+.bar {
+  color: orange ! important;
+}
+
+.baz {
+  color: orange!important;
+}
+
+.qux {
+  color: orange! important;
+}
diff --git a/tests/sass/space-before-bang.scss b/tests/sass/space-before-bang.scss
new file mode 100644
index 0000000..05a11b2
--- /dev/null
+++ b/tests/sass/space-before-bang.scss
@@ -0,0 +1,15 @@
+.foo {
+  color: red!important;
+}
+
+.bar {
+  color: orange !important;
+}
+
+.baz {
+  color: green! important;
+}
+
+.quz {
+  color: pink ! important;
+}