Skip to content

Commit

Permalink
Merge pull request #829 from sasstools/release/1.9.0
Browse files Browse the repository at this point in the history
Release/1.9.0
  • Loading branch information
DanPurdy committed Aug 18, 2016
2 parents 2ca0b3b + 9db9bea commit 38d43ad
Show file tree
Hide file tree
Showing 22 changed files with 615 additions and 148 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,25 @@
# Sass Lint Changelog

## v1.9.0

**August 18, 2016**

**Fixes**
* Fixed an issue with teh indentation rule when it encountered at-rules with no block immediately preceeding a map [#779](https://github.com/sasstools/sass-lint/issues/779) [#783](https://github.com/sasstools/sass-lint/issues/783)
* Fixed an issue in `single-lint-per-selector` where inline comments were seen as selectors [#789](https://github.com/sasstools/sass-lint/issues/789)
* Fixed an issue with interpolation in placeholders within the `bem-depth` rule [#782](https://github.com/sasstools/sass-lint/issues/782)
* Removed duplicated code from `no-mergeable-selectors` to helper methods

**Documentation**
* Fixed typos in no-vendor-prefixes rule documentation [#787](https://github.com/sasstools/sass-lint/issues/787)
* Added link to Visual Studio extension [#815](https://github.com/sasstools/sass-lint/pull/815)

**New Rules**
* Added the `no-color-hex` rule to disallow all hexadecimal colour definitions [#754](https://github.com/sasstools/sass-lint/issues/754)

**Updates**
* Gonzales-pe updated to version 3.4.4 which fixes a lot of longstanding issues see the [Changelog](https://github.com/tonyganch/gonzales-pe/blob/dev/CHANGELOG.md)

## v1.8.2

**June 23, 2016**
Expand Down
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -194,3 +194,4 @@ Our AST is [Gonzales-PE](https://github.com/tonyganch/gonzales-pe/tree/dev). Eac
* [Sublime Text](https://github.com/skovhus/SublimeLinter-contrib-sass-lint)
* [Brackets](https://github.com/petetnt/brackets-sass-lint)
* [IntelliJ IDEA, RubyMine, WebStorm, PhpStorm, PyCharm](https://github.com/idok/sass-lint-plugin)
* [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=glen-84.sass-lint)
33 changes: 33 additions & 0 deletions docs/rules/no-color-hex.md
@@ -0,0 +1,33 @@
# No Color Hex

Rule `no-color-hex` will disallow the use of hexadecimal colors

## Examples

When enabled the following are disallowed.

```scss
$foo-color: #456;

.bar {
background: linear-gradient(top, #3ff, #ddd);
}

.baz {
color: #fff;
}
```

When enabled the following are allowed:

```scss
$foo-color: red;

.bar {
background: linear-gradient(top, blue, green);
}

.baz {
color: white;
}
```
16 changes: 8 additions & 8 deletions docs/rules/no-vendor-prefixes.md
Expand Up @@ -41,14 +41,14 @@ When enabled, the following are disallowed:

### Additional Identifiers

When `additional-identifiers` contains a custom prefix value of `test` as show below
When `additional-identifiers` contains a custom prefix value of `khtml` as show below

```yaml
no-vendor-prefix:
no-vendor-prefixes:
- 1
-
'additional-identifiers':
- 'khtml'
additional-identifiers:
- khtml
```

The following would now also be disallowed
Expand All @@ -64,12 +64,12 @@ The following would now also be disallowed
When `excluded-identifiers` contains currently disallowed prefix values such as `webkit` and `moz` as show below

```yaml
no-vendor-prefix:
no-vendor-prefixes:
- 1
-
'excluded-identifiers':
- 'webkit'
- 'moz'
excluded-identifiers:
- webkit
- moz
```

The following would now be allowed
Expand Down
1 change: 1 addition & 0 deletions lib/config/sass-lint.yml
Expand Up @@ -18,6 +18,7 @@ rules:

# Disallows
no-attribute-selectors: 0
no-color-hex: 0
no-color-keywords: 1
no-color-literals: 1
no-combinators: 0
Expand Down
26 changes: 14 additions & 12 deletions lib/rules/bem-depth.js
@@ -1,6 +1,7 @@
'use strict';

var helpers = require('../helpers');
var selectorHelpers = require('../selector-helpers');

/**
* Get number of BEM elements in
Expand Down Expand Up @@ -32,18 +33,19 @@ module.exports = {
maxDepth = parser.options['max-depth'];

if (node.is('placeholder')) {
name = node.first('ident') && node.first('ident').content;
depth = bemDepth(name);

if (name && depth > maxDepth) {
result = helpers.addUnique(result, {
'ruleId': parser.rule.name,
'line': node.start.line,
'column': node.start.column,
'message': ['Placeholder \'%', name, '\' should have ', maxDepth, ' or fewer BEM elements, but ',
depth, ' were found.'].join(''),
'severity': parser.severity
});
name = selectorHelpers.constructSelector(node);
if (name) {
depth = bemDepth(name);
if (depth > maxDepth) {
result = helpers.addUnique(result, {
'ruleId': parser.rule.name,
'line': node.start.line,
'column': node.start.column,
'message': ['Placeholder \'%', name, '\' should have ', maxDepth, ' or fewer BEM elements, but ',
depth, ' were found.'].join(''),
'severity': parser.severity
});
}
}
}
else {
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/indentation.js
Expand Up @@ -166,7 +166,7 @@ module.exports = {
mixedWarning = false;
}
// if we're in an atrule make we need to possibly handle multiline arguments
if (n.is('atrule')) {
if (n.is('atrule') && n.contains('block')) {
inAtRule = true;
inBlock = false;
}
Expand Down
22 changes: 22 additions & 0 deletions lib/rules/no-color-hex.js
@@ -0,0 +1,22 @@
'use strict';

var helpers = require('../helpers');

module.exports = {
'name': 'no-color-hex',
'defaults': {},
'detect': function (ast, parser) {
var result = [];

ast.traverseByType('color', function (value) {
result = helpers.addUnique(result, {
'ruleId': parser.rule.name,
'line': value.start.line,
'column': value.start.column,
'message': 'Hexadecimal colors should not be used',
'severity': parser.severity
});
});
return result;
}
};
113 changes: 5 additions & 108 deletions lib/rules/no-mergeable-selectors.js
@@ -1,120 +1,17 @@
'use strict';

var helpers = require('../helpers');
var helpers = require('../helpers'),
selectorHelpers = require('../selector-helpers');

var mergeableNodes = ['atrule', 'include', 'ruleset'],
validAtRules = ['media'],
simpleIdents = ['ident', 'number', 'operator', 'combinator', 'string', 'parentSelector', 'delimiter', 'typeSelector', 'attributeMatch'],
curLevel = 0,
curSelector = [],
parentSelector = [],
selectorList = [],
syntax = '';


/**
* Adds grammar around our content blocks to construct selectors with
* more readable formats.
*
* @param {object} val - The current value node
* @param {string} prefix - The grammar to prefix the value with
* @param {string} suffix - The grammar to add after the value
* @returns {string} The correct readable format
*/
var addGrammar = function (val, prefix, suffix) {
return prefix + val.content + suffix;
};

/**
* Adds grammar around our content blocks to construct selectors with
* more readable formats and loops the content as they're within sub blocks.
*
* @param {object} val - The current value node
* @param {string} prefix - The grammar to prefix the value with
* @param {string} suffix - The grammar to add after the value
* @param {function} constructSelector - The callback we wish to use which means constructSelector in this instance
* @returns {string} The correct readable format
*/
var constructSubSelector = function (val, prefix, suffix, constructSelector) {
var content = prefix;
val.forEach(function (subItem) {
content += constructSelector(subItem);
});

return content + suffix;
};

/**
* Constructs a syntax complete selector for our selector matching and warning output
*
* @param {object} val - The current node / part of our selector
* @returns {string} - Content: The current node with correct syntax e.g. class my-class = '.my-class'
*/
var constructSelector = function (val) {
var content = null;

if (val.is('id')) {
content = addGrammar(val, '#', '');
}

else if (val.is('class')) {
content = addGrammar(val, '.', '');
}

else if (simpleIdents.indexOf(val.type) !== -1) {
content = val.content;
}

else if (val.is('attributeSelector')) {
content = constructSubSelector(val, '[', ']', constructSelector);
}

else if (val.is('atkeyword')) {
content = constructSubSelector(val, '@', '', constructSelector);
}

else if (val.is('placeholder')) {
content = constructSubSelector(val, '%', '', constructSelector);
}

else if (val.is('variable')) {
content = constructSubSelector(val, '$', '', constructSelector);
}

else if (val.is('pseudoClass')) {
content = addGrammar(val, ':', '');
}

else if (val.is('pseudoElement')) {
content = addGrammar(val, '::', '');
}

else if (val.is('nth')) {
content = addGrammar(val, '(', ')');
}

else if (val.is('nthSelector')) {
content = constructSubSelector(val, ':', '', constructSelector);
}

else if (val.is('parentheses')) {
content = constructSubSelector(val, '(', ')', constructSelector);
}

else if (val.is('space')) {
content = ' ';
}

else if (val.is('parentSelectorExtension') || val.is('attributeName') || val.is('attributeValue') || val.is('dimension')) {
content = constructSubSelector(val, '', '', constructSelector);
}

else if (val.is('interpolation')) {
content = constructSubSelector(val, '#{', '}', constructSelector);
}
return content;
};

/**
* Traverses a block and calls our callback function for each block encountered
*
Expand Down Expand Up @@ -161,11 +58,11 @@ var checkRuleset = function (ruleNode) {
if (!ruleNodeItem.is('block')) {
if (ruleNodeItem.is('selector')) {
ruleNodeItem.forEach(function (selectorContent) {
ruleSet += constructSelector(selectorContent);
ruleSet += selectorHelpers.constructSelector(selectorContent);
});
}
else if (ruleNodeItem.is('delimiter') || ruleNodeItem.is('space')) {
ruleSet += constructSelector(ruleNodeItem);
ruleSet += selectorHelpers.constructSelector(ruleNodeItem);
}
}
});
Expand All @@ -184,7 +81,7 @@ var checkAtRule = function (atRule) {
var test = '';
atRule.forEach(function (atRuleItem) {
if (!atRuleItem.is('block')) {
test += constructSelector(atRuleItem);
test += selectorHelpers.constructSelector(atRuleItem);
}
});
updateList(test, true, atRule.start.line, atRule.start.column);
Expand Down
47 changes: 33 additions & 14 deletions lib/rules/single-line-per-selector.js
Expand Up @@ -2,6 +2,31 @@

var helpers = require('../helpers');

/**
* Checks a ruleset for selectors or EOL characters. If a selector is found before an EOL
* then it returns the selector node for reporting or returns false
*
* @param {Object} ruleset - The ruleset node
* @param {number} index - The current index of the delimiter
* @returns {Object|boolean} Either the selector node or false
*/
var checkLineForSelector = function (ruleset, index) {
var curIndex = index += 1;
if (ruleset.content[curIndex]) {
for (; curIndex < ruleset.content.length; curIndex++) {
var curType = ruleset.content[curIndex].type;
if (curType === 'space' && helpers.hasEOL(ruleset.content[curIndex])) {
return false;
}
if (curType === 'selector' || curType === 'typeSelector') {
return ruleset.content[curIndex];
}
}
}

return false;
};

module.exports = {
'name': 'single-line-per-selector',
'defaults': {},
Expand All @@ -10,22 +35,16 @@ module.exports = {

ast.traverseByType('ruleset', function (ruleset) {
ruleset.forEach('delimiter', function (delimiter, j) {
var next = ruleset.content[j + 1] || false;
var next = checkLineForSelector(ruleset, j);

if (next) {
if (next.is('selector')) {
next = next.content[0];
}

if (!(next.is('space') && helpers.hasEOL(next.content))) {
result = helpers.addUnique(result, {
'ruleId': parser.rule.name,
'line': next.start.line,
'column': next.start.column,
'message': 'Selectors must be placed on new lines',
'severity': parser.severity
});
}
result = helpers.addUnique(result, {
'ruleId': parser.rule.name,
'line': next.start.line,
'column': next.start.column,
'message': 'Selectors must be placed on new lines',
'severity': parser.severity
});
}
});
});
Expand Down

0 comments on commit 38d43ad

Please sign in to comment.