Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement new rule no-excessive-empty-lines (fixes #1015) #1294

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@ node_modules

# Users Environment Variables
.lock-wscript

.vscode
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
47 changes: 47 additions & 0 deletions docs/rules/no-excessive-empty-lines.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# No Excessive 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

## 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:

```scss
.foo {
content: 'bar';
content: 'baz';

.waldo {
content: 'where';
}
}
```

When enabled the following are disallowed:

```scss


.foo {
content: 'bar';
content: 'baz'


.waldo {
content: 'where'

}
}


```
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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']) {
Expand Down
127 changes: 127 additions & 0 deletions lib/rules/no-excessive-empty-lines.js
Original file line number Diff line number Diff line change
@@ -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;
}
};
35 changes: 35 additions & 0 deletions tests/rules/no-excessive-empty-lines.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use strict';

var lint = require('./_lint');

//////////////////////////////
// SCSS syntax tests
//////////////////////////////
describe('no excessive empty lines - scss', function () {
var file = lint.file('no-excessive-empty-lines.scss');

it('enforce', function (done) {
lint.test(file, {
'no-excessive-empty-lines': 1
}, function (data) {
lint.assert.equal(15, data.warningCount);
done();
});
});
});

//////////////////////////////
// Sass syntax tests
//////////////////////////////
describe('no excessive empty lines - sass', function () {
var file = lint.file('no-excessive-empty-lines.sass');

it('enforce', function (done) {
lint.test(file, {
'no-excessive-empty-lines': 1
}, function (data) {
lint.assert.equal(15, data.warningCount);
done();
});
});
});
93 changes: 93 additions & 0 deletions tests/sass/no-excessive-empty-lines.sass
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
.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'
Loading