Repository: lesshint/lesshint
Pull request #341 (2017-02-11 19:21:12)

Fixed @mdo URL typo in linters/README.md
The Twitter link for mdo was relative instead of absolute.


Patch:
diff --git a/lib/config/defaults.json b/lib/config/defaults.json
index 01ef585..eee960d 100644
--- a/lib/config/defaults.json
+++ b/lib/config/defaults.json
@@ -187,5 +187,11 @@
     "zeroUnit": {
         "enabled": true,
         "style": "keep_unit"
+    },
+
+    "variableValue": {
+        "enabled": true,
+        "always": [],
+        "never": []
     }
 }
diff --git a/lib/linter.js b/lib/linter.js
index be74e12..c6f104a 100644
--- a/lib/linter.js
+++ b/lib/linter.js
@@ -41,7 +41,8 @@ const defaultLinters = [
     require('./linters/url_format'),
     require('./linters/url_quotes'),
     require('./linters/zero_unit'),
-    require('./linters/max_char_per_line')
+    require('./linters/max_char_per_line'),
+    require('./linters/variable_value')
 ];
 
 const parseRules = function (node) {
diff --git a/lib/linters/README.md b/lib/linters/README.md
index f156a77..8a646ad 100644
--- a/lib/linters/README.md
+++ b/lib/linters/README.md
@@ -34,6 +34,7 @@
 * [trailingWhitespace](#trailingwhitespace)
 * [urlFormat](#urlformat)
 * [urlQuotes](#urlquotes)
+* [variableValue](#variablevalue)
 * [zeroUnit](#zerounit)
 
 In addition to the linter specific options outlined below, each linter also accepts these options:
@@ -854,6 +855,42 @@ The [CSS spec](http://dev.w3.org/csswg/css-values/#url-value) also recommends th
 }
 ```
 
+## variableValue
+Force variable or non variable value in property.
+
+Option     | Description
+---------- | ----------
+`always`    | Array of property names
+`never`    | Array of property names
+
+### always ['color'] valid
+```less
+.foo {
+    color: @success;
+}
+```
+
+### always ['color'] invalid
+```less
+.foo {
+    color: #333;
+}
+```
+
+### never ['color'] valid
+```less
+.foo {
+    color: #333;
+}
+```
+
+### never ['color'] invalid
+```less
+.foo {
+    color: @success;
+}
+```
+
 ## zeroUnit
 Zero values should include a unit for consistency with other values.
 
@@ -879,4 +916,4 @@ Option      | Description
 
 *Note: This rule doesn't apply to [angles](https://developer.mozilla.org/en-US/docs/Web/CSS/angle) or [time units](https://developer.mozilla.org/en-US/docs/Web/CSS/time) since they always require a unit.*
 
-Most of these rules are based on [@mdo](twitter.com/mdo)s [code guide](http://codeguide.co/#css).
+Most of these rules are based on [@mdo](https://twitter.com/mdo)'s [code guide](http://codeguide.co/#css).
diff --git a/lib/linters/variable_value.js b/lib/linters/variable_value.js
new file mode 100644
index 0000000..6e2fe4e
--- /dev/null
+++ b/lib/linters/variable_value.js
@@ -0,0 +1,37 @@
+'use strict';
+
+const util = require('util');
+
+module.exports = {
+    name: 'variableValue',
+    nodeTypes: ['decl'],
+    message: '%s is not allowed in "%s" property.',
+
+    lint: function variableValueLinter (config, node) {
+        const variableRegex = /@/i;
+
+        if (config.never && config.never.indexOf(node.prop) !== -1) {
+            if (variableRegex.test(node.value)) {
+                return [{
+                    column: node.source.start.column +
+                            node.prop.length +
+                            node.raws.between.length,
+                    line: node.source.start.line,
+                    message: util.format(this.message, 'Variable', node.prop)
+                }];
+            }
+        }
+
+        if (config.always && config.always.indexOf(node.prop) !== -1) {
+            if (!variableRegex.test(node.value)) {
+                return [{
+                    column: node.source.start.column +
+                            node.prop.length +
+                            node.raws.between.length,
+                    line: node.source.start.line,
+                    message: util.format(this.message, 'Non variable', node.prop)
+                }];
+            }
+        }
+    }
+};


Test patch:
diff --git a/test/specs/linters/variable_value.js b/test/specs/linters/variable_value.js
new file mode 100644
index 0000000..22a037b
--- /dev/null
+++ b/test/specs/linters/variable_value.js
@@ -0,0 +1,104 @@
+'use strict';
+
+const expect = require('chai').expect;
+const spec = require('../util.js').setup();
+
+describe('lesshint', function () {
+    describe('#variableValue()', function () {
+        it('should have the proper node types', function () {
+            const source = 'color: #ABC;';
+
+            return spec.parse(source, function (ast) {
+                expect(spec.linter.nodeTypes).to.include(ast.root.first.type);
+            });
+        });
+
+        it('should ignore if property not in lists', function () {
+            const source = 'font-weight: 700;';
+            const options = {
+                always: ['font-size'],
+                never: ['color']
+            };
+
+            return spec.parse(source, function (ast) {
+                const result = spec.linter.lint(options, ast.root.first);
+
+                expect(result).to.be.undefined;
+            });
+        });
+
+        it('should ignore if no lists', function () {
+            const source = 'font-size: #333;';
+            const options = {
+            };
+
+            return spec.parse(source, function (ast) {
+                const result = spec.linter.lint(options, ast.root.first);
+
+                expect(result).to.be.undefined;
+            });
+        });
+
+        it('should not allow variable if in never list', function () {
+            const source = 'color: @app-color;';
+            const options = {
+                never: ['color']
+            };
+            const expected = [{
+                line: 1,
+                column: 8,
+                message: 'Variable is not allowed in "color" property.'
+            }];
+
+            return spec.parse(source, function (ast) {
+                const result = spec.linter.lint(options, ast.root.first);
+
+                expect(result).to.deep.equal(expected);
+            });
+        });
+
+        it('should allow non variable if in never list', function () {
+            const source = 'color: #333;';
+            const options = {
+                never: ['color']
+            };
+
+            return spec.parse(source, function (ast) {
+                const result = spec.linter.lint(options, ast.root.first);
+
+                expect(result).to.be.undefined;
+            });
+        });
+
+        it('should not allow non variable if in always list', function () {
+            const source = 'color: #333;';
+            const options = {
+                always: ['color']
+            };
+            const expected = [{
+                line: 1,
+                column: 8,
+                message: 'Non variable is not allowed in "color" property.'
+            }];
+
+            return spec.parse(source, function (ast) {
+                const result = spec.linter.lint(options, ast.root.first);
+
+                expect(result).to.deep.equal(expected);
+            });
+        });
+
+        it('should allow variable if in always list', function () {
+            const source = 'color: @app-color;';
+            const options = {
+                always: ['color']
+            };
+
+            return spec.parse(source, function (ast) {
+                const result = spec.linter.lint(options, ast.root.first);
+
+                expect(result).to.be.undefined;
+            });
+        });
+    });
+});