Repository: lesshint/lesshint
Pull request #48 (2015-08-24 23:43:06)

Property Ordering by Alpha
Thanks for all the hard work guys!

I just ran into the question of ordering properties alphabetically. I noticed that the issue was brought up, so I put something together really quick. Let me know how it looks.

@jsdevel


jsdevel: +1
jwilsson: Great job! Just some minor things as per the notes, then we're good to go.

Patch:
diff --git a/lib/config/defaults.json b/lib/config/defaults.json
index a57c3ba..87c1f1d 100644
--- a/lib/config/defaults.json
+++ b/lib/config/defaults.json
@@ -66,6 +66,11 @@
         "style": "include_zero"
     },
 
+    "propertyOrdering": {
+        "enabled": true,
+        "style": "alpha"
+    },
+
     "propertyUnits": {
         "enabled": true,
         "global": [
diff --git a/lib/linter.js b/lib/linter.js
index 7bf45cb..4b8eb77 100644
--- a/lib/linter.js
+++ b/lib/linter.js
@@ -19,6 +19,7 @@ var linters = [
     require('./linters/important_rule'),
     require('./linters/leading_zero'),
     require('./linters/property_units'),
+    require('./linters/property_ordering'),
     require('./linters/qualifying_element'),
     require('./linters/single_line_per_property'),
     require('./linters/single_line_per_selector'),
diff --git a/lib/linters/README.md b/lib/linters/README.md
index ffa4a04..be29757 100644
--- a/lib/linters/README.md
+++ b/lib/linters/README.md
@@ -17,6 +17,7 @@ Each linter also accept a `enabled` option to turn if off/on completely. Another
 * [importPath](#importpath)
 * [importantRule](#importantrule)
 * [leadingZero](#leadingzero)
+* [propertyOrdering](#propertyordering)
 * [propertyUnits](#propertyunits)
 * [qualifyingElement](#qualifyingelement)
 * [singleLinePerProperty](#singlelineperproperty)
@@ -305,6 +306,14 @@ Option     | Description
 }
 ```
 
+## propertyOrdering
+Check for property ordering
+
+Option       | Description
+------------ | ----------
+`type`       | Ordering type (Only alpha supported right now)
+
+
 ## propertyUnits
 Specify which units are allowed for property values.
 
diff --git a/lib/linters/property_ordering.js b/lib/linters/property_ordering.js
new file mode 100644
index 0000000..0d3240c
--- /dev/null
+++ b/lib/linters/property_ordering.js
@@ -0,0 +1,46 @@
+'use strict';
+
+module.exports = function (options) {
+    var config = options.config;
+    var node = options.node;
+    var previousProp = null;
+    var results = null;
+
+    // Bail if the linter isn't wanted
+    if (!config.propertyOrdering || (config.propertyOrdering && !config.propertyOrdering.enabled)) {
+        return null;
+    }
+
+    // Only support alpha for now
+    if (config.propertyOrdering.style !== 'alpha') {
+        throw new Error(
+            'Invalid setting value for propertyOrdering: ' + config.propertyOrdering.style
+        );
+    }
+
+    // Not applicable, bail
+    if (node.type !== 'block') {
+        return null;
+    }
+
+    node.forEach('declaration', function (declaration) {
+        if (results) { return; }
+
+        var property = declaration.first('property').content[0];
+        var currentProperty = property.content.toLowerCase();
+
+        // Check for proper ordering
+        if (previousProp && previousProp.localeCompare(currentProperty) > -1) {
+            results =  {
+                column: property.start.column,
+                line: property.start.line,
+                linter: 'propertyOrdering',
+                message: 'Property ordering is not alphabetized'
+            };
+        }
+
+        previousProp = currentProperty;
+    });
+
+    return results;
+};


Test patch:
diff --git a/test/specs/linters/property_ordering.js b/test/specs/linters/property_ordering.js
new file mode 100644
index 0000000..823e04c
--- /dev/null
+++ b/test/specs/linters/property_ordering.js
@@ -0,0 +1,98 @@
+var assert = require('assert');
+
+describe('lesshint', function () {
+    var linter = require('../../../lib/linter');
+    var propertyOrdering = require('../../../lib/linters/property_ordering');
+
+    describe('#propertyOrdering()', function () {
+        it('should allow blocks with only one property', function () {
+            var source = '.foo { color: red; }';
+            var ast;
+
+            var options = {
+                propertyOrdering: {
+                    enabled: true,
+                    style: 'alpha'
+                }
+            };
+
+            ast = linter.parseAST(source);
+            ast = ast.first().first('block');
+
+            assert.strictEqual(null, propertyOrdering({
+                config: options,
+                node: ast
+            }));
+        });
+
+        it('should allow blocks with alphabetized properties', function () {
+            var source = '.foo { color: red; padding-top: 4px; right: 5px}';
+            var ast;
+
+            var options = {
+                propertyOrdering: {
+                    enabled: true,
+                    style: 'alpha'
+                }
+            };
+
+            ast = linter.parseAST(source);
+            ast = ast.first().first('block');
+
+            assert.strictEqual(null, propertyOrdering({
+                config: options,
+                node: ast
+            }));
+        });
+
+        it('should not allow blocks with non-alphabetized properties', function () {
+            var source = '.foo { padding-top: 4px; color: red; right: 5px}';
+            var actual;
+            var ast;
+
+            var expected = {
+                column: 26,
+                line: 1,
+                linter: 'propertyOrdering',
+                message: 'Property ordering is not alphabetized'
+            };
+
+            var options = {
+                propertyOrdering: {
+                    enabled: true,
+                    style: 'alpha'
+                }
+            };
+
+            ast = linter.parseAST(source);
+            ast = ast.first().first('block');
+
+            actual = propertyOrdering({
+                config: options,
+                node: ast
+            });
+
+            assert.deepEqual(actual, expected);
+        });
+
+        it('should throw on invalid "style" value', function () {
+            var source = '.foo { border: 0; }';
+            var ast;
+
+            var options = {
+                propertyOrdering: {
+                    enabled: true,
+                    style: 'invalid'
+                }
+            };
+
+            ast = linter.parseAST(source);
+            ast = ast.first().first('block').first('declaration');
+
+            assert.throws(propertyOrdering.bind(null, {
+                config: options,
+                node: ast
+            }), Error);
+        });
+    });
+});