diff --git a/docs/rules/declarations-before-nesting.md b/docs/rules/declarations-before-nesting.md new file mode 100644 index 00000000..d68aebcd --- /dev/null +++ b/docs/rules/declarations-before-nesting.md @@ -0,0 +1,29 @@ +# Declarations Before Nesting + +Rule `declarations-before-nesting` will enforce that declarations should be written before nesting in a ruleset. + +## Examples + +When enabled, the following are allowed: + +```scss +.foo { + content: 'baz'; + + .bar { + content: 'qux'; + } +} +``` + +When enabled, the following are disallowed: + +```scss +.foo { + .bar { + content: 'qux'; + } + + content: 'baz'; +} +``` diff --git a/lib/config/sass-lint.yml b/lib/config/sass-lint.yml index 7ff5e2c9..36ce1470 100644 --- a/lib/config/sass-lint.yml +++ b/lib/config/sass-lint.yml @@ -45,6 +45,7 @@ rules: property-units: 0 # Nesting + declarations-before-nesting: 1 force-attribute-nesting: 1 force-element-nesting: 1 force-pseudo-nesting: 1 diff --git a/lib/rules/declarations-before-nesting.js b/lib/rules/declarations-before-nesting.js new file mode 100644 index 00000000..992cad24 --- /dev/null +++ b/lib/rules/declarations-before-nesting.js @@ -0,0 +1,47 @@ +'use strict'; + +var helpers = require('../helpers'); + +module.exports = { + 'name': 'declarations-before-nesting', + 'defaults': {}, + 'detect': function (ast, parser) { + var result = [], + error; + + ast.traverseByType('block', function (block) { + if (block.contains('ruleset') && block.contains('declaration')) { + var rulesetIndex; + + block.forEach(function (item, j) { + var declarationIndex; + var declaration; + + if (item.is('ruleset') && rulesetIndex === void 0) { + rulesetIndex = j; + } + + if (item.is('declaration')) { + declarationIndex = j; + declaration = item; + } + + if (rulesetIndex < declarationIndex && declaration) { + error = { + 'ruleId': parser.rule.name, + 'line': declaration.start.line, + 'column': declaration.start.column, + 'message': 'Declarations should come before nestings', + 'severity': parser.severity + }; + result = helpers.addUnique(result, error); + } + }); + + rulesetIndex = null; + } + }); + + return result; + } +}; diff --git a/tests/rules/declarations-before-nesting.js b/tests/rules/declarations-before-nesting.js new file mode 100644 index 00000000..d7009a7e --- /dev/null +++ b/tests/rules/declarations-before-nesting.js @@ -0,0 +1,36 @@ +'use strict'; + +var lint = require('./_lint'); + +////////////////////////////// +// SCSS syntax tests +////////////////////////////// +describe('declarations before nesting - scss', function () { + var file = lint.file('declarations-before-nesting.scss'); + + it('enforce', function (done) { + lint.test(file, { + 'declarations-before-nesting': 1 + }, function (data) { + console.log(data); + lint.assert.equal(4, data.warningCount); + done(); + }); + }); +}); + +////////////////////////////// +// Sass syntax tests +////////////////////////////// +describe('declarations before nesting - sass', function () { + var file = lint.file('declarations-before-nesting.sass'); + + it('enforce', function (done) { + lint.test(file, { + 'declarations-before-nesting': 1 + }, function (data) { + lint.assert.equal(4, data.warningCount); + done(); + }); + }); +}); diff --git a/tests/sass/declarations-before-nesting.sass b/tests/sass/declarations-before-nesting.sass new file mode 100644 index 00000000..00bbb6a9 --- /dev/null +++ b/tests/sass/declarations-before-nesting.sass @@ -0,0 +1,27 @@ +.bar + content: 'baz' + + .qux + content: 'baz' + +.foo + .bar + content: 'where' + + content: 'baz' + + .baz + content: 'where' + + content: 'baz' + +.foo + .bar + content: 'where' + + .baz + content: 'quz' + + content: 'baz' + + content: 'baz' diff --git a/tests/sass/declarations-before-nesting.scss b/tests/sass/declarations-before-nesting.scss new file mode 100644 index 00000000..efb31d67 --- /dev/null +++ b/tests/sass/declarations-before-nesting.scss @@ -0,0 +1,35 @@ +.bar { + content: 'baz'; + + .qux { + content: 'baz'; + } +} + +.foo { + .bar { + content: 'where'; + } + + content: 'baz'; + + .baz { + content: 'where'; + } + + content: 'baz'; +} + +.foo { + .bar { + content: 'where'; + + .baz { + content: 'quz'; + } + + content: 'baz'; + } + + content: 'baz'; +}