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

Feature/toggle rules in src #402

Merged
Show file tree
Hide file tree
Changes from 5 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
75 changes: 75 additions & 0 deletions docs/options/toggle-rules-in-src.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Toggling Rules Inside Source Files

For special cases where a particular lint doesn't make sense in a specific area of a file, special inline comments can be used to enable/disable linters. Some examples are provided below:

## Disable a rule for the entire file

```scss
// sass-lint:disable border-zero
p {
border: none; // No lint reported
}
```

## Disable more than 1 rule

```scss
// sass-lint:disable border-zero, quotes
p {
border: none; // No lint reported
content: "hello"; // No lint reported
}
```

## Disable a rule for a single line

```scss
p {
border: none; // sass-lint:disable-line border-zero
}
```

## Disable all lints within a block (and all contained blocks)

```scss
p {
// sass-lint:disable-block border-zero
border: none; // No result reported
}

a {
border: none; // Failing result reported
}
```

## Disable and enable again

```scss
// sass-lint:disable border-zero
p {
border: none; // No result reported
}
// sass-lint:enable border-zero

a {
border: none; // Failing result reported
}
```

## Disable/enable all linters

```scss
// sass-lint:disable-all
p {
border: none; // No result reported
}
// sass-lint:enable-all

a {
border: none; // Failing result reported
}
```

## doc-disabled-rules rule

It is highly recommended that you use the doc-disabled-rules rule.
57 changes: 57 additions & 0 deletions docs/rules/doc-disabled-rules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Document Disabled Rules

Rule `doc-disabled-rules` will enforce that disabled rules have a comment immediately preceding the disabled rule.

## Examples

When enabled, the following are disallowed.

```scss
// sass-lint:disable-all
p {
border: none;
}
// sass-lint:enable-all

// sass-lint:disable border-zero
a {
border: none;
}

li {
// sass-lint:disable-block border-zero
border: none;
}

dd {
border: none; // sass-lint:disable-line border-zero
}
```

When enabled, the following are allowed.

```scss
// Paragraphs are special ponies
// sass-lint:disable-all
p {
border: none;
}
// sass-lint:enable-all

// We really prefer `border: none` in this file, for reasons.
// sass-lint:disable border-zero
a {
border: none;
}

li {
// We really prefer `border: none` in this file, for reasons.
// sass-lint:disable-block border-zero
border: none;
}

dd {
// We really prefer `border: none` in this file, for reasons.
border: none; // sass-lint:disable-line border-zero
}
```
10 changes: 8 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ var slConfig = require('./lib/config'),
groot = require('./lib/groot'),
helpers = require('./lib/helpers'),
slRules = require('./lib/rules'),
ruleToggler = require('./lib/ruleToggler'),
glob = require('glob'),
path = require('path'),
jsonFormatter = require('eslint/lib/formatters/json'),
fs = require('fs-extra');

var getToggledRules = ruleToggler.getToggledRules,
isResultEnabled = ruleToggler.isResultEnabled;

