From 1dd3e80b47cf17696ec2eb5689fe1f6421403518 Mon Sep 17 00:00:00 2001 From: fiso Date: Wed, 8 May 2019 22:52:14 +0200 Subject: [PATCH 1/6] :mag: Add no-multiple-empty-lines rule --- docs/rules/no-multiple-empty-lines.md | 36 ++++++++ lib/rules/no-multiple-empty-lines.js | 34 +++++++ tests/rules/no-multiple-empty-lines.js | 35 ++++++++ tests/sass/no-multiple-empty-lines.sass | 90 +++++++++++++++++++ tests/sass/no-multiple-empty-lines.scss | 113 ++++++++++++++++++++++++ 5 files changed, 308 insertions(+) create mode 100644 docs/rules/no-multiple-empty-lines.md create mode 100644 lib/rules/no-multiple-empty-lines.js create mode 100644 tests/rules/no-multiple-empty-lines.js create mode 100644 tests/sass/no-multiple-empty-lines.sass create mode 100644 tests/sass/no-multiple-empty-lines.scss diff --git a/docs/rules/no-multiple-empty-lines.md b/docs/rules/no-multiple-empty-lines.md new file mode 100644 index 00000000..55868ecc --- /dev/null +++ b/docs/rules/no-multiple-empty-lines.md @@ -0,0 +1,36 @@ +# No Multiple Empty Lines + +Rule `no-multiple-empty-lines` will disallow multiple consecutive empty lines. + +## Examples + +When enabled the following are allowed: + +```scss +.foo { + content: 'bar'; + content: 'baz'; + + .waldo { + content: 'where'; + } +} +``` + +When enabled the following are disallowed: + +```scss + + +.foo { + content: 'bar'; + content: 'baz' + + + .waldo { + content: 'where' + } +} + + +``` diff --git a/lib/rules/no-multiple-empty-lines.js b/lib/rules/no-multiple-empty-lines.js new file mode 100644 index 00000000..83d4d633 --- /dev/null +++ b/lib/rules/no-multiple-empty-lines.js @@ -0,0 +1,34 @@ +'use strict'; + +var helpers = require('../helpers'); + +module.exports = { + 'name': 'no-multiple-empty-lines', + 'defaults': { + }, + 'detect': function (ast, parser) { + let result = []; + + const source = ast.toString(); + const re = /^\n\n+/gm; + let m = null; + do { + m = re.exec(source); + if (m) { + const lineNumber = source + .substr(0, m.index) + .split('\n').length; + + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': lineNumber, + 'column': 1, + 'message': 'Multiple consecutive empty lines not allowed', + 'severity': parser.severity + }); + } + } while (m); + + return result; + } +}; diff --git a/tests/rules/no-multiple-empty-lines.js b/tests/rules/no-multiple-empty-lines.js new file mode 100644 index 00000000..94a8fb09 --- /dev/null +++ b/tests/rules/no-multiple-empty-lines.js @@ -0,0 +1,35 @@ +'use strict'; + +var lint = require('./_lint'); + +////////////////////////////// +// SCSS syntax tests +////////////////////////////// +describe('no multiple empty lines - scss', function () { + var file = lint.file('no-multiple-empty-lines.scss'); + + it('enforce', function (done) { + lint.test(file, { + 'no-multiple-empty-lines': 1 + }, function (data) { + lint.assert.equal(5, data.warningCount); + done(); + }); + }); +}); + +////////////////////////////// +// Sass syntax tests +////////////////////////////// +describe('no multiple empty lines - sass', function () { + var file = lint.file('no-multiple-empty-lines.sass'); + + it('enforce', function (done) { + lint.test(file, { + 'no-multiple-empty-lines': 1 + }, function (data) { + lint.assert.equal(11, data.warningCount); + done(); + }); + }); +}); diff --git a/tests/sass/no-multiple-empty-lines.sass b/tests/sass/no-multiple-empty-lines.sass new file mode 100644 index 00000000..ce8ba6dc --- /dev/null +++ b/tests/sass/no-multiple-empty-lines.sass @@ -0,0 +1,90 @@ +.foo + content: 'foo' + +.foo + content: 'foo' +.bar + content: 'bar' + +.foo + @include bar + +.foo + @include bar($qux) + content: 'foo' + + +.foo + @include bar($qux) + content: 'foo' + + +.foo + + @include bar($qux) + content: 'foo' + + +.foo + @include bar($qux) + &:after + content: 'foo' + + + +.foo + + &__bar + content: 'foo' + + + content: 'foo' + +.foo + content: 'foo' + + &__bar + content: 'foo' + + +.foo + content: 'foo' + + &__bar + content: 'foo' + + + +.foo + &:before + content: 'foo' + + +.foo + content: 'foo' + + &::first-line + content: 'foo' + + +h1 + content: 'foo' +h2 + content: 'foo' +h3 + content: 'foo' +h4 + content: 'foo' + + +.foo + .bar + content: 'foo' +.foo + .bar, +h2 + content: 'foo' + + +[type=text] + content: 'foo' +h1.foo + content: 'foo' diff --git a/tests/sass/no-multiple-empty-lines.scss b/tests/sass/no-multiple-empty-lines.scss new file mode 100644 index 00000000..d47d87f6 --- /dev/null +++ b/tests/sass/no-multiple-empty-lines.scss @@ -0,0 +1,113 @@ +.foo { + content: 'foo'; +} + +.foo { + content: 'foo'; +} +.bar { + content: 'bar'; +} + +.foo { + @include bar; +} + +.foo { + @include bar($qux) { + content: 'foo'; + } +} + +.foo { + @include bar($qux) { + content: 'foo'; + } +} + +.foo { + + @include bar($qux) { + content: 'foo'; + } +} + +.foo { + @include bar($qux) { + &:after { + content: 'foo'; + } + } +} + +.foo { + + &__bar { + content: 'foo'; + } + + content: 'foo'; +} + +.foo { + content: 'foo'; + + &__bar { + content: 'foo'; + } +} + +.foo { + content: 'foo'; + + &__bar { + content: 'foo'; + } +} + + +.foo { + &:before { + content: 'foo'; + } +} + +.foo { + content: 'foo'; + + &::first-line { + content: 'foo'; + } +} + + +h1 { content: 'foo'; } +h2 { content: 'foo'; } +h3 { content: 'foo'; } +h4 { content: 'foo'; } + + +h1 { content: 'foo'; } + +h2 { content: 'foo'; } + +h3 { content: 'foo'; } + +h4 { content: 'foo'; } + + +.foo + .bar { + content: 'foo'; +} +.foo + .bar, +h2 { + content: 'foo'; +} + + +[type=text] { + content: 'foo'; +} +h1.foo { + content: 'foo'; +} From 9653690cf68f8ebc7c6ef6a0464035002634caf9 Mon Sep 17 00:00:00 2001 From: fiso Date: Wed, 8 May 2019 23:25:09 +0200 Subject: [PATCH 2/6] Change error count message to be singular/plural-aware --- index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 8bd498f7..f4d551b7 100644 --- a/index.js +++ b/index.js @@ -310,7 +310,8 @@ sassLint.failOnError = function (results, options, configPath) { configOptions = this.getConfig(options, configPath).options; if (errorCount.count > 0) { - throw new exceptions.SassLintFailureError(errorCount.count + ' errors were detected in \n- ' + errorCount.files.join('\n- ')); + const pluralized = errorCount.count === 1 ? 'error was' : 'errors were'; + throw new exceptions.SassLintFailureError(errorCount.count + ' ' + pluralized + ' detected in \n- ' + errorCount.files.join('\n- ')); } if (!isNaN(configOptions['max-warnings']) && warningCount.count > configOptions['max-warnings']) { From 9d48a292fb40b46eebbcac28c6dbdfced209b4aa Mon Sep 17 00:00:00 2001 From: fiso Date: Thu, 9 May 2019 11:02:44 +0200 Subject: [PATCH 3/6] :art: Gitignore vscode metadata --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index d2d8fcb3..5bfac4ea 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ node_modules # Users Environment Variables .lock-wscript + +.vscode From 759d34d778fc8f6d19e6d44c10ff226f96e42093 Mon Sep 17 00:00:00 2001 From: fiso Date: Thu, 9 May 2019 11:03:55 +0200 Subject: [PATCH 4/6] :art: Rename `no-multiple-empty-lines` to `no-excessive-empty-lines` and make it accept some parameters --- ...y-lines.md => no-excessive-empty-lines.md} | 9 +- lib/rules/no-excessive-empty-lines.js | 127 ++++++++++++++++++ lib/rules/no-multiple-empty-lines.js | 34 ----- ...y-lines.js => no-excessive-empty-lines.js} | 16 +-- ...nes.sass => no-excessive-empty-lines.sass} | 3 + ...nes.scss => no-excessive-empty-lines.scss} | 5 + 6 files changed, 150 insertions(+), 44 deletions(-) rename docs/rules/{no-multiple-empty-lines.md => no-excessive-empty-lines.md} (53%) create mode 100644 lib/rules/no-excessive-empty-lines.js delete mode 100644 lib/rules/no-multiple-empty-lines.js rename tests/rules/{no-multiple-empty-lines.js => no-excessive-empty-lines.js} (52%) rename tests/sass/{no-multiple-empty-lines.sass => no-excessive-empty-lines.sass} (99%) rename tests/sass/{no-multiple-empty-lines.scss => no-excessive-empty-lines.scss} (99%) diff --git a/docs/rules/no-multiple-empty-lines.md b/docs/rules/no-excessive-empty-lines.md similarity index 53% rename from docs/rules/no-multiple-empty-lines.md rename to docs/rules/no-excessive-empty-lines.md index 55868ecc..443ddcba 100644 --- a/docs/rules/no-multiple-empty-lines.md +++ b/docs/rules/no-excessive-empty-lines.md @@ -1,6 +1,10 @@ -# No Multiple Empty Lines +# No Excessive Empty Lines -Rule `no-multiple-empty-lines` will disallow multiple consecutive empty lines. +Rule `no-excessive-empty-lines` will disallow excessive amounts of empty lines. + +- Consecutive blank lines are not allowed +- Blank lines at the start of a block are not allowed +- Blank lines at the end of a block are not allowed ## Examples @@ -29,6 +33,7 @@ When enabled the following are disallowed: .waldo { content: 'where' + } } diff --git a/lib/rules/no-excessive-empty-lines.js b/lib/rules/no-excessive-empty-lines.js new file mode 100644 index 00000000..fa1f3c00 --- /dev/null +++ b/lib/rules/no-excessive-empty-lines.js @@ -0,0 +1,127 @@ +'use strict'; + +var helpers = require('../helpers'); + +const countOccurences = function (str, substr) { + return str.split(substr).length - 1; +}; + +const checkNodeForEmptyness = function (node) { + if (node.type !== 'space') { + return false; + } + + return countOccurences(node.content, '\n') > 1; +}; + +const addError = function (result, parser, message, line) { + return helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': line, + 'column': 1, + message, + 'severity': parser.severity + }); +}; + +const multipleConsecutiveError = function (result, parser, line) { + return addError(result, parser, 'Multiple consecutive empty lines not allowed', line); +}; + +const startOfBlockError = function (result, parser, line) { + return addError(result, parser, 'Empty lines at start of block not allowed', line); +}; + +const endOfBlockError = function (result, parser, line) { + return addError(result, parser, 'Empty lines at end of block not allowed', line); +}; + +module.exports = { + 'name': 'no-excessive-empty-lines', + 'defaults': { + 'allow-consecutive': false, + 'allow-at-block-end': false, + 'allow-at-block-start': false, + }, + 'detect': function (ast, parser) { + let result = []; + + if (!parser.options['allow-consecutive']) { + const source = ast.toString(); + const re = /^\n\n+/gm; + let m = null; + do { + m = re.exec(source); + if (m) { + const lineNumber = source + .substr(0, m.index) + .split('\n').length; + + result = multipleConsecutiveError(result, parser, lineNumber); + } + } while (m); + } + + const forbidAtStart = !parser.options['allow-at-block-start']; + const forbidAtEnd = !parser.options['allow-at-block-end']; + + if (forbidAtStart || forbidAtEnd) { + ast.traverseByType('block', function (node) { + if (!Array.isArray(node.content) || node.content.length < 1) { + return true; + } + + if (ast.syntax === 'scss') { + if (forbidAtStart) { + if (checkNodeForEmptyness(node.content[0])) { + result = startOfBlockError(result, parser, node.start.line + 1); + } + } + + if (forbidAtEnd) { + const n = node.content[node.content.length - 1]; + if (checkNodeForEmptyness(n)) { + result = endOfBlockError(result, parser, n.start.line + 1); + } + } + } + else if (ast.syntax === 'sass') { + if (forbidAtStart) { + if (node.content[0].type === 'space' && + countOccurences(node.content[0].content, '\n')) { + result = startOfBlockError(result, parser, node.start.line); + } + } + } + + return true; + }); + } + + if (!parser.options['allow-at-block-end']) { + ast.traverseByType('block', function (node) { + if (!Array.isArray(node.content) || node.content.length < 1) { + return true; + } + + if (node.content[0].type !== 'space') { + return true; + } + + if (countOccurences(node.content[0].content, '\n') > 1) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': node.start.line + 1, + 'column': 1, + 'message': 'Empty lines at end of block not allowed', + 'severity': parser.severity + }); + } + + return true; + }); + } + + return result; + } +}; diff --git a/lib/rules/no-multiple-empty-lines.js b/lib/rules/no-multiple-empty-lines.js deleted file mode 100644 index 83d4d633..00000000 --- a/lib/rules/no-multiple-empty-lines.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict'; - -var helpers = require('../helpers'); - -module.exports = { - 'name': 'no-multiple-empty-lines', - 'defaults': { - }, - 'detect': function (ast, parser) { - let result = []; - - const source = ast.toString(); - const re = /^\n\n+/gm; - let m = null; - do { - m = re.exec(source); - if (m) { - const lineNumber = source - .substr(0, m.index) - .split('\n').length; - - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': lineNumber, - 'column': 1, - 'message': 'Multiple consecutive empty lines not allowed', - 'severity': parser.severity - }); - } - } while (m); - - return result; - } -}; diff --git a/tests/rules/no-multiple-empty-lines.js b/tests/rules/no-excessive-empty-lines.js similarity index 52% rename from tests/rules/no-multiple-empty-lines.js rename to tests/rules/no-excessive-empty-lines.js index 94a8fb09..acc6a054 100644 --- a/tests/rules/no-multiple-empty-lines.js +++ b/tests/rules/no-excessive-empty-lines.js @@ -5,14 +5,14 @@ var lint = require('./_lint'); ////////////////////////////// // SCSS syntax tests ////////////////////////////// -describe('no multiple empty lines - scss', function () { - var file = lint.file('no-multiple-empty-lines.scss'); +describe('no excessive empty lines - scss', function () { + var file = lint.file('no-excessive-empty-lines.scss'); it('enforce', function (done) { lint.test(file, { - 'no-multiple-empty-lines': 1 + 'no-excessive-empty-lines': 1 }, function (data) { - lint.assert.equal(5, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); @@ -21,14 +21,14 @@ describe('no multiple empty lines - scss', function () { ////////////////////////////// // Sass syntax tests ////////////////////////////// -describe('no multiple empty lines - sass', function () { - var file = lint.file('no-multiple-empty-lines.sass'); +describe('no excessive empty lines - sass', function () { + var file = lint.file('no-excessive-empty-lines.sass'); it('enforce', function (done) { lint.test(file, { - 'no-multiple-empty-lines': 1 + 'no-excessive-empty-lines': 1 }, function (data) { - lint.assert.equal(11, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); diff --git a/tests/sass/no-multiple-empty-lines.sass b/tests/sass/no-excessive-empty-lines.sass similarity index 99% rename from tests/sass/no-multiple-empty-lines.sass rename to tests/sass/no-excessive-empty-lines.sass index ce8ba6dc..da949055 100644 --- a/tests/sass/no-multiple-empty-lines.sass +++ b/tests/sass/no-excessive-empty-lines.sass @@ -1,6 +1,9 @@ .foo + content: 'foo' + + .foo content: 'foo' .bar diff --git a/tests/sass/no-multiple-empty-lines.scss b/tests/sass/no-excessive-empty-lines.scss similarity index 99% rename from tests/sass/no-multiple-empty-lines.scss rename to tests/sass/no-excessive-empty-lines.scss index d47d87f6..dba49ea9 100644 --- a/tests/sass/no-multiple-empty-lines.scss +++ b/tests/sass/no-excessive-empty-lines.scss @@ -1,5 +1,8 @@ .foo { + content: 'foo'; + + } .foo { @@ -38,6 +41,8 @@ content: 'foo'; } } + + } .foo { From 8d6b35e6376ed7ebc16c53204d4a6858b7b14f5e Mon Sep 17 00:00:00 2001 From: fiso Date: Mon, 12 Aug 2019 15:38:56 +0200 Subject: [PATCH 5/6] :memo: Add documentation for no-excessive-empty-lines rule --- docs/rules/no-excessive-empty-lines.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/rules/no-excessive-empty-lines.md b/docs/rules/no-excessive-empty-lines.md index 443ddcba..195e00ba 100644 --- a/docs/rules/no-excessive-empty-lines.md +++ b/docs/rules/no-excessive-empty-lines.md @@ -6,6 +6,12 @@ Rule `no-excessive-empty-lines` will disallow excessive amounts of empty lines. - Blank lines at the start of a block are not allowed - Blank lines at the end of a block are not allowed +## Options + +* `allow-consecutive`: `true`/`false` (defaults to `false`) +* `allow-at-block-end`: `true`/`false` (defaults to `false`) +* `allow-at-block-start`: `true`/`false` (defaults to `false`)`) + ## Examples When enabled the following are allowed: From 982ac82aa86fc04a6a1338100a751fe3f512e61e Mon Sep 17 00:00:00 2001 From: fiso Date: Mon, 12 Aug 2019 15:39:57 +0200 Subject: [PATCH 6/6] :memo: Fix a typo in contribution doc --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f31702e4..8c1069e5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -73,7 +73,7 @@ By making a contribution to this project, I certify that: ## Emoji Cheatsheet -When creating creating commits or updating the CHANGELOG, please **start** the commit message or update with one of the following applicable Emoji. Emoji should not be used at the start of issue or pull request titles. +When creating commits or updating the CHANGELOG, please **start** the commit message or update with one of the following applicable Emoji. Emoji should not be used at the start of issue or pull request titles. * :art: `:art:` when improving the format/structure of the code * :racehorse: `:racehorse:` when improving performance