var sassLint = function (config) {
config = require('./lib/config')(config);
Expand Down Expand Up @@ -37,11 +40,14 @@ sassLint.lintText = function (file, options, configPath) {
detects,
results = [],
errors = 0,
warnings = 0;
warnings = 0,
ruleToggles = getToggledRules(ast),
isEnabledFilter = isResultEnabled(ruleToggles);

if (ast.content.length > 0) {
rules.forEach(function (rule) {
detects = rule.rule.detect(ast, rule);
detects = rule.rule.detect(ast, rule)
.filter(isEnabledFilter);
results = results.concat(detects);
if (detects.length) {
if (rule.severity === 1) {
Expand Down
151 changes: 151 additions & 0 deletions lib/ruleToggler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
'use strict';

var addDisable = function (toggledRules, rules, line, column) {
rules.map(function (rule) {
toggledRules.ruleEnable[rule] = toggledRules.ruleEnable[rule] || [];
toggledRules.ruleEnable[rule].push([false, line, column]);
});
};

var addEnable = function (toggledRules, rules, line, column) {
rules.map(function (rule) {
toggledRules.ruleEnable[rule] = toggledRules.ruleEnable[rule] || [];
toggledRules.ruleEnable[rule].push([true, line, column]);
});
};

var addDisableBlock = function (toggledRules, rules, block) {
rules.map(function (rule) {
toggledRules.ruleEnable[rule] = toggledRules.ruleEnable[rule] || [];
toggledRules.ruleEnable[rule].push([false, block.start.line, block.start.column]);
toggledRules.ruleEnable[rule].push([true, block.end.line, block.end.column]);
});
};

var addDisableAll = function (toggledRules, line, column) {
toggledRules.globalEnable
.push([false, line, column]);
};

var addEnableAll = function (toggledRules, line, column) {
toggledRules.globalEnable
.push([true, line, column]);
};

var addDisableLine = function (toggledRules, rules, line) {
rules.map(function (rule) {
toggledRules.ruleEnable[rule] = toggledRules.ruleEnable[rule] || [];
// NOTE: corner case not handled here: a 2nd disable inside an ignored line, which is unrealistically pathological.
toggledRules.ruleEnable[rule].push([false, line, 1]);
toggledRules.ruleEnable[rule].push([true, line + 1, 1]);
});
};

var sortRange = function (toggleRangeA, toggleRangeB) {
var aLine = toggleRangeA[1],
aCol = toggleRangeA[2],
bLine = toggleRangeB[1],
bCol = toggleRangeA[2];
if (aLine < bLine) {
return -1;
}
if (bLine < aLine) {
return 1;
}
if (aCol < bCol) {
return -1;
}
if (bCol < aCol) {
return 1;
}
return 0;
};

module.exports.getToggledRules = function (ast) {
var toggledRules = {
ruleEnable: {
// Format in here is [isDisabled, line, column]
},
globalEnable: []
};
if (!ast.traverseByTypes) {
return toggledRules;
}
ast.traverseByTypes(['multilineComment', 'singlelineComment'], function (comment, i, parent) {
var content = comment.content;
if (!content) {
return;
}
var tokens = content.split(/[\s,]+/)
.filter(function (s) {
return s.trim().length > 0;
});
if (!tokens.length) {
return;
}
var first = tokens[0],
rules = tokens.slice(1);
switch (first) {
case 'sass-lint:disable':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This indentation isn't flagging up? Looks odd to me but if it's ok with eslint

addDisable(toggledRules, rules, comment.start.line, comment.start.column);
break;
case 'sass-lint:enable':
addEnable(toggledRules, rules, comment.start.line, comment.start.column);
break;
case 'sass-lint:disable-block':
// TODO: not sure what the appropriate behavior is if there is no parent block; currently NPEs
addDisableBlock(toggledRules, rules, parent);
break;
case 'sass-lint:disable-all':
addDisableAll(toggledRules, comment.start.line, comment.start.column);
break;
case 'sass-lint:enable-all':
addEnableAll(toggledRules, comment.start.line, comment.start.column);
break;
case 'sass-lint:disable-line':
addDisableLine(toggledRules, rules, comment.start.line);
break;
default:
return;
}
});
// Sort these toggle stacks so reading them is easier (algorithmically).
toggledRules.globalEnable.sort(sortRange);
Object.keys(toggledRules.ruleEnable).map(function (ruleId) {
toggledRules.ruleEnable[ruleId].sort(sortRange);
});
return toggledRules;
};

module.exports.isResultEnabled = function (toggledRules) {
return function (ruleResult) {
var ruleId = ruleResult.ruleId;
// Convention: if no column or line, assume rule is targetting 1.
var line = ruleResult.line || 1;
var column = ruleResult.column || 1;
var isGloballyEnabled = toggledRules.globalEnable
.reduce(function (acc, toggleRange) {
if (line <= toggleRange[1] && column <= toggleRange[2]) {
return acc;
}
return toggleRange[0];
}, true);
if (!isGloballyEnabled) {
return false;
}
if (!toggledRules.ruleEnable[ruleId]) {
return true;
}
var isRuleEnabled = toggledRules.ruleEnable[ruleId]
.reduce(function (acc, toggleRange) {
if (line <= toggleRange[1] && column <= toggleRange[2]) {
return acc;
}
return toggleRange[0];
}, true);
if (!isRuleEnabled) {
return false;
}
return true;
};
};
Loading