From 338d381b9c6a5bfb97e169492887b75495e06c01 Mon Sep 17 00:00:00 2001 From: Jakub Skala Date: Thu, 28 Jan 2016 13:18:29 +0100 Subject: [PATCH 001/137] Correct link to Gonzales-PE node types. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 04cda20f..b25d3e4e 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Sass Lint [`v1.1.0`](https://github.com/sasstools/sass-lint/releases/tag/v1.1.0) ## Creating Rules -Our AST is [Gonzales-PE](https://github.com/tonyganch/gonzales-pe/tree/dev). Each rule will be passed the full AST which they can traverse as they please. There are many different [node types](https://github.com/tonyganch/gonzales-pe/blob/dev/doc/node-types.md) that may be traversed, and an extensive [API for working with nodes](https://github.com/tonyganch/gonzales-pe/tree/dev#api). The file of the rule must have the same name as the name of the rule. All of the available rules are in our [rules directory](https://github.com/sasstools/sass-lint/tree/develop/lib/rules). Default options will be merged in with user config. +Our AST is [Gonzales-PE](https://github.com/tonyganch/gonzales-pe/tree/dev). Each rule will be passed the full AST which they can traverse as they please. There are many different [node types](https://github.com/tonyganch/gonzales-pe/blob/dev/docs/node-types.md) that may be traversed, and an extensive [API for working with nodes](https://github.com/tonyganch/gonzales-pe/tree/dev#api). The file of the rule must have the same name as the name of the rule. All of the available rules are in our [rules directory](https://github.com/sasstools/sass-lint/tree/develop/lib/rules). Default options will be merged in with user config. ## Task Runner Integration From 9b5f45d6f96370e6c70bfbd1809e649606fcd081 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sun, 31 Jan 2016 18:40:53 +0000 Subject: [PATCH 002/137] Refactor rule to work with gonzales v3.2.1 --- lib/rules/brace-style.js | 280 +++++++++++++++++++++++---------------- 1 file changed, 167 insertions(+), 113 deletions(-) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index adf638f5..f13e6308 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -2,39 +2,54 @@ var helpers = require('../helpers'); +/** + * Determine if statement is a single line statement + * + * @param {Object} node - The statement to check + * @returns {bool} True or false + */ var isSingleLineStatement = function (node) { return node.start.line === node.end.line; }; -var createPreviousNode = function (node) { - return { - content: '', - start: { - line: node.start.line, - column: node.start.column - 1 - }, - end: { - line: node.end.line, - column: node.end.column - } - }; +/** + * Determine if opening brace of statement is on a new line + * + * @param {Object} nodeA - The previous block + * @param {Object} nodeB - The current block + * @returns {bool} True or false + */ +var isOpeningBraceOnNewLine = function (nodeA, nodeB) { + return nodeA.end.line === nodeB.start.line; }; -var getElsePrevious = function (atKeyword, parent, i) { - if (atKeyword.contains('ident')) { - if (atKeyword.first('ident').content === 'else') { - var prev = parent.get(i - 1); - - if (prev.is('space')) { - return prev; - } - else if (prev.is('atruleb')) { - return createPreviousNode(atKeyword.first('ident')); - } +/** + * Determine if condition starts on a new line by checking the leading node for + * an end-of-line + * + * @param {Object} node - The node that is our condition + * @param {Object} parentNode - The condition node's parent + * @param {Number} j - The index of our node in the context of the parent's children + * @returns {bool|null} True or false if relevant else null + */ +var isConditionOnNewLine = function (node, parentNode, j) { + // Only check if it's an @else condition + if (node.contains('ident') && node.first('ident').content === 'else') { + // Reverse back up tree + let previousChild = parentNode.get(--j); + + // Determine if we have a leading new line + if (previousChild.is('space') && helpers.hasEOL(previousChild.content)) { + return true; } + return false; } + return; }; +/** + * Rule exports + */ module.exports = { 'name': 'brace-style', 'defaults': { @@ -44,116 +59,155 @@ module.exports = { 'detect': function (ast, parser) { var result = []; - ast.traverseByTypes(['conditionalStatement', 'atruleb', 'selector', 'mixin', 'loop'], function (block, i, parent) { - var next = false, - previous = false, - isSingleLine = false; - - // Store leading space node for @elseif statements - if (block.is('atruleb')) { - if (block.contains('atkeyword')) { - previous = getElsePrevious(block.first('atkeyword'), parent, i); - } + ast.traverseByTypes(['conditionalStatement', 'atrule', 'ruleset', 'mixin', 'loop'], function (block, i, parent) { + var currentNode = false, + previousNode = false, + rules = { + singleLineStatement: null, + openingBraceOnNewLine: null, + conditionOnNewLine: null, + }, + messages = [ + 'Single line statements are not allowed', + 'Opening brace must be on same line as condition', + 'Brace must be on a new line', + 'Statement must start on same line as closing brace of previous statement', + 'Statement must begin on a new line' + ]; + + /** + * Store nodes that we will use to run rules against + */ + currentNode = block.contains('block') ? block.first('block') : false; + + // Rulesets + if (block.is('ruleset')) { + previousNode = block.contains('selector') ? block.last('selector') : false; } - // Store leading space node for @else statements + // Conditonal statements if (block.is('conditionalStatement')) { - if (block.contains('condition')) { - var condition = block.first('condition'); + let previousParent = block.contains('condition') ? block.last('condition') : false; + previousNode = previousParent.contains('atkeyword') ? previousParent.last('atkeyword') : false; + } - if (condition.contains('atkeyword')) { - previous = getElsePrevious(condition.first('atkeyword'), parent, i); - } + // Functions, Mixins, Loops + if (block.is('atrule') || block.is('mixin') || block.is('loop')) { + previousNode = block.contains('atkeyword') ? block.last('atkeyword') : false; + } + + /** + * Node checks - If we've picked up a return @rule ignore it + */ + if (block.is('atrule')) { + if (previousNode.contains('ident') && previousNode.first('ident').content === 'return') { + return false; } } - // Store trailing space node for conditional statements, mixins and loops - if (block.is('atruleb') || block.is('conditionalStatement') || block.is('mixin') || block.is('loop')) { - // Since a node of type block should be the last node within 'block', - // go back 2 to determine if there is a newline - next = block.content[block.content.length - 2]; + /** + * The rule checks + */ - // Determine if single line statement - isSingleLine = isSingleLineStatement(block); - } + // Determine if single line statement + rules.singleLineStatement = isSingleLineStatement(block); - // Store trailing space node for selectors - if (block.is('selector')) { - if (block.contains('simpleSelector')) { - var lastSelector = block.last('simpleSelector'); + // Determine if condition is on a new line + if (block.is('atrule') || block.is('conditionalStatement')) { + rules.conditionOnNewLine = isConditionOnNewLine(previousNode, parent, i); + } - next = lastSelector.content[lastSelector.content.length - 1]; - } + if (previousNode && currentNode) { + // Determine if opening brace is on new line + rules.openingBraceOnNewLine = isOpeningBraceOnNewLine(previousNode, currentNode); + } - // Determine if single line statement - if (parent.is('ruleset')) { - isSingleLine = isSingleLineStatement(parent); + /** + * Build results + */ + if (rules.singleLineStatement) { + if (parser.options['allow-single-line'] === false) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': block.start.line, + 'column': block.start.column, + 'message': messages[0], + 'severity': parser.severity + }); } + return false; } - if ((parser.options['allow-single-line'] === false) && isSingleLine) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': block.start.line, - 'column': block.start.column, - 'message': 'Single line statements are not allowed', - 'severity': parser.severity - }); - } - else { - - if (next) { - if (!helpers.hasEOL(next.content)) { - if (parser.options.style === 'allman') { - // Report if it's not single line statement - if (!((parser.options['allow-single-line'] === true) && isSingleLine)) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': next.start.line, - 'column': next.start.column, - 'message': 'Brace must be on a new line', - 'severity': parser.severity - }); - } - } + if (previousNode && currentNode) { + /** + * Brace style: 1tbs + */ + if (parser.options.style === '1tbs') { + if (rules.openingBraceOnNewLine === false) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': currentNode.start.line, + 'column': currentNode.start.column, + 'message': messages[1], + 'severity': parser.severity + }); } - - if (helpers.hasEOL(next.content)) { - if (parser.options.style === '1tbs' || parser.options.style === 'stroustrup') { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': next.start.line, - 'column': next.start.column, - 'message': 'Brace must be on same line as condition', - 'severity': parser.severity - }); - } + if (rules.conditionOnNewLine === true) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': previousNode.start.line, + 'column': previousNode.start.column, + 'message': messages[3], + 'severity': parser.severity + }); } } - if (previous) { - if (!helpers.hasEOL(previous.content)) { - if (parser.options.style === 'allman' || parser.options.style === 'stroustrup') { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': previous.start.line, - 'column': previous.start.column, - 'message': 'Statement should begin on a new line', - 'severity': parser.severity - }); - } + /** + * Brace style: stroustrup + */ + if (parser.options.style === 'stroustrup') { + if (rules.openingBraceOnNewLine === false) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': currentNode.start.line, + 'column': currentNode.start.column, + 'message': messages[1], + 'severity': parser.severity + }); + } + if (rules.conditionOnNewLine === false) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': previousNode.start.line, + 'column': previousNode.start.column, + 'message': messages[4], + 'severity': parser.severity + }); } + } - if (helpers.hasEOL(previous.content)) { - if (parser.options.style === '1tbs') { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': previous.start.line, - 'column': previous.start.column, - 'message': 'Statement on next line should begin on the current line', - 'severity': parser.severity - }); - } + /** + * Brace style: allman + */ + if (parser.options.style === 'allman') { + if (rules.openingBraceOnNewLine === true) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': currentNode.start.line, + 'column': currentNode.start.column, + 'message': messages[2], + 'severity': parser.severity + }); + } + if (rules.conditionOnNewLine === false) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': previousNode.start.line, + 'column': previousNode.start.column, + 'message': messages[4], + 'severity': parser.severity + }); } } } From ba131739855662129a5fb8cc56573cb4b7ad889e Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sun, 31 Jan 2016 18:41:13 +0000 Subject: [PATCH 003/137] Update tests and desired number of warnings --- tests/rules/brace-style.js | 6 +++--- tests/sass/brace-style.scss | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/rules/brace-style.js b/tests/rules/brace-style.js index 04f188e8..9e9d91f9 100644 --- a/tests/rules/brace-style.js +++ b/tests/rules/brace-style.js @@ -12,7 +12,7 @@ describe('brace style - scss', function () { lint.test(file, { 'brace-style': 1 }, function (data) { - lint.assert.equal(35, data.warningCount); + lint.assert.equal(32, data.warningCount); done(); }); }); @@ -42,7 +42,7 @@ describe('brace style - scss', function () { } ] }, function (data) { - lint.assert.equal(39, data.warningCount); + lint.assert.equal(36, data.warningCount); done(); }); }); @@ -72,7 +72,7 @@ describe('brace style - scss', function () { } ] }, function (data) { - lint.assert.equal(72, data.warningCount); + lint.assert.equal(69, data.warningCount); done(); }); }); diff --git a/tests/sass/brace-style.scss b/tests/sass/brace-style.scss index cd65c3c4..f744c032 100644 --- a/tests/sass/brace-style.scss +++ b/tests/sass/brace-style.scss @@ -292,7 +292,6 @@ h2 - @if($foo) { $bar: 'foo'; @@ -419,7 +418,6 @@ $total: 4; @if ($foo) { $bar: 'foo'; } @else { $bar: false; } @if ($foo) { $bar: 'foo'; } @else if { $bar: 'bar'; } @else { $bar: false; } - @if ($foo) { $bar: 'foo'; } @else { $bar: false; } From 50067104d3b2a84c57180f5322a69675a7ac5e4e Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sun, 31 Jan 2016 19:13:30 +0000 Subject: [PATCH 004/137] :art: Add check for closing brace --- lib/rules/brace-style.js | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index f13e6308..685137c9 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -23,6 +23,19 @@ var isOpeningBraceOnNewLine = function (nodeA, nodeB) { return nodeA.end.line === nodeB.start.line; }; +var isClosingBraceOnNewLine = function (node) { + if (node.contains('block')) { + let content = node.first('block'), + contentLength = content.length - 1, + lastNode = content.get(contentLength); + + if (lastNode.is('space') && helpers.hasEOL(lastNode.content)) { + return true; + } + return false; + } +} + /** * Determine if condition starts on a new line by checking the leading node for * an end-of-line @@ -65,6 +78,7 @@ module.exports = { rules = { singleLineStatement: null, openingBraceOnNewLine: null, + closingBraceOnNewLine: null, conditionOnNewLine: null, }, messages = [ @@ -72,7 +86,8 @@ module.exports = { 'Opening brace must be on same line as condition', 'Brace must be on a new line', 'Statement must start on same line as closing brace of previous statement', - 'Statement must begin on a new line' + 'Statement must begin on a new line', + 'Closing brace must be on a new line' ]; /** @@ -117,15 +132,29 @@ module.exports = { rules.conditionOnNewLine = isConditionOnNewLine(previousNode, parent, i); } + // Determine if opening brace is on new line if (previousNode && currentNode) { - // Determine if opening brace is on new line rules.openingBraceOnNewLine = isOpeningBraceOnNewLine(previousNode, currentNode); } + // Determine if closing brace is on new line + rules.closingBraceOnNewLine = isClosingBraceOnNewLine(block); + /** * Build results */ - if (rules.singleLineStatement) { + + if (rules.singleLineStatement === false && rules.closingBraceOnNewLine === false) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': currentNode.end.line, + 'column': currentNode.end.column, + 'message': messages[5], + 'severity': parser.severity + }); + } + + if (rules.singleLineStatement === true) { if (parser.options['allow-single-line'] === false) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, @@ -138,6 +167,7 @@ module.exports = { return false; } + if (previousNode && currentNode) { /** * Brace style: 1tbs From 24de508d1d75f43c482cd2a12172bd766e1f7ab3 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sun, 31 Jan 2016 19:13:51 +0000 Subject: [PATCH 005/137] :white_check_mark: Add tests to check for closing brace on new line --- tests/rules/brace-style.js | 12 ++++++------ tests/sass/brace-style.scss | 22 +++++++++++++++++++++- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/tests/rules/brace-style.js b/tests/rules/brace-style.js index 9e9d91f9..ac2a86ac 100644 --- a/tests/rules/brace-style.js +++ b/tests/rules/brace-style.js @@ -12,7 +12,7 @@ describe('brace style - scss', function () { lint.test(file, { 'brace-style': 1 }, function (data) { - lint.assert.equal(32, data.warningCount); + lint.assert.equal(36, data.warningCount); done(); }); }); @@ -27,7 +27,7 @@ describe('brace style - scss', function () { } ] }, function (data) { - lint.assert.equal(54, data.warningCount); + lint.assert.equal(58, data.warningCount); done(); }); }); @@ -42,7 +42,7 @@ describe('brace style - scss', function () { } ] }, function (data) { - lint.assert.equal(36, data.warningCount); + lint.assert.equal(40, data.warningCount); done(); }); }); @@ -57,7 +57,7 @@ describe('brace style - scss', function () { } ] }, function (data) { - lint.assert.equal(58, data.warningCount); + lint.assert.equal(62, data.warningCount); done(); }); }); @@ -72,7 +72,7 @@ describe('brace style - scss', function () { } ] }, function (data) { - lint.assert.equal(69, data.warningCount); + lint.assert.equal(77, data.warningCount); done(); }); }); @@ -87,7 +87,7 @@ describe('brace style - scss', function () { } ] }, function (data) { - lint.assert.equal(91, data.warningCount); + lint.assert.equal(99, data.warningCount); done(); }); }); diff --git a/tests/sass/brace-style.scss b/tests/sass/brace-style.scss index f744c032..2ab8d1f7 100644 --- a/tests/sass/brace-style.scss +++ b/tests/sass/brace-style.scss @@ -444,10 +444,30 @@ $total: 4; -// No parens +// ======================= +// No paren tests +// ======================= @if $foo { $bar: 'foo'; } @if $foo { $bar: 'foo'; } + + + +// ======================= +// Closing brace tests +// ======================= + +.foo { + content: 'foo'; } + +@if($foo) { + $bar: 'foo'; } + +@function foo() { + @return 'foo'; } + +@mixin bar() { + content: 'bar'; } From 84de9c0948beea5ca23d16142318850d806218d2 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sun, 31 Jan 2016 19:19:02 +0000 Subject: [PATCH 006/137] :art: Correct eslint issues --- lib/rules/brace-style.js | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index 685137c9..cd350ad2 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -25,7 +25,7 @@ var isOpeningBraceOnNewLine = function (nodeA, nodeB) { var isClosingBraceOnNewLine = function (node) { if (node.contains('block')) { - let content = node.first('block'), + var content = node.first('block'), contentLength = content.length - 1, lastNode = content.get(contentLength); @@ -34,7 +34,7 @@ var isClosingBraceOnNewLine = function (node) { } return false; } -} +}; /** * Determine if condition starts on a new line by checking the leading node for @@ -49,7 +49,7 @@ var isConditionOnNewLine = function (node, parentNode, j) { // Only check if it's an @else condition if (node.contains('ident') && node.first('ident').content === 'else') { // Reverse back up tree - let previousChild = parentNode.get(--j); + var previousChild = parentNode.get(--j); // Determine if we have a leading new line if (previousChild.is('space') && helpers.hasEOL(previousChild.content)) { @@ -57,7 +57,7 @@ var isConditionOnNewLine = function (node, parentNode, j) { } return false; } - return; + return null; }; /** @@ -75,11 +75,11 @@ module.exports = { ast.traverseByTypes(['conditionalStatement', 'atrule', 'ruleset', 'mixin', 'loop'], function (block, i, parent) { var currentNode = false, previousNode = false, - rules = { + checks = { singleLineStatement: null, openingBraceOnNewLine: null, closingBraceOnNewLine: null, - conditionOnNewLine: null, + conditionOnNewLine: null }, messages = [ 'Single line statements are not allowed', @@ -102,8 +102,8 @@ module.exports = { // Conditonal statements if (block.is('conditionalStatement')) { - let previousParent = block.contains('condition') ? block.last('condition') : false; - previousNode = previousParent.contains('atkeyword') ? previousParent.last('atkeyword') : false; + var previousParent = block.contains('condition') ? block.last('condition') : false; + previousNode = previousParent && previousParent.contains('atkeyword') ? previousParent.last('atkeyword') : false; } // Functions, Mixins, Loops @@ -125,26 +125,26 @@ module.exports = { */ // Determine if single line statement - rules.singleLineStatement = isSingleLineStatement(block); + checks.singleLineStatement = isSingleLineStatement(block); // Determine if condition is on a new line if (block.is('atrule') || block.is('conditionalStatement')) { - rules.conditionOnNewLine = isConditionOnNewLine(previousNode, parent, i); + checks.conditionOnNewLine = isConditionOnNewLine(previousNode, parent, i); } // Determine if opening brace is on new line if (previousNode && currentNode) { - rules.openingBraceOnNewLine = isOpeningBraceOnNewLine(previousNode, currentNode); + checks.openingBraceOnNewLine = isOpeningBraceOnNewLine(previousNode, currentNode); } // Determine if closing brace is on new line - rules.closingBraceOnNewLine = isClosingBraceOnNewLine(block); + checks.closingBraceOnNewLine = isClosingBraceOnNewLine(block); /** * Build results */ - if (rules.singleLineStatement === false && rules.closingBraceOnNewLine === false) { + if (checks.singleLineStatement === false && checks.closingBraceOnNewLine === false) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': currentNode.end.line, @@ -154,7 +154,7 @@ module.exports = { }); } - if (rules.singleLineStatement === true) { + if (checks.singleLineStatement === true) { if (parser.options['allow-single-line'] === false) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, @@ -173,7 +173,7 @@ module.exports = { * Brace style: 1tbs */ if (parser.options.style === '1tbs') { - if (rules.openingBraceOnNewLine === false) { + if (checks.openingBraceOnNewLine === false) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': currentNode.start.line, @@ -182,7 +182,7 @@ module.exports = { 'severity': parser.severity }); } - if (rules.conditionOnNewLine === true) { + if (checks.conditionOnNewLine === true) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': previousNode.start.line, @@ -197,7 +197,7 @@ module.exports = { * Brace style: stroustrup */ if (parser.options.style === 'stroustrup') { - if (rules.openingBraceOnNewLine === false) { + if (checks.openingBraceOnNewLine === false) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': currentNode.start.line, @@ -206,7 +206,7 @@ module.exports = { 'severity': parser.severity }); } - if (rules.conditionOnNewLine === false) { + if (checks.conditionOnNewLine === false) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': previousNode.start.line, @@ -221,7 +221,7 @@ module.exports = { * Brace style: allman */ if (parser.options.style === 'allman') { - if (rules.openingBraceOnNewLine === true) { + if (checks.openingBraceOnNewLine === true) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': currentNode.start.line, @@ -230,7 +230,7 @@ module.exports = { 'severity': parser.severity }); } - if (rules.conditionOnNewLine === false) { + if (checks.conditionOnNewLine === false) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': previousNode.start.line, From 5244954cb08a40da0b49f704b613922141deb2a6 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Mon, 1 Feb 2016 10:47:53 +0000 Subject: [PATCH 007/137] :memo: Add jsdoc for closing brace function --- lib/rules/brace-style.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index cd350ad2..f281a643 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -23,6 +23,12 @@ var isOpeningBraceOnNewLine = function (nodeA, nodeB) { return nodeA.end.line === nodeB.start.line; }; +/** + * Determine if closing brace of statement is on new line + * + * @param {Object} node - The current block + * @returns {bool|null} True or false if relevant else null + */ var isClosingBraceOnNewLine = function (node) { if (node.contains('block')) { var content = node.first('block'), @@ -34,6 +40,7 @@ var isClosingBraceOnNewLine = function (node) { } return false; } + return null; }; /** From 75a3af9b6209060cd245f8b2ac37866ba88d4e3b Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Mon, 1 Feb 2016 22:03:23 +0000 Subject: [PATCH 008/137] :bug: Replace deprecated node type simpleSelector with selector --- lib/rules/placeholder-in-extend.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/placeholder-in-extend.js b/lib/rules/placeholder-in-extend.js index 4fbdc407..a082bf01 100644 --- a/lib/rules/placeholder-in-extend.js +++ b/lib/rules/placeholder-in-extend.js @@ -12,7 +12,7 @@ module.exports = { keyword.forEach(function (item) { if (item.content === 'extend') { - parent.forEach('simpleSelector', function (selector) { + parent.forEach('selector', function (selector) { var placeholder = false; selector.content.forEach(function (selectorPiece) { From 27a6aa1e099464fcd631e44a1169087887595f7e Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Mon, 1 Feb 2016 22:58:46 +0000 Subject: [PATCH 009/137] :art: Refactor space-after-bang rule to work with gonzales-3.2.1 --- lib/rules/space-after-bang.js | 43 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/lib/rules/space-after-bang.js b/lib/rules/space-after-bang.js index 7c8dc039..4fb68a21 100644 --- a/lib/rules/space-after-bang.js +++ b/lib/rules/space-after-bang.js @@ -8,31 +8,30 @@ module.exports = { 'include': false }, 'detect': function (ast, parser) { - var result = []; + var result = [], + regex = /!\s/; ast.traverseByTypes(['important', 'default'], function (block) { - block.traverse(function (item) { - if (item.type === 'space') { - if (parser.options.include) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': block.start.line, - 'column': block.start.column + 1, - 'message': 'Bangs (!) should be followed by a space', - 'severity': parser.severity - }); - } - else { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': block.start.line, - 'column': block.start.column, - 'message': 'Bangs (!) should not be followed by a space', - 'severity': parser.severity - }); - } + if (block.content.match(regex) !== null) { + if (parser.options.include) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': block.start.line, + 'column': block.start.column + 1, + 'message': 'Bangs (!) should be followed by a space', + 'severity': parser.severity + }); } - }); + else { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': block.start.line, + 'column': block.start.column, + 'message': 'Bangs (!) should not be followed by a space', + 'severity': parser.severity + }); + } + } }); return result; From 46bd24994ae6b47d3e1a953727c507fea6f6d17f Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Mon, 1 Feb 2016 23:33:45 +0000 Subject: [PATCH 010/137] :art: Refactor rule to work with gonzales 3.2.1 --- lib/rules/space-after-comma.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/rules/space-after-comma.js b/lib/rules/space-after-comma.js index 3143dafb..6ac16ffc 100644 --- a/lib/rules/space-after-comma.js +++ b/lib/rules/space-after-comma.js @@ -11,18 +11,25 @@ module.exports = { var result = []; ast.traverseByTypes(['operator', 'delimiter'], function (operator, i, parent) { - var next; + var next, + doubleNext; if (operator.content === ',') { - next = parent.content[i + 1]; + next = parent.content[i + 1] || false; + doubleNext = parent.content[i + 2] || false; if (next) { if (operator.is('delimiter')) { - if (next.is('simpleSelector')) { + if (next.is('selector')) { next = next.content[0]; } } + if ((next.is('space') && !helpers.hasEOL(next.content)) && !parser.options.include) { + if (doubleNext && doubleNext.is('singlelineComment')) { + return false; + } + result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': next.start.line, @@ -31,6 +38,7 @@ module.exports = { 'severity': parser.severity }); } + if (!next.is('space') && parser.options.include) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, From 13f47666e6c27dddeaa243d702e374917e536b6d Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Wed, 3 Feb 2016 18:23:01 +0000 Subject: [PATCH 011/137] Update gonzales dependancy to latest release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a30407f8..7de87ab1 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "eslint": "^1.1.0", "fs-extra": "^0.26.0", "glob": "^6.0.0", - "gonzales-pe": "3.0.0-31", + "gonzales-pe": "^3.2.1", "js-yaml": "^3.2.6", "lodash.capitalize": "^3.0.0", "lodash.kebabcase": "^3.0.1", From b3ebd5d2c0f88bf394989639bc1dc0adc2f47862 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Wed, 3 Feb 2016 22:38:52 +0000 Subject: [PATCH 012/137] Replace comment style for continutity --- lib/rules/brace-style.js | 46 +++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index f281a643..126691c1 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -67,9 +67,6 @@ var isConditionOnNewLine = function (node, parentNode, j) { return null; }; -/** - * Rule exports - */ module.exports = { 'name': 'brace-style', 'defaults': { @@ -97,9 +94,9 @@ module.exports = { 'Closing brace must be on a new line' ]; - /** - * Store nodes that we will use to run rules against - */ + ////////////////////////////// + // Assign current & previous nodes + ////////////////////////////// currentNode = block.contains('block') ? block.first('block') : false; // Rulesets @@ -118,18 +115,16 @@ module.exports = { previousNode = block.contains('atkeyword') ? block.last('atkeyword') : false; } - /** - * Node checks - If we've picked up a return @rule ignore it - */ + // If we've picked up a return @rule ignore it if (block.is('atrule')) { if (previousNode.contains('ident') && previousNode.first('ident').content === 'return') { return false; } } - /** - * The rule checks - */ + ////////////////////////////// + // The rule checks + ////////////////////////////// // Determine if single line statement checks.singleLineStatement = isSingleLineStatement(block); @@ -147,10 +142,9 @@ module.exports = { // Determine if closing brace is on new line checks.closingBraceOnNewLine = isClosingBraceOnNewLine(block); - /** - * Build results - */ - + ////////////////////////////// + // Build results + ////////////////////////////// if (checks.singleLineStatement === false && checks.closingBraceOnNewLine === false) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, @@ -174,11 +168,11 @@ module.exports = { return false; } - if (previousNode && currentNode) { - /** - * Brace style: 1tbs - */ + + ////////////////////////////// + // Brace style: 1tbs + ////////////////////////////// if (parser.options.style === '1tbs') { if (checks.openingBraceOnNewLine === false) { result = helpers.addUnique(result, { @@ -200,9 +194,9 @@ module.exports = { } } - /** - * Brace style: stroustrup - */ + ////////////////////////////// + // Brace style: stroustrup + ////////////////////////////// if (parser.options.style === 'stroustrup') { if (checks.openingBraceOnNewLine === false) { result = helpers.addUnique(result, { @@ -224,9 +218,9 @@ module.exports = { } } - /** - * Brace style: allman - */ + ////////////////////////////// + // Brace style: allman + ////////////////////////////// if (parser.options.style === 'allman') { if (checks.openingBraceOnNewLine === true) { result = helpers.addUnique(result, { From 07813ff529f014526d31260c2a5df1e8338a0e26 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Thu, 4 Feb 2016 23:29:41 +0000 Subject: [PATCH 013/137] :art: Refactor space-around-operator rule to work with Gonzales 3.2 --- lib/rules/space-around-operator.js | 19 +++++++++++++++++-- tests/rules/space-around-operator.js | 8 ++++---- tests/sass/space-around-operator.sass | 10 ++++------ tests/sass/space-around-operator.scss | 22 ++++++++++------------ 4 files changed, 35 insertions(+), 24 deletions(-) diff --git a/lib/rules/space-around-operator.js b/lib/rules/space-around-operator.js index f01813b1..0b2de5d2 100644 --- a/lib/rules/space-around-operator.js +++ b/lib/rules/space-around-operator.js @@ -4,6 +4,12 @@ var helpers = require('../helpers'); var operators = ['+', '-', '/', '*', '%', '<', '>', '==', '!=', '<=', '>=']; +/** + * Determine a relational operator based on the operator node + * + * @param {Object} node - The operator node + * @returns {string} Returns a relational operator + */ var getRelationalOperator = function (node) { if (node.content === '<') { return '<='; @@ -14,6 +20,16 @@ var getRelationalOperator = function (node) { } }; +/** + * Check the spacing around an operator + * + * @param {Object} node - The node to check the spacing around + * @param {int} i - The node's child index of it's parent + * @param {Object} parent - The parent node + * @param {Object} parser - The parser object + * @param {Object} result - The result object + * @returns {bool|null} false if exception + */ var checkSpacing = function (node, i, parent, parser, result) { if (node.is('operator') || node.is('unaryOperator')) { var previous = parent.content[i - 1] || false, @@ -131,8 +147,7 @@ module.exports = { 'detect': function (ast, parser) { var result = []; - // FIXME: Gonzales v3 - No longer need atrulerq (for Sass) - ast.traverseByTypes(['condition', 'atruleb', 'value', 'atrulerq'], function (node) { + ast.traverseByTypes(['condition', 'atrule', 'value'], function (node) { node.forEach(function (item, i, parent) { // Perform another loop of the children if we come across a parenthesis // parent node diff --git a/tests/rules/space-around-operator.js b/tests/rules/space-around-operator.js index bd493a56..957c1599 100644 --- a/tests/rules/space-around-operator.js +++ b/tests/rules/space-around-operator.js @@ -12,7 +12,7 @@ describe('space around operator - scss', function () { lint.test(file, { 'space-around-operator': 1 }, function (data) { - lint.assert.equal(84, data.warningCount); + lint.assert.equal(91, data.warningCount); done(); }); }); @@ -26,7 +26,7 @@ describe('space around operator - scss', function () { } ] }, function (data) { - lint.assert.equal(78, data.warningCount); + lint.assert.equal(90, data.warningCount); done(); }); }); @@ -42,7 +42,7 @@ describe('space around operator - sass', function () { lint.test(file, { 'space-around-operator': 1 }, function (data) { - lint.assert.equal(84, data.warningCount); + lint.assert.equal(87, data.warningCount); done(); }); }); @@ -56,7 +56,7 @@ describe('space around operator - sass', function () { } ] }, function (data) { - lint.assert.equal(78, data.warningCount); + lint.assert.equal(84, data.warningCount); done(); }); }); diff --git a/tests/sass/space-around-operator.sass b/tests/sass/space-around-operator.sass index 2fbc4ebd..17d232b3 100644 --- a/tests/sass/space-around-operator.sass +++ b/tests/sass/space-around-operator.sass @@ -66,7 +66,7 @@ $qux: 2 +1 $foo: (1+1) $bar: (2-1) $baz: (1/2) -// $qux: (2*1) FIXME: Gonzales - FIXED IN BETA +$qux: (2*1) // $norf: (5%2) Not valid SCSS. @@ -75,7 +75,7 @@ $baz: (1/2) $foo: (1 +1) $bar: (2 -1) $baz: (1 /2) -// $qux: (2 *1) FIXME: Gonzales - FIXED IN BETA +$qux: (2 *1) // $norf: (5 %2) FIXME: Gonzales - FIXED IN BETA @@ -84,7 +84,7 @@ $baz: (1 /2) $foo: (1+ 1) $bar: (2- 1) $baz: (1/ 2) -// $qux: (2* 1) FIXME: Gonzales - FIXED IN BETA +$qux: (2* 1) // $norf: (5% 2) Not valid SCSS. Parses as 5 percent. @@ -396,9 +396,7 @@ $qux: 2 * 1 $foo: (1 + 1) $bar: (2 - 1) $baz: (1 / 2) -// Include: false will ignore this, so count will be one down on what it should -// be -$qux: (2 * 1) // FIXME: Gonzales - FIXED IN BETA +$qux: (2 * 1) // $norf: (5 % 2) FIXME: Gonzales - FIXED IN BETA // Space with no parens diff --git a/tests/sass/space-around-operator.scss b/tests/sass/space-around-operator.scss index 1fe75a39..47cae5f4 100644 --- a/tests/sass/space-around-operator.scss +++ b/tests/sass/space-around-operator.scss @@ -48,7 +48,7 @@ $foo: 1 +1; $bar: 2 -1; $baz: 1 /2; $qux: 2 *1; -// $norf: 5 %2; FIXME: Gonzales - FIXED IN BETA +$norf: 5 %2; // No space before @@ -66,14 +66,14 @@ $foo: 1 + 1; $bar: 2 + 1; $baz: 1 + 2; $qux: 2 +1; -// $norf: 5 % 2; FIXME: Gonzales - FIXED IN BETA +$norf: 5 % 2; // No space with parens $foo: (1+1); $bar: (2-1); $baz: (1/2); -// $qux: (2*1); FIXME: Gonzales - FIXED IN BETA +$qux: (2*1); // $norf: (5%2); Not valid SCSS. @@ -82,8 +82,8 @@ $baz: (1/2); $foo: (1 +1); $bar: (2 -1); $baz: (1 /2); -// $qux: (2 *1); FIXME: Gonzales - FIXED IN BETA -// $norf: (5 %2); FIXME: Gonzales - FIXED IN BETA +$qux: (2 *1); +$norf: (5 %2); // No space before with parens @@ -91,7 +91,7 @@ $baz: (1 /2); $foo: (1+ 1); $bar: (2- 1); $baz: (1/ 2); -// $qux: (2* 1); FIXME: Gonzales - FIXED IN BETA +$qux: (2* 1); // $norf: (5% 2); Not valid SCSS. Parses as 5 percent. @@ -101,7 +101,7 @@ $foo: (1 + 1); $bar: (2 + 1); $baz: (1 + 2); $qux: (2 +1); -// $norf: (5 % 2); FIXME: Gonzales - FIXED IN BETA +$norf: (5 % 2); @@ -405,17 +405,15 @@ $foo: 1 + 1; $bar: 2 - 1; $baz: 1 / 2; $qux: 2 * 1; -// $norf: 5 % 2; FIXME: Gonzales - FIXED IN BETA +$norf: 5 % 2; // Values with parens $foo: (1 + 1); $bar: (2 - 1); $baz: (1 / 2); -// Include: false will ignore this, so count will be one down on what it should -// be -$qux: (2 * 1); // FIXME: Gonzales - FIXED IN BETA -// $norf: (5 % 2); FIXME: Gonzales - FIXED IN BETA +$qux: (2 * 1); +$norf: (5 % 2); // Space with no parens From 3837ebeabcb22ab8cf6706bb24e900d0040f978c Mon Sep 17 00:00:00 2001 From: Daniel Tschinder Date: Fri, 5 Feb 2016 14:18:11 +0100 Subject: [PATCH 014/137] :bug: Fix space-before-brace to work with gonzales 3.2 node.last() changed from returning undefined to null. As typeof null === object the check was failing. The new check is now accounting for all falsy values, and as false i captured in a separate check the logic is still the same. --- lib/rules/space-before-brace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/space-before-brace.js b/lib/rules/space-before-brace.js index bdcff459..cdf0b096 100644 --- a/lib/rules/space-before-brace.js +++ b/lib/rules/space-before-brace.js @@ -7,7 +7,7 @@ var getLastWhitespace = function (node) { return null; } - if (typeof node !== 'object') { + if (!node) { return false; } if (node.is('space')) { From 36290d0beb430a29bf8f704fbd1bc6bc6edce06d Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Fri, 5 Feb 2016 15:18:24 +0000 Subject: [PATCH 015/137] :art: Refactor code to further improve readability --- lib/rules/brace-style.js | 276 ++++++++++++++++++++------------------- 1 file changed, 141 insertions(+), 135 deletions(-) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index 126691c1..6e5215ca 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -2,6 +2,58 @@ var helpers = require('../helpers'); +/** + * Get the current block within a node + * + * @param {Object} node - The node containing our desired block + * @returns {Object} The current block of the node + */ +var getCurrentNode = function (node) { + return node.contains('block') ? node.first('block') : false; +}; + +/** + * Get the previous node + * + * @param {Object} node - Our current node + * @returns {Object|bool} The previous node or false if not found + */ +var getPreviousNode = function (node) { + // Rulesets + if (node.is('ruleset')) { + return node.contains('selector') ? node.last('selector') : false; + } + + // Conditonal statements + if (node.is('conditionalStatement')) { + var previousParent = node.contains('condition') ? node.last('condition') : false; + return previousParent && previousParent.contains('atkeyword') ? previousParent.last('atkeyword') : false; + } + + // Functions, Mixins, Loops + if (node.is('atrule') || node.is('mixin') || node.is('loop')) { + return node.contains('atkeyword') ? node.last('atkeyword') : false; + } +}; + +/** + * Determine if current node is an exception and end checks if it is + * + * @param {Object} node - The original node + * @param {object} currentNode - The current node block + * @param {Object} previousNode - The node previous to our current node + * @returns {bool} Wether or not the it is an exception + */ +var isException = function (node, currentNode, previousNode) { + // If we've picked up a return @rule ignore it + if (node.is('atrule')) { + if (previousNode.contains('ident') && previousNode.first('ident').content === 'return') { + return true; + } + } + return false; +}; + /** * Determine if statement is a single line statement * @@ -67,6 +119,56 @@ var isConditionOnNewLine = function (node, parentNode, j) { return null; }; +/** + * Run the rule checks and store return their results + * + * @param {Object} node - The original node + * @param {Object} currentNode - The current node block + * @param {Object} previousNode - The node previous to our current node + * @param {Object} parentNode - The parent of the original node + * @param {int} i - The index of the original node + * @returns {Object} The results of the rule checks + */ +var runRuleChecks = function (node, currentNode, previousNode, parentNode, i) { + var checks = {}; + + // Determine if single line statement + checks.singleLineStatement = isSingleLineStatement(node); + + // Determine if condition is on a new line + if (node.is('atrule') || node.is('conditionalStatement')) { + checks.conditionOnNewLine = isConditionOnNewLine(previousNode, parentNode, i); + } + + // Determine if opening brace is on new line + if (previousNode && currentNode) { + checks.openingBraceOnNewLine = isOpeningBraceOnNewLine(previousNode, currentNode); + } + + // Determine if closing brace is on new line + checks.closingBraceOnNewLine = isClosingBraceOnNewLine(node); + + return checks; +}; + +/** + * Create an issue using the supplied information + * + * @param {Object} parser - The parser + * @param {Object} node - The node with the issue + * @param {string} message - The message to display + * @returns {Object} An object containing an issue + */ +var createIssue = function (parser, node, message) { + return { + 'ruleId': parser.rule.name, + 'line': node.end.line, + 'column': node.end.column, + 'message': message, + 'severity': parser.severity + }; +}; + module.exports = { 'name': 'brace-style', 'defaults': { @@ -76,7 +178,7 @@ module.exports = { 'detect': function (ast, parser) { var result = []; - ast.traverseByTypes(['conditionalStatement', 'atrule', 'ruleset', 'mixin', 'loop'], function (block, i, parent) { + ast.traverseByTypes(['conditionalStatement', 'atrule', 'ruleset', 'mixin', 'loop'], function (node, i, parent) { var currentNode = false, previousNode = false, checks = { @@ -94,151 +196,55 @@ module.exports = { 'Closing brace must be on a new line' ]; - ////////////////////////////// - // Assign current & previous nodes - ////////////////////////////// - currentNode = block.contains('block') ? block.first('block') : false; - - // Rulesets - if (block.is('ruleset')) { - previousNode = block.contains('selector') ? block.last('selector') : false; - } + // Assign current & previous nodes based on node type + currentNode = getCurrentNode(node); + previousNode = getPreviousNode(node); - // Conditonal statements - if (block.is('conditionalStatement')) { - var previousParent = block.contains('condition') ? block.last('condition') : false; - previousNode = previousParent && previousParent.contains('atkeyword') ? previousParent.last('atkeyword') : false; - } + // If not an exception carry on + if (!isException(node, currentNode, previousNode)) { - // Functions, Mixins, Loops - if (block.is('atrule') || block.is('mixin') || block.is('loop')) { - previousNode = block.contains('atkeyword') ? block.last('atkeyword') : false; - } + // Run and store rule check results + checks = runRuleChecks(node, currentNode, previousNode, parent, i); - // If we've picked up a return @rule ignore it - if (block.is('atrule')) { - if (previousNode.contains('ident') && previousNode.first('ident').content === 'return') { - return false; + // Build single-line statement results + if (checks.singleLineStatement === false && checks.closingBraceOnNewLine === false) { + result = helpers.addUnique(result, createIssue(parser, currentNode, messages[5])); } - } - - ////////////////////////////// - // The rule checks - ////////////////////////////// - // Determine if single line statement - checks.singleLineStatement = isSingleLineStatement(block); - - // Determine if condition is on a new line - if (block.is('atrule') || block.is('conditionalStatement')) { - checks.conditionOnNewLine = isConditionOnNewLine(previousNode, parent, i); - } - - // Determine if opening brace is on new line - if (previousNode && currentNode) { - checks.openingBraceOnNewLine = isOpeningBraceOnNewLine(previousNode, currentNode); - } - - // Determine if closing brace is on new line - checks.closingBraceOnNewLine = isClosingBraceOnNewLine(block); - - ////////////////////////////// - // Build results - ////////////////////////////// - if (checks.singleLineStatement === false && checks.closingBraceOnNewLine === false) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': currentNode.end.line, - 'column': currentNode.end.column, - 'message': messages[5], - 'severity': parser.severity - }); - } - - if (checks.singleLineStatement === true) { - if (parser.options['allow-single-line'] === false) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': block.start.line, - 'column': block.start.column, - 'message': messages[0], - 'severity': parser.severity - }); - } - return false; - } - - if (previousNode && currentNode) { - - ////////////////////////////// - // Brace style: 1tbs - ////////////////////////////// - if (parser.options.style === '1tbs') { - if (checks.openingBraceOnNewLine === false) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': currentNode.start.line, - 'column': currentNode.start.column, - 'message': messages[1], - 'severity': parser.severity - }); - } - if (checks.conditionOnNewLine === true) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': previousNode.start.line, - 'column': previousNode.start.column, - 'message': messages[3], - 'severity': parser.severity - }); + if (checks.singleLineStatement === true) { + if (parser.options['allow-single-line'] === false) { + result = helpers.addUnique(result, createIssue(parser, node, messages[0])); } + return false; } - ////////////////////////////// - // Brace style: stroustrup - ////////////////////////////// - if (parser.options.style === 'stroustrup') { - if (checks.openingBraceOnNewLine === false) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': currentNode.start.line, - 'column': currentNode.start.column, - 'message': messages[1], - 'severity': parser.severity - }); + // Build brace-style results + if (previousNode && currentNode) { + if (parser.options.style === '1tbs') { + if (checks.openingBraceOnNewLine === false) { + result = helpers.addUnique(result, createIssue(parser, currentNode, messages[1])); + } + if (checks.conditionOnNewLine === true) { + result = helpers.addUnique(result, createIssue(parser, previousNode, messages[3])); + } } - if (checks.conditionOnNewLine === false) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': previousNode.start.line, - 'column': previousNode.start.column, - 'message': messages[4], - 'severity': parser.severity - }); - } - } - ////////////////////////////// - // Brace style: allman - ////////////////////////////// - if (parser.options.style === 'allman') { - if (checks.openingBraceOnNewLine === true) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': currentNode.start.line, - 'column': currentNode.start.column, - 'message': messages[2], - 'severity': parser.severity - }); + if (parser.options.style === 'stroustrup') { + if (checks.openingBraceOnNewLine === false) { + result = helpers.addUnique(result, createIssue(parser, currentNode, messages[1])); + } + if (checks.conditionOnNewLine === false) { + result = helpers.addUnique(result, createIssue(parser, previousNode, messages[4])); + } } - if (checks.conditionOnNewLine === false) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': previousNode.start.line, - 'column': previousNode.start.column, - 'message': messages[4], - 'severity': parser.severity - }); + + if (parser.options.style === 'allman') { + if (checks.openingBraceOnNewLine === true) { + result = helpers.addUnique(result, createIssue(parser, currentNode, messages[2])); + } + if (checks.conditionOnNewLine === false) { + result = helpers.addUnique(result, createIssue(parser, previousNode, messages[4])); + } } } } From a1c312492296eb5e563676e4892920bb06a8b669 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Fri, 5 Feb 2016 22:59:13 +0000 Subject: [PATCH 016/137] :art: Refactor extends-before-mixins rule to work with Gonzales 3.2 --- lib/rules/extends-before-mixins.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/rules/extends-before-mixins.js b/lib/rules/extends-before-mixins.js index 5f565fec..6aff2593 100644 --- a/lib/rules/extends-before-mixins.js +++ b/lib/rules/extends-before-mixins.js @@ -12,11 +12,13 @@ module.exports = { var lastMixin = null; block.forEach(function (item, j) { - if (item.type === 'include' || item.type === 'extend') { - if (item.first('atkeyword')) { + // TODO: Remove tempory fix - atrule type is work around for issue: + // https://github.com/tonyganch/gonzales-pe/issues/147 + if (item.is('include') || item.is('extend') || item.is('atrule')) { + if (item.contains('atkeyword')) { var atkeyword = item.first('atkeyword'); - if (atkeyword.first('ident')) { + if (atkeyword.contains('ident')) { var ident = atkeyword.first('ident'); if (ident.content === 'extend') { @@ -34,7 +36,7 @@ module.exports = { } } - if (item.type === 'include') { + if (item.is('include')) { lastMixin = j; } }); From 3e75cb89a48edc94ee806b91a9f711f8cf5d79af Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Fri, 5 Feb 2016 23:41:04 +0000 Subject: [PATCH 017/137] :art: Refactor extends-before-declarations rule to work with Gonzales 3.2 --- lib/rules/extends-before-declarations.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/rules/extends-before-declarations.js b/lib/rules/extends-before-declarations.js index 8597be7e..c920b744 100644 --- a/lib/rules/extends-before-declarations.js +++ b/lib/rules/extends-before-declarations.js @@ -13,25 +13,25 @@ module.exports = { var lastDeclaration = null; block.forEach(function (item, j) { - if ((item.type === 'include' || item.type === 'extend') && + // TODO: Remove tempory fix - atrule type is work around for issue: + // https://github.com/tonyganch/gonzales-pe/issues/147 + if ((item.is('include') || item.is('extend') || item.is('atrule')) && item.first('atkeyword')) { if (item.first('atkeyword').first('ident').content === 'extend') { if (j > lastDeclaration && lastDeclaration !== null) { - item.forEach('simpleSelector', function () { - error = { - 'ruleId': parser.rule.name, - 'line': item.start.line, - 'column': item.start.column, - 'message': 'Extends should come before declarations', - 'severity': parser.severity - }; - result = helpers.addUnique(result, error); - }); + error = { + 'ruleId': parser.rule.name, + 'line': item.start.line, + 'column': item.start.column, + 'message': 'Extends should come before declarations', + 'severity': parser.severity + }; + result = helpers.addUnique(result, error); } } } - if (item.type === 'declaration') { + if (item.is('declaration')) { lastDeclaration = j; } }); From b4594fd2d8811dfac076a499c5d61542add28201 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sat, 6 Feb 2016 00:06:03 +0000 Subject: [PATCH 018/137] :art: Refactor single-line-per-selector (SCSS) to work with Gonzales 3.2 --- lib/rules/single-line-per-selector.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rules/single-line-per-selector.js b/lib/rules/single-line-per-selector.js index 0f7a9ab6..9ba9c7ca 100644 --- a/lib/rules/single-line-per-selector.js +++ b/lib/rules/single-line-per-selector.js @@ -8,12 +8,12 @@ module.exports = { 'detect': function (ast, parser) { var result = []; - ast.traverseByType('selector', function (selector) { - selector.forEach('delimiter', function (delimiter, i) { - var next = selector.content[i + 1]; + ast.traverseByType('ruleset', function (ruleset) { + ruleset.forEach('delimiter', function (delimiter, j) { + var next = ruleset.content[j + 1] || false; if (next) { - if (next.is('simpleSelector')) { + if (next.is('selector')) { next = next.content[0]; } From 4b9021ca66708e72e6f9ba4bbf2049ae5598f647 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sat, 6 Feb 2016 00:45:58 +0000 Subject: [PATCH 019/137] :art: Refcator no-qualifying-elements rule to work with Gonzales 3.2 --- lib/rules/no-qualifying-elements.js | 45 +++++++++++++++++++---------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/lib/rules/no-qualifying-elements.js b/lib/rules/no-qualifying-elements.js index 0973434b..117efc94 100644 --- a/lib/rules/no-qualifying-elements.js +++ b/lib/rules/no-qualifying-elements.js @@ -12,21 +12,36 @@ module.exports = { 'detect': function (ast, parser) { var result = []; - ast.traverseByType('simpleSelector', function (selectors) { - - selectors.content.forEach(function (item, i) { - if (item.is('class') || item.is('attribute') || item.is('id')) { - var previous = selectors.content[i - 1] || false; - - if (previous && previous.is('ident')) { - if (!parser.options['allow-element-with-' + item.type]) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': item.start.line, - 'column': item.start.column, - 'message': 'Qualifying elements are not allowed for ' + item.type + ' selectors', - 'severity': parser.severity - }); + ast.traverseByType('selector', function (selector) { + selector.forEach(function (item, i) { + if (item.is('attributeSelector') || item.is('class') || item.is('id')) { + var previous = selector.content[i - 1] || false; + + if (previous && previous.is('typeSelector')) { + if (previous.contains('ident')) { + var type = null; + + if (item.is('attributeSelector')) { + type = 'attribute'; + } + + if (item.is('class')) { + type = 'class'; + } + + if (item.is('id')) { + type = 'id'; + } + + if (type && !parser.options['allow-element-with-' + type]) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': item.start.line, + 'column': item.start.column, + 'message': 'Qualifying elements are not allowed for ' + type + ' selectors', + 'severity': parser.severity + }); + } } } } From c7bed0352704673f78a53bf20a1a213d46939be6 Mon Sep 17 00:00:00 2001 From: Daniel Tschinder Date: Sat, 6 Feb 2016 14:52:08 +0100 Subject: [PATCH 020/137] :bug: Correct tests for empty-line-between-blocks rule The tests were working, but the tests are wrong as there are 10 instead of 9 errors in the sass file. Now the tests correctly fail. --- tests/rules/empty-line-between-blocks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/rules/empty-line-between-blocks.js b/tests/rules/empty-line-between-blocks.js index ca6a0157..f3d54ecc 100644 --- a/tests/rules/empty-line-between-blocks.js +++ b/tests/rules/empty-line-between-blocks.js @@ -208,7 +208,7 @@ describe('empty line between blocks - sass', function () { lint.test(file, { 'empty-line-between-blocks': 1 }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(10, data.warningCount); done(); }); }); @@ -225,7 +225,7 @@ describe('empty line between blocks - sass', function () { } ] }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(10, data.warningCount); done(); }); }); From 141fce3a98302ba9e47d810e705384bb0e016ea3 Mon Sep 17 00:00:00 2001 From: Daniel Tschinder Date: Sat, 6 Feb 2016 14:55:23 +0100 Subject: [PATCH 021/137] :bug: Fix empty-line-between-blocks rule for gonzales 3.2 The checks were assuming that a ruleset always looks like this: [ selector, space, block ]. In newer versions of gonzales they can look like this: [ selector, delimiter, selector, space, block ]. --- lib/rules/empty-line-between-blocks.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/rules/empty-line-between-blocks.js b/lib/rules/empty-line-between-blocks.js index 3c3682d1..04711f53 100644 --- a/lib/rules/empty-line-between-blocks.js +++ b/lib/rules/empty-line-between-blocks.js @@ -163,14 +163,7 @@ module.exports = { // If it's a new line, lets go back up to the selector if (previous.is('space') && helpers.hasEOL(previous.content)) { - - // If we have a node (most likely type of selector) - if (parent.content[i - 2]) { - - if (typeof parent.content[i - 3] === 'undefined') { - space = findNearestReturnSass(p, j); - } - } + space = findNearestReturnSass(p, j); } }); } From 8360e4786d878b20407c55d8289d739f7ef8dfee Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sat, 6 Feb 2016 14:17:53 +0000 Subject: [PATCH 022/137] :memo: Correct travis badge to show master build --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 18d29f94..62ca0fff 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Sass Lint [![npm version](https://badge.fury.io/js/sass-lint.svg)](http://badge.fury.io/js/sass-lint) [![Build Status](https://travis-ci.org/sasstools/sass-lint.svg)](https://travis-ci.org/sasstools/sass-lint) [![Coverage Status](https://coveralls.io/repos/sasstools/sass-lint/badge.svg?branch=develop&service=github)](https://coveralls.io/github/sasstools/sass-lint?branch=develop) [![Dependency Status](https://david-dm.org/sasstools/sass-lint.svg)](https://david-dm.org/sasstools/sass-lint#info=dependencies&view=list) [![Dev Dependency Status](https://david-dm.org/sasstools/sass-lint/dev-status.svg)](https://david-dm.org/sasstools/sass-lint#info=devDependencies&view=list) +# Sass Lint [![npm version](https://badge.fury.io/js/sass-lint.svg)](http://badge.fury.io/js/sass-lint) [![Build Status](https://travis-ci.org/sasstools/sass-lint.svg?branch=master)](https://travis-ci.org/sasstools/sass-lint) [![Coverage Status](https://coveralls.io/repos/sasstools/sass-lint/badge.svg?branch=develop&service=github)](https://coveralls.io/github/sasstools/sass-lint?branch=develop) [![Dependency Status](https://david-dm.org/sasstools/sass-lint.svg)](https://david-dm.org/sasstools/sass-lint#info=dependencies&view=list) [![Dev Dependency Status](https://david-dm.org/sasstools/sass-lint/dev-status.svg)](https://david-dm.org/sasstools/sass-lint#info=devDependencies&view=list) A Node-only Sass linter for both `sass` and `scss` syntax! From 3805acf5727199520712857f699e6ac0afd2e0c8 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sat, 6 Feb 2016 14:21:58 +0000 Subject: [PATCH 023/137] :memo: Change to develop for continuity --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 62ca0fff..73c0b6b6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Sass Lint [![npm version](https://badge.fury.io/js/sass-lint.svg)](http://badge.fury.io/js/sass-lint) [![Build Status](https://travis-ci.org/sasstools/sass-lint.svg?branch=master)](https://travis-ci.org/sasstools/sass-lint) [![Coverage Status](https://coveralls.io/repos/sasstools/sass-lint/badge.svg?branch=develop&service=github)](https://coveralls.io/github/sasstools/sass-lint?branch=develop) [![Dependency Status](https://david-dm.org/sasstools/sass-lint.svg)](https://david-dm.org/sasstools/sass-lint#info=dependencies&view=list) [![Dev Dependency Status](https://david-dm.org/sasstools/sass-lint/dev-status.svg)](https://david-dm.org/sasstools/sass-lint#info=devDependencies&view=list) +# Sass Lint [![npm version](https://badge.fury.io/js/sass-lint.svg)](http://badge.fury.io/js/sass-lint) [![Build Status](https://travis-ci.org/sasstools/sass-lint.svg?branch=develop)](https://travis-ci.org/sasstools/sass-lint) [![Coverage Status](https://coveralls.io/repos/sasstools/sass-lint/badge.svg?branch=develop&service=github)](https://coveralls.io/github/sasstools/sass-lint?branch=develop) [![Dependency Status](https://david-dm.org/sasstools/sass-lint.svg)](https://david-dm.org/sasstools/sass-lint#info=dependencies&view=list) [![Dev Dependency Status](https://david-dm.org/sasstools/sass-lint/dev-status.svg)](https://david-dm.org/sasstools/sass-lint#info=devDependencies&view=list) A Node-only Sass linter for both `sass` and `scss` syntax! From 867cbc00e986856f8b4bbb7d2660d89fb3fbc49e Mon Sep 17 00:00:00 2001 From: Daniel Tschinder Date: Sat, 6 Feb 2016 23:02:49 +0100 Subject: [PATCH 024/137] :bug: Fix indentation for gonzales 3.2 atruleb has been merged into atrule atrulers was reblaced by block. --- lib/rules/indentation.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/rules/indentation.js b/lib/rules/indentation.js index acbad341..f7c07644 100644 --- a/lib/rules/indentation.js +++ b/lib/rules/indentation.js @@ -82,13 +82,12 @@ module.exports = { } // if a block node is encountered we first check to see if it's within an include/function - // by checking if the node also contains argumentts, if it does we skip the block as we add a level + // by checking if the node also contains arguments, if it does we skip the block as we add a level // for arguments anyway. If not the the block is a usual ruleset block and should be treated accordingly // The other checks are kept from 1.0 and work for their respective types. if ((n.is('block') && !node.contains('arguments')) - || n.is('atrulers') || n.is('arguments') - || (n.is('parentheses') && !node.is('atruleb')) + || (n.is('parentheses') && !node.is('atrule')) ) { level++; } From 3ce555ea58e95c5c1b10eb55bb8dc1c190491203 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sun, 7 Feb 2016 11:48:18 +0000 Subject: [PATCH 025/137] :bug: Add support for global --- lib/rules/space-after-bang.js | 2 +- tests/rules/space-after-bang.js | 8 ++++---- tests/sass/space-after-bang.sass | 17 +++++++++++++---- tests/sass/space-after-bang.scss | 16 +++++++++++++--- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/lib/rules/space-after-bang.js b/lib/rules/space-after-bang.js index 4fb68a21..d6a2b2f1 100644 --- a/lib/rules/space-after-bang.js +++ b/lib/rules/space-after-bang.js @@ -11,7 +11,7 @@ module.exports = { var result = [], regex = /!\s/; - ast.traverseByTypes(['important', 'default'], function (block) { + ast.traverseByTypes(['important', 'default', 'global'], function (block) { if (block.content.match(regex) !== null) { if (parser.options.include) { result = helpers.addUnique(result, { diff --git a/tests/rules/space-after-bang.js b/tests/rules/space-after-bang.js index d4439a0d..ad4cc4c2 100644 --- a/tests/rules/space-after-bang.js +++ b/tests/rules/space-after-bang.js @@ -12,7 +12,7 @@ describe('space after bang - scss', function () { lint.test(file, { 'space-after-bang': 1 }, function (data) { - lint.assert.equal(4, data.warningCount); + lint.assert.equal(6, data.warningCount); done(); }); }); @@ -26,7 +26,7 @@ describe('space after bang - scss', function () { } ] }, function (data) { - lint.assert.equal(4, data.warningCount); + lint.assert.equal(6, data.warningCount); done(); }); }); @@ -42,7 +42,7 @@ describe('space after bang - sass', function () { lint.test(file, { 'space-after-bang': 1 }, function (data) { - lint.assert.equal(4, data.warningCount); + lint.assert.equal(6, data.warningCount); done(); }); }); @@ -56,7 +56,7 @@ describe('space after bang - sass', function () { } ] }, function (data) { - lint.assert.equal(4, data.warningCount); + lint.assert.equal(6, data.warningCount); done(); }); }); diff --git a/tests/sass/space-after-bang.sass b/tests/sass/space-after-bang.sass index 53584bb1..f2250fd8 100644 --- a/tests/sass/space-after-bang.sass +++ b/tests/sass/space-after-bang.sass @@ -20,9 +20,18 @@ $foo: red!default -$foo: orange !default +$bar: orange !default -$foo: blue! default +$baz: blue! default -$foo: green ! default - +$qux: green ! default + +// Global + +$foo: red!global + +$bar: orange !global + +$baz: blue! global + +$qux: green ! global diff --git a/tests/sass/space-after-bang.scss b/tests/sass/space-after-bang.scss index d4c445e7..4fd76e42 100644 --- a/tests/sass/space-after-bang.scss +++ b/tests/sass/space-after-bang.scss @@ -20,8 +20,18 @@ $foo: red!default; -$foo: red !default; +$bar: red !default; -$foo: red! default; +$baz: red! default; -$foo: red ! default; +$qux: red ! default; + +// Global + +$foo: red!global; + +$bar: red !global; + +$baz: red! global; + +$qux: red ! global; From e9aa19f3d1a4e90b4f16b4bf9b1974e8410e2435 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sun, 7 Feb 2016 11:59:57 +0000 Subject: [PATCH 026/137] Add support for optional --- lib/rules/space-after-bang.js | 2 +- tests/rules/space-after-bang.js | 8 ++++---- tests/sass/space-after-bang.sass | 15 +++++++++++++++ tests/sass/space-after-bang.scss | 19 +++++++++++++++++++ 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/lib/rules/space-after-bang.js b/lib/rules/space-after-bang.js index d6a2b2f1..2377c9af 100644 --- a/lib/rules/space-after-bang.js +++ b/lib/rules/space-after-bang.js @@ -11,7 +11,7 @@ module.exports = { var result = [], regex = /!\s/; - ast.traverseByTypes(['important', 'default', 'global'], function (block) { + ast.traverseByTypes(['important', 'default', 'global', 'optional'], function (block) { if (block.content.match(regex) !== null) { if (parser.options.include) { result = helpers.addUnique(result, { diff --git a/tests/rules/space-after-bang.js b/tests/rules/space-after-bang.js index ad4cc4c2..2b340d69 100644 --- a/tests/rules/space-after-bang.js +++ b/tests/rules/space-after-bang.js @@ -12,7 +12,7 @@ describe('space after bang - scss', function () { lint.test(file, { 'space-after-bang': 1 }, function (data) { - lint.assert.equal(6, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -26,7 +26,7 @@ describe('space after bang - scss', function () { } ] }, function (data) { - lint.assert.equal(6, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -42,7 +42,7 @@ describe('space after bang - sass', function () { lint.test(file, { 'space-after-bang': 1 }, function (data) { - lint.assert.equal(6, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -56,7 +56,7 @@ describe('space after bang - sass', function () { } ] }, function (data) { - lint.assert.equal(6, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); diff --git a/tests/sass/space-after-bang.sass b/tests/sass/space-after-bang.sass index f2250fd8..d247d047 100644 --- a/tests/sass/space-after-bang.sass +++ b/tests/sass/space-after-bang.sass @@ -35,3 +35,18 @@ $bar: orange !global $baz: blue! global $qux: green ! global + +// Optional + +.foo + @extend .notice !optional + +.bar + @extend .notice ! optional + +// Both the following are invalid +// .baz +// @extend .notice!optional +// +// .qux +// @extend .notice! optional diff --git a/tests/sass/space-after-bang.scss b/tests/sass/space-after-bang.scss index 4fd76e42..a6d890be 100644 --- a/tests/sass/space-after-bang.scss +++ b/tests/sass/space-after-bang.scss @@ -35,3 +35,22 @@ $bar: red !global; $baz: red! global; $qux: red ! global; + +// Optional + +.foo { + @extend .notice !optional; +} + +.bar { + @extend .notice ! optional; +} + +// Both the following are invalid +// .baz { +// @extend .notice!optional; +// } +// +// .qux { +// @extend .notice! optional; +// } From c1632de25276524666879c249bfe396637e75eec Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Sun, 7 Feb 2016 12:24:27 +0000 Subject: [PATCH 027/137] :arrow_up: Add node version --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 1d2fc89f..e9607977 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: node_js node_js: - '0.10' - '0.12' + - '4.2.x' - node - iojs before_script: npm link From b65b945087f17b14f99b8c79df9867c195955004 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Sun, 7 Feb 2016 12:31:49 +0000 Subject: [PATCH 028/137] Fix version number for travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e9607977..98453264 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: node_js node_js: - '0.10' - '0.12' - - '4.2.x' + - '4.2' - node - iojs before_script: npm link From dd2af7eca03bb1fbfb851c0e71e5209bf569078f Mon Sep 17 00:00:00 2001 From: Daniel Tschinder Date: Sun, 7 Feb 2016 11:38:58 +0100 Subject: [PATCH 029/137] :bug: Fix rule nesting-depth Currently only the scss part is fixed sass throws Fatal. --- lib/rules/nesting-depth.js | 39 +++++++++++++++++------------------ tests/rules/nesting-depth.js | 32 ++++++++++++++++++++++++++-- tests/sass/nesting-depth.sass | 14 +++++-------- tests/sass/nesting-depth.scss | 7 ++++++- 4 files changed, 60 insertions(+), 32 deletions(-) diff --git a/lib/rules/nesting-depth.js b/lib/rules/nesting-depth.js index 58ab62e6..eb944825 100644 --- a/lib/rules/nesting-depth.js +++ b/lib/rules/nesting-depth.js @@ -16,30 +16,29 @@ module.exports = { if (node.contains('block')) { node.forEach('block', function (block) { if (block.contains('ruleset')) { + depth++; block.forEach('ruleset', function (ruleset) { - ruleset.forEach('selector', function (selector) { - depth++; + var selector = ruleset.first('selector'); - if (depth > parser.options['max-depth']) { - selector.forEach('simpleSelector', function (simpleSelector) { - var nodeLineColumn = simpleSelector.start.line + ':' + simpleSelector.start.column; + if (depth > parser.options['max-depth']) { + var nodeLineColumn = selector.start.line + ':' + selector.start.column; - if (nodes[nodeLineColumn]) { - if (depth > nodes[nodeLineColumn].depth) { - nodes[nodeLineColumn].depth = depth; - } - } - else { - nodes[nodeLineColumn] = { - 'line': simpleSelector.start.line, - 'column': simpleSelector.start.column, - 'depth': depth - }; - } - }); + if (nodes[nodeLineColumn]) { + if (depth > nodes[nodeLineColumn].depth) { + nodes[nodeLineColumn].depth = depth; + } } - }); - recursiveSearch(ruleset); + else { + nodes[nodeLineColumn] = { + 'line': selector.start.line, + 'column': selector.start.column, + 'depth': depth + }; + } + } + else { + recursiveSearch(ruleset); + } }); } }); diff --git a/tests/rules/nesting-depth.js b/tests/rules/nesting-depth.js index f4d1fd9c..c848d203 100644 --- a/tests/rules/nesting-depth.js +++ b/tests/rules/nesting-depth.js @@ -12,7 +12,21 @@ describe('nesting depth - scss', function () { lint.test(file, { 'nesting-depth': 1 }, function (data) { - lint.assert.equal(2, data.warningCount); + lint.assert.equal(3, data.warningCount); + done(); + }); + }); + + it('[max-depth: 3]', function (done) { + lint.test(file, { + 'nesting-depth': [ + 1, + { + 'max-depth': 3 + } + ] + }, function (data) { + lint.assert.equal(1, data.warningCount); done(); }); }); @@ -28,7 +42,21 @@ describe('nesting depth - sass', function () { lint.test(file, { 'nesting-depth': 1 }, function (data) { - lint.assert.equal(2, data.warningCount); + lint.assert.equal(3, data.warningCount); + done(); + }); + }); + + it('[max-depth: 3]', function (done) { + lint.test(file, { + 'nesting-depth': [ + 1, + { + 'max-depth': 3 + } + ] + }, function (data) { + lint.assert.equal(1, data.warningCount); done(); }); }); diff --git a/tests/sass/nesting-depth.sass b/tests/sass/nesting-depth.sass index bce42ab0..0bd06de3 100644 --- a/tests/sass/nesting-depth.sass +++ b/tests/sass/nesting-depth.sass @@ -8,16 +8,16 @@ .fail content: 'bob' - &:hover + &:hover, + &:active content: 'fail' + .bar + content: 'fail' + .bar content: 'fail' - - - - .block content: 'bar' @@ -27,7 +27,3 @@ &--modifier content: 'bar' - - - - diff --git a/tests/sass/nesting-depth.scss b/tests/sass/nesting-depth.scss index f05e4d7c..ab2e6795 100644 --- a/tests/sass/nesting-depth.scss +++ b/tests/sass/nesting-depth.scss @@ -8,8 +8,13 @@ .fail { content: 'bob'; - &:hover { + &:hover, + &:active { content: 'fail'; + + .bar { + content: 'fail'; + } } .bar { From 8be9a5952f44c7250e42e4432712204203df9f5b Mon Sep 17 00:00:00 2001 From: greenkeeperio-bot Date: Sun, 7 Feb 2016 21:55:09 -0500 Subject: [PATCH 030/137] chore(package): update lodash.capitalize to version 4.1.0 http://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a30407f8..0a5821a7 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "glob": "^6.0.0", "gonzales-pe": "3.0.0-31", "js-yaml": "^3.2.6", - "lodash.capitalize": "^3.0.0", + "lodash.capitalize": "^4.1.0", "lodash.kebabcase": "^3.0.1", "merge": "^1.2.0", "util": "^0.10.3" From f6088fcb2e19a0ac1f025fcf133b7f6262779b07 Mon Sep 17 00:00:00 2001 From: Ben Rothman Date: Fri, 5 Feb 2016 01:11:19 -0600 Subject: [PATCH 031/137] :bug: Fix collectSuffixExtensions and add tests --- lib/helpers.js | 38 ++++++-------------- tests/helpers.js | 93 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 99 insertions(+), 32 deletions(-) diff --git a/lib/helpers.js b/lib/helpers.js index 71f2e46c..cda8d39c 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -4,16 +4,7 @@ var util = require('util'), fs = require('fs'), path = require('path'), yaml = require('js-yaml'), - merge = require('merge'); - -/** - * Easy access to the 'merge' library's cloning functionality - * @param {object} obj Object to clone - * @returns {object} Clone of obj - */ -var clone = function (obj) { - return merge(true, obj); -}; + gonzales = require('gonzales-pe'); var helpers = {}; @@ -407,7 +398,7 @@ helpers.attemptTraversal = function (node, traversalPath) { * (without '.', '#', etc) resulting from suffix extensions */ helpers.collectSuffixExtensions = function (ruleset, selectorType) { - var parentSelectors = helpers.attemptTraversal(ruleset, ['selector', 'simpleSelector', selectorType, 'ident']), + var parentSelectors = helpers.attemptTraversal(ruleset, ['selector', selectorType, 'ident']), childSuffixes = helpers.attemptTraversal(ruleset, ['block', 'ruleset']), selectorList = []; @@ -419,25 +410,18 @@ helpers.collectSuffixExtensions = function (ruleset, selectorType) { // extended, so lots of looping is required. var processChildSuffix = function (child, parents) { var currentParents = [], - selectors = helpers.attemptTraversal(child, ['selector', 'simpleSelector']), + selectors = helpers.attemptTraversal(child, ['selector', 'parentSelectorExtension', 'ident']), nestedChildSuffixes = helpers.attemptTraversal(child, ['block', 'ruleset']); selectors.forEach(function (childSuffixNode) { - var extendedNode; - - if (childSuffixNode.length >= 2 && - childSuffixNode.contains('parentSelector') && - childSuffixNode.contains('ident')) { - - // append suffix extension to all parent selectors - parents.forEach(function (parent) { - // clone so we don't modify the actual AST - extendedNode = clone(childSuffixNode.first('ident')); - extendedNode.content = parent.content + extendedNode.content; - - currentParents.push(extendedNode); - }); - } + // append suffix extension to all parent selectors + parents.forEach(function (parent) { + // clone so we don't modify the actual AST + var clonedChildSuffixNode = gonzales.createNode(childSuffixNode); + clonedChildSuffixNode.content = parent.content + clonedChildSuffixNode.content; + + currentParents.push(clonedChildSuffixNode); + }); }); selectorList = selectorList.concat(currentParents); diff --git a/tests/helpers.js b/tests/helpers.js index 885ba040..88614652 100644 --- a/tests/helpers.js +++ b/tests/helpers.js @@ -1576,7 +1576,7 @@ describe('helpers', function () { ////////////////////////////// // attemptTraversal ////////////////////////////// - it('attemptTraversal - collect all nodes', function () { + it('attemptTraversal - SCSS - collect all nodes', function () { var stylesheet = gonzales.parse(['', '.a {', ' .b {', @@ -1599,7 +1599,7 @@ describe('helpers', function () { ); }); - it('attemptTraversal - empty array when traversal fails', function () { + it('attemptTraversal - SCSS - empty array when traversal fails', function () { var stylesheet = gonzales.parse(['', '.a {', ' color: red;', @@ -1611,10 +1611,42 @@ describe('helpers', function () { ); }); + it('attemptTraversal - Sass - collect all nodes', function () { + var stylesheet = gonzales.parse(['', + '.a', + ' .b', + ' color: red', + ' .c', + ' color: blue', + ' .d', + ' color: green', + ''].join('\n'), { syntax: 'sass' }); + + assert.deepEqual( + helpers.attemptTraversal(stylesheet, ['ruleset', 'block', 'ruleset', 'block', 'declaration', 'property', 'ident']) + .map(function (node) { + return node.content; + }), + ['color', 'color', 'color'] + ); + }); + + it('attemptTraversal - Sass - empty array when traversal fails', function () { + var stylesheet = gonzales.parse(['', + '.a', + ' color: red', + ''].join('\n'), { syntax: 'sass' }); + + assert.equal( + helpers.attemptTraversal(stylesheet, ['ruleset', 'block', 'ruleset', 'block']).length, + 0 + ); + }); + ////////////////////////////// // collectSuffixExtensions ////////////////////////////// - it('collectSuffixExtensions - no extensions', function () { + it('collectSuffixExtensions - SCSS - no extensions', function () { var ruleset = gonzales.parse(['', '.a {', ' .b {', @@ -1633,7 +1665,7 @@ describe('helpers', function () { ); }); - it('collectSuffixExtensions - BEM example', function () { + it('collectSuffixExtensions - SCSS - BEM example', function () { var ruleset = gonzales.parse(['', '.block {', ' &__element {', @@ -1652,7 +1684,7 @@ describe('helpers', function () { ); }); - it('collectSuffixExtensions - many parents and children', function () { + it('collectSuffixExtensions - SCSS - many parents and children', function () { var ruleset = gonzales.parse(['', '.a,', '.b {', @@ -1673,4 +1705,55 @@ describe('helpers', function () { ['a', 'b', 'ac', 'bc', 'ad', 'bd', 'ace', 'bce', 'ade', 'bde', 'acf', 'bcf', 'adf', 'bdf'] ); }); + + it('collectSuffixExtensions - Sass - no extensions', function () { + var ruleset = gonzales.parse(['', + '.a', + ' .b', + ' .c', + ' width: 2px', + ''].join('\n'), { syntax: 'sass' }) + .first('ruleset'); + + assert.deepEqual( + helpers.collectSuffixExtensions(ruleset, 'class').map(function (node) { + return node.content; + }), + ['a'] + ); + }); + + it('collectSuffixExtensions - Sass - BEM example', function () { + var ruleset = gonzales.parse(['', + '.block', + ' &__element', + ' &--modifier', + ' width: 2px', + ''].join('\n'), { syntax: 'sass' }) + .first('ruleset'); + + assert.deepEqual( + helpers.collectSuffixExtensions(ruleset, 'class').map(function (node) { + return node.content; + }), + ['block', 'block__element', 'block__element--modifier'] + ); + }); + + it('collectSuffixExtensions - Sass - many parents and children', function () { + var ruleset = gonzales.parse(['', + '.a, .b', + ' &c, &d', + ' &e, &f', + ' width: 2px', + ''].join('\n'), { syntax: 'sass' }) + .first('ruleset'); + + assert.deepEqual( + helpers.collectSuffixExtensions(ruleset, 'class').map(function (node) { + return node.content; + }), + ['a', 'b', 'ac', 'bc', 'ad', 'bd', 'ace', 'bce', 'ade', 'bde', 'acf', 'bcf', 'adf', 'bdf'] + ); + }); }); From 5d4c62de1861ea920963925a44e5cfc7bd14b8ff Mon Sep 17 00:00:00 2001 From: Ben Rothman Date: Sun, 7 Feb 2016 23:49:03 -0600 Subject: [PATCH 032/137] Fix mixin-name-format --- lib/rules/mixin-name-format.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/rules/mixin-name-format.js b/lib/rules/mixin-name-format.js index c5ce6faa..4550452a 100644 --- a/lib/rules/mixin-name-format.js +++ b/lib/rules/mixin-name-format.js @@ -32,10 +32,8 @@ module.exports = { } } - if (node.contains('simpleSelector')) { - if (node.first('simpleSelector').contains('ident')) { - name = node.first('simpleSelector').first('ident').content; - } + if (node.contains('ident')) { + name = node.first('ident').content; } } From aee567a67823bc83a21bc8fd1a488fa3bea98be1 Mon Sep 17 00:00:00 2001 From: greenkeeperio-bot Date: Tue, 9 Feb 2016 04:20:19 -0500 Subject: [PATCH 033/137] chore(package): update should to version 8.2.2 http://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0a5821a7..0ddab576 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,6 @@ "deep-equal": "^1.0.1", "istanbul": "^0.4.0", "mocha": "^2.2.5", - "should": "^7.0.4" + "should": "^8.2.2" } } From edb941d2a958ee7292f6d8ace1ec3c3b191d70a2 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Tue, 9 Feb 2016 12:32:12 +0000 Subject: [PATCH 034/137] :bug: SCSS only rule - add syntax check --- lib/rules/brace-style.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index 6e5215ca..26f18a87 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -196,6 +196,11 @@ module.exports = { 'Closing brace must be on a new line' ]; + // SCSS syntax only rule + if (ast.syntax === 'sass') { + return false; + } + // Assign current & previous nodes based on node type currentNode = getCurrentNode(node); previousNode = getPreviousNode(node); From dd937a59b116cc1a0038fb6c1bc966a039bdc225 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Tue, 9 Feb 2016 22:53:33 +0000 Subject: [PATCH 035/137] :art: Fix mixins-before-declaration rule to work with latest gonzales --- lib/rules/mixins-before-declarations.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/rules/mixins-before-declarations.js b/lib/rules/mixins-before-declarations.js index 14c113d4..5c5d17cd 100644 --- a/lib/rules/mixins-before-declarations.js +++ b/lib/rules/mixins-before-declarations.js @@ -18,26 +18,23 @@ module.exports = { var depth = 0, declarationCount = [depth]; - parent.forEach( function (item) { - if (item.type === 'ruleset') { + parent.forEach(function (item) { + if (item.is('ruleset')) { depth++; declarationCount[depth] = 0; } - else if (item.type === 'declaration') { + else if (item.is('declaration')) { if (item.first().is('property')) { - var prop = item.first(); if (prop.first().is('ident')) { - declarationCount[depth]++; - } } } - else if (item.type === 'include') { - item.forEach('simpleSelector', function (name) { - if (parser.options.exclude.indexOf(name.content[0].content) === -1 && declarationCount[depth] > 0) { + else if (item.is('include')) { + item.forEach('ident', function (name) { + if (parser.options.exclude.indexOf(name.content) === -1 && declarationCount[depth] > 0) { error = { 'ruleId': parser.rule.name, 'line': item.start.line, From 32a69de279660d19731c9f909ea310c99d3e323b Mon Sep 17 00:00:00 2001 From: greenkeeperio-bot Date: Wed, 10 Feb 2016 14:33:24 -0500 Subject: [PATCH 036/137] chore(package): update glob to version 7.0.0 http://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0ddab576..f97deb14 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "commander": "^2.8.1", "eslint": "^1.1.0", "fs-extra": "^0.26.0", - "glob": "^6.0.0", + "glob": "^7.0.0", "gonzales-pe": "3.0.0-31", "js-yaml": "^3.2.6", "lodash.capitalize": "^4.1.0", From 5eede1cb743475044b542ec7faf739b2ef38d22a Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Thu, 11 Feb 2016 23:15:30 +0000 Subject: [PATCH 037/137] :bug: Fix TypeError in indentation rule --- lib/rules/indentation.js | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/lib/rules/indentation.js b/lib/rules/indentation.js index acbad341..b7da28e7 100644 --- a/lib/rules/indentation.js +++ b/lib/rules/indentation.js @@ -51,23 +51,27 @@ module.exports = { } if (spaceLength / parser.options.size !== level) { - if (count !== spaceLength) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': node.content[i + 1].start.line, - 'column': node.content[i + 1].start.column, - 'message': 'Mixed tabs and spaces', - 'severity': parser.severity - }); - } - if (i !== node.length - 1) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': node.content[i + 1].start.line, - 'column': node.content[i + 1].start.column, - 'message': 'Indentation of ' + spaceLength + ', expected ' + level * parser.options.size, - 'severity': parser.severity - }); + var next = node.content[i + 1] || false; + + if (next) { + if (count !== spaceLength) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': next.start.line, + 'column': next.start.column, + 'message': 'Mixed tabs and spaces', + 'severity': parser.severity + }); + } + if (i !== node.length - 1) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': next.start.line, + 'column': next.start.column, + 'message': 'Indentation of ' + spaceLength + ', expected ' + level * parser.options.size, + 'severity': parser.severity + }); + } } } } From fa82c72bdb95517a83fdbf3c04ee453ae6436819 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Fri, 12 Feb 2016 21:01:45 +0000 Subject: [PATCH 038/137] :bug: Fix PR feedback - tweak comments and add fallbacks --- lib/rules/brace-style.js | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index 26f18a87..286c8799 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -38,14 +38,14 @@ var getPreviousNode = function (node) { /** * Determine if current node is an exception and end checks if it is + * If we've picked up a return @rule ignore it * * @param {Object} node - The original node * @param {object} currentNode - The current node block * @param {Object} previousNode - The node previous to our current node - * @returns {bool} Wether or not the it is an exception + * @returns {bool} Wtether or not the it is an exception */ var isException = function (node, currentNode, previousNode) { - // If we've picked up a return @rule ignore it if (node.is('atrule')) { if (previousNode.contains('ident') && previousNode.first('ident').content === 'return') { return true; @@ -108,11 +108,14 @@ var isConditionOnNewLine = function (node, parentNode, j) { // Only check if it's an @else condition if (node.contains('ident') && node.first('ident').content === 'else') { // Reverse back up tree - var previousChild = parentNode.get(--j); + var previousChild = parentNode.get(--j) || false; - // Determine if we have a leading new line - if (previousChild.is('space') && helpers.hasEOL(previousChild.content)) { - return true; + if (previousChild) { + // Determine if we have a leading new line + if (previousChild.is('space') && helpers.hasEOL(previousChild.content)) { + return true; + } + return false; } return false; } @@ -120,16 +123,16 @@ var isConditionOnNewLine = function (node, parentNode, j) { }; /** - * Run the rule checks and store return their results + * Run the rule checks and return their results * * @param {Object} node - The original node * @param {Object} currentNode - The current node block * @param {Object} previousNode - The node previous to our current node * @param {Object} parentNode - The parent of the original node - * @param {int} i - The index of the original node + * @param {int} index - The index of the original node * @returns {Object} The results of the rule checks */ -var runRuleChecks = function (node, currentNode, previousNode, parentNode, i) { +var runRuleChecks = function (node, currentNode, previousNode, parentNode, index) { var checks = {}; // Determine if single line statement @@ -137,7 +140,7 @@ var runRuleChecks = function (node, currentNode, previousNode, parentNode, i) { // Determine if condition is on a new line if (node.is('atrule') || node.is('conditionalStatement')) { - checks.conditionOnNewLine = isConditionOnNewLine(previousNode, parentNode, i); + checks.conditionOnNewLine = isConditionOnNewLine(previousNode, parentNode, index); } // Determine if opening brace is on new line @@ -189,9 +192,9 @@ module.exports = { }, messages = [ 'Single line statements are not allowed', - 'Opening brace must be on same line as condition', + 'Opening brace must be on the same line as condition', 'Brace must be on a new line', - 'Statement must start on same line as closing brace of previous statement', + 'Statement must start on the same line as the closing brace of the previous statement', 'Statement must begin on a new line', 'Closing brace must be on a new line' ]; From bc3c48d540a7963e7a26af29851817de15e14629 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Fri, 12 Feb 2016 21:02:32 +0000 Subject: [PATCH 039/137] :bug: Correct type --- lib/rules/brace-style.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index 286c8799..59067bad 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -43,7 +43,7 @@ var getPreviousNode = function (node) { * @param {Object} node - The original node * @param {object} currentNode - The current node block * @param {Object} previousNode - The node previous to our current node - * @returns {bool} Wtether or not the it is an exception + * @returns {bool} Whether or not the it is an exception */ var isException = function (node, currentNode, previousNode) { if (node.is('atrule')) { From 30c5e8f63730aee0e687ee74577335d4821e3bb9 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sun, 31 Jan 2016 18:40:53 +0000 Subject: [PATCH 040/137] Refactor rule to work with gonzales v3.2.1 --- lib/rules/brace-style.js | 280 +++++++++++++++++++++++---------------- 1 file changed, 167 insertions(+), 113 deletions(-) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index adf638f5..f13e6308 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -2,39 +2,54 @@ var helpers = require('../helpers'); +/** + * Determine if statement is a single line statement + * + * @param {Object} node - The statement to check + * @returns {bool} True or false + */ var isSingleLineStatement = function (node) { return node.start.line === node.end.line; }; -var createPreviousNode = function (node) { - return { - content: '', - start: { - line: node.start.line, - column: node.start.column - 1 - }, - end: { - line: node.end.line, - column: node.end.column - } - }; +/** + * Determine if opening brace of statement is on a new line + * + * @param {Object} nodeA - The previous block + * @param {Object} nodeB - The current block + * @returns {bool} True or false + */ +var isOpeningBraceOnNewLine = function (nodeA, nodeB) { + return nodeA.end.line === nodeB.start.line; }; -var getElsePrevious = function (atKeyword, parent, i) { - if (atKeyword.contains('ident')) { - if (atKeyword.first('ident').content === 'else') { - var prev = parent.get(i - 1); - - if (prev.is('space')) { - return prev; - } - else if (prev.is('atruleb')) { - return createPreviousNode(atKeyword.first('ident')); - } +/** + * Determine if condition starts on a new line by checking the leading node for + * an end-of-line + * + * @param {Object} node - The node that is our condition + * @param {Object} parentNode - The condition node's parent + * @param {Number} j - The index of our node in the context of the parent's children + * @returns {bool|null} True or false if relevant else null + */ +var isConditionOnNewLine = function (node, parentNode, j) { + // Only check if it's an @else condition + if (node.contains('ident') && node.first('ident').content === 'else') { + // Reverse back up tree + let previousChild = parentNode.get(--j); + + // Determine if we have a leading new line + if (previousChild.is('space') && helpers.hasEOL(previousChild.content)) { + return true; } + return false; } + return; }; +/** + * Rule exports + */ module.exports = { 'name': 'brace-style', 'defaults': { @@ -44,116 +59,155 @@ module.exports = { 'detect': function (ast, parser) { var result = []; - ast.traverseByTypes(['conditionalStatement', 'atruleb', 'selector', 'mixin', 'loop'], function (block, i, parent) { - var next = false, - previous = false, - isSingleLine = false; - - // Store leading space node for @elseif statements - if (block.is('atruleb')) { - if (block.contains('atkeyword')) { - previous = getElsePrevious(block.first('atkeyword'), parent, i); - } + ast.traverseByTypes(['conditionalStatement', 'atrule', 'ruleset', 'mixin', 'loop'], function (block, i, parent) { + var currentNode = false, + previousNode = false, + rules = { + singleLineStatement: null, + openingBraceOnNewLine: null, + conditionOnNewLine: null, + }, + messages = [ + 'Single line statements are not allowed', + 'Opening brace must be on same line as condition', + 'Brace must be on a new line', + 'Statement must start on same line as closing brace of previous statement', + 'Statement must begin on a new line' + ]; + + /** + * Store nodes that we will use to run rules against + */ + currentNode = block.contains('block') ? block.first('block') : false; + + // Rulesets + if (block.is('ruleset')) { + previousNode = block.contains('selector') ? block.last('selector') : false; } - // Store leading space node for @else statements + // Conditonal statements if (block.is('conditionalStatement')) { - if (block.contains('condition')) { - var condition = block.first('condition'); + let previousParent = block.contains('condition') ? block.last('condition') : false; + previousNode = previousParent.contains('atkeyword') ? previousParent.last('atkeyword') : false; + } - if (condition.contains('atkeyword')) { - previous = getElsePrevious(condition.first('atkeyword'), parent, i); - } + // Functions, Mixins, Loops + if (block.is('atrule') || block.is('mixin') || block.is('loop')) { + previousNode = block.contains('atkeyword') ? block.last('atkeyword') : false; + } + + /** + * Node checks - If we've picked up a return @rule ignore it + */ + if (block.is('atrule')) { + if (previousNode.contains('ident') && previousNode.first('ident').content === 'return') { + return false; } } - // Store trailing space node for conditional statements, mixins and loops - if (block.is('atruleb') || block.is('conditionalStatement') || block.is('mixin') || block.is('loop')) { - // Since a node of type block should be the last node within 'block', - // go back 2 to determine if there is a newline - next = block.content[block.content.length - 2]; + /** + * The rule checks + */ - // Determine if single line statement - isSingleLine = isSingleLineStatement(block); - } + // Determine if single line statement + rules.singleLineStatement = isSingleLineStatement(block); - // Store trailing space node for selectors - if (block.is('selector')) { - if (block.contains('simpleSelector')) { - var lastSelector = block.last('simpleSelector'); + // Determine if condition is on a new line + if (block.is('atrule') || block.is('conditionalStatement')) { + rules.conditionOnNewLine = isConditionOnNewLine(previousNode, parent, i); + } - next = lastSelector.content[lastSelector.content.length - 1]; - } + if (previousNode && currentNode) { + // Determine if opening brace is on new line + rules.openingBraceOnNewLine = isOpeningBraceOnNewLine(previousNode, currentNode); + } - // Determine if single line statement - if (parent.is('ruleset')) { - isSingleLine = isSingleLineStatement(parent); + /** + * Build results + */ + if (rules.singleLineStatement) { + if (parser.options['allow-single-line'] === false) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': block.start.line, + 'column': block.start.column, + 'message': messages[0], + 'severity': parser.severity + }); } + return false; } - if ((parser.options['allow-single-line'] === false) && isSingleLine) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': block.start.line, - 'column': block.start.column, - 'message': 'Single line statements are not allowed', - 'severity': parser.severity - }); - } - else { - - if (next) { - if (!helpers.hasEOL(next.content)) { - if (parser.options.style === 'allman') { - // Report if it's not single line statement - if (!((parser.options['allow-single-line'] === true) && isSingleLine)) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': next.start.line, - 'column': next.start.column, - 'message': 'Brace must be on a new line', - 'severity': parser.severity - }); - } - } + if (previousNode && currentNode) { + /** + * Brace style: 1tbs + */ + if (parser.options.style === '1tbs') { + if (rules.openingBraceOnNewLine === false) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': currentNode.start.line, + 'column': currentNode.start.column, + 'message': messages[1], + 'severity': parser.severity + }); } - - if (helpers.hasEOL(next.content)) { - if (parser.options.style === '1tbs' || parser.options.style === 'stroustrup') { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': next.start.line, - 'column': next.start.column, - 'message': 'Brace must be on same line as condition', - 'severity': parser.severity - }); - } + if (rules.conditionOnNewLine === true) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': previousNode.start.line, + 'column': previousNode.start.column, + 'message': messages[3], + 'severity': parser.severity + }); } } - if (previous) { - if (!helpers.hasEOL(previous.content)) { - if (parser.options.style === 'allman' || parser.options.style === 'stroustrup') { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': previous.start.line, - 'column': previous.start.column, - 'message': 'Statement should begin on a new line', - 'severity': parser.severity - }); - } + /** + * Brace style: stroustrup + */ + if (parser.options.style === 'stroustrup') { + if (rules.openingBraceOnNewLine === false) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': currentNode.start.line, + 'column': currentNode.start.column, + 'message': messages[1], + 'severity': parser.severity + }); + } + if (rules.conditionOnNewLine === false) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': previousNode.start.line, + 'column': previousNode.start.column, + 'message': messages[4], + 'severity': parser.severity + }); } + } - if (helpers.hasEOL(previous.content)) { - if (parser.options.style === '1tbs') { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': previous.start.line, - 'column': previous.start.column, - 'message': 'Statement on next line should begin on the current line', - 'severity': parser.severity - }); - } + /** + * Brace style: allman + */ + if (parser.options.style === 'allman') { + if (rules.openingBraceOnNewLine === true) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': currentNode.start.line, + 'column': currentNode.start.column, + 'message': messages[2], + 'severity': parser.severity + }); + } + if (rules.conditionOnNewLine === false) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': previousNode.start.line, + 'column': previousNode.start.column, + 'message': messages[4], + 'severity': parser.severity + }); } } } From bca59b07bdcb4417b0ef37f5a7e4815c6e9ecd48 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sun, 31 Jan 2016 18:41:13 +0000 Subject: [PATCH 041/137] Update tests and desired number of warnings --- tests/rules/brace-style.js | 6 +++--- tests/sass/brace-style.scss | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/rules/brace-style.js b/tests/rules/brace-style.js index 04f188e8..9e9d91f9 100644 --- a/tests/rules/brace-style.js +++ b/tests/rules/brace-style.js @@ -12,7 +12,7 @@ describe('brace style - scss', function () { lint.test(file, { 'brace-style': 1 }, function (data) { - lint.assert.equal(35, data.warningCount); + lint.assert.equal(32, data.warningCount); done(); }); }); @@ -42,7 +42,7 @@ describe('brace style - scss', function () { } ] }, function (data) { - lint.assert.equal(39, data.warningCount); + lint.assert.equal(36, data.warningCount); done(); }); }); @@ -72,7 +72,7 @@ describe('brace style - scss', function () { } ] }, function (data) { - lint.assert.equal(72, data.warningCount); + lint.assert.equal(69, data.warningCount); done(); }); }); diff --git a/tests/sass/brace-style.scss b/tests/sass/brace-style.scss index cd65c3c4..f744c032 100644 --- a/tests/sass/brace-style.scss +++ b/tests/sass/brace-style.scss @@ -292,7 +292,6 @@ h2 - @if($foo) { $bar: 'foo'; @@ -419,7 +418,6 @@ $total: 4; @if ($foo) { $bar: 'foo'; } @else { $bar: false; } @if ($foo) { $bar: 'foo'; } @else if { $bar: 'bar'; } @else { $bar: false; } - @if ($foo) { $bar: 'foo'; } @else { $bar: false; } From 159348dd7edac07d907c30397d0d6707e98f034f Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sun, 31 Jan 2016 19:13:30 +0000 Subject: [PATCH 042/137] :art: Add check for closing brace --- lib/rules/brace-style.js | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index f13e6308..685137c9 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -23,6 +23,19 @@ var isOpeningBraceOnNewLine = function (nodeA, nodeB) { return nodeA.end.line === nodeB.start.line; }; +var isClosingBraceOnNewLine = function (node) { + if (node.contains('block')) { + let content = node.first('block'), + contentLength = content.length - 1, + lastNode = content.get(contentLength); + + if (lastNode.is('space') && helpers.hasEOL(lastNode.content)) { + return true; + } + return false; + } +} + /** * Determine if condition starts on a new line by checking the leading node for * an end-of-line @@ -65,6 +78,7 @@ module.exports = { rules = { singleLineStatement: null, openingBraceOnNewLine: null, + closingBraceOnNewLine: null, conditionOnNewLine: null, }, messages = [ @@ -72,7 +86,8 @@ module.exports = { 'Opening brace must be on same line as condition', 'Brace must be on a new line', 'Statement must start on same line as closing brace of previous statement', - 'Statement must begin on a new line' + 'Statement must begin on a new line', + 'Closing brace must be on a new line' ]; /** @@ -117,15 +132,29 @@ module.exports = { rules.conditionOnNewLine = isConditionOnNewLine(previousNode, parent, i); } + // Determine if opening brace is on new line if (previousNode && currentNode) { - // Determine if opening brace is on new line rules.openingBraceOnNewLine = isOpeningBraceOnNewLine(previousNode, currentNode); } + // Determine if closing brace is on new line + rules.closingBraceOnNewLine = isClosingBraceOnNewLine(block); + /** * Build results */ - if (rules.singleLineStatement) { + + if (rules.singleLineStatement === false && rules.closingBraceOnNewLine === false) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': currentNode.end.line, + 'column': currentNode.end.column, + 'message': messages[5], + 'severity': parser.severity + }); + } + + if (rules.singleLineStatement === true) { if (parser.options['allow-single-line'] === false) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, @@ -138,6 +167,7 @@ module.exports = { return false; } + if (previousNode && currentNode) { /** * Brace style: 1tbs From dcd76c97d3b9921dfcd86f80c288cf49f06adbae Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sun, 31 Jan 2016 19:13:51 +0000 Subject: [PATCH 043/137] :white_check_mark: Add tests to check for closing brace on new line --- tests/rules/brace-style.js | 12 ++++++------ tests/sass/brace-style.scss | 22 +++++++++++++++++++++- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/tests/rules/brace-style.js b/tests/rules/brace-style.js index 9e9d91f9..ac2a86ac 100644 --- a/tests/rules/brace-style.js +++ b/tests/rules/brace-style.js @@ -12,7 +12,7 @@ describe('brace style - scss', function () { lint.test(file, { 'brace-style': 1 }, function (data) { - lint.assert.equal(32, data.warningCount); + lint.assert.equal(36, data.warningCount); done(); }); }); @@ -27,7 +27,7 @@ describe('brace style - scss', function () { } ] }, function (data) { - lint.assert.equal(54, data.warningCount); + lint.assert.equal(58, data.warningCount); done(); }); }); @@ -42,7 +42,7 @@ describe('brace style - scss', function () { } ] }, function (data) { - lint.assert.equal(36, data.warningCount); + lint.assert.equal(40, data.warningCount); done(); }); }); @@ -57,7 +57,7 @@ describe('brace style - scss', function () { } ] }, function (data) { - lint.assert.equal(58, data.warningCount); + lint.assert.equal(62, data.warningCount); done(); }); }); @@ -72,7 +72,7 @@ describe('brace style - scss', function () { } ] }, function (data) { - lint.assert.equal(69, data.warningCount); + lint.assert.equal(77, data.warningCount); done(); }); }); @@ -87,7 +87,7 @@ describe('brace style - scss', function () { } ] }, function (data) { - lint.assert.equal(91, data.warningCount); + lint.assert.equal(99, data.warningCount); done(); }); }); diff --git a/tests/sass/brace-style.scss b/tests/sass/brace-style.scss index f744c032..2ab8d1f7 100644 --- a/tests/sass/brace-style.scss +++ b/tests/sass/brace-style.scss @@ -444,10 +444,30 @@ $total: 4; -// No parens +// ======================= +// No paren tests +// ======================= @if $foo { $bar: 'foo'; } @if $foo { $bar: 'foo'; } + + + +// ======================= +// Closing brace tests +// ======================= + +.foo { + content: 'foo'; } + +@if($foo) { + $bar: 'foo'; } + +@function foo() { + @return 'foo'; } + +@mixin bar() { + content: 'bar'; } From be839432314e72c73ab61107c9c8c42ce36832a7 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sun, 31 Jan 2016 19:19:02 +0000 Subject: [PATCH 044/137] :art: Correct eslint issues --- lib/rules/brace-style.js | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index 685137c9..cd350ad2 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -25,7 +25,7 @@ var isOpeningBraceOnNewLine = function (nodeA, nodeB) { var isClosingBraceOnNewLine = function (node) { if (node.contains('block')) { - let content = node.first('block'), + var content = node.first('block'), contentLength = content.length - 1, lastNode = content.get(contentLength); @@ -34,7 +34,7 @@ var isClosingBraceOnNewLine = function (node) { } return false; } -} +}; /** * Determine if condition starts on a new line by checking the leading node for @@ -49,7 +49,7 @@ var isConditionOnNewLine = function (node, parentNode, j) { // Only check if it's an @else condition if (node.contains('ident') && node.first('ident').content === 'else') { // Reverse back up tree - let previousChild = parentNode.get(--j); + var previousChild = parentNode.get(--j); // Determine if we have a leading new line if (previousChild.is('space') && helpers.hasEOL(previousChild.content)) { @@ -57,7 +57,7 @@ var isConditionOnNewLine = function (node, parentNode, j) { } return false; } - return; + return null; }; /** @@ -75,11 +75,11 @@ module.exports = { ast.traverseByTypes(['conditionalStatement', 'atrule', 'ruleset', 'mixin', 'loop'], function (block, i, parent) { var currentNode = false, previousNode = false, - rules = { + checks = { singleLineStatement: null, openingBraceOnNewLine: null, closingBraceOnNewLine: null, - conditionOnNewLine: null, + conditionOnNewLine: null }, messages = [ 'Single line statements are not allowed', @@ -102,8 +102,8 @@ module.exports = { // Conditonal statements if (block.is('conditionalStatement')) { - let previousParent = block.contains('condition') ? block.last('condition') : false; - previousNode = previousParent.contains('atkeyword') ? previousParent.last('atkeyword') : false; + var previousParent = block.contains('condition') ? block.last('condition') : false; + previousNode = previousParent && previousParent.contains('atkeyword') ? previousParent.last('atkeyword') : false; } // Functions, Mixins, Loops @@ -125,26 +125,26 @@ module.exports = { */ // Determine if single line statement - rules.singleLineStatement = isSingleLineStatement(block); + checks.singleLineStatement = isSingleLineStatement(block); // Determine if condition is on a new line if (block.is('atrule') || block.is('conditionalStatement')) { - rules.conditionOnNewLine = isConditionOnNewLine(previousNode, parent, i); + checks.conditionOnNewLine = isConditionOnNewLine(previousNode, parent, i); } // Determine if opening brace is on new line if (previousNode && currentNode) { - rules.openingBraceOnNewLine = isOpeningBraceOnNewLine(previousNode, currentNode); + checks.openingBraceOnNewLine = isOpeningBraceOnNewLine(previousNode, currentNode); } // Determine if closing brace is on new line - rules.closingBraceOnNewLine = isClosingBraceOnNewLine(block); + checks.closingBraceOnNewLine = isClosingBraceOnNewLine(block); /** * Build results */ - if (rules.singleLineStatement === false && rules.closingBraceOnNewLine === false) { + if (checks.singleLineStatement === false && checks.closingBraceOnNewLine === false) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': currentNode.end.line, @@ -154,7 +154,7 @@ module.exports = { }); } - if (rules.singleLineStatement === true) { + if (checks.singleLineStatement === true) { if (parser.options['allow-single-line'] === false) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, @@ -173,7 +173,7 @@ module.exports = { * Brace style: 1tbs */ if (parser.options.style === '1tbs') { - if (rules.openingBraceOnNewLine === false) { + if (checks.openingBraceOnNewLine === false) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': currentNode.start.line, @@ -182,7 +182,7 @@ module.exports = { 'severity': parser.severity }); } - if (rules.conditionOnNewLine === true) { + if (checks.conditionOnNewLine === true) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': previousNode.start.line, @@ -197,7 +197,7 @@ module.exports = { * Brace style: stroustrup */ if (parser.options.style === 'stroustrup') { - if (rules.openingBraceOnNewLine === false) { + if (checks.openingBraceOnNewLine === false) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': currentNode.start.line, @@ -206,7 +206,7 @@ module.exports = { 'severity': parser.severity }); } - if (rules.conditionOnNewLine === false) { + if (checks.conditionOnNewLine === false) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': previousNode.start.line, @@ -221,7 +221,7 @@ module.exports = { * Brace style: allman */ if (parser.options.style === 'allman') { - if (rules.openingBraceOnNewLine === true) { + if (checks.openingBraceOnNewLine === true) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': currentNode.start.line, @@ -230,7 +230,7 @@ module.exports = { 'severity': parser.severity }); } - if (rules.conditionOnNewLine === false) { + if (checks.conditionOnNewLine === false) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': previousNode.start.line, From dc9ba39c1987be09594ef308191a762dd6ffc019 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Mon, 1 Feb 2016 10:47:53 +0000 Subject: [PATCH 045/137] :memo: Add jsdoc for closing brace function --- lib/rules/brace-style.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index cd350ad2..f281a643 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -23,6 +23,12 @@ var isOpeningBraceOnNewLine = function (nodeA, nodeB) { return nodeA.end.line === nodeB.start.line; }; +/** + * Determine if closing brace of statement is on new line + * + * @param {Object} node - The current block + * @returns {bool|null} True or false if relevant else null + */ var isClosingBraceOnNewLine = function (node) { if (node.contains('block')) { var content = node.first('block'), @@ -34,6 +40,7 @@ var isClosingBraceOnNewLine = function (node) { } return false; } + return null; }; /** From 7264bad40c2558c95883225193823e75b5280181 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Mon, 1 Feb 2016 22:03:23 +0000 Subject: [PATCH 046/137] :bug: Replace deprecated node type simpleSelector with selector --- lib/rules/placeholder-in-extend.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/placeholder-in-extend.js b/lib/rules/placeholder-in-extend.js index 4fbdc407..a082bf01 100644 --- a/lib/rules/placeholder-in-extend.js +++ b/lib/rules/placeholder-in-extend.js @@ -12,7 +12,7 @@ module.exports = { keyword.forEach(function (item) { if (item.content === 'extend') { - parent.forEach('simpleSelector', function (selector) { + parent.forEach('selector', function (selector) { var placeholder = false; selector.content.forEach(function (selectorPiece) { From 9c5d7c3b3690fab568df4fe1fb7acc0d386d82dd Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Mon, 1 Feb 2016 22:58:46 +0000 Subject: [PATCH 047/137] :art: Refactor space-after-bang rule to work with gonzales-3.2.1 --- lib/rules/space-after-bang.js | 43 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/lib/rules/space-after-bang.js b/lib/rules/space-after-bang.js index 7c8dc039..4fb68a21 100644 --- a/lib/rules/space-after-bang.js +++ b/lib/rules/space-after-bang.js @@ -8,31 +8,30 @@ module.exports = { 'include': false }, 'detect': function (ast, parser) { - var result = []; + var result = [], + regex = /!\s/; ast.traverseByTypes(['important', 'default'], function (block) { - block.traverse(function (item) { - if (item.type === 'space') { - if (parser.options.include) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': block.start.line, - 'column': block.start.column + 1, - 'message': 'Bangs (!) should be followed by a space', - 'severity': parser.severity - }); - } - else { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': block.start.line, - 'column': block.start.column, - 'message': 'Bangs (!) should not be followed by a space', - 'severity': parser.severity - }); - } + if (block.content.match(regex) !== null) { + if (parser.options.include) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': block.start.line, + 'column': block.start.column + 1, + 'message': 'Bangs (!) should be followed by a space', + 'severity': parser.severity + }); } - }); + else { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': block.start.line, + 'column': block.start.column, + 'message': 'Bangs (!) should not be followed by a space', + 'severity': parser.severity + }); + } + } }); return result; From 22f41b9cfcf10af245b9bb185d9eb04c2fa82da6 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Mon, 1 Feb 2016 23:33:45 +0000 Subject: [PATCH 048/137] :art: Refactor rule to work with gonzales 3.2.1 --- lib/rules/space-after-comma.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/rules/space-after-comma.js b/lib/rules/space-after-comma.js index 3143dafb..6ac16ffc 100644 --- a/lib/rules/space-after-comma.js +++ b/lib/rules/space-after-comma.js @@ -11,18 +11,25 @@ module.exports = { var result = []; ast.traverseByTypes(['operator', 'delimiter'], function (operator, i, parent) { - var next; + var next, + doubleNext; if (operator.content === ',') { - next = parent.content[i + 1]; + next = parent.content[i + 1] || false; + doubleNext = parent.content[i + 2] || false; if (next) { if (operator.is('delimiter')) { - if (next.is('simpleSelector')) { + if (next.is('selector')) { next = next.content[0]; } } + if ((next.is('space') && !helpers.hasEOL(next.content)) && !parser.options.include) { + if (doubleNext && doubleNext.is('singlelineComment')) { + return false; + } + result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': next.start.line, @@ -31,6 +38,7 @@ module.exports = { 'severity': parser.severity }); } + if (!next.is('space') && parser.options.include) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, From eb82c35a5a533ec6c596de35915d72a6e7235c64 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Wed, 3 Feb 2016 18:23:01 +0000 Subject: [PATCH 049/137] Update gonzales dependancy to latest release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f97deb14..4b6831d7 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "eslint": "^1.1.0", "fs-extra": "^0.26.0", "glob": "^7.0.0", - "gonzales-pe": "3.0.0-31", + "gonzales-pe": "^3.2.6", "js-yaml": "^3.2.6", "lodash.capitalize": "^4.1.0", "lodash.kebabcase": "^3.0.1", From 8963b466368f2cf29be57fd3a2c48082abe5b7ec Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Wed, 3 Feb 2016 22:38:52 +0000 Subject: [PATCH 050/137] Replace comment style for continutity --- lib/rules/brace-style.js | 46 +++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index f281a643..126691c1 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -67,9 +67,6 @@ var isConditionOnNewLine = function (node, parentNode, j) { return null; }; -/** - * Rule exports - */ module.exports = { 'name': 'brace-style', 'defaults': { @@ -97,9 +94,9 @@ module.exports = { 'Closing brace must be on a new line' ]; - /** - * Store nodes that we will use to run rules against - */ + ////////////////////////////// + // Assign current & previous nodes + ////////////////////////////// currentNode = block.contains('block') ? block.first('block') : false; // Rulesets @@ -118,18 +115,16 @@ module.exports = { previousNode = block.contains('atkeyword') ? block.last('atkeyword') : false; } - /** - * Node checks - If we've picked up a return @rule ignore it - */ + // If we've picked up a return @rule ignore it if (block.is('atrule')) { if (previousNode.contains('ident') && previousNode.first('ident').content === 'return') { return false; } } - /** - * The rule checks - */ + ////////////////////////////// + // The rule checks + ////////////////////////////// // Determine if single line statement checks.singleLineStatement = isSingleLineStatement(block); @@ -147,10 +142,9 @@ module.exports = { // Determine if closing brace is on new line checks.closingBraceOnNewLine = isClosingBraceOnNewLine(block); - /** - * Build results - */ - + ////////////////////////////// + // Build results + ////////////////////////////// if (checks.singleLineStatement === false && checks.closingBraceOnNewLine === false) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, @@ -174,11 +168,11 @@ module.exports = { return false; } - if (previousNode && currentNode) { - /** - * Brace style: 1tbs - */ + + ////////////////////////////// + // Brace style: 1tbs + ////////////////////////////// if (parser.options.style === '1tbs') { if (checks.openingBraceOnNewLine === false) { result = helpers.addUnique(result, { @@ -200,9 +194,9 @@ module.exports = { } } - /** - * Brace style: stroustrup - */ + ////////////////////////////// + // Brace style: stroustrup + ////////////////////////////// if (parser.options.style === 'stroustrup') { if (checks.openingBraceOnNewLine === false) { result = helpers.addUnique(result, { @@ -224,9 +218,9 @@ module.exports = { } } - /** - * Brace style: allman - */ + ////////////////////////////// + // Brace style: allman + ////////////////////////////// if (parser.options.style === 'allman') { if (checks.openingBraceOnNewLine === true) { result = helpers.addUnique(result, { From 699202e4ffc0d723683179af643e324f4eebdabe Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Thu, 4 Feb 2016 23:29:41 +0000 Subject: [PATCH 051/137] :art: Refactor space-around-operator rule to work with Gonzales 3.2 --- lib/rules/space-around-operator.js | 19 +++++++++++++++++-- tests/rules/space-around-operator.js | 8 ++++---- tests/sass/space-around-operator.sass | 10 ++++------ tests/sass/space-around-operator.scss | 22 ++++++++++------------ 4 files changed, 35 insertions(+), 24 deletions(-) diff --git a/lib/rules/space-around-operator.js b/lib/rules/space-around-operator.js index f01813b1..0b2de5d2 100644 --- a/lib/rules/space-around-operator.js +++ b/lib/rules/space-around-operator.js @@ -4,6 +4,12 @@ var helpers = require('../helpers'); var operators = ['+', '-', '/', '*', '%', '<', '>', '==', '!=', '<=', '>=']; +/** + * Determine a relational operator based on the operator node + * + * @param {Object} node - The operator node + * @returns {string} Returns a relational operator + */ var getRelationalOperator = function (node) { if (node.content === '<') { return '<='; @@ -14,6 +20,16 @@ var getRelationalOperator = function (node) { } }; +/** + * Check the spacing around an operator + * + * @param {Object} node - The node to check the spacing around + * @param {int} i - The node's child index of it's parent + * @param {Object} parent - The parent node + * @param {Object} parser - The parser object + * @param {Object} result - The result object + * @returns {bool|null} false if exception + */ var checkSpacing = function (node, i, parent, parser, result) { if (node.is('operator') || node.is('unaryOperator')) { var previous = parent.content[i - 1] || false, @@ -131,8 +147,7 @@ module.exports = { 'detect': function (ast, parser) { var result = []; - // FIXME: Gonzales v3 - No longer need atrulerq (for Sass) - ast.traverseByTypes(['condition', 'atruleb', 'value', 'atrulerq'], function (node) { + ast.traverseByTypes(['condition', 'atrule', 'value'], function (node) { node.forEach(function (item, i, parent) { // Perform another loop of the children if we come across a parenthesis // parent node diff --git a/tests/rules/space-around-operator.js b/tests/rules/space-around-operator.js index bd493a56..957c1599 100644 --- a/tests/rules/space-around-operator.js +++ b/tests/rules/space-around-operator.js @@ -12,7 +12,7 @@ describe('space around operator - scss', function () { lint.test(file, { 'space-around-operator': 1 }, function (data) { - lint.assert.equal(84, data.warningCount); + lint.assert.equal(91, data.warningCount); done(); }); }); @@ -26,7 +26,7 @@ describe('space around operator - scss', function () { } ] }, function (data) { - lint.assert.equal(78, data.warningCount); + lint.assert.equal(90, data.warningCount); done(); }); }); @@ -42,7 +42,7 @@ describe('space around operator - sass', function () { lint.test(file, { 'space-around-operator': 1 }, function (data) { - lint.assert.equal(84, data.warningCount); + lint.assert.equal(87, data.warningCount); done(); }); }); @@ -56,7 +56,7 @@ describe('space around operator - sass', function () { } ] }, function (data) { - lint.assert.equal(78, data.warningCount); + lint.assert.equal(84, data.warningCount); done(); }); }); diff --git a/tests/sass/space-around-operator.sass b/tests/sass/space-around-operator.sass index 2fbc4ebd..17d232b3 100644 --- a/tests/sass/space-around-operator.sass +++ b/tests/sass/space-around-operator.sass @@ -66,7 +66,7 @@ $qux: 2 +1 $foo: (1+1) $bar: (2-1) $baz: (1/2) -// $qux: (2*1) FIXME: Gonzales - FIXED IN BETA +$qux: (2*1) // $norf: (5%2) Not valid SCSS. @@ -75,7 +75,7 @@ $baz: (1/2) $foo: (1 +1) $bar: (2 -1) $baz: (1 /2) -// $qux: (2 *1) FIXME: Gonzales - FIXED IN BETA +$qux: (2 *1) // $norf: (5 %2) FIXME: Gonzales - FIXED IN BETA @@ -84,7 +84,7 @@ $baz: (1 /2) $foo: (1+ 1) $bar: (2- 1) $baz: (1/ 2) -// $qux: (2* 1) FIXME: Gonzales - FIXED IN BETA +$qux: (2* 1) // $norf: (5% 2) Not valid SCSS. Parses as 5 percent. @@ -396,9 +396,7 @@ $qux: 2 * 1 $foo: (1 + 1) $bar: (2 - 1) $baz: (1 / 2) -// Include: false will ignore this, so count will be one down on what it should -// be -$qux: (2 * 1) // FIXME: Gonzales - FIXED IN BETA +$qux: (2 * 1) // $norf: (5 % 2) FIXME: Gonzales - FIXED IN BETA // Space with no parens diff --git a/tests/sass/space-around-operator.scss b/tests/sass/space-around-operator.scss index 1fe75a39..47cae5f4 100644 --- a/tests/sass/space-around-operator.scss +++ b/tests/sass/space-around-operator.scss @@ -48,7 +48,7 @@ $foo: 1 +1; $bar: 2 -1; $baz: 1 /2; $qux: 2 *1; -// $norf: 5 %2; FIXME: Gonzales - FIXED IN BETA +$norf: 5 %2; // No space before @@ -66,14 +66,14 @@ $foo: 1 + 1; $bar: 2 + 1; $baz: 1 + 2; $qux: 2 +1; -// $norf: 5 % 2; FIXME: Gonzales - FIXED IN BETA +$norf: 5 % 2; // No space with parens $foo: (1+1); $bar: (2-1); $baz: (1/2); -// $qux: (2*1); FIXME: Gonzales - FIXED IN BETA +$qux: (2*1); // $norf: (5%2); Not valid SCSS. @@ -82,8 +82,8 @@ $baz: (1/2); $foo: (1 +1); $bar: (2 -1); $baz: (1 /2); -// $qux: (2 *1); FIXME: Gonzales - FIXED IN BETA -// $norf: (5 %2); FIXME: Gonzales - FIXED IN BETA +$qux: (2 *1); +$norf: (5 %2); // No space before with parens @@ -91,7 +91,7 @@ $baz: (1 /2); $foo: (1+ 1); $bar: (2- 1); $baz: (1/ 2); -// $qux: (2* 1); FIXME: Gonzales - FIXED IN BETA +$qux: (2* 1); // $norf: (5% 2); Not valid SCSS. Parses as 5 percent. @@ -101,7 +101,7 @@ $foo: (1 + 1); $bar: (2 + 1); $baz: (1 + 2); $qux: (2 +1); -// $norf: (5 % 2); FIXME: Gonzales - FIXED IN BETA +$norf: (5 % 2); @@ -405,17 +405,15 @@ $foo: 1 + 1; $bar: 2 - 1; $baz: 1 / 2; $qux: 2 * 1; -// $norf: 5 % 2; FIXME: Gonzales - FIXED IN BETA +$norf: 5 % 2; // Values with parens $foo: (1 + 1); $bar: (2 - 1); $baz: (1 / 2); -// Include: false will ignore this, so count will be one down on what it should -// be -$qux: (2 * 1); // FIXME: Gonzales - FIXED IN BETA -// $norf: (5 % 2); FIXME: Gonzales - FIXED IN BETA +$qux: (2 * 1); +$norf: (5 % 2); // Space with no parens From a19f8d8df471d5e9df66361c8da358b9453effaf Mon Sep 17 00:00:00 2001 From: Daniel Tschinder Date: Fri, 5 Feb 2016 14:18:11 +0100 Subject: [PATCH 052/137] :bug: Fix space-before-brace to work with gonzales 3.2 node.last() changed from returning undefined to null. As typeof null === object the check was failing. The new check is now accounting for all falsy values, and as false i captured in a separate check the logic is still the same. --- lib/rules/space-before-brace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/space-before-brace.js b/lib/rules/space-before-brace.js index bdcff459..cdf0b096 100644 --- a/lib/rules/space-before-brace.js +++ b/lib/rules/space-before-brace.js @@ -7,7 +7,7 @@ var getLastWhitespace = function (node) { return null; } - if (typeof node !== 'object') { + if (!node) { return false; } if (node.is('space')) { From db46bde618336b79e0485729dbfc0431f2f46386 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Fri, 5 Feb 2016 15:18:24 +0000 Subject: [PATCH 053/137] :art: Refactor code to further improve readability --- lib/rules/brace-style.js | 276 ++++++++++++++++++++------------------- 1 file changed, 141 insertions(+), 135 deletions(-) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index 126691c1..6e5215ca 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -2,6 +2,58 @@ var helpers = require('../helpers'); +/** + * Get the current block within a node + * + * @param {Object} node - The node containing our desired block + * @returns {Object} The current block of the node + */ +var getCurrentNode = function (node) { + return node.contains('block') ? node.first('block') : false; +}; + +/** + * Get the previous node + * + * @param {Object} node - Our current node + * @returns {Object|bool} The previous node or false if not found + */ +var getPreviousNode = function (node) { + // Rulesets + if (node.is('ruleset')) { + return node.contains('selector') ? node.last('selector') : false; + } + + // Conditonal statements + if (node.is('conditionalStatement')) { + var previousParent = node.contains('condition') ? node.last('condition') : false; + return previousParent && previousParent.contains('atkeyword') ? previousParent.last('atkeyword') : false; + } + + // Functions, Mixins, Loops + if (node.is('atrule') || node.is('mixin') || node.is('loop')) { + return node.contains('atkeyword') ? node.last('atkeyword') : false; + } +}; + +/** + * Determine if current node is an exception and end checks if it is + * + * @param {Object} node - The original node + * @param {object} currentNode - The current node block + * @param {Object} previousNode - The node previous to our current node + * @returns {bool} Wether or not the it is an exception + */ +var isException = function (node, currentNode, previousNode) { + // If we've picked up a return @rule ignore it + if (node.is('atrule')) { + if (previousNode.contains('ident') && previousNode.first('ident').content === 'return') { + return true; + } + } + return false; +}; + /** * Determine if statement is a single line statement * @@ -67,6 +119,56 @@ var isConditionOnNewLine = function (node, parentNode, j) { return null; }; +/** + * Run the rule checks and store return their results + * + * @param {Object} node - The original node + * @param {Object} currentNode - The current node block + * @param {Object} previousNode - The node previous to our current node + * @param {Object} parentNode - The parent of the original node + * @param {int} i - The index of the original node + * @returns {Object} The results of the rule checks + */ +var runRuleChecks = function (node, currentNode, previousNode, parentNode, i) { + var checks = {}; + + // Determine if single line statement + checks.singleLineStatement = isSingleLineStatement(node); + + // Determine if condition is on a new line + if (node.is('atrule') || node.is('conditionalStatement')) { + checks.conditionOnNewLine = isConditionOnNewLine(previousNode, parentNode, i); + } + + // Determine if opening brace is on new line + if (previousNode && currentNode) { + checks.openingBraceOnNewLine = isOpeningBraceOnNewLine(previousNode, currentNode); + } + + // Determine if closing brace is on new line + checks.closingBraceOnNewLine = isClosingBraceOnNewLine(node); + + return checks; +}; + +/** + * Create an issue using the supplied information + * + * @param {Object} parser - The parser + * @param {Object} node - The node with the issue + * @param {string} message - The message to display + * @returns {Object} An object containing an issue + */ +var createIssue = function (parser, node, message) { + return { + 'ruleId': parser.rule.name, + 'line': node.end.line, + 'column': node.end.column, + 'message': message, + 'severity': parser.severity + }; +}; + module.exports = { 'name': 'brace-style', 'defaults': { @@ -76,7 +178,7 @@ module.exports = { 'detect': function (ast, parser) { var result = []; - ast.traverseByTypes(['conditionalStatement', 'atrule', 'ruleset', 'mixin', 'loop'], function (block, i, parent) { + ast.traverseByTypes(['conditionalStatement', 'atrule', 'ruleset', 'mixin', 'loop'], function (node, i, parent) { var currentNode = false, previousNode = false, checks = { @@ -94,151 +196,55 @@ module.exports = { 'Closing brace must be on a new line' ]; - ////////////////////////////// - // Assign current & previous nodes - ////////////////////////////// - currentNode = block.contains('block') ? block.first('block') : false; - - // Rulesets - if (block.is('ruleset')) { - previousNode = block.contains('selector') ? block.last('selector') : false; - } + // Assign current & previous nodes based on node type + currentNode = getCurrentNode(node); + previousNode = getPreviousNode(node); - // Conditonal statements - if (block.is('conditionalStatement')) { - var previousParent = block.contains('condition') ? block.last('condition') : false; - previousNode = previousParent && previousParent.contains('atkeyword') ? previousParent.last('atkeyword') : false; - } + // If not an exception carry on + if (!isException(node, currentNode, previousNode)) { - // Functions, Mixins, Loops - if (block.is('atrule') || block.is('mixin') || block.is('loop')) { - previousNode = block.contains('atkeyword') ? block.last('atkeyword') : false; - } + // Run and store rule check results + checks = runRuleChecks(node, currentNode, previousNode, parent, i); - // If we've picked up a return @rule ignore it - if (block.is('atrule')) { - if (previousNode.contains('ident') && previousNode.first('ident').content === 'return') { - return false; + // Build single-line statement results + if (checks.singleLineStatement === false && checks.closingBraceOnNewLine === false) { + result = helpers.addUnique(result, createIssue(parser, currentNode, messages[5])); } - } - - ////////////////////////////// - // The rule checks - ////////////////////////////// - // Determine if single line statement - checks.singleLineStatement = isSingleLineStatement(block); - - // Determine if condition is on a new line - if (block.is('atrule') || block.is('conditionalStatement')) { - checks.conditionOnNewLine = isConditionOnNewLine(previousNode, parent, i); - } - - // Determine if opening brace is on new line - if (previousNode && currentNode) { - checks.openingBraceOnNewLine = isOpeningBraceOnNewLine(previousNode, currentNode); - } - - // Determine if closing brace is on new line - checks.closingBraceOnNewLine = isClosingBraceOnNewLine(block); - - ////////////////////////////// - // Build results - ////////////////////////////// - if (checks.singleLineStatement === false && checks.closingBraceOnNewLine === false) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': currentNode.end.line, - 'column': currentNode.end.column, - 'message': messages[5], - 'severity': parser.severity - }); - } - - if (checks.singleLineStatement === true) { - if (parser.options['allow-single-line'] === false) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': block.start.line, - 'column': block.start.column, - 'message': messages[0], - 'severity': parser.severity - }); - } - return false; - } - - if (previousNode && currentNode) { - - ////////////////////////////// - // Brace style: 1tbs - ////////////////////////////// - if (parser.options.style === '1tbs') { - if (checks.openingBraceOnNewLine === false) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': currentNode.start.line, - 'column': currentNode.start.column, - 'message': messages[1], - 'severity': parser.severity - }); - } - if (checks.conditionOnNewLine === true) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': previousNode.start.line, - 'column': previousNode.start.column, - 'message': messages[3], - 'severity': parser.severity - }); + if (checks.singleLineStatement === true) { + if (parser.options['allow-single-line'] === false) { + result = helpers.addUnique(result, createIssue(parser, node, messages[0])); } + return false; } - ////////////////////////////// - // Brace style: stroustrup - ////////////////////////////// - if (parser.options.style === 'stroustrup') { - if (checks.openingBraceOnNewLine === false) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': currentNode.start.line, - 'column': currentNode.start.column, - 'message': messages[1], - 'severity': parser.severity - }); + // Build brace-style results + if (previousNode && currentNode) { + if (parser.options.style === '1tbs') { + if (checks.openingBraceOnNewLine === false) { + result = helpers.addUnique(result, createIssue(parser, currentNode, messages[1])); + } + if (checks.conditionOnNewLine === true) { + result = helpers.addUnique(result, createIssue(parser, previousNode, messages[3])); + } } - if (checks.conditionOnNewLine === false) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': previousNode.start.line, - 'column': previousNode.start.column, - 'message': messages[4], - 'severity': parser.severity - }); - } - } - ////////////////////////////// - // Brace style: allman - ////////////////////////////// - if (parser.options.style === 'allman') { - if (checks.openingBraceOnNewLine === true) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': currentNode.start.line, - 'column': currentNode.start.column, - 'message': messages[2], - 'severity': parser.severity - }); + if (parser.options.style === 'stroustrup') { + if (checks.openingBraceOnNewLine === false) { + result = helpers.addUnique(result, createIssue(parser, currentNode, messages[1])); + } + if (checks.conditionOnNewLine === false) { + result = helpers.addUnique(result, createIssue(parser, previousNode, messages[4])); + } } - if (checks.conditionOnNewLine === false) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': previousNode.start.line, - 'column': previousNode.start.column, - 'message': messages[4], - 'severity': parser.severity - }); + + if (parser.options.style === 'allman') { + if (checks.openingBraceOnNewLine === true) { + result = helpers.addUnique(result, createIssue(parser, currentNode, messages[2])); + } + if (checks.conditionOnNewLine === false) { + result = helpers.addUnique(result, createIssue(parser, previousNode, messages[4])); + } } } } From 7fc2f582a70a21b1865a5691613c5cf5de3627a5 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Fri, 5 Feb 2016 22:59:13 +0000 Subject: [PATCH 054/137] :art: Refactor extends-before-mixins rule to work with Gonzales 3.2 --- lib/rules/extends-before-mixins.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/rules/extends-before-mixins.js b/lib/rules/extends-before-mixins.js index 5f565fec..6aff2593 100644 --- a/lib/rules/extends-before-mixins.js +++ b/lib/rules/extends-before-mixins.js @@ -12,11 +12,13 @@ module.exports = { var lastMixin = null; block.forEach(function (item, j) { - if (item.type === 'include' || item.type === 'extend') { - if (item.first('atkeyword')) { + // TODO: Remove tempory fix - atrule type is work around for issue: + // https://github.com/tonyganch/gonzales-pe/issues/147 + if (item.is('include') || item.is('extend') || item.is('atrule')) { + if (item.contains('atkeyword')) { var atkeyword = item.first('atkeyword'); - if (atkeyword.first('ident')) { + if (atkeyword.contains('ident')) { var ident = atkeyword.first('ident'); if (ident.content === 'extend') { @@ -34,7 +36,7 @@ module.exports = { } } - if (item.type === 'include') { + if (item.is('include')) { lastMixin = j; } }); From d48ad967704d07212a355ad9a7db62a61dd71556 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Fri, 5 Feb 2016 23:41:04 +0000 Subject: [PATCH 055/137] :art: Refactor extends-before-declarations rule to work with Gonzales 3.2 --- lib/rules/extends-before-declarations.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/rules/extends-before-declarations.js b/lib/rules/extends-before-declarations.js index 8597be7e..c920b744 100644 --- a/lib/rules/extends-before-declarations.js +++ b/lib/rules/extends-before-declarations.js @@ -13,25 +13,25 @@ module.exports = { var lastDeclaration = null; block.forEach(function (item, j) { - if ((item.type === 'include' || item.type === 'extend') && + // TODO: Remove tempory fix - atrule type is work around for issue: + // https://github.com/tonyganch/gonzales-pe/issues/147 + if ((item.is('include') || item.is('extend') || item.is('atrule')) && item.first('atkeyword')) { if (item.first('atkeyword').first('ident').content === 'extend') { if (j > lastDeclaration && lastDeclaration !== null) { - item.forEach('simpleSelector', function () { - error = { - 'ruleId': parser.rule.name, - 'line': item.start.line, - 'column': item.start.column, - 'message': 'Extends should come before declarations', - 'severity': parser.severity - }; - result = helpers.addUnique(result, error); - }); + error = { + 'ruleId': parser.rule.name, + 'line': item.start.line, + 'column': item.start.column, + 'message': 'Extends should come before declarations', + 'severity': parser.severity + }; + result = helpers.addUnique(result, error); } } } - if (item.type === 'declaration') { + if (item.is('declaration')) { lastDeclaration = j; } }); From e0401ae85982155b1def320084ea5c5bc9d83888 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sat, 6 Feb 2016 00:06:03 +0000 Subject: [PATCH 056/137] :art: Refactor single-line-per-selector (SCSS) to work with Gonzales 3.2 --- lib/rules/single-line-per-selector.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rules/single-line-per-selector.js b/lib/rules/single-line-per-selector.js index 0f7a9ab6..9ba9c7ca 100644 --- a/lib/rules/single-line-per-selector.js +++ b/lib/rules/single-line-per-selector.js @@ -8,12 +8,12 @@ module.exports = { 'detect': function (ast, parser) { var result = []; - ast.traverseByType('selector', function (selector) { - selector.forEach('delimiter', function (delimiter, i) { - var next = selector.content[i + 1]; + ast.traverseByType('ruleset', function (ruleset) { + ruleset.forEach('delimiter', function (delimiter, j) { + var next = ruleset.content[j + 1] || false; if (next) { - if (next.is('simpleSelector')) { + if (next.is('selector')) { next = next.content[0]; } From a456f183092f49199fa2be11b88b6b91e4956386 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sat, 6 Feb 2016 00:45:58 +0000 Subject: [PATCH 057/137] :art: Refcator no-qualifying-elements rule to work with Gonzales 3.2 --- lib/rules/no-qualifying-elements.js | 45 +++++++++++++++++++---------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/lib/rules/no-qualifying-elements.js b/lib/rules/no-qualifying-elements.js index 0973434b..117efc94 100644 --- a/lib/rules/no-qualifying-elements.js +++ b/lib/rules/no-qualifying-elements.js @@ -12,21 +12,36 @@ module.exports = { 'detect': function (ast, parser) { var result = []; - ast.traverseByType('simpleSelector', function (selectors) { - - selectors.content.forEach(function (item, i) { - if (item.is('class') || item.is('attribute') || item.is('id')) { - var previous = selectors.content[i - 1] || false; - - if (previous && previous.is('ident')) { - if (!parser.options['allow-element-with-' + item.type]) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': item.start.line, - 'column': item.start.column, - 'message': 'Qualifying elements are not allowed for ' + item.type + ' selectors', - 'severity': parser.severity - }); + ast.traverseByType('selector', function (selector) { + selector.forEach(function (item, i) { + if (item.is('attributeSelector') || item.is('class') || item.is('id')) { + var previous = selector.content[i - 1] || false; + + if (previous && previous.is('typeSelector')) { + if (previous.contains('ident')) { + var type = null; + + if (item.is('attributeSelector')) { + type = 'attribute'; + } + + if (item.is('class')) { + type = 'class'; + } + + if (item.is('id')) { + type = 'id'; + } + + if (type && !parser.options['allow-element-with-' + type]) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': item.start.line, + 'column': item.start.column, + 'message': 'Qualifying elements are not allowed for ' + type + ' selectors', + 'severity': parser.severity + }); + } } } } From c5dc7194a1588d73a58b3412fb228e1e5277c6ff Mon Sep 17 00:00:00 2001 From: Daniel Tschinder Date: Sat, 6 Feb 2016 14:52:08 +0100 Subject: [PATCH 058/137] :bug: Correct tests for empty-line-between-blocks rule The tests were working, but the tests are wrong as there are 10 instead of 9 errors in the sass file. Now the tests correctly fail. --- tests/rules/empty-line-between-blocks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/rules/empty-line-between-blocks.js b/tests/rules/empty-line-between-blocks.js index ca6a0157..f3d54ecc 100644 --- a/tests/rules/empty-line-between-blocks.js +++ b/tests/rules/empty-line-between-blocks.js @@ -208,7 +208,7 @@ describe('empty line between blocks - sass', function () { lint.test(file, { 'empty-line-between-blocks': 1 }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(10, data.warningCount); done(); }); }); @@ -225,7 +225,7 @@ describe('empty line between blocks - sass', function () { } ] }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(10, data.warningCount); done(); }); }); From 44192e34205dcab27c682cac3987b188603a758d Mon Sep 17 00:00:00 2001 From: Daniel Tschinder Date: Sat, 6 Feb 2016 14:55:23 +0100 Subject: [PATCH 059/137] :bug: Fix empty-line-between-blocks rule for gonzales 3.2 The checks were assuming that a ruleset always looks like this: [ selector, space, block ]. In newer versions of gonzales they can look like this: [ selector, delimiter, selector, space, block ]. --- lib/rules/empty-line-between-blocks.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/rules/empty-line-between-blocks.js b/lib/rules/empty-line-between-blocks.js index 3c3682d1..04711f53 100644 --- a/lib/rules/empty-line-between-blocks.js +++ b/lib/rules/empty-line-between-blocks.js @@ -163,14 +163,7 @@ module.exports = { // If it's a new line, lets go back up to the selector if (previous.is('space') && helpers.hasEOL(previous.content)) { - - // If we have a node (most likely type of selector) - if (parent.content[i - 2]) { - - if (typeof parent.content[i - 3] === 'undefined') { - space = findNearestReturnSass(p, j); - } - } + space = findNearestReturnSass(p, j); } }); } From f8fc8f3cae38e4ac4e14c2e8672aefb9f0a67b1f Mon Sep 17 00:00:00 2001 From: Daniel Tschinder Date: Sat, 6 Feb 2016 23:02:49 +0100 Subject: [PATCH 060/137] :bug: Fix indentation for gonzales 3.2 atruleb has been merged into atrule atrulers was reblaced by block. --- lib/rules/indentation.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/rules/indentation.js b/lib/rules/indentation.js index b7da28e7..0d7d4fea 100644 --- a/lib/rules/indentation.js +++ b/lib/rules/indentation.js @@ -86,13 +86,12 @@ module.exports = { } // if a block node is encountered we first check to see if it's within an include/function - // by checking if the node also contains argumentts, if it does we skip the block as we add a level + // by checking if the node also contains arguments, if it does we skip the block as we add a level // for arguments anyway. If not the the block is a usual ruleset block and should be treated accordingly // The other checks are kept from 1.0 and work for their respective types. if ((n.is('block') && !node.contains('arguments')) - || n.is('atrulers') || n.is('arguments') - || (n.is('parentheses') && !node.is('atruleb')) + || (n.is('parentheses') && !node.is('atrule')) ) { level++; } From 8f6f5954b11d849fdeb246b92e7f3ab43acb683a Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sun, 7 Feb 2016 11:48:18 +0000 Subject: [PATCH 061/137] :bug: Add support for global --- lib/rules/space-after-bang.js | 2 +- tests/rules/space-after-bang.js | 8 ++++---- tests/sass/space-after-bang.sass | 17 +++++++++++++---- tests/sass/space-after-bang.scss | 16 +++++++++++++--- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/lib/rules/space-after-bang.js b/lib/rules/space-after-bang.js index 4fb68a21..d6a2b2f1 100644 --- a/lib/rules/space-after-bang.js +++ b/lib/rules/space-after-bang.js @@ -11,7 +11,7 @@ module.exports = { var result = [], regex = /!\s/; - ast.traverseByTypes(['important', 'default'], function (block) { + ast.traverseByTypes(['important', 'default', 'global'], function (block) { if (block.content.match(regex) !== null) { if (parser.options.include) { result = helpers.addUnique(result, { diff --git a/tests/rules/space-after-bang.js b/tests/rules/space-after-bang.js index d4439a0d..ad4cc4c2 100644 --- a/tests/rules/space-after-bang.js +++ b/tests/rules/space-after-bang.js @@ -12,7 +12,7 @@ describe('space after bang - scss', function () { lint.test(file, { 'space-after-bang': 1 }, function (data) { - lint.assert.equal(4, data.warningCount); + lint.assert.equal(6, data.warningCount); done(); }); }); @@ -26,7 +26,7 @@ describe('space after bang - scss', function () { } ] }, function (data) { - lint.assert.equal(4, data.warningCount); + lint.assert.equal(6, data.warningCount); done(); }); }); @@ -42,7 +42,7 @@ describe('space after bang - sass', function () { lint.test(file, { 'space-after-bang': 1 }, function (data) { - lint.assert.equal(4, data.warningCount); + lint.assert.equal(6, data.warningCount); done(); }); }); @@ -56,7 +56,7 @@ describe('space after bang - sass', function () { } ] }, function (data) { - lint.assert.equal(4, data.warningCount); + lint.assert.equal(6, data.warningCount); done(); }); }); diff --git a/tests/sass/space-after-bang.sass b/tests/sass/space-after-bang.sass index 53584bb1..f2250fd8 100644 --- a/tests/sass/space-after-bang.sass +++ b/tests/sass/space-after-bang.sass @@ -20,9 +20,18 @@ $foo: red!default -$foo: orange !default +$bar: orange !default -$foo: blue! default +$baz: blue! default -$foo: green ! default - +$qux: green ! default + +// Global + +$foo: red!global + +$bar: orange !global + +$baz: blue! global + +$qux: green ! global diff --git a/tests/sass/space-after-bang.scss b/tests/sass/space-after-bang.scss index d4c445e7..4fd76e42 100644 --- a/tests/sass/space-after-bang.scss +++ b/tests/sass/space-after-bang.scss @@ -20,8 +20,18 @@ $foo: red!default; -$foo: red !default; +$bar: red !default; -$foo: red! default; +$baz: red! default; -$foo: red ! default; +$qux: red ! default; + +// Global + +$foo: red!global; + +$bar: red !global; + +$baz: red! global; + +$qux: red ! global; From d1b38b398cdf4ce90ca64616cde4786ea874ba3e Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sun, 7 Feb 2016 11:59:57 +0000 Subject: [PATCH 062/137] Add support for optional --- lib/rules/space-after-bang.js | 2 +- tests/rules/space-after-bang.js | 8 ++++---- tests/sass/space-after-bang.sass | 15 +++++++++++++++ tests/sass/space-after-bang.scss | 19 +++++++++++++++++++ 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/lib/rules/space-after-bang.js b/lib/rules/space-after-bang.js index d6a2b2f1..2377c9af 100644 --- a/lib/rules/space-after-bang.js +++ b/lib/rules/space-after-bang.js @@ -11,7 +11,7 @@ module.exports = { var result = [], regex = /!\s/; - ast.traverseByTypes(['important', 'default', 'global'], function (block) { + ast.traverseByTypes(['important', 'default', 'global', 'optional'], function (block) { if (block.content.match(regex) !== null) { if (parser.options.include) { result = helpers.addUnique(result, { diff --git a/tests/rules/space-after-bang.js b/tests/rules/space-after-bang.js index ad4cc4c2..2b340d69 100644 --- a/tests/rules/space-after-bang.js +++ b/tests/rules/space-after-bang.js @@ -12,7 +12,7 @@ describe('space after bang - scss', function () { lint.test(file, { 'space-after-bang': 1 }, function (data) { - lint.assert.equal(6, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -26,7 +26,7 @@ describe('space after bang - scss', function () { } ] }, function (data) { - lint.assert.equal(6, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -42,7 +42,7 @@ describe('space after bang - sass', function () { lint.test(file, { 'space-after-bang': 1 }, function (data) { - lint.assert.equal(6, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -56,7 +56,7 @@ describe('space after bang - sass', function () { } ] }, function (data) { - lint.assert.equal(6, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); diff --git a/tests/sass/space-after-bang.sass b/tests/sass/space-after-bang.sass index f2250fd8..d247d047 100644 --- a/tests/sass/space-after-bang.sass +++ b/tests/sass/space-after-bang.sass @@ -35,3 +35,18 @@ $bar: orange !global $baz: blue! global $qux: green ! global + +// Optional + +.foo + @extend .notice !optional + +.bar + @extend .notice ! optional + +// Both the following are invalid +// .baz +// @extend .notice!optional +// +// .qux +// @extend .notice! optional diff --git a/tests/sass/space-after-bang.scss b/tests/sass/space-after-bang.scss index 4fd76e42..a6d890be 100644 --- a/tests/sass/space-after-bang.scss +++ b/tests/sass/space-after-bang.scss @@ -35,3 +35,22 @@ $bar: red !global; $baz: red! global; $qux: red ! global; + +// Optional + +.foo { + @extend .notice !optional; +} + +.bar { + @extend .notice ! optional; +} + +// Both the following are invalid +// .baz { +// @extend .notice!optional; +// } +// +// .qux { +// @extend .notice! optional; +// } From 138eca3efbad4998400a810f3f84b548a86d423a Mon Sep 17 00:00:00 2001 From: Daniel Tschinder Date: Sun, 7 Feb 2016 11:38:58 +0100 Subject: [PATCH 063/137] :bug: Fix rule nesting-depth Currently only the scss part is fixed sass throws Fatal. --- lib/rules/nesting-depth.js | 39 +++++++++++++++++------------------ tests/rules/nesting-depth.js | 32 ++++++++++++++++++++++++++-- tests/sass/nesting-depth.sass | 14 +++++-------- tests/sass/nesting-depth.scss | 7 ++++++- 4 files changed, 60 insertions(+), 32 deletions(-) diff --git a/lib/rules/nesting-depth.js b/lib/rules/nesting-depth.js index 58ab62e6..eb944825 100644 --- a/lib/rules/nesting-depth.js +++ b/lib/rules/nesting-depth.js @@ -16,30 +16,29 @@ module.exports = { if (node.contains('block')) { node.forEach('block', function (block) { if (block.contains('ruleset')) { + depth++; block.forEach('ruleset', function (ruleset) { - ruleset.forEach('selector', function (selector) { - depth++; + var selector = ruleset.first('selector'); - if (depth > parser.options['max-depth']) { - selector.forEach('simpleSelector', function (simpleSelector) { - var nodeLineColumn = simpleSelector.start.line + ':' + simpleSelector.start.column; + if (depth > parser.options['max-depth']) { + var nodeLineColumn = selector.start.line + ':' + selector.start.column; - if (nodes[nodeLineColumn]) { - if (depth > nodes[nodeLineColumn].depth) { - nodes[nodeLineColumn].depth = depth; - } - } - else { - nodes[nodeLineColumn] = { - 'line': simpleSelector.start.line, - 'column': simpleSelector.start.column, - 'depth': depth - }; - } - }); + if (nodes[nodeLineColumn]) { + if (depth > nodes[nodeLineColumn].depth) { + nodes[nodeLineColumn].depth = depth; + } } - }); - recursiveSearch(ruleset); + else { + nodes[nodeLineColumn] = { + 'line': selector.start.line, + 'column': selector.start.column, + 'depth': depth + }; + } + } + else { + recursiveSearch(ruleset); + } }); } }); diff --git a/tests/rules/nesting-depth.js b/tests/rules/nesting-depth.js index f4d1fd9c..c848d203 100644 --- a/tests/rules/nesting-depth.js +++ b/tests/rules/nesting-depth.js @@ -12,7 +12,21 @@ describe('nesting depth - scss', function () { lint.test(file, { 'nesting-depth': 1 }, function (data) { - lint.assert.equal(2, data.warningCount); + lint.assert.equal(3, data.warningCount); + done(); + }); + }); + + it('[max-depth: 3]', function (done) { + lint.test(file, { + 'nesting-depth': [ + 1, + { + 'max-depth': 3 + } + ] + }, function (data) { + lint.assert.equal(1, data.warningCount); done(); }); }); @@ -28,7 +42,21 @@ describe('nesting depth - sass', function () { lint.test(file, { 'nesting-depth': 1 }, function (data) { - lint.assert.equal(2, data.warningCount); + lint.assert.equal(3, data.warningCount); + done(); + }); + }); + + it('[max-depth: 3]', function (done) { + lint.test(file, { + 'nesting-depth': [ + 1, + { + 'max-depth': 3 + } + ] + }, function (data) { + lint.assert.equal(1, data.warningCount); done(); }); }); diff --git a/tests/sass/nesting-depth.sass b/tests/sass/nesting-depth.sass index bce42ab0..0bd06de3 100644 --- a/tests/sass/nesting-depth.sass +++ b/tests/sass/nesting-depth.sass @@ -8,16 +8,16 @@ .fail content: 'bob' - &:hover + &:hover, + &:active content: 'fail' + .bar + content: 'fail' + .bar content: 'fail' - - - - .block content: 'bar' @@ -27,7 +27,3 @@ &--modifier content: 'bar' - - - - diff --git a/tests/sass/nesting-depth.scss b/tests/sass/nesting-depth.scss index f05e4d7c..ab2e6795 100644 --- a/tests/sass/nesting-depth.scss +++ b/tests/sass/nesting-depth.scss @@ -8,8 +8,13 @@ .fail { content: 'bob'; - &:hover { + &:hover, + &:active { content: 'fail'; + + .bar { + content: 'fail'; + } } .bar { From 24847612b57c84df386ff42e5e8cd15aa2d96f27 Mon Sep 17 00:00:00 2001 From: Ben Rothman Date: Fri, 5 Feb 2016 01:11:19 -0600 Subject: [PATCH 064/137] :bug: Fix collectSuffixExtensions and add tests --- lib/helpers.js | 38 ++++++-------------- tests/helpers.js | 93 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 99 insertions(+), 32 deletions(-) diff --git a/lib/helpers.js b/lib/helpers.js index 71f2e46c..cda8d39c 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -4,16 +4,7 @@ var util = require('util'), fs = require('fs'), path = require('path'), yaml = require('js-yaml'), - merge = require('merge'); - -/** - * Easy access to the 'merge' library's cloning functionality - * @param {object} obj Object to clone - * @returns {object} Clone of obj - */ -var clone = function (obj) { - return merge(true, obj); -}; + gonzales = require('gonzales-pe'); var helpers = {}; @@ -407,7 +398,7 @@ helpers.attemptTraversal = function (node, traversalPath) { * (without '.', '#', etc) resulting from suffix extensions */ helpers.collectSuffixExtensions = function (ruleset, selectorType) { - var parentSelectors = helpers.attemptTraversal(ruleset, ['selector', 'simpleSelector', selectorType, 'ident']), + var parentSelectors = helpers.attemptTraversal(ruleset, ['selector', selectorType, 'ident']), childSuffixes = helpers.attemptTraversal(ruleset, ['block', 'ruleset']), selectorList = []; @@ -419,25 +410,18 @@ helpers.collectSuffixExtensions = function (ruleset, selectorType) { // extended, so lots of looping is required. var processChildSuffix = function (child, parents) { var currentParents = [], - selectors = helpers.attemptTraversal(child, ['selector', 'simpleSelector']), + selectors = helpers.attemptTraversal(child, ['selector', 'parentSelectorExtension', 'ident']), nestedChildSuffixes = helpers.attemptTraversal(child, ['block', 'ruleset']); selectors.forEach(function (childSuffixNode) { - var extendedNode; - - if (childSuffixNode.length >= 2 && - childSuffixNode.contains('parentSelector') && - childSuffixNode.contains('ident')) { - - // append suffix extension to all parent selectors - parents.forEach(function (parent) { - // clone so we don't modify the actual AST - extendedNode = clone(childSuffixNode.first('ident')); - extendedNode.content = parent.content + extendedNode.content; - - currentParents.push(extendedNode); - }); - } + // append suffix extension to all parent selectors + parents.forEach(function (parent) { + // clone so we don't modify the actual AST + var clonedChildSuffixNode = gonzales.createNode(childSuffixNode); + clonedChildSuffixNode.content = parent.content + clonedChildSuffixNode.content; + + currentParents.push(clonedChildSuffixNode); + }); }); selectorList = selectorList.concat(currentParents); diff --git a/tests/helpers.js b/tests/helpers.js index 885ba040..88614652 100644 --- a/tests/helpers.js +++ b/tests/helpers.js @@ -1576,7 +1576,7 @@ describe('helpers', function () { ////////////////////////////// // attemptTraversal ////////////////////////////// - it('attemptTraversal - collect all nodes', function () { + it('attemptTraversal - SCSS - collect all nodes', function () { var stylesheet = gonzales.parse(['', '.a {', ' .b {', @@ -1599,7 +1599,7 @@ describe('helpers', function () { ); }); - it('attemptTraversal - empty array when traversal fails', function () { + it('attemptTraversal - SCSS - empty array when traversal fails', function () { var stylesheet = gonzales.parse(['', '.a {', ' color: red;', @@ -1611,10 +1611,42 @@ describe('helpers', function () { ); }); + it('attemptTraversal - Sass - collect all nodes', function () { + var stylesheet = gonzales.parse(['', + '.a', + ' .b', + ' color: red', + ' .c', + ' color: blue', + ' .d', + ' color: green', + ''].join('\n'), { syntax: 'sass' }); + + assert.deepEqual( + helpers.attemptTraversal(stylesheet, ['ruleset', 'block', 'ruleset', 'block', 'declaration', 'property', 'ident']) + .map(function (node) { + return node.content; + }), + ['color', 'color', 'color'] + ); + }); + + it('attemptTraversal - Sass - empty array when traversal fails', function () { + var stylesheet = gonzales.parse(['', + '.a', + ' color: red', + ''].join('\n'), { syntax: 'sass' }); + + assert.equal( + helpers.attemptTraversal(stylesheet, ['ruleset', 'block', 'ruleset', 'block']).length, + 0 + ); + }); + ////////////////////////////// // collectSuffixExtensions ////////////////////////////// - it('collectSuffixExtensions - no extensions', function () { + it('collectSuffixExtensions - SCSS - no extensions', function () { var ruleset = gonzales.parse(['', '.a {', ' .b {', @@ -1633,7 +1665,7 @@ describe('helpers', function () { ); }); - it('collectSuffixExtensions - BEM example', function () { + it('collectSuffixExtensions - SCSS - BEM example', function () { var ruleset = gonzales.parse(['', '.block {', ' &__element {', @@ -1652,7 +1684,7 @@ describe('helpers', function () { ); }); - it('collectSuffixExtensions - many parents and children', function () { + it('collectSuffixExtensions - SCSS - many parents and children', function () { var ruleset = gonzales.parse(['', '.a,', '.b {', @@ -1673,4 +1705,55 @@ describe('helpers', function () { ['a', 'b', 'ac', 'bc', 'ad', 'bd', 'ace', 'bce', 'ade', 'bde', 'acf', 'bcf', 'adf', 'bdf'] ); }); + + it('collectSuffixExtensions - Sass - no extensions', function () { + var ruleset = gonzales.parse(['', + '.a', + ' .b', + ' .c', + ' width: 2px', + ''].join('\n'), { syntax: 'sass' }) + .first('ruleset'); + + assert.deepEqual( + helpers.collectSuffixExtensions(ruleset, 'class').map(function (node) { + return node.content; + }), + ['a'] + ); + }); + + it('collectSuffixExtensions - Sass - BEM example', function () { + var ruleset = gonzales.parse(['', + '.block', + ' &__element', + ' &--modifier', + ' width: 2px', + ''].join('\n'), { syntax: 'sass' }) + .first('ruleset'); + + assert.deepEqual( + helpers.collectSuffixExtensions(ruleset, 'class').map(function (node) { + return node.content; + }), + ['block', 'block__element', 'block__element--modifier'] + ); + }); + + it('collectSuffixExtensions - Sass - many parents and children', function () { + var ruleset = gonzales.parse(['', + '.a, .b', + ' &c, &d', + ' &e, &f', + ' width: 2px', + ''].join('\n'), { syntax: 'sass' }) + .first('ruleset'); + + assert.deepEqual( + helpers.collectSuffixExtensions(ruleset, 'class').map(function (node) { + return node.content; + }), + ['a', 'b', 'ac', 'bc', 'ad', 'bd', 'ace', 'bce', 'ade', 'bde', 'acf', 'bcf', 'adf', 'bdf'] + ); + }); }); From 6e188e6f239cbf4d06274eebfed6a08979244335 Mon Sep 17 00:00:00 2001 From: Ben Rothman Date: Sun, 7 Feb 2016 23:49:03 -0600 Subject: [PATCH 065/137] Fix mixin-name-format --- lib/rules/mixin-name-format.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/rules/mixin-name-format.js b/lib/rules/mixin-name-format.js index c5ce6faa..4550452a 100644 --- a/lib/rules/mixin-name-format.js +++ b/lib/rules/mixin-name-format.js @@ -32,10 +32,8 @@ module.exports = { } } - if (node.contains('simpleSelector')) { - if (node.first('simpleSelector').contains('ident')) { - name = node.first('simpleSelector').first('ident').content; - } + if (node.contains('ident')) { + name = node.first('ident').content; } } From 4832f3b8c8b5b1583d0a34efae56618fd76b71ad Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Tue, 9 Feb 2016 12:32:12 +0000 Subject: [PATCH 066/137] :bug: SCSS only rule - add syntax check --- lib/rules/brace-style.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index 6e5215ca..26f18a87 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -196,6 +196,11 @@ module.exports = { 'Closing brace must be on a new line' ]; + // SCSS syntax only rule + if (ast.syntax === 'sass') { + return false; + } + // Assign current & previous nodes based on node type currentNode = getCurrentNode(node); previousNode = getPreviousNode(node); From 8addfb0dffbc46ff053a44b5f3cbcb1c3f4ba340 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Tue, 9 Feb 2016 22:53:33 +0000 Subject: [PATCH 067/137] :art: Fix mixins-before-declaration rule to work with latest gonzales --- lib/rules/mixins-before-declarations.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/rules/mixins-before-declarations.js b/lib/rules/mixins-before-declarations.js index 14c113d4..5c5d17cd 100644 --- a/lib/rules/mixins-before-declarations.js +++ b/lib/rules/mixins-before-declarations.js @@ -18,26 +18,23 @@ module.exports = { var depth = 0, declarationCount = [depth]; - parent.forEach( function (item) { - if (item.type === 'ruleset') { + parent.forEach(function (item) { + if (item.is('ruleset')) { depth++; declarationCount[depth] = 0; } - else if (item.type === 'declaration') { + else if (item.is('declaration')) { if (item.first().is('property')) { - var prop = item.first(); if (prop.first().is('ident')) { - declarationCount[depth]++; - } } } - else if (item.type === 'include') { - item.forEach('simpleSelector', function (name) { - if (parser.options.exclude.indexOf(name.content[0].content) === -1 && declarationCount[depth] > 0) { + else if (item.is('include')) { + item.forEach('ident', function (name) { + if (parser.options.exclude.indexOf(name.content) === -1 && declarationCount[depth] > 0) { error = { 'ruleId': parser.rule.name, 'line': item.start.line, From fac2f26b1720f4f2ba87e8848fc552df0cc38f20 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Fri, 12 Feb 2016 21:01:45 +0000 Subject: [PATCH 068/137] :bug: Fix PR feedback - tweak comments and add fallbacks --- lib/rules/brace-style.js | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index 26f18a87..286c8799 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -38,14 +38,14 @@ var getPreviousNode = function (node) { /** * Determine if current node is an exception and end checks if it is + * If we've picked up a return @rule ignore it * * @param {Object} node - The original node * @param {object} currentNode - The current node block * @param {Object} previousNode - The node previous to our current node - * @returns {bool} Wether or not the it is an exception + * @returns {bool} Wtether or not the it is an exception */ var isException = function (node, currentNode, previousNode) { - // If we've picked up a return @rule ignore it if (node.is('atrule')) { if (previousNode.contains('ident') && previousNode.first('ident').content === 'return') { return true; @@ -108,11 +108,14 @@ var isConditionOnNewLine = function (node, parentNode, j) { // Only check if it's an @else condition if (node.contains('ident') && node.first('ident').content === 'else') { // Reverse back up tree - var previousChild = parentNode.get(--j); + var previousChild = parentNode.get(--j) || false; - // Determine if we have a leading new line - if (previousChild.is('space') && helpers.hasEOL(previousChild.content)) { - return true; + if (previousChild) { + // Determine if we have a leading new line + if (previousChild.is('space') && helpers.hasEOL(previousChild.content)) { + return true; + } + return false; } return false; } @@ -120,16 +123,16 @@ var isConditionOnNewLine = function (node, parentNode, j) { }; /** - * Run the rule checks and store return their results + * Run the rule checks and return their results * * @param {Object} node - The original node * @param {Object} currentNode - The current node block * @param {Object} previousNode - The node previous to our current node * @param {Object} parentNode - The parent of the original node - * @param {int} i - The index of the original node + * @param {int} index - The index of the original node * @returns {Object} The results of the rule checks */ -var runRuleChecks = function (node, currentNode, previousNode, parentNode, i) { +var runRuleChecks = function (node, currentNode, previousNode, parentNode, index) { var checks = {}; // Determine if single line statement @@ -137,7 +140,7 @@ var runRuleChecks = function (node, currentNode, previousNode, parentNode, i) { // Determine if condition is on a new line if (node.is('atrule') || node.is('conditionalStatement')) { - checks.conditionOnNewLine = isConditionOnNewLine(previousNode, parentNode, i); + checks.conditionOnNewLine = isConditionOnNewLine(previousNode, parentNode, index); } // Determine if opening brace is on new line @@ -189,9 +192,9 @@ module.exports = { }, messages = [ 'Single line statements are not allowed', - 'Opening brace must be on same line as condition', + 'Opening brace must be on the same line as condition', 'Brace must be on a new line', - 'Statement must start on same line as closing brace of previous statement', + 'Statement must start on the same line as the closing brace of the previous statement', 'Statement must begin on a new line', 'Closing brace must be on a new line' ]; From c4af57c39bb6853e61f6974c6280c214474dbbe1 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Fri, 12 Feb 2016 21:02:32 +0000 Subject: [PATCH 069/137] :bug: Correct type --- lib/rules/brace-style.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index 286c8799..59067bad 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -43,7 +43,7 @@ var getPreviousNode = function (node) { * @param {Object} node - The original node * @param {object} currentNode - The current node block * @param {Object} previousNode - The node previous to our current node - * @returns {bool} Wtether or not the it is an exception + * @returns {bool} Whether or not the it is an exception */ var isException = function (node, currentNode, previousNode) { if (node.is('atrule')) { From 4213b66c6f809058efe33428d686ad33203f6201 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Sat, 13 Feb 2016 01:28:56 +0000 Subject: [PATCH 070/137] Fix force attribute nesting --- lib/rules/force-attribute-nesting.js | 59 +++++----------------------- 1 file changed, 10 insertions(+), 49 deletions(-) diff --git a/lib/rules/force-attribute-nesting.js b/lib/rules/force-attribute-nesting.js index 861e752e..297232c7 100644 --- a/lib/rules/force-attribute-nesting.js +++ b/lib/rules/force-attribute-nesting.js @@ -6,8 +6,8 @@ var helpers = require('../helpers'), // Our nestable selector types, separated by type for ease of use with rules // we replace ident with 'selector' for readability' -var nestableElements = ['selector', 'class', 'id'], - nestableAttributes = ['attribute'], +var nestableElements = ['selector', 'class', 'id', 'typeSelector'], + nestableAttributes = ['attributeSelector'], nestablePseudo = ['pseudoClass', 'pseudoElement', 'nth', 'nthSelector']; /** @@ -27,61 +27,22 @@ module.exports = { elements = nestableElements.concat(nestableAttributes, nestablePseudo); ast.traverseByType('ruleset', function (ruleset) { - var delimOrder = []; ruleset.forEach('selector', function (selector) { - // Where we'll store the previous value var previousVal; - - // Keep track of the order of elements & delimeters - selector.forEach(function (el) { - var curNode = helpers.mapDelims(el); - - if (curNode !== false) { - delimOrder.push(curNode); - } - }); - - selector.forEach('simpleSelector', function (simpleSelector) { - // check if the next selector is proceeded by a delimiter - // if it is add it to the curSelector output - var nextType = delimOrder[0]; - - if (nextType === 'd') { - // Empty the previous value store - previousVal = null; - - // remove the next delim and selector from our order as we are only - // looping over selectors - delimOrder.splice(0, 2); - } - else { - // if no delim then just remove the current selectors marker in the - // order array - delimOrder.splice(0, 1); - } - - // Loop each selector while checking if it should be nested - simpleSelector.forEach(function (node) { - // Construct the selector and store the current selector value - var constructedSelector = helpers.constructSelector(node), - currentVal = constructedSelector.type; - - if (helpers.isNestable(currentVal, previousVal, elements, nestableAttributes)) { + selector.forEach(function (item) { + if (previousVal) { + if (helpers.isNestable(item.type, previousVal.type, elements, nestableAttributes)) { helpers.addUnique(result, { 'ruleId': parser.rule.name, - 'line': node.start.line, - 'column': node.start.column, - 'message': formatOutput(currentVal) + ' `' + constructedSelector.content + '` should be nested within its parent ' + previousVal, + 'line': selector.start.line, + 'column': selector.start.column, + 'message': formatOutput(item.type) + 'should be nested within its parent ' + previousVal.type, 'severity': parser.severity }); } - - if (currentVal) { - // Store current value as previous and continue with the loop - previousVal = currentVal; - } - }); + } + previousVal = item; }); }); }); From d3d05f139a827a744ffa73d5b1cf32b3852328e6 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Sat, 13 Feb 2016 01:50:19 +0000 Subject: [PATCH 071/137] Fix force pseudo nesting --- lib/rules/force-pseudo-nesting.js | 59 ++++++------------------------- 1 file changed, 10 insertions(+), 49 deletions(-) diff --git a/lib/rules/force-pseudo-nesting.js b/lib/rules/force-pseudo-nesting.js index 3f1b2d73..82ce9ede 100644 --- a/lib/rules/force-pseudo-nesting.js +++ b/lib/rules/force-pseudo-nesting.js @@ -6,8 +6,8 @@ var helpers = require('../helpers'), // Our nestable selector types, separated by type for ease of use with rules // we replace ident with 'selector' for readability' -var nestableElements = ['selector', 'class', 'id'], - nestableAttributes = ['attribute'], +var nestableElements = ['selector', 'class', 'id', 'typeSelector'], + nestableAttributes = ['attributeSelector'], nestablePseudo = ['pseudoClass', 'pseudoElement', 'nth', 'nthSelector']; /** @@ -34,61 +34,22 @@ module.exports = { elements = nestableElements.concat(nestableAttributes, nestablePseudo); ast.traverseByType('ruleset', function (ruleset) { - var delimOrder = []; ruleset.forEach('selector', function (selector) { - // Where we'll store the previous value var previousVal; - - // Keep track of the order of elements & delimeters - selector.forEach(function (el) { - var curNode = helpers.mapDelims(el); - - if (curNode !== false) { - delimOrder.push(curNode); - } - }); - - selector.forEach('simpleSelector', function (simpleSelector) { - // check if the next selector is proceeded by a delimiter - // if it is add it to the curSelector output - var nextType = delimOrder[0]; - - if (nextType === 'd') { - // Empty the previous value store - previousVal = null; - - // remove the next delim and selector from our order as we are only - // looping over selectors - delimOrder.splice(0, 2); - } - else { - // if no delim then just remove the current selectors marker in the - // order array - delimOrder.splice(0, 1); - } - - // Loop each selector while checking if it should be nested - simpleSelector.forEach(function (node) { - // Construct the selector and store the current selector value - var constructedSelector = helpers.constructSelector(node), - currentVal = constructedSelector.type; - - if (helpers.isNestable(currentVal, previousVal, elements, nestablePseudo)) { + selector.forEach(function (item) { + if (previousVal) { + if (helpers.isNestable(item.type, previousVal.type, elements, nestablePseudo)) { helpers.addUnique(result, { 'ruleId': parser.rule.name, - 'line': node.start.line, - 'column': node.start.column, - 'message': formatOutput(currentVal) + ' `' + constructedSelector.content + '` should be nested within its parent ' + previousVal, + 'line': selector.start.line, + 'column': selector.start.column, + 'message': formatOutput(item.type) + 'should be nested within its parent ' + previousVal.type, 'severity': parser.severity }); } - - if (currentVal) { - // Store current value as previous and continue with the loop - previousVal = currentVal; - } - }); + } + previousVal = item; }); }); }); From d0a21e734d10ecf83cd3841ddc33540df79ccc36 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Sat, 13 Feb 2016 02:14:37 +0000 Subject: [PATCH 072/137] Fix force element nesting --- lib/rules/force-element-nesting.js | 67 +++++++----------------------- 1 file changed, 15 insertions(+), 52 deletions(-) diff --git a/lib/rules/force-element-nesting.js b/lib/rules/force-element-nesting.js index 7e9692bb..8f0ff55d 100644 --- a/lib/rules/force-element-nesting.js +++ b/lib/rules/force-element-nesting.js @@ -4,10 +4,10 @@ var helpers = require('../helpers'), capitalize = require('lodash.capitalize'), kebabcase = require('lodash.kebabcase'); -// Our nestable selector types, separated by type for ease of use with rules -// we replace ident with 'selector' for readability' -var nestableElements = ['selector', 'class', 'id'], - nestableAttributes = ['attribute'], + // Our nestable selector types, separated by type for ease of use with rules + // we replace ident with 'selector' for readability' +var nestableElements = ['selector', 'class', 'id', 'typeSelector', 'parentSelectorExtension'], + nestableAttributes = ['attributeSelector'], nestablePseudo = ['pseudoClass', 'pseudoElement', 'nth', 'nthSelector']; /** @@ -27,61 +27,24 @@ module.exports = { elements = nestableElements.concat(nestableAttributes, nestablePseudo); ast.traverseByType('ruleset', function (ruleset) { - var delimOrder = []; + ruleset.forEach(function (selector) { - ruleset.forEach('selector', function (selector) { - // Where we'll store the previous value var previousVal; - - // Keep track of the order of elements & delimeters - selector.forEach(function (el) { - var curNode = helpers.mapDelims(el); - - if (curNode !== false) { - delimOrder.push(curNode); - } - }); - - selector.forEach('simpleSelector', function (simpleSelector) { - // check if the next selector is proceeded by a delimiter - // if it is add it to the curSelector output - var nextType = delimOrder[0]; - - if (nextType === 'd') { - // Empty the previous value store - previousVal = null; - - // remove the next delim and selector from our order as we are only - // looping over selectors - delimOrder.splice(0, 2); - } - else { - // if no delim then just remove the current selectors marker in the - // order array - delimOrder.splice(0, 1); - } - - // Loop each selector while checking if it should be nested - simpleSelector.forEach(function (node) { - // Construct the selector and store the current selector value - var constructedSelector = helpers.constructSelector(node), - currentVal = constructedSelector.type; - - if (helpers.isNestable(currentVal, previousVal, elements, nestableElements)) { + selector.forEach(function (item) { + if (previousVal) { + if (helpers.isNestable(item.type, previousVal.type, elements, nestableElements)) { helpers.addUnique(result, { 'ruleId': parser.rule.name, - 'line': node.start.line, - 'column': node.start.column, - 'message': formatOutput(currentVal) + ' `' + constructedSelector.content + '` should be nested within its parent ' + previousVal, + 'line': selector.start.line, + 'column': selector.start.column, + 'message': formatOutput(item.type) + 'should be nested within its parent ' + previousVal.type, 'severity': parser.severity }); } - - if (currentVal) { - // Store current value as previous and continue with the loop - previousVal = currentVal; - } - }); + } + if (!item.is('space')) { + previousVal = item; + } }); }); }); From da401d15c55a012d193271c06d53a4cdfa7e4b77 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Sat, 13 Feb 2016 02:18:24 +0000 Subject: [PATCH 073/137] Add missing parent selector extension --- lib/rules/force-pseudo-nesting.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/force-pseudo-nesting.js b/lib/rules/force-pseudo-nesting.js index 82ce9ede..6be47d74 100644 --- a/lib/rules/force-pseudo-nesting.js +++ b/lib/rules/force-pseudo-nesting.js @@ -6,7 +6,7 @@ var helpers = require('../helpers'), // Our nestable selector types, separated by type for ease of use with rules // we replace ident with 'selector' for readability' -var nestableElements = ['selector', 'class', 'id', 'typeSelector'], +var nestableElements = ['selector', 'class', 'id', 'typeSelector', 'parentSelectorExtension'], nestableAttributes = ['attributeSelector'], nestablePseudo = ['pseudoClass', 'pseudoElement', 'nth', 'nthSelector']; From 25a356b0c8b127b8345edba4f12ecdc5b99bacb2 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Sat, 13 Feb 2016 02:19:24 +0000 Subject: [PATCH 074/137] Add missing parent selector extension node type --- lib/rules/force-attribute-nesting.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/force-attribute-nesting.js b/lib/rules/force-attribute-nesting.js index 297232c7..b657ab54 100644 --- a/lib/rules/force-attribute-nesting.js +++ b/lib/rules/force-attribute-nesting.js @@ -6,7 +6,7 @@ var helpers = require('../helpers'), // Our nestable selector types, separated by type for ease of use with rules // we replace ident with 'selector' for readability' -var nestableElements = ['selector', 'class', 'id', 'typeSelector'], +var nestableElements = ['selector', 'class', 'id', 'typeSelector', 'parentSelectorExtension'], nestableAttributes = ['attributeSelector'], nestablePseudo = ['pseudoClass', 'pseudoElement', 'nth', 'nthSelector']; From b99210a694f14f2718165005d7848669598ee67e Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Sat, 13 Feb 2016 02:21:40 +0000 Subject: [PATCH 075/137] Fix odd indentation --- lib/rules/force-element-nesting.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rules/force-element-nesting.js b/lib/rules/force-element-nesting.js index 8f0ff55d..14b4a18a 100644 --- a/lib/rules/force-element-nesting.js +++ b/lib/rules/force-element-nesting.js @@ -4,8 +4,8 @@ var helpers = require('../helpers'), capitalize = require('lodash.capitalize'), kebabcase = require('lodash.kebabcase'); - // Our nestable selector types, separated by type for ease of use with rules - // we replace ident with 'selector' for readability' +// Our nestable selector types, separated by type for ease of use with rules +// we replace ident with 'selector' for readability' var nestableElements = ['selector', 'class', 'id', 'typeSelector', 'parentSelectorExtension'], nestableAttributes = ['attributeSelector'], nestablePseudo = ['pseudoClass', 'pseudoElement', 'nth', 'nthSelector']; From ee305239524184e1aa85a40fb72563633a13c02e Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Sat, 13 Feb 2016 02:25:23 +0000 Subject: [PATCH 076/137] Add missing space --- lib/rules/force-element-nesting.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/force-element-nesting.js b/lib/rules/force-element-nesting.js index 14b4a18a..a81b5904 100644 --- a/lib/rules/force-element-nesting.js +++ b/lib/rules/force-element-nesting.js @@ -37,7 +37,7 @@ module.exports = { 'ruleId': parser.rule.name, 'line': selector.start.line, 'column': selector.start.column, - 'message': formatOutput(item.type) + 'should be nested within its parent ' + previousVal.type, + 'message': formatOutput(item.type) + ' should be nested within its parent ' + previousVal.type, 'severity': parser.severity }); } From 8a998724556a1b562a26f9404329f66db8298722 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Sat, 13 Feb 2016 02:26:06 +0000 Subject: [PATCH 077/137] Add missing space --- lib/rules/force-pseudo-nesting.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/force-pseudo-nesting.js b/lib/rules/force-pseudo-nesting.js index 6be47d74..51f31076 100644 --- a/lib/rules/force-pseudo-nesting.js +++ b/lib/rules/force-pseudo-nesting.js @@ -44,7 +44,7 @@ module.exports = { 'ruleId': parser.rule.name, 'line': selector.start.line, 'column': selector.start.column, - 'message': formatOutput(item.type) + 'should be nested within its parent ' + previousVal.type, + 'message': formatOutput(item.type) + ' should be nested within its parent ' + previousVal.type, 'severity': parser.severity }); } From 2381a4de08a2f282eff6654eea62824a0ab2cffc Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Sat, 13 Feb 2016 02:26:58 +0000 Subject: [PATCH 078/137] Add missing space --- lib/rules/force-attribute-nesting.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/force-attribute-nesting.js b/lib/rules/force-attribute-nesting.js index b657ab54..419aa432 100644 --- a/lib/rules/force-attribute-nesting.js +++ b/lib/rules/force-attribute-nesting.js @@ -37,7 +37,7 @@ module.exports = { 'ruleId': parser.rule.name, 'line': selector.start.line, 'column': selector.start.column, - 'message': formatOutput(item.type) + 'should be nested within its parent ' + previousVal.type, + 'message': formatOutput(item.type) + ' should be nested within its parent ' + previousVal.type, 'severity': parser.severity }); } From facb5e72ff694c6c11cbc63c00f9230a15cc9e8c Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Sat, 13 Feb 2016 02:28:23 +0000 Subject: [PATCH 079/137] Improve error message --- lib/rules/force-attribute-nesting.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/force-attribute-nesting.js b/lib/rules/force-attribute-nesting.js index 419aa432..cdb9dddd 100644 --- a/lib/rules/force-attribute-nesting.js +++ b/lib/rules/force-attribute-nesting.js @@ -37,7 +37,7 @@ module.exports = { 'ruleId': parser.rule.name, 'line': selector.start.line, 'column': selector.start.column, - 'message': formatOutput(item.type) + ' should be nested within its parent ' + previousVal.type, + 'message': formatOutput(item.type) + ' should be nested within its parent ' + formatOutput(previousVal.type), 'severity': parser.severity }); } From efe73ae60404b2f61776d303912a2c6578ff1336 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Sat, 13 Feb 2016 02:29:08 +0000 Subject: [PATCH 080/137] Improve error message --- lib/rules/force-pseudo-nesting.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/force-pseudo-nesting.js b/lib/rules/force-pseudo-nesting.js index 51f31076..6ddc8fa0 100644 --- a/lib/rules/force-pseudo-nesting.js +++ b/lib/rules/force-pseudo-nesting.js @@ -44,7 +44,7 @@ module.exports = { 'ruleId': parser.rule.name, 'line': selector.start.line, 'column': selector.start.column, - 'message': formatOutput(item.type) + ' should be nested within its parent ' + previousVal.type, + 'message': formatOutput(item.type) + ' should be nested within its parent ' + formatOutput(previousVal.type), 'severity': parser.severity }); } From a8fcf6445b7b3a1e7fd9ce91872ef229d56e2c7d Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Sat, 13 Feb 2016 02:29:47 +0000 Subject: [PATCH 081/137] Improve error message --- lib/rules/force-element-nesting.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/force-element-nesting.js b/lib/rules/force-element-nesting.js index a81b5904..22530c46 100644 --- a/lib/rules/force-element-nesting.js +++ b/lib/rules/force-element-nesting.js @@ -37,7 +37,7 @@ module.exports = { 'ruleId': parser.rule.name, 'line': selector.start.line, 'column': selector.start.column, - 'message': formatOutput(item.type) + ' should be nested within its parent ' + previousVal.type, + 'message': formatOutput(item.type) + ' should be nested within its parent ' + formatOutput(previousVal.type), 'severity': parser.severity }); } From 425af5bc97d085a188ed55634de81d28ef4676e1 Mon Sep 17 00:00:00 2001 From: greenkeeperio-bot Date: Tue, 16 Feb 2016 04:41:59 -0500 Subject: [PATCH 082/137] chore(package): update lodash.kebabcase to version 4.0.0 http://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f97deb14..7316f419 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "gonzales-pe": "3.0.0-31", "js-yaml": "^3.2.6", "lodash.capitalize": "^4.1.0", - "lodash.kebabcase": "^3.0.1", + "lodash.kebabcase": "^4.0.0", "merge": "^1.2.0", "util": "^0.10.3" }, From a6bed62358d7fff2a6b6d6fa39d65e589cd7a23c Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sat, 20 Feb 2016 10:21:54 +0000 Subject: [PATCH 083/137] :bug: Fix dot in filename issue with clean-import-paths rule --- lib/rules/clean-import-paths.js | 2 +- tests/sass/clean-import-paths.scss | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/rules/clean-import-paths.js b/lib/rules/clean-import-paths.js index 47cda299..0616fdb4 100644 --- a/lib/rules/clean-import-paths.js +++ b/lib/rules/clean-import-paths.js @@ -71,7 +71,7 @@ module.exports = { var filename = path.basename(importPath), fileExtension = path.extname(filename); - if (fileExtension !== '.css') { + if (fileExtension === '.sass' || fileExtension === '.scss' || fileExtension === '') { if (filename.charAt(0) === '_') { if (!parser.options['leading-underscore']) { result = helpers.addUnique(result, { diff --git a/tests/sass/clean-import-paths.scss b/tests/sass/clean-import-paths.scss index bcc4ad1a..da485511 100644 --- a/tests/sass/clean-import-paths.scss +++ b/tests/sass/clean-import-paths.scss @@ -25,3 +25,6 @@ @if variable-exists(google-fonts-url) { @import url($google-fonts-url); } + +// Test dot in filename +@import '../../node_modules/inuit-normalize/generic.normalize'; From 8eaebe8bcfdc37f3dd31e53e59dcf679f45d40d6 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sat, 20 Feb 2016 10:21:54 +0000 Subject: [PATCH 084/137] :bug: Fix dot in filename issue with clean-import-paths rule --- lib/rules/clean-import-paths.js | 2 +- tests/sass/clean-import-paths.scss | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/rules/clean-import-paths.js b/lib/rules/clean-import-paths.js index 47cda299..0616fdb4 100644 --- a/lib/rules/clean-import-paths.js +++ b/lib/rules/clean-import-paths.js @@ -71,7 +71,7 @@ module.exports = { var filename = path.basename(importPath), fileExtension = path.extname(filename); - if (fileExtension !== '.css') { + if (fileExtension === '.sass' || fileExtension === '.scss' || fileExtension === '') { if (filename.charAt(0) === '_') { if (!parser.options['leading-underscore']) { result = helpers.addUnique(result, { diff --git a/tests/sass/clean-import-paths.scss b/tests/sass/clean-import-paths.scss index bcc4ad1a..da485511 100644 --- a/tests/sass/clean-import-paths.scss +++ b/tests/sass/clean-import-paths.scss @@ -25,3 +25,6 @@ @if variable-exists(google-fonts-url) { @import url($google-fonts-url); } + +// Test dot in filename +@import '../../node_modules/inuit-normalize/generic.normalize'; From 2cf7e54f7ed1b379dd941b853872601786e1a9e1 Mon Sep 17 00:00:00 2001 From: mdmoreau Date: Fri, 26 Feb 2016 10:33:47 -0500 Subject: [PATCH 085/137] smacss sort order background-attachment --- lib/config/property-sort-orders/smacss.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/config/property-sort-orders/smacss.yml b/lib/config/property-sort-orders/smacss.yml index e5fd2ba7..408fd48b 100644 --- a/lib/config/property-sort-orders/smacss.yml +++ b/lib/config/property-sort-orders/smacss.yml @@ -101,6 +101,7 @@ order: # Background - 'background' + - 'background-attachment' - 'background-clip' - 'background-color' - 'background-image' From f9ceada1658fdfe11627d8a3cccecc0774cbf312 Mon Sep 17 00:00:00 2001 From: mdmoreau Date: Fri, 26 Feb 2016 10:38:07 -0500 Subject: [PATCH 086/137] smacss sort order transform and transition --- lib/config/property-sort-orders/smacss.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/config/property-sort-orders/smacss.yml b/lib/config/property-sort-orders/smacss.yml index 408fd48b..afe59c5d 100644 --- a/lib/config/property-sort-orders/smacss.yml +++ b/lib/config/property-sort-orders/smacss.yml @@ -59,7 +59,15 @@ order: - 'column-width' - 'transform' + - 'transform-box' + - 'transform-origin' + - 'transform-style' + - 'transition' + - 'transition-delay' + - 'transition-duration' + - 'transition-property' + - 'transition-timing-function' # Border From fc9995c418a5f2b67015464d6089007794f43ed3 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Wed, 2 Mar 2016 10:54:10 +0000 Subject: [PATCH 087/137] :art: Update no-mergeable-selectors for gonzales 3.2 --- lib/rules/no-mergeable-selectors.js | 403 ++++++++++++++++------------ 1 file changed, 234 insertions(+), 169 deletions(-) diff --git a/lib/rules/no-mergeable-selectors.js b/lib/rules/no-mergeable-selectors.js index 6550205e..724ad61f 100644 --- a/lib/rules/no-mergeable-selectors.js +++ b/lib/rules/no-mergeable-selectors.js @@ -2,225 +2,290 @@ var helpers = require('../helpers'); +var mergeableNodes = ['atrule', 'include', 'ruleset'], + validAtRules = ['media'], + simpleIdents = ['ident', 'number', 'operator', 'combinator', 'string', 'parentSelector', 'delimiter', 'typeSelector', 'attributeMatch'], + curLevel = 0, + curSelector = [], + parentSelector = [], + selectorList = [], + syntax = ''; + + /** - * Constructs a syntax complete selector for our selector matching and warning output - * @param {object} val - the current node / part of our selector - * @returns {object} - content: The current node with correct syntax e.g. class my-class = '.my-class' + * 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; +}; -var constructSelector = function (val) { +/** + * 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); + }); - var content = val.content; + 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 = '#' + val.content; + content = addGrammar(val, '#', ''); } else if (val.is('class')) { - content = '.' + val.content; + content = addGrammar(val, '.', ''); } - else if (val.is('ident')) { + else if (simpleIdents.indexOf(val.type) !== -1) { content = val.content; } - else if (val.is('attribute')) { - var selector = '['; + else if (val.is('attributeSelector')) { + content = constructSubSelector(val, '[', ']', constructSelector); + } - val.forEach( function (attrib) { - selector += constructSelector(attrib); - }); + 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); - content = selector + ']'; } else if (val.is('pseudoClass')) { - content = ':' + val.content; + content = addGrammar(val, ':', ''); } else if (val.is('pseudoElement')) { - content = '::' + val.content; + content = addGrammar(val, '::', ''); } else if (val.is('nth')) { - content = '(' + val.content + ')'; + content = addGrammar(val, '(', ')'); } else if (val.is('nthSelector')) { - var nthSelector = ':'; - - val.forEach( function (attrib) { - nthSelector += constructSelector(attrib); - }); + content = constructSubSelector(val, ':', '', constructSelector); + } - content = nthSelector; + else if (val.is('parentheses')) { + content = constructSubSelector(val, '(', ')', constructSelector); } else if (val.is('space')) { content = ' '; } - else if (val.is('parentSelector')) { - content = val.content; - } - - else if (val.is('combinator')) { - content = val.content; + else if (val.is('parentSelectorExtension') || val.is('attributeName') || val.is('attributeValue') || val.is('dimension')) { + content = constructSubSelector(val, '', '', constructSelector); } return content; +}; +/** + * Traverses a block and calls our callback function for each block encountered + * + * @param {object} block - The current node / part of our selector + * @param {object} cb - The callback function we wish to apply to each block + * @returns {undefined} + */ +var traverseBlock = function (block, cb) { + block.forEach(function (contentItem) { + cb(contentItem); + }); }; -module.exports = { - 'name': 'no-mergeable-selectors', - 'defaults': { - 'whitelist': [] - }, - 'detect': function (ast, parser) { - var result = [], - selectorList = [], - parentSelector = [], - - // we use this array to keep track of the number of nested blocks and their levels - // seen as we will always start with a single block at level 0 we just the first - // level count to 1 - childBlocks = [1], - level = 0; - - ast.traverseByType('ruleset', function (ruleset) { - var selectorBlock = { - selector: parentSelector.join(' '), - line: '' - }, - curSelector = '', - delimOrder = []; - - ruleset.forEach('selector', function (selector) { - // Keep track of the order of elements & delimeters - selector.forEach( function (el) { - var curNode = helpers.mapDelims(el); - - if (curNode !== false) { - delimOrder.push(curNode); - } - }); +/** + * Traverses a block and calls our callback function for each block encountered + * + * @param {string} ruleSet - The current selector + * @param {boolean} isAtRule - Whether the ruleSet is an atRule + * @param {string} line - The line that the ruleset starts + * @param {string} col - The column that the ruleset starts + * @returns {undefined} + */ +var updateList = function (ruleSet, isAtRule, line, col) { + parentSelector[curLevel] = ruleSet; + curSelector = { + selector: helpers.stripLastSpace(parentSelector.join('')), + line: line, + column: col + }; + if (!isAtRule) { + selectorList.push(curSelector); + } +}; - selector.forEach('simpleSelector', function (simpleSelector) { - // check if the next selector is proceeded by a delimiter - // if it is add it to the curSelector output - var nextType = delimOrder[0]; - - if (nextType === 'd') { - curSelector += ', '; - // remove the next delim and selector from our order as we are only looping over selectors - delimOrder.splice(0, 2); - - } - else { - // if no delim then just remove the current selectors marker in the order array - delimOrder.splice(0, 1); - } - - simpleSelector.forEach(function (val) { - // construct our selector from its content parts - curSelector += constructSelector(val); - }); +/** + * Checks a rulesets contents for selectors and calls our consstructSelector method + * + * @param {object} ruleNode - The current node / part of our selector + * @returns {undefined} + */ +var checkRuleset = function (ruleNode) { + var ruleSet = ''; + ruleNode.forEach(function (ruleNodeItem) { + if (!ruleNodeItem.is('block')) { + if (ruleNodeItem.is('selector')) { + ruleNodeItem.forEach(function (selectorContent) { + ruleSet += constructSelector(selectorContent); }); + } + else if (ruleNodeItem.is('delimiter') || ruleNodeItem.is('space')) { + ruleSet += constructSelector(ruleNodeItem); + } + } + }); + if (ruleSet !== '') { + updateList(ruleSet, false, ruleNode.start.line, ruleNode.start.column); + } +}; - // Gonzales keeps the spaces after the selector, we remove them here to keep the selectors recognisable - // and consisten in the result output. - curSelector = helpers.stripLastSpace(curSelector); - - // check to see if we are on a level other than default (0) - if (level) { - // remove 1 from the block count on the current level - childBlocks[level] -= 1; - } - - // check to see if the current ruleset contains any child rulesets - if (ruleset.first('block').contains('ruleset')) { - ruleset.first('block').forEach('ruleset', function () { - - // Keep a record of the number of rulesets on the next level of nesting - if (childBlocks[level + 1]) { - childBlocks[level + 1] += 1; - } - else { - childBlocks[level + 1] = 1; - } - }); - - // as we have another level we add a space to the end of the current selector reference - if (parentSelector[level]) { - parentSelector[level] += curSelector + ' '; - } - else { - parentSelector[level] = curSelector + ' '; - } - - // increase our level counter before we start parsing the child rulesets - level = level + 1; - } - // if no child rulesets/blocks then we need to find the level we will next be working on - else { - - // we scan backwards through our block counter array until we find a number greater than 0 - // We've been decrementing our block counts on the correct level as we've parsed them so a - // level with a positive count means there are still rulesets here to parse. - for (var i = childBlocks.length - 1; i >= 0; i-- ) { - if (childBlocks[i] === 0) { - - // remove the last element from the parent selector array as our level decreases effectively allowing - // us to correctly concat our parent and child selectors - parentSelector.splice(parentSelector.length - 1, 1); - - // if we're not on level 0 then we want to decrement the level as there are no child rules of this block left to parse. - // We then remove the 0 count element from the end of the childblocks array to make it ready for use again. - if (level) { - level = level - 1; - childBlocks.splice(childBlocks.length - 1, 1); - } - } - else { - - // if the current level has rulesets to parse we break out of the for loop - break; - } - } - } - - // set the line of the current selector blocks start for our error messages - // keep a reference with this for our current selector - selectorBlock.line = ruleset.start.line; - selectorBlock.selector += curSelector; +/** + * Checks an atRule contents for selectors and calls our consstructSelector method + * + * @param {object} atRule - The current node / atRule part of our selector + * @returns {undefined} + */ +var checkAtRule = function (atRule) { + var test = ''; + atRule.forEach(function (atRuleItem) { + if (!atRuleItem.is('block')) { + test += constructSelector(atRuleItem); + } + }); + updateList(test, true, atRule.start.line, atRule.start.column); +}; - }); +/** + * Checks an atRule to see if if it's part of our mergeable at rule list. + * It also checks for Sass syntax as gonzales currently has issues with the syntax + * + * @param {object} node - The current node / atRule part of our selector + * @returns {boolean} Whether this atRule should be merged or not + */ +var isMergeableAtRule = function (node) { + var isMergeable = false; + node.forEach(function (item) { + // Gonzales has issues with nest levels in media queries :( + if (item.is('atkeyword') && validAtRules.indexOf(item.first('ident').content) !== -1 && syntax !== 'sass') { + isMergeable = true; + } + }); + + return isMergeable; +}; - // we check to see if our selector has already been encountered, if it has we generate a lint warning/error - // detailing which selector is failing and on which line. - var present = helpers.propertySearch(selectorList, selectorBlock.selector, 'selector'); - - if (present !== -1) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': ruleset.start.line, - 'column': ruleset.start.column, - 'message': 'Rule `' + curSelector + '` should be merged with the rule on line ' + selectorList[present].line, - 'severity': parser.severity - }); +/** + * Checks if a node contains a block and if so calls our traverseBlock method. Also + * handles our current level counter. + * + * @param {object} node - The current node / atRule part of our selector + * @param {object} cb - The callback function we wish to pass through + * @returns {undefined} + */ +var checkForBlock = function (node, cb) { + if (node.contains('block')) { + curLevel += 1; + node.forEach('block', function (block) { + traverseBlock(block.content, cb); + }); + curLevel -= 1; + parentSelector.pop(); + } +}; + +/** + * Traverses a node and checks for rulesets and at rules and then fires off to the + * respective method for them to be handled + * + * @param {object} node - The current node / atRule part of our selector + * @returns {undefined} + */ +var traverseNode = function (node) { + if (mergeableNodes.indexOf(node.type) !== -1) { + if (node.is('ruleset')) { + checkRuleset(node); + checkForBlock(node, traverseNode); + } + else if (node.is('atrule')) { + if (isMergeableAtRule(node)) { + checkAtRule(node); + checkForBlock(node, traverseNode); } - else { + } + } +}; - // if the current selector is whitelisted we don't add it to the selector list to be checked against. - if (parser.options.whitelist.indexOf(selectorBlock.selector) === -1) { +/** + * Checks our selector list for mergeable selectors and reports errors where needed + * + * @param {object} parser - The parser object + * @returns {array} Array of result objects + */ +var checkMergeable = function (parser) { + var result = []; + selectorList.forEach(function (item, index, arr) { + var pos = helpers.propertySearch(arr, item.selector, 'selector'); + if (pos !== index && parser.options.whitelist.indexOf(item.selector) === -1) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': item.line, + 'column': item.column, + 'message': 'Rule `' + item.selector + '` should be merged with the rule on line ' + selectorList[pos].line, + 'severity': parser.severity + }); + } + }); + return result; +}; - // push the selector to our master list/array of selectors currently parsed without error. - selectorList.push(selectorBlock); - } - } +module.exports = { + 'name': 'no-mergeable-selectors', + 'defaults': { + 'whitelist': [] + }, + 'detect': function (ast, parser) { + curLevel = 0; + curSelector = []; + parentSelector = []; + selectorList = []; + syntax = ast.syntax; + ast.traverseByType('stylesheet', function (styleSheet) { + traverseBlock(styleSheet, traverseNode); }); - return result; + return checkMergeable(parser); } }; From 789c6e48d2969608c520904545cbbdacae032a3f Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Wed, 2 Mar 2016 10:55:19 +0000 Subject: [PATCH 088/137] :white_check_mark: Update no-mergeable-selector tests --- tests/rules/no-mergeable-selectors.js | 11 ++-- tests/sass/no-mergeable-selectors.sass | 70 ++++++++++++++++++++++-- tests/sass/no-mergeable-selectors.scss | 75 ++++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 10 deletions(-) diff --git a/tests/rules/no-mergeable-selectors.js b/tests/rules/no-mergeable-selectors.js index 06269122..33eb53b3 100644 --- a/tests/rules/no-mergeable-selectors.js +++ b/tests/rules/no-mergeable-selectors.js @@ -9,7 +9,7 @@ describe('no mergeable selectors - scss', function () { lint.test(file, { 'no-mergeable-selectors': 1 }, function (data) { - lint.assert.equal(19, data.warningCount); + lint.assert.equal(21, data.warningCount); done(); }); }); @@ -25,13 +25,14 @@ describe('no mergeable selectors - scss', function () { } ] }, function (data) { - lint.assert.equal(18, data.warningCount); + lint.assert.equal(20, data.warningCount); done(); }); }); }); +// 1 less warning than scss syntax as we dont attempt to merge media queries describe('no mergeable selectors - sass', function () { var file = lint.file('no-mergeable-selectors.sass'); @@ -39,11 +40,12 @@ describe('no mergeable selectors - sass', function () { lint.test(file, { 'no-mergeable-selectors': 1 }, function (data) { - lint.assert.equal(19, data.warningCount); + lint.assert.equal(20, data.warningCount); done(); }); }); + // 1 less warning than scss syntax as we dont attempt to merge media queries it('[whitelist: div p]', function (done) { lint.test(file, { 'no-mergeable-selectors': [ @@ -55,9 +57,8 @@ describe('no mergeable selectors - sass', function () { } ] }, function (data) { - lint.assert.equal(18, data.warningCount); + lint.assert.equal(19, data.warningCount); done(); }); }); - }); diff --git a/tests/sass/no-mergeable-selectors.sass b/tests/sass/no-mergeable-selectors.sass index a12bb7c4..f17312b6 100644 --- a/tests/sass/no-mergeable-selectors.sass +++ b/tests/sass/no-mergeable-selectors.sass @@ -103,7 +103,6 @@ input:read-only content: '' .b-some - .image + .buttons margin-top: 14px @@ -140,7 +139,6 @@ input:read-only // mergeable with the previous block .b-some - &__image width: 100% @@ -152,8 +150,8 @@ input:read-only content: '' p::first-line - color: #ff0000 - font-variant: small-caps + color: #ff0000 + font-variant: small-caps // mergeable with the rule above p::first-line @@ -161,7 +159,7 @@ p::first-line font-variant: small-caps ul ~ p - color: #ff0000 + color: #ff0000 .test .bar @@ -172,6 +170,68 @@ ul ~ p .bar color: red +// // make sure a selector inside a supports query is not marked as mergable +// @supports (display: flex) +// .bar +// display: flex + //make sure a low level selector doesn't flag with a nested selector above .bar content: '' + + +.bar + @media (max-width: 40em) and (min-width: 20em) and (orientation: landscape) + content: '' + +// ////////////////////////////////////////////////////////////////////////////////////////////////// +// THE FOLLOWING WILL BE MANUALLY IGNORED BY THE RULE DUE TO GONZALES ISSUE WITH MEDIA QUERY NESTING +// ///////////////////////////////////////////////////////////////////////////////////////////////// + + +// shouldn't merge with the previous bar above +%bar + @media (max-width: 40em) and (min-width: 20em) and (orientation: landscape) + content: '' + +// shouldn't merge with bar above +@media (max-width: 40em) and (min-width: 20em) and (orientation: landscape) + .bar + content: '' + +// should merge with media query above +@media (max-width: 40em) and (min-width: 20em) and (orientation: landscape) + .bar + content: '' + +// should merge with media query above +@media (max-width: 40em) and (min-width: 20em) and (orientation: landscape) + %bar + content: '' + +// shouldn't merge +@media (max-width: 20em) and (min-width: 10em) and (orientation: landscape) + .bar + content: '' + +// CRASHES GONZALES +// // Adds keyframes blocks +// =keyframes($name) +// @keyframes #{$name} +// @content + +// keyframes should be ignored completely +// +keyframes(fade-in) +// 0% +// opacity: 0 +// +// 100% +// opacity: 1 +// +// +// +keyframes(fade-out) +// 0% +// opacity: 1 +// +// 100% +// opacity: 1 diff --git a/tests/sass/no-mergeable-selectors.scss b/tests/sass/no-mergeable-selectors.scss index 003deebb..76ac5fd6 100644 --- a/tests/sass/no-mergeable-selectors.scss +++ b/tests/sass/no-mergeable-selectors.scss @@ -221,7 +221,82 @@ ul ~ p { } } +// make sure a selector inside a supports query is not marked as mergable +@supports (display: flex) { + .abar { + display: flex; + } +} + //make sure a low level selector doesn't flag with a nested selector above .bar { content: ''; } + +// should merge with the previous bar above +.bar { + @media(max-width: 40em) and (min-width: 20em) and (orientation: landscape) { + content: ''; + } +} + +// shouldn't merge with the previous bar above +%bar { + @media(max-width: 40em) and (min-width: 20em) and (orientation: landscape) { + content: ''; + } +} + +// shouldn't merge with bar above +@media(max-width: 40em) and (min-width: 20em) and (orientation: landscape) { + .bar { + content: ''; + } +} + +// should merge with media query above +@media(max-width: 40em) and (min-width: 20em) and (orientation: landscape) { + .bar { + content: ''; + } +} + +// should merge with media query above +@media(max-width: 40em) and (min-width: 20em) and (orientation: landscape) { + %bar { + content: ''; + } +} + +// shouldn't merge +@media(max-width: 20em) and (min-width: 10em) and (orientation: landscape) { + .bar { + content: ''; + } +} + +// Adds keyframes blocks +@mixin keyframes($name) { + @keyframes #{$name} { @content; } +} + +//keyframes should be ignored completely +@include keyframes(fade-in) { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +@include keyframes(fade-out) { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + } +} From 2c399d0ce2ad8618c771b17d53ae69766c14cfdc Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Wed, 2 Mar 2016 10:55:35 +0000 Subject: [PATCH 089/137] :white_check_mark: Update helper tests --- tests/helpers.js | 514 ----------------------------------------------- 1 file changed, 514 deletions(-) diff --git a/tests/helpers.js b/tests/helpers.js index 88614652..30b76ecc 100644 --- a/tests/helpers.js +++ b/tests/helpers.js @@ -15,281 +15,6 @@ var haystack = [ } ]; -var idNode = gonzales.createNode({ - type: 'id', - content: [ - gonzales.createNode({ - type: 'ident', - content: 'header', - syntax: 'scss', - start: { - line: 1, - column: 2 - }, - end: { - line: 1, - column: 7 - } - }) - ], - syntax: 'scss', - start: { - line: 1, - column: 1 - }, - end: { - line: 1, - column: 7 - } -}); - - -var identNode = gonzales.createNode({ - type: 'ident', - content: 'input', - syntax: 'scss', - start: { - line: 1, - column: 1 - }, - end: { - line: 1, - column: 5 - } -}); - - -var classNode = gonzales.createNode({ - type: 'class', - content: [ - gonzales.createNode({ - type: 'ident', - content: 'header', - syntax: 'scss', - start: { - line: 1, - column: 2 - }, - end: { - line: 1, - column: 7 - } - }) - ], - syntax: 'scss', - start: { - line: 1, - column: 1 - }, - end: { - line: 1, - column: 7 - } -}); - - -var attributeNode = gonzales.createNode({ - type: 'attribute', - content: [ - gonzales.createNode({ - type: 'ident', - content: 'type', - syntax: 'scss', - start: { - line: 1, - column: 7 - }, - end: { - line: 1, - column: 10 - } - }), - gonzales.createNode({ - type: 'attributeSelector', - content: '=', - syntax: 'scss', - start: { - line: 1, - column: 11 - }, - end: { - line: 1, - column: 11 - } - }), - gonzales.createNode({ - type: 'ident', - content: 'radio', - syntax: 'scss', - start: { - line: 1, - column: 12 - }, - end: { - line: 1, - column: 16 - } - }) - ], - syntax: 'scss', - start: { - line: 1, - column: 6 - }, - end: { - line: 1, - column: 17 - } -}); - - -var pseudoNode = gonzales.createNode({ - type: 'pseudoClass', - content: [ - gonzales.createNode({ - type: 'ident', - content: 'last-child', - syntax: 'scss', - start: { - line: 1, - column: 3 - }, - end: { - line: 1, - column: 12 - } - }) - ], - syntax: 'scss', - start: { - line: 1, - column: 2 - }, - end: { - line: 1, - column: 12 - } -}); - - -var pseudoElementNode = gonzales.createNode({ - type: 'pseudoElement', - content: [ - gonzales.createNode({ - type: 'ident', - content: 'first-line', - syntax: 'scss', - start: { - line: 1, - column: 3 - }, - end: { - line: 1, - column: 12 - } - }) - ], - syntax: 'scss', - start: { - line: 1, - column: 2 - }, - end: { - line: 1, - column: 12 - } -}); - - - -var nthSelectorNode = gonzales.createNode({ - type: 'nthSelector', - content: [ - gonzales.createNode({ - type: 'ident', - content: 'nth-of-type', - syntax: 'scss', - start: { - line: 1, - column: 3 - }, - end: { - line: 1, - column: 13 - } - }), - gonzales.createNode({ - type: 'nth', - content: '2', - syntax: 'scss', - start: { - line: 1, - column: 15 - }, - end: { - line: 1, - column: 15 - } - }) - ], - syntax: 'scss', - start: { - line: 1, - column: 2 - }, - end: { - line: 1, - column: 16 - } -}); - - -var spaceNode = gonzales.createNode({ - type: 'space', - content: ' ', - syntax: 'scss', - start: { - line: 1, - column: 1 - }, - end: { - line: 1, - column: 1 - } -}); - - -var parentSelectorNode = gonzales.createNode({ - type: 'parentSelector', - content: '&', - syntax: 'scss', - start: { - line: 1, - column: 1 - }, - end: { - line: 1, - column: 1 - } -}); - - -var combinatorNode = gonzales.createNode({ - type: 'combinator', - content: '+', - syntax: 'scss', - start: { - line: 1, - column: 6 - }, - end: { - line: 1, - column: 6 - } -}); - - - var classBlock = { type: 'class', @@ -330,44 +55,6 @@ var classBlock = indexHasChanged: [ 0 ] }; -var nodeSimpleSelector = { - type: 'simpleSelector', - content: - [ - { - type: 'ident', - content: 'h1', - syntax: 'scss', - start: { line: 16, column: 1 }, - end: { line: 16, column: 2 }, - indexHasChanged: [ 0 ] - } - ], - syntax: 'scss', - start: { line: 16, column: 1 }, - end: { line: 16, column: 2 }, - indexHasChanged: [ 0 ] - }, - - nodeDelim = { - type: 'delimiter', - content: ',', - syntax: 'scss', - start: { line: 16, column: 3 }, - end: { line: 16, column: 3 }, - indexHasChanged: [ 0 ] - }, - - nodeSpace = { - type: 'space', - content: ' ', - syntax: 'scss', - start: { line: 225, column: 5 }, - end: { line: 225, column: 5 }, - indexHasChanged: [ 0 ] - }; - - var detectTestA = { line: 1, column: 1 @@ -1344,34 +1031,6 @@ describe('helpers', function () { done(); }); - ////////////////////////////// - // mapDelims - ////////////////////////////// - - it('mapDelims - selector', function (done) { - - var result = helpers.mapDelims(nodeSimpleSelector); - - assert.equal('s', result); - done(); - }); - - it('mapDelims - delim', function (done) { - - var result = helpers.mapDelims(nodeDelim); - - assert.equal('d', result); - done(); - }); - - it('mapDelims - space', function (done) { - - var result = helpers.mapDelims(nodeSpace); - - assert.equal(false, result); - done(); - }); - ////////////////////////////// // isNestable ////////////////////////////// @@ -1400,179 +1059,6 @@ describe('helpers', function () { done(); }); - ////////////////////////////// - // constructSelector - ////////////////////////////// - - it('constructSelector - id node - [#header]', function (done) { - var result = helpers.constructSelector(idNode), - expect = '#header'; - - assert.equal(expect, result.content); - done(); - }); - - it('constructSelector - id node - [id]', function (done) { - var result = helpers.constructSelector(idNode), - expect = 'id'; - - assert.equal(expect, result.type); - done(); - }); - - - it('constructSelector - class node - [.header]', function (done) { - var result = helpers.constructSelector(classNode), - expect = '.header'; - - assert.equal(expect, result.content); - done(); - }); - - it('constructSelector - class node - [class]', function (done) { - var result = helpers.constructSelector(classNode), - expect = 'class'; - - assert.equal(expect, result.type); - done(); - }); - - - it('constructSelector - ident node - [input]', function (done) { - var result = helpers.constructSelector(identNode), - expect = 'input'; - - assert.equal(expect, result.content); - done(); - }); - - it('constructSelector - ident node - [selector]', function (done) { - var result = helpers.constructSelector(identNode), - expect = 'selector'; - - assert.equal(expect, result.type); - done(); - }); - - - it('constructSelector - attribute node - [type=radio]', function (done) { - var result = helpers.constructSelector(attributeNode), - expect = '[type=radio]'; - - assert.equal(expect, result.content); - done(); - }); - - it('constructSelector - attribute node - [attribute]', function (done) { - var result = helpers.constructSelector(attributeNode), - expect = 'attribute'; - - assert.equal(expect, result.type); - done(); - }); - - - it('constructSelector - pseudo node - [:last-child]', function (done) { - var result = helpers.constructSelector(pseudoNode), - expect = ':last-child'; - - assert.equal(expect, result.content); - done(); - }); - - it('constructSelector - pseudo node - [pseudoClass]', function (done) { - var result = helpers.constructSelector(pseudoNode), - expect = 'pseudoClass'; - - assert.equal(expect, result.type); - done(); - }); - - - it('constructSelector - pseudo element node - [::first-line]', function (done) { - var result = helpers.constructSelector(pseudoElementNode), - expect = '::first-line'; - - assert.equal(expect, result.content); - done(); - }); - - it('constructSelector - pseudo element node - [pseudoElement]', function (done) { - var result = helpers.constructSelector(pseudoElementNode), - expect = 'pseudoElement'; - - assert.equal(expect, result.type); - done(); - }); - - - it('constructSelector - nthSelector node - [:nth-of-type(2)]', function (done) { - var result = helpers.constructSelector(nthSelectorNode), - expect = ':nth-of-type(2)'; - - assert.equal(expect, result.content); - done(); - }); - - it('constructSelector - nthSelector node - [nthSelector]', function (done) { - var result = helpers.constructSelector(nthSelectorNode), - expect = 'nthSelector'; - - assert.equal(expect, result.type); - done(); - }); - - - it('constructSelector - space node - [ ]', function (done) { - var result = helpers.constructSelector(spaceNode), - expect = ' '; - - assert.equal(expect, result.content); - done(); - }); - - it('constructSelector - space node - []', function (done) { - var result = helpers.constructSelector(spaceNode), - expect = ''; - - assert.equal(expect, result.type); - done(); - }); - - - it('constructSelector - parent selector node - [&]', function (done) { - var result = helpers.constructSelector(parentSelectorNode), - expect = '&'; - - assert.equal(expect, result.content); - done(); - }); - - it('constructSelector - parent selector node - [parentSelector]', function (done) { - var result = helpers.constructSelector(parentSelectorNode), - expect = 'parentSelector'; - - assert.equal(expect, result.type); - done(); - }); - - - it('constructSelector - combinator node - [+]', function (done) { - var result = helpers.constructSelector(combinatorNode), - expect = '+'; - - assert.equal(expect, result.content); - done(); - }); - - it('constructSelector - combinator node - [combinator]', function (done) { - var result = helpers.constructSelector(combinatorNode), - expect = 'combinator'; - - assert.equal(expect, result.type); - done(); - }); - ////////////////////////////// // attemptTraversal ////////////////////////////// From b75219c8b3f59d829d6ecf95b79d573261223e7a Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Wed, 2 Mar 2016 10:57:04 +0000 Subject: [PATCH 090/137] :fire: Removing unnecessary helper functions --- lib/helpers.js | 107 ------------------------------------------------- 1 file changed, 107 deletions(-) diff --git a/lib/helpers.js b/lib/helpers.js index cda8d39c..6b9df434 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -231,112 +231,6 @@ helpers.stripLastSpace = function (selector) { }; -/** - * Scans through our selectors and keeps track of the order of selectors and delimiters - * @param {object} selector - the current selector tree from our AST - * @returns {array} mappedElements - an array / list representing the order of selectors and delimiters encountered - */ - -helpers.mapDelims = function (val) { - - if (val.type === 'simpleSelector') { - return 's'; - } - - if (val.type === 'delimiter') { - return 'd'; - } - - return false; -}; - -/** - * Constructs a syntax complete selector - * @param {object} node - the current node / part of our selector - * @returns {object} constructedSelector - The constructed selector - * @returns {string} constructedSelector.content - The selector string - * @returns {string} constructedSelector.type - The type of the selector - */ -helpers.constructSelector = function (node) { - var content = node.content, - type = ''; - - if (node.type === 'id') { - content = '#' + node.content; - type = 'id'; - } - - else if (node.type === 'class') { - content = '.' + node.content; - type = 'class'; - } - - else if (node.type === 'ident') { - content = node.content; - type = 'selector'; - } - - else if (node.type === 'attribute') { - var selector = '['; - - node.forEach(function (attrib) { - var selectorPiece = helpers.constructSelector(attrib); - - selector += selectorPiece.content; - }); - - content = selector + ']'; - type = 'attribute'; - } - - else if (node.type === 'pseudoClass') { - content = ':' + node.content; - type = 'pseudoClass'; - } - - else if (node.type === 'pseudoElement') { - content = '::' + node.content; - type = 'pseudoElement'; - } - - else if (node.type === 'nth') { - content = '(' + node.content + ')'; - type = 'nth'; - } - - else if (node.type === 'nthSelector') { - var nthSelector = ':'; - - node.forEach(function (attrib) { - var selectorPiece = helpers.constructSelector(attrib); - - nthSelector += selectorPiece.content; - }); - - content = nthSelector; - type = 'nthSelector'; - } - - else if (node.type === 'space') { - content = ' '; - } - - else if (node.type === 'parentSelector') { - content = node.content; - type = 'parentSelector'; - } - - else if (node.type === 'combinator') { - content = node.content; - type = 'combinator'; - } - - return { - content: content, - type: type - }; -}; - /** * Checks the current selector value against the previous selector value and assesses whether they are * a) currently an enforced selector type for nesting (user specified - all true by default) @@ -386,7 +280,6 @@ helpers.attemptTraversal = function (node, traversalPath) { currentNodeList.forEach(processChildNode); currentNodeList = nextNodeList; } - return currentNodeList; }; From 4495f0d7ac5752bd8b13d206c878151bd57376a1 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Wed, 2 Mar 2016 17:47:54 +0000 Subject: [PATCH 091/137] :art: Tidy up rule from PR feedback --- lib/rules/no-mergeable-selectors.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rules/no-mergeable-selectors.js b/lib/rules/no-mergeable-selectors.js index 724ad61f..248be2d3 100644 --- a/lib/rules/no-mergeable-selectors.js +++ b/lib/rules/no-mergeable-selectors.js @@ -69,16 +69,15 @@ var constructSelector = function (val) { content = constructSubSelector(val, '[', ']', constructSelector); } - else if (val.is('atkeyword') ) { + else if (val.is('atkeyword')) { content = constructSubSelector(val, '@', '', constructSelector); - } - else if (val.is('placeholder') ) { + else if (val.is('placeholder')) { content = constructSubSelector(val, '%', '', constructSelector); } - else if (val.is('variable') ) { + else if (val.is('variable')) { content = constructSubSelector(val, '$', '', constructSelector); } @@ -199,6 +198,7 @@ var checkAtRule = function (atRule) { var isMergeableAtRule = function (node) { var isMergeable = false; node.forEach(function (item) { + // TODO Check back when Gonzales updates to fix this // Gonzales has issues with nest levels in media queries :( if (item.is('atkeyword') && validAtRules.indexOf(item.first('ident').content) !== -1 && syntax !== 'sass') { isMergeable = true; From 1a3246efa59af4f90fcd943fbe9f8f5886811177 Mon Sep 17 00:00:00 2001 From: greenkeeperio-bot Date: Wed, 9 Mar 2016 13:30:52 -0500 Subject: [PATCH 092/137] chore(package): update js-yaml to version 3.5.4 http://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7316f419..f45f59dd 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "fs-extra": "^0.26.0", "glob": "^7.0.0", "gonzales-pe": "3.0.0-31", - "js-yaml": "^3.2.6", + "js-yaml": "^3.5.4", "lodash.capitalize": "^4.1.0", "lodash.kebabcase": "^4.0.0", "merge": "^1.2.0", From f55d3a993ff50e320caa1692e85ed9af6a576fbf Mon Sep 17 00:00:00 2001 From: Jesse House Date: Wed, 9 Mar 2016 20:32:21 -0800 Subject: [PATCH 093/137] Add maxWarnings CLI option Exit with non-zero value when the number of warnings is greater than the maxWarning option. Easily fail CI builds without needing to update all sass-lint rules to errors. Usage: `sass-lint --max-warnings 0 -c sass-lint.config.yml sass/**/*.scss` This closes #567 Based on the eslint maxWarnings CLI option: * http://eslint.org/docs/user-guide/command-line-interface#options --- bin/sass-lint.js | 9 ++++++++- docs/cli/readme.md | 1 + tests/cli.js | 12 ++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/bin/sass-lint.js b/bin/sass-lint.js index 33bea9dd..787e9330 100755 --- a/bin/sass-lint.js +++ b/bin/sass-lint.js @@ -10,6 +10,12 @@ var configPath, configOptions = {}, exitCode = 0; +var tooManyWarnings = function (detects) { + var warningCount = lint.warningCount(detects).count; + + return warningCount > 0 && warningCount > program.maxWarnings; +}; + var detectPattern = function (pattern) { var detects; @@ -19,7 +25,7 @@ var detectPattern = function (pattern) { lint.outputResults(detects, configOptions, configPath); } - if (lint.errorCount(detects).count) { + if (lint.errorCount(detects).count || tooManyWarnings(detects)) { exitCode = 1; } @@ -38,6 +44,7 @@ program .option('-f, --format [format]', 'pass one of the available eslint formats') .option('-o, --output [output]', 'the path and filename where you would like output to be written') .option('-s, --syntax [syntax]', 'syntax to evaluate the file(s) with (either sass or scss)') + .option('--max-warnings [integer]', 'Number of warnings to trigger nonzero exit code') .parse(process.argv); diff --git a/docs/cli/readme.md b/docs/cli/readme.md index 160ed347..739124df 100644 --- a/docs/cli/readme.md +++ b/docs/cli/readme.md @@ -16,6 +16,7 @@ Command Line Flag | Description `-f`,`--format [format]` | Pass one of the available [Eslint formats](https://github.com/eslint/eslint/tree/master/lib/formatters) to format the output of sass-lint results. `-h`,`--help` | Outputs usage information for the CLI `-i`,`--ignore [pattern]` | A pattern that should be ignored from linting. Multiple patterns can be used by separating each pattern by `, `. Patterns should be wrapped in quotes (will be merged with other ignore options) +`--max-warnings [integer]`| Normally, if SassLint runs and finds no errors (only warnings), it will exit with a success exit status. However, if this option is specified and the total warning count is greater than the specified threshold, SassLint will exit with an error status. `-o`,`--output [output]` | The path plus file name relative to where Sass Lint is being run from where the output should be written to. `-q`,`--no-exit` | Prevents the CLI from throwing an error if there is one (useful for development work) `-s`,`--syntax` | Syntax to evaluate the given file(s) with, either sass or scss. Use with care: overrides filename extension-based syntax detection. diff --git a/tests/cli.js b/tests/cli.js index a22f96c8..ba023f77 100644 --- a/tests/cli.js +++ b/tests/cli.js @@ -320,6 +320,18 @@ describe('cli', function () { }); }); + it('should exit with exit code 1 when more warnings than --max-warnings', function (done) { + var command = 'sass-lint -c tests/yml/.color-keyword-errors.yml tests/sass/cli.scss --max-warnings 0'; + + childProcess.exec(command, function (err) { + if (err && err.code === 1) { + return done(); + } + + return done(new Error('Error code not 1')); + }); + }); + /** * We disabled eslints handle callback err rule here as we are deliberately throwing errors that we don't care about */ From 5c2d79a91d07f625523368fdaf60f15de8c8a529 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Mon, 28 Mar 2016 17:55:25 +0100 Subject: [PATCH 094/137] :arrow_up: Update Eslint to ^2.5.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bc32a11e..3ff8de0b 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "homepage": "https://github.com/sasstools/sass-lint", "dependencies": { "commander": "^2.8.1", - "eslint": "^1.1.0", + "eslint": "^2.5.1", "fs-extra": "^0.26.0", "glob": "^7.0.0", "gonzales-pe": "^3.2.6", From 04e125dd87037eb8a880d16a0ecec95972ca657c Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Mon, 28 Mar 2016 17:56:02 +0100 Subject: [PATCH 095/137] :bug: Fix eslint rule duplications and update rules to eslint 2.x format --- .eslintrc | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.eslintrc b/.eslintrc index ce31c538..1353aff2 100644 --- a/.eslintrc +++ b/.eslintrc @@ -54,7 +54,7 @@ rules: no-caller: 2 no-console: 0 no-delete-var: 2 - no-empty-label: 2 + no-labels: 2 no-eval: 2 no-extend-native: 2 no-extra-bind: 2 @@ -64,7 +64,6 @@ rules: no-invalid-this: 2 no-iterator: 2 no-label-var: 2 - no-labels: 2 no-lone-blocks: 2 no-loop-func: 2 no-mixed-spaces-and-tabs: @@ -110,15 +109,16 @@ rules: - 2 - before: false after: true - space-after-keywords: - - 2 - - always + keyword-spacing: + - 2 + - + before: true + after: true space-before-blocks: 2 space-before-function-paren: - 2 - always space-infix-ops: 2 - space-return-throw-case: 2 space-unary-ops: - 2 - words: true @@ -144,11 +144,9 @@ rules: # Previously on by default in node environment no-catch-shadow: 0 - no-console: 0 no-mixed-requires: 2 no-new-require: 2 no-path-concat: 2 - no-process-exit: 2 handle-callback-err: - 2 - err From 4f6b101caa1e22563622adb17370039ca5fd4292 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Mon, 28 Mar 2016 17:56:30 +0100 Subject: [PATCH 096/137] Fix Eslint indentation errors --- tests/config.js | 6 ++-- tests/helpers.js | 76 ++++++++++++++++++++++++------------------------ tests/output.js | 28 +++++++++--------- 3 files changed, 55 insertions(+), 55 deletions(-) diff --git a/tests/config.js b/tests/config.js index 0ac61fa9..f77906d1 100644 --- a/tests/config.js +++ b/tests/config.js @@ -24,9 +24,9 @@ var custOptions = function () { 'no-duplicate-properties': 0, 'indentation': [ 2, - { - 'size': 4 - } + { + 'size': 4 + } ] } }; diff --git a/tests/helpers.js b/tests/helpers.js index 30b76ecc..e9b12db9 100644 --- a/tests/helpers.js +++ b/tests/helpers.js @@ -5,30 +5,30 @@ var assert = require('assert'), gonzales = require('gonzales-pe'); var haystack = [ - { - prop: 'a', - propb: 'b' - }, - { - prop: 'c', - propb: 'd' - } + { + prop: 'a', + propb: 'b' + }, + { + prop: 'c', + propb: 'd' + } ]; var classBlock = { type: 'class', content: - [ - { - type: 'ident', - content: 'foo', - syntax: 'scss', - start: { line: 5, column: 2 }, - end: { line: 5, column: 4 }, - indexHasChanged: [ 0 ] - } - ], + [ + { + type: 'ident', + content: 'foo', + syntax: 'scss', + start: { line: 5, column: 2 }, + end: { line: 5, column: 4 }, + indexHasChanged: [ 0 ] + } + ], syntax: 'scss', start: { line: 5, column: 1 }, end: { line: 5, column: 4 }, @@ -39,16 +39,16 @@ var classBlock = { type: 'class', content: - [ - { - type: 'ident', - content: 'test', - syntax: 'scss', - start: { line: 9, column: 2 }, - end: { line: 9, column: 5 }, - indexHasChanged: [ 0 ] - } - ], + [ + { + type: 'ident', + content: 'test', + syntax: 'scss', + start: { line: 9, column: 2 }, + end: { line: 9, column: 5 }, + indexHasChanged: [ 0 ] + } + ], syntax: 'scss', start: { line: 9, column: 1 }, end: { line: 9, column: 5 }, @@ -129,16 +129,16 @@ describe('helpers', function () { { type: 'class', content: - [ - { - type: 'ident', - content: 'foo', - syntax: 'scss', - start: { line: 5, column: 2 }, - end: { line: 5, column: 4 }, - indexHasChanged: [ 0 ] - } - ], + [ + { + type: 'ident', + content: 'foo', + syntax: 'scss', + start: { line: 5, column: 2 }, + end: { line: 5, column: 4 }, + indexHasChanged: [ 0 ] + } + ], syntax: 'scss', start: { line: 5, column: 1 }, end: { line: 5, column: 4 }, diff --git a/tests/output.js b/tests/output.js index 02689707..de6dc101 100644 --- a/tests/output.js +++ b/tests/output.js @@ -12,20 +12,20 @@ var results = [{ warningCount: 2, errorCount: 0, messages: [ - { - ruleId: 'empty-line-between-blocks', - line: 14, - column: 2, - message: 'Space expected between blocks', - severity: 1 - }, - { - ruleId: 'empty-line-between-blocks', - line: 17, - column: 2, - message: 'Space expected between blocks', - severity: 1 - } + { + ruleId: 'empty-line-between-blocks', + line: 14, + column: 2, + message: 'Space expected between blocks', + severity: 1 + }, + { + ruleId: 'empty-line-between-blocks', + line: 17, + column: 2, + message: 'Space expected between blocks', + severity: 1 + } ] }]; From 14e9cdd73a15e7ee5e247b265e9fa2e5abe9f5f5 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Mon, 28 Mar 2016 17:57:05 +0100 Subject: [PATCH 097/137] :art: Follow eslint consistent returns rule --- lib/rules/brace-style.js | 4 ++++ lib/rules/empty-line-between-blocks.js | 2 ++ lib/rules/mixin-name-format.js | 1 + lib/rules/space-after-comma.js | 1 + lib/rules/space-around-operator.js | 2 ++ tests/cli.js | 10 +++++----- 6 files changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index 59067bad..2d35a134 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -34,6 +34,8 @@ var getPreviousNode = function (node) { if (node.is('atrule') || node.is('mixin') || node.is('loop')) { return node.contains('atkeyword') ? node.last('atkeyword') : false; } + + return false; }; /** @@ -256,6 +258,8 @@ module.exports = { } } } + + return true; }); return result; diff --git a/lib/rules/empty-line-between-blocks.js b/lib/rules/empty-line-between-blocks.js index 04711f53..b4c12385 100644 --- a/lib/rules/empty-line-between-blocks.js +++ b/lib/rules/empty-line-between-blocks.js @@ -46,6 +46,7 @@ var findNearestReturnSCSS = function (parent, i) { } } } + return false; }; var findNearestReturnSass = function (parent, i) { @@ -191,6 +192,7 @@ module.exports = { } } } + return true; }); return result; diff --git a/lib/rules/mixin-name-format.js b/lib/rules/mixin-name-format.js index 4550452a..d2940ebc 100644 --- a/lib/rules/mixin-name-format.js +++ b/lib/rules/mixin-name-format.js @@ -81,6 +81,7 @@ module.exports = { }); } } + return true; }); return result; diff --git a/lib/rules/space-after-comma.js b/lib/rules/space-after-comma.js index 6ac16ffc..1dc8a902 100644 --- a/lib/rules/space-after-comma.js +++ b/lib/rules/space-after-comma.js @@ -50,6 +50,7 @@ module.exports = { } } } + return true; }); return result; diff --git a/lib/rules/space-around-operator.js b/lib/rules/space-around-operator.js index 0b2de5d2..23921f14 100644 --- a/lib/rules/space-around-operator.js +++ b/lib/rules/space-around-operator.js @@ -18,6 +18,7 @@ var getRelationalOperator = function (node) { if (node.content === '>') { return '>='; } + return false; }; /** @@ -137,6 +138,7 @@ var checkSpacing = function (node, i, parent, parser, result) { } } } + return true; }; module.exports = { diff --git a/tests/cli.js b/tests/cli.js index a22f96c8..0393ddff 100644 --- a/tests/cli.js +++ b/tests/cli.js @@ -16,7 +16,7 @@ describe('cli', function () { assert(stdout.indexOf('Usage') > 0); - done(null); + return done(null); }); }); @@ -30,7 +30,7 @@ describe('cli', function () { should(stdout).match(/^[0-9]+.[0-9]+(.[0-9]+)?/); - done(null); + return done(null); }); }); @@ -230,7 +230,7 @@ describe('cli', function () { else { assert.equal(expectedOutputLength, stdout.length); - done(); + return done(); } }); }); @@ -254,7 +254,7 @@ describe('cli', function () { assert(stdout.indexOf(file) === -1); }); - done(); + return done(); }); }); @@ -277,7 +277,7 @@ describe('cli', function () { assert(stdout.indexOf(file) === -1); }); - done(); + return done(); }); }); From 2b41a7fba815812cf19b89375b6d83d112c358c7 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Mon, 28 Mar 2016 19:20:33 +0100 Subject: [PATCH 098/137] :bug: Fix valid default properties --- lib/rules/variable-for-property.js | 23 ++++++++++++++++++++++- tests/rules/variable-for-property.js | 6 ++++-- tests/sass/variable-for-property.sass | 10 ++++++++-- tests/sass/variable-for-property.scss | 7 +++++++ 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/lib/rules/variable-for-property.js b/lib/rules/variable-for-property.js index d2b3306d..2990cf40 100644 --- a/lib/rules/variable-for-property.js +++ b/lib/rules/variable-for-property.js @@ -2,6 +2,27 @@ var helpers = require('../helpers'); +// The whitelisted ident values +var whitelistedValues = ['inherit', 'initial', 'transparent', 'none', 'currentColor']; + +/** + * Checks If the property is of a valid type, either its a variable or it's a whitelisted ident value + * + * @param {Object} propertyElem - The property element + * @returns {boolean} Whether the property is valid or not + */ +var isValidProperty = function (propertyElem) { + if (propertyElem) { + if (propertyElem.type === 'variable') { + return true; + } + else if (propertyElem.type === 'ident' && whitelistedValues.indexOf(propertyElem.content) !== -1) { + return true; + } + } + return false; +}; + module.exports = { 'name': 'variable-for-property', 'defaults': { @@ -19,7 +40,7 @@ module.exports = { if (declarationType === 'ident') { if (parser.options.properties.indexOf(declarationIdent) !== -1) { node.forEach(function (valElem) { - if (valElem.type !== 'variable') { + if (!isValidProperty(valElem)) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': declaration.start.line, diff --git a/tests/rules/variable-for-property.js b/tests/rules/variable-for-property.js index 8117173e..7601fda2 100644 --- a/tests/rules/variable-for-property.js +++ b/tests/rules/variable-for-property.js @@ -24,7 +24,8 @@ describe('variable for property - scss', function () { { 'properties': [ 'margin', - 'content' + 'content', + 'background' ] } ] @@ -57,7 +58,8 @@ describe('variable for property - sass', function () { { 'properties': [ 'margin', - 'content' + 'content', + 'background' ] } ] diff --git a/tests/sass/variable-for-property.sass b/tests/sass/variable-for-property.sass index 121d302e..66cfdf89 100644 --- a/tests/sass/variable-for-property.sass +++ b/tests/sass/variable-for-property.sass @@ -4,7 +4,7 @@ &__element margin: $margin - + =blue() @@ -17,8 +17,14 @@ &__element margin: 0 - + =red() margin: 0 + +.test + background: inherit + background: initial + background: transparent + background: none diff --git a/tests/sass/variable-for-property.scss b/tests/sass/variable-for-property.scss index fbdc4c5b..f72709ae 100644 --- a/tests/sass/variable-for-property.scss +++ b/tests/sass/variable-for-property.scss @@ -23,3 +23,10 @@ @mixin red() { margin: 0; } + +.test { + background: inherit; + background: initial; + background: transparent; + background: none; +} From a30177096982cc9432e471fdb14bfb3aa60216d7 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Mon, 28 Mar 2016 19:28:21 +0100 Subject: [PATCH 099/137] :memo: Update variable for property documentation --- docs/rules/variable-for-property.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/rules/variable-for-property.md b/docs/rules/variable-for-property.md index 83b49393..850667a5 100644 --- a/docs/rules/variable-for-property.md +++ b/docs/rules/variable-for-property.md @@ -1,6 +1,12 @@ # Variable For Property -Rule `variable-for-property` will enforce the use of variables for the values of specified properties. There are no properties by default. +Rule `variable-for-property` will enforce the use of variables for the values of specified properties. +There are no properties by default, except for reserved words listed below which are always whitelisted: +* inherit +* initial +* transparent +* none +* currentColor ## Options From e88c11d682a427d04c0e86b26b28d79fb4f697fa Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Mon, 28 Mar 2016 20:25:52 +0100 Subject: [PATCH 100/137] :bug: Fixes path errors with no config on CLI --- index.js | 1 - lib/config.js | 24 +++++++++++------------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/index.js b/index.js index 787f074d..252646fa 100644 --- a/index.js +++ b/index.js @@ -164,7 +164,6 @@ sassLint.lintFiles = function (files, options, configPath) { } else { files = this.getConfig(options, configPath).files; - if (typeof files === 'string') { files = glob.sync(files); } diff --git a/lib/config.js b/lib/config.js index c424b812..84ced306 100644 --- a/lib/config.js +++ b/lib/config.js @@ -14,29 +14,27 @@ var loadDefaults = function loadDefaults () { var findFile = function findFile (configPath, filename) { var HOME = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE, - dirname, - parentDirname; + dirname = null, + parentDirname = null; configPath = configPath || path.join(process.cwd(), filename); - if (fs.existsSync(configPath)) { + if (configPath && fs.existsSync(configPath)) { + dirname = path.dirname(configPath); + parentDirname = path.dirname(dirname); return fs.realpathSync(configPath); } - dirname = path.dirname(configPath); - parentDirname = path.dirname(dirname); - - if (dirname === HOME || dirname === parentDirname) { + if (dirname === null || dirname === HOME || dirname === parentDirname) { return null; } - configPath = path.join(parentDirname, filename); return findFile(configPath, filename); }; module.exports = function (options, configPath) { - var meta, + var meta = null, metaPath, configMerge = false, configMergeExists = false, @@ -70,10 +68,11 @@ module.exports = function (options, configPath) { if (!configPath) { metaPath = findFile(false, 'package.json'); - meta = require(metaPath); - - if (meta.sasslintConfig) { + if (metaPath) { + meta = require(metaPath); + } + if (meta && meta.sasslintConfig) { configPath = path.resolve(path.dirname(metaPath), meta.sasslintConfig); } else { @@ -90,7 +89,6 @@ module.exports = function (options, configPath) { config.rules = config.rules ? config.rules : {}; } } - // check to see if user config contains an options property and whether property has a property called merge-default-rules configMergeExists = (config.options && typeof config.options['merge-default-rules'] !== 'undefined'); From d92046404db7820a1a700922ec9c73f4c5ccf26d Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Mon, 28 Mar 2016 20:43:11 +0100 Subject: [PATCH 101/137] :white_check_mark: Add tests for missing config on CLI --- tests/cli.js | 50 ++++++++++++++++++++++++--------------- tests/sass/cli-clean.scss | 5 ++++ 2 files changed, 36 insertions(+), 19 deletions(-) create mode 100644 tests/sass/cli-clean.scss diff --git a/tests/cli.js b/tests/cli.js index a22f96c8..bda7fbe6 100644 --- a/tests/cli.js +++ b/tests/cli.js @@ -2,14 +2,14 @@ var assert = require('assert'), should = require('should'), fs = require('fs-extra'), path = require('path'), - childProcess = require('child_process'); + exec = require('child_process').exec; describe('cli', function () { it('should return help instructions', function (done) { var command = 'sass-lint -h'; - childProcess.exec(command, function (err, stdout) { + exec(command, function (err, stdout) { if (err) { return done(err); } @@ -23,7 +23,7 @@ describe('cli', function () { it('should return a version', function (done) { var command = 'sass-lint -V'; - childProcess.exec(command, function (err, stdout) { + exec(command, function (err, stdout) { if (err) { return done(err); } @@ -37,7 +37,7 @@ describe('cli', function () { it('CLI format option should output JSON', function (done) { var command = 'sass-lint -c tests/yml/.stylish-output.yml tests/sass/cli.scss --verbose --format json'; - childProcess.exec(command, function (err, stdout) { + exec(command, function (err, stdout) { if (err) { return done(err); @@ -58,7 +58,7 @@ describe('cli', function () { var command = 'sass-lint -c tests/yml/.stylish-output.yml tests/sass/cli.scss --verbose --format json --output tests/cli-output.json', outputFile = path.resolve(process.cwd(), 'tests/cli-output.json'); - childProcess.exec(command, function (err) { + exec(command, function (err) { if (err) { return done(err); @@ -81,7 +81,7 @@ describe('cli', function () { var command = 'sass-lint -c tests/yml/.stylish-output.yml tests/sass/cli.scss --verbose --format json --output tests/cli-output.json', outputFile = path.resolve(process.cwd(), 'tests/cli-output.json'); - childProcess.exec(command, function (err) { + exec(command, function (err) { if (err) { return done(err); @@ -114,7 +114,7 @@ describe('cli', function () { var command = 'sass-lint -c tests/yml/.stylish-output.yml tests/sass/cli.scss --verbose --format JSON --output tests/cli-output.json', outputFile = path.resolve(process.cwd(), 'tests/cli-output.json'); - childProcess.exec(command, function (err) { + exec(command, function (err) { if (err) { return done(err); @@ -148,7 +148,7 @@ describe('cli', function () { it('should return JSON from a custom config', function (done) { var command = 'sass-lint -c tests/yml/.color-keyword-errors.yml tests/sass/cli.scss --verbose'; - childProcess.exec(command, function (err, stdout) { + exec(command, function (err, stdout) { if (err) { return done(err); @@ -170,7 +170,7 @@ describe('cli', function () { it('output should return no errors/warnings', function (done) { var command = 'sass-lint -c tests/yml/.json-lint.yml tests/sass/cli.scss --verbose'; - childProcess.exec(command, function (err, stdout) { + exec(command, function (err, stdout) { var result = 0; @@ -192,7 +192,7 @@ describe('cli', function () { it('should return a warning', function (done) { var command = 'sass-lint -c tests/yml/.color-keyword-errors.yml tests/sass/cli.scss --verbose'; - childProcess.exec(command, function (err, stdout) { + exec(command, function (err, stdout) { var result = ''; @@ -222,7 +222,7 @@ describe('cli', function () { var command = 'sass-lint -c tests/yml/.stylish-errors.yml tests/sass/cli.scss --verbose', expectedOutputLength = 155; - childProcess.exec(command, function (err, stdout) { + exec(command, function (err, stdout) { if (err) { return done(err); @@ -244,7 +244,7 @@ describe('cli', function () { var command = 'sass-lint -i \'**/*.s+(a|c)ss\''; - childProcess.exec(command, function (err, stdout) { + exec(command, function (err, stdout) { if (err) { return done(err); @@ -267,7 +267,7 @@ describe('cli', function () { var command = 'sass-lint -i \'**/*.scss, **/*.sass \''; - childProcess.exec(command, function (err, stdout) { + exec(command, function (err, stdout) { if (err) { return done(err); @@ -290,7 +290,7 @@ describe('cli', function () { var command = 'sass-lint --syntax scss tests/sass/cli.txt --verbose'; - childProcess.exec(command, function (err, stdout) { + exec(command, function (err, stdout) { var result = 0; @@ -311,7 +311,7 @@ describe('cli', function () { it('should exit with exit code 1 when quiet', function (done) { var command = 'sass-lint -c tests/yml/.error-output.yml tests/sass/cli-error.scss --verbose --no-exit'; - childProcess.exec(command, function (err) { + exec(command, function (err) { if (err.code === 1) { return done(); } @@ -320,13 +320,25 @@ describe('cli', function () { }); }); + it('should not exit with an error if no config is specified', function (done) { + var command = 'sass-lint tests/sass/cli-clean.scss --verbose --no-exit'; + + exec(command, function (err) { + if (!err) { + return done(); + } + + return done(new Error('Exited with error code 1')); + }); + }); + /** * We disabled eslints handle callback err rule here as we are deliberately throwing errors that we don't care about */ it('parse errors should report as a lint error', function (done) { var command = 'sass-lint --config tests/yml/.stylish-output.yml tests/sass/parse.scss --verbose --no-exit --format json'; - childProcess.exec(command, function (err, stdout) { // eslint-disable-line handle-callback-err + exec(command, function (err, stdout) { // eslint-disable-line handle-callback-err var result = JSON.parse(stdout)[0]; assert.equal(1, result.errorCount); @@ -337,7 +349,7 @@ describe('cli', function () { it('parse errors should report as severity 2', function (done) { var command = 'sass-lint --config tests/yml/.stylish-output.yml tests/sass/parse.scss --verbose --no-exit --format json'; - childProcess.exec(command, function (err, stdout) { // eslint-disable-line handle-callback-err + exec(command, function (err, stdout) { // eslint-disable-line handle-callback-err var result = JSON.parse(stdout)[0], messages = result.messages[0], severity = 2; @@ -350,7 +362,7 @@ describe('cli', function () { it('parse errors should report the correct message', function (done) { var command = 'sass-lint --config tests/yml/.stylish-output.yml tests/sass/parse.scss --verbose --no-exit --format json'; - childProcess.exec(command, function (err, stdout) { // eslint-disable-line handle-callback-err + exec(command, function (err, stdout) { // eslint-disable-line handle-callback-err var result = JSON.parse(stdout)[0], message = result.messages[0].message, expected = 'Please check validity of the block starting from line #5'; @@ -363,7 +375,7 @@ describe('cli', function () { it('parse errors rule Id should be \'Fatal\'', function (done) { var command = 'sass-lint --config tests/yml/.stylish-output.yml tests/sass/parse.scss --verbose --no-exit --format json'; - childProcess.exec(command, function (err, stdout) { // eslint-disable-line handle-callback-err + exec(command, function (err, stdout) { // eslint-disable-line handle-callback-err var result = JSON.parse(stdout)[0], messages = result.messages[0], ruleId = 'Fatal'; diff --git a/tests/sass/cli-clean.scss b/tests/sass/cli-clean.scss new file mode 100644 index 00000000..82c15287 --- /dev/null +++ b/tests/sass/cli-clean.scss @@ -0,0 +1,5 @@ +$red: #f00; + +.cli { + color: $red; +} From efd3c8767c49c439c5963ac9d5096774a76b049c Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Mon, 28 Mar 2016 23:12:39 +0100 Subject: [PATCH 102/137] :arrow_up: Check latest eslint version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3ff8de0b..e8ac7901 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "homepage": "https://github.com/sasstools/sass-lint", "dependencies": { "commander": "^2.8.1", - "eslint": "^2.5.1", + "eslint": "^2.5.3", "fs-extra": "^0.26.0", "glob": "^7.0.0", "gonzales-pe": "^3.2.6", From d37d4bab351a0114469eca7d362ed2edda13193b Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Mon, 28 Mar 2016 23:17:47 +0100 Subject: [PATCH 103/137] :memo: Update readme with better information --- README.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 73c0b6b6..0bce6c11 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,33 @@ A Node-only Sass linter for both `sass` and `scss` syntax! +--- + ## Install +You can get `sass-lint` from [NPM](https://www.npmjs.com/package/sass-lint) ``` npm install sass-lint --save-dev ``` +--- + ## Configuring -Use the [Sample Config](docs/sass-lint.yml) as a guide to create your `.sass-lint.yml` in the root of where you are running Sass Lint from. The default configuration can be found [here](https://github.com/sasstools/sass-lint/blob/master/lib/config/sass-lint.yml). +Sass-lint can be configured from a `.sass-lint.yml` file in your project. If you don't have one in the root of your project or you would like all your projects to follow a standard config file then you can specify the path to one in your projects `pacakge.json` file. + +For example: +```javascript +{ + "name": "my-project", + "version": "1.0.0", + "sasslintConfig": "PATH/TO/YOUR/CONFIG/FILE" +} +``` + +Use the [Sample Config](https://github.com/sasstools/sass-lint/tree/master/docs/sass-lint.yml) as a guide to create your own `.sass-lint.yml` config file. The default configuration can be found [here](https://github.com/sasstools/sass-lint/blob/master/lib/config/sass-lint.yml). + +### [Configuration Documentation](https://github.com/sasstools/sass-lint/tree/master/docs/options) *Migrating from SCSS-Lint*: If you already have a config for SCSS-Lint, you can instantly convert it to the equivalent Sass Lint config at [sasstools.github.io/make-sass-lint-config](http://sasstools.github.io/make-sass-lint-config/). @@ -18,13 +36,27 @@ Use the [Sample Config](docs/sass-lint.yml) as a guide to create your `.sass-lin The following are options that you can use to config the Sass Linter. +* [cache-config](https://github.com/sasstools/sass-lint/tree/master/docs/options/cache-config.md) - Allows you to cache your config for a small speed boost when not changing the contents of your config file +* [config-file](https://github.com/sasstools/sass-lint/tree/master/docs/options/config-file.md) - Specify another config file to load +* [formatter](https://github.com/sasstools/sass-lint/tree/master/docs/options/formatter.md) - Choose the format for any warnings/errors to be displayed +* [merge-default-rules](https://github.com/sasstools/sass-lint/tree/master/docs/options/merge-default-rules.md) - Allows you to merge your rules with the default config file included with sass-lint +* [output-file](https://github.com/sasstools/sass-lint/tree/master/docs/options/output-file.md) - Choose to write the linters output to a file + + #### Files The `files` option can either be set to a [glob](https://github.com/isaacs/node-glob) or it can be set to an object, where the key `include` is set to the glob you want to include, and `ignore` set to either a glob string or an array of glob strings that you would like to ignore. +```yml +files: + include: 'sass/**/*.s+(a|c)ss' + ignore: + - 'sass/vendor/**/*.*' +``` + #### Rules -For all [rules](docs/rules), setting their severity to `0` turns it off, setting to `1` sets it as a warning (something that should not be committed in), and setting to `2` set it to an error (something that should not be written). If a rule is set to just a severity, it will use the default configuration (where available). +For all [rules](https://github.com/sasstools/sass-lint/tree/master/docs/rules), setting their severity to `0` turns it off, setting to `1` sets it as a warning (something that should not be committed in), and setting to `2` sets it to an error (something that should not be written). If a rule is set to just a severity, it will use the default configuration (where available). If you want to configure options, set the rule to an array, where the first item in the array is the severity, and the second item in the array is an object including the options you would like to set. @@ -37,9 +69,28 @@ indentation: size: 2 ``` +### [Rules Documentation](https://github.com/sasstools/sass-lint/tree/master/docs/rules) + +--- + ## CLI -Sass Lint [`v1.1.0`](https://github.com/sasstools/sass-lint/releases/tag/v1.1.0) introduced the ability to run Sass Lint through a command line interface. See the [CLI Docs](docs/cli) for full documentation on how to use the CLI. +Sass Lint [`v1.1.0`](https://github.com/sasstools/sass-lint/releases/tag/v1.1.0) introduced the ability to run Sass Lint through a command line interface. See the [CLI Docs](https://github.com/sasstools/sass-lint/tree/master/docs/cli) for full documentation on how to use the CLI. + +There are small differences which are useful to understand over other CLI tools you may have encountered with other linters. + +By default any rule set to severity: `2` in your config will throw an error which will stop the CLI on the first error it encounters. If you wish to see a list of errors and not have the CLI then you'll need to use the `-q` or `--no-exit` flag. + +Warnings or any rule set to severity: `1` in your config by default will not be reported by the CLI tool unless you use verbose flag `-v` or `--verbose`. + +With this in mind if you would like to have the CLI show both warnings and errors then at the very least you should start with the following command. +`sass-lint -v -q` + +This will be revisited and updated in `sass-lint` v2.0.0 + +### [CLI Documentation](https://github.com/sasstools/sass-lint/tree/master/docs/cli) + +--- ## Creating Rules From 5f08adc545cc464a01679d0c8ff7de0ec5f80faf Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Tue, 29 Mar 2016 00:04:19 +0100 Subject: [PATCH 104/137] :memo: Improve CLI documentation --- README.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0bce6c11..36373469 100644 --- a/README.md +++ b/README.md @@ -79,13 +79,26 @@ Sass Lint [`v1.1.0`](https://github.com/sasstools/sass-lint/releases/tag/v1.1.0) There are small differences which are useful to understand over other CLI tools you may have encountered with other linters. -By default any rule set to severity: `2` in your config will throw an error which will stop the CLI on the first error it encounters. If you wish to see a list of errors and not have the CLI then you'll need to use the `-q` or `--no-exit` flag. +By default any rule set to severity: `2` in your config will throw an error which will stop the CLI on the first error it encounters. If you wish to see a list of errors and not have the CLI exit then you'll need to use the `-q` or `--no-exit` flag. Warnings or any rule set to severity: `1` in your config by default will not be reported by the CLI tool unless you use verbose flag `-v` or `--verbose`. -With this in mind if you would like to have the CLI show both warnings and errors then at the very least you should start with the following command. +With this in mind if you would like to have the CLI show both warnings and errors then at the very least your starting point to use the cli should be the following command. `sass-lint -v -q` +Below is an example of the command being used to load a config `-c app/config/.sass-lint.yml` file show errors and warnings on the command line and target a glob pattern `**/*.scss` being used. + +`sass-lint -c app/config/.sass-lint.yml '**/*.scss' -v -q` +or with long form flags +`sass-lint --config app/config/.sass-lint.yml '**/*.scss' --verbose --no-exit` + +To add a list of files to ignore `tests/**/*.scss, dist/other.scss` into the mix you could do the following: +`sass-lint -c app/config/.sass-lint.yml '**/*.scss' -v -q -i 'tests/**/*.scss'` +or with long form flags +`sass-lint --config app/config/.sass-lint.yml '**/*.scss' --verbose --no-exit --ignore 'tests/**/*.scss, dist/other.scss'` + +Notice that glob patterns need to be wrapped in quotation or single quote marks in order to be passed to sass-lint correctly and if you want to ignore multiple paths you also need to wrap it in quotation marks and seperate each pattern/fil with a comma and a space `, `. + This will be revisited and updated in `sass-lint` v2.0.0 ### [CLI Documentation](https://github.com/sasstools/sass-lint/tree/master/docs/cli) From 699f0ee521b9dcfe16b7d0e828ff45bb2deea450 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Tue, 29 Mar 2016 00:06:01 +0100 Subject: [PATCH 105/137] :memo: Improve code example spacing --- README.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 36373469..def7a82f 100644 --- a/README.md +++ b/README.md @@ -88,19 +88,31 @@ With this in mind if you would like to have the CLI show both warnings and error Below is an example of the command being used to load a config `-c app/config/.sass-lint.yml` file show errors and warnings on the command line and target a glob pattern `**/*.scss` being used. -`sass-lint -c app/config/.sass-lint.yml '**/*.scss' -v -q` +``` +sass-lint -c app/config/.sass-lint.yml '**/*.scss' -v -q +``` + or with long form flags -`sass-lint --config app/config/.sass-lint.yml '**/*.scss' --verbose --no-exit` + +``` +sass-lint --config app/config/.sass-lint.yml '**/*.scss' --verbose --no-exit +``` To add a list of files to ignore `tests/**/*.scss, dist/other.scss` into the mix you could do the following: -`sass-lint -c app/config/.sass-lint.yml '**/*.scss' -v -q -i 'tests/**/*.scss'` +``` +sass-lint -c app/config/.sass-lint.yml '**/*.scss' -v -q -i 'tests/**/*.scss' +``` or with long form flags -`sass-lint --config app/config/.sass-lint.yml '**/*.scss' --verbose --no-exit --ignore 'tests/**/*.scss, dist/other.scss'` +``` +sass-lint --config app/config/.sass-lint.yml '**/*.scss' --verbose --no-exit --ignore 'tests/**/*.scss, dist/other.scss' +``` Notice that glob patterns need to be wrapped in quotation or single quote marks in order to be passed to sass-lint correctly and if you want to ignore multiple paths you also need to wrap it in quotation marks and seperate each pattern/fil with a comma and a space `, `. This will be revisited and updated in `sass-lint` v2.0.0 +For further information you can visit our CLI documentation linked below. + ### [CLI Documentation](https://github.com/sasstools/sass-lint/tree/master/docs/cli) --- From f15e0469730f2aa1f4ae8042f2796c2d2c948925 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Tue, 29 Mar 2016 00:08:58 +0100 Subject: [PATCH 106/137] Add section titles to cli examples --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index def7a82f..4e3c110a 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,10 @@ Warnings or any rule set to severity: `1` in your config by default will not be With this in mind if you would like to have the CLI show both warnings and errors then at the very least your starting point to use the cli should be the following command. `sass-lint -v -q` +### CLI Examples + +#### Specify a config + Below is an example of the command being used to load a config `-c app/config/.sass-lint.yml` file show errors and warnings on the command line and target a glob pattern `**/*.scss` being used. ``` @@ -98,6 +102,7 @@ or with long form flags sass-lint --config app/config/.sass-lint.yml '**/*.scss' --verbose --no-exit ``` +#### Ignore files/patterns To add a list of files to ignore `tests/**/*.scss, dist/other.scss` into the mix you could do the following: ``` sass-lint -c app/config/.sass-lint.yml '**/*.scss' -v -q -i 'tests/**/*.scss' @@ -107,7 +112,8 @@ or with long form flags sass-lint --config app/config/.sass-lint.yml '**/*.scss' --verbose --no-exit --ignore 'tests/**/*.scss, dist/other.scss' ``` -Notice that glob patterns need to be wrapped in quotation or single quote marks in order to be passed to sass-lint correctly and if you want to ignore multiple paths you also need to wrap it in quotation marks and seperate each pattern/fil with a comma and a space `, `. + +> Notice that glob patterns need to be wrapped in quotation or single quote marks in order to be passed to sass-lint correctly and if you want to ignore multiple paths you also need to wrap it in quotation marks and seperate each pattern/fil with a comma and a space `, `. This will be revisited and updated in `sass-lint` v2.0.0 From 5da5f050b2ff4e4da7221d555927cbefade84b74 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Tue, 29 Mar 2016 00:22:56 +0100 Subject: [PATCH 107/137] :memo: Add contributions pointer to readme --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 4e3c110a..18f0b024 100644 --- a/README.md +++ b/README.md @@ -123,10 +123,20 @@ For further information you can visit our CLI documentation linked below. --- +## Contributions + +We welcome all contributions to this project but please do read our [contribution guidelines](https://github.com/sasstools/sass-lint/blob/master/CONTRIBUTING.md) first, especially before opening a pull request. It would also be good to read our [code of conduct](https://github.com/sasstools/sass-lint/blob/master/CODE_OF_CONDUCT.md). + +Please don't feel hurt or embarrassed if you find your issues/PR's that don't follow these guidelines closed as it can be a very time consuming process managing the quantity of issues and PR's we receive. If you have any questions just ask! + +--- + ## Creating Rules Our AST is [Gonzales-PE](https://github.com/tonyganch/gonzales-pe/tree/dev). Each rule will be passed the full AST which they can traverse as they please. There are many different [node types](https://github.com/tonyganch/gonzales-pe/blob/dev/docs/node-types.md) that may be traversed, and an extensive [API for working with nodes](https://github.com/tonyganch/gonzales-pe/tree/dev#api). The file of the rule must have the same name as the name of the rule. All of the available rules are in our [rules directory](https://github.com/sasstools/sass-lint/tree/develop/lib/rules). Default options will be merged in with user config. +--- + ## Task Runner Integration * [Gulp](https://www.npmjs.com/package/gulp-sass-lint) From 7dfe9974549c47459f69b0f1cb769e82e1598cbc Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Tue, 29 Mar 2016 13:05:05 +0100 Subject: [PATCH 108/137] :bug: Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 18f0b024..f62a4df2 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ npm install sass-lint --save-dev ## Configuring -Sass-lint can be configured from a `.sass-lint.yml` file in your project. If you don't have one in the root of your project or you would like all your projects to follow a standard config file then you can specify the path to one in your projects `pacakge.json` file. +Sass-lint can be configured from a `.sass-lint.yml` file in your project. If you don't have one in the root of your project or you would like all your projects to follow a standard config file then you can specify the path to one in your projects `package.json` file. For example: ```javascript From f5744ec226e770b63de3846debb45082ec0418c0 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Tue, 29 Mar 2016 18:52:38 +0100 Subject: [PATCH 109/137] :fire: Use temporary gonzales fork --- lib/groot.js | 2 +- lib/helpers.js | 2 +- package.json | 2 +- tests/helpers.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/groot.js b/lib/groot.js index f3bad09e..1f99c46b 100644 --- a/lib/groot.js +++ b/lib/groot.js @@ -3,7 +3,7 @@ ////////////////////////////// 'use strict'; -var gonzales = require('gonzales-pe'); +var gonzales = require('gonzales-pe-sl'); module.exports = function (text, syntax, filename) { var tree; diff --git a/lib/helpers.js b/lib/helpers.js index 6b9df434..a41ed987 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -4,7 +4,7 @@ var util = require('util'), fs = require('fs'), path = require('path'), yaml = require('js-yaml'), - gonzales = require('gonzales-pe'); + gonzales = require('gonzales-pe-sl'); var helpers = {}; diff --git a/package.json b/package.json index e8ac7901..e41e0dd6 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "eslint": "^2.5.3", "fs-extra": "^0.26.0", "glob": "^7.0.0", - "gonzales-pe": "^3.2.6", + "gonzales-pe-sl": "3.2.6", "js-yaml": "^3.5.4", "lodash.capitalize": "^4.1.0", "lodash.kebabcase": "^4.0.0", diff --git a/tests/helpers.js b/tests/helpers.js index e9b12db9..aeee4e45 100644 --- a/tests/helpers.js +++ b/tests/helpers.js @@ -2,7 +2,7 @@ var assert = require('assert'), helpers = require('../lib/helpers'), - gonzales = require('gonzales-pe'); + gonzales = require('gonzales-pe-sl'); var haystack = [ { From bceaa15b203401ae96fa27dcf0c04e7de6600414 Mon Sep 17 00:00:00 2001 From: greenkeeperio-bot Date: Mon, 4 Apr 2016 19:35:23 -0400 Subject: [PATCH 110/137] chore(package): update eslint to version 2.7.0 http://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f45f59dd..ec27b6d2 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "homepage": "https://github.com/sasstools/sass-lint", "dependencies": { "commander": "^2.8.1", - "eslint": "^1.1.0", + "eslint": "^2.7.0", "fs-extra": "^0.26.0", "glob": "^7.0.0", "gonzales-pe": "3.0.0-31", From c012ba245db042ac6bb800f500fd58b14e2f1390 Mon Sep 17 00:00:00 2001 From: greenkeeperio-bot Date: Fri, 15 Apr 2016 08:04:19 -0400 Subject: [PATCH 111/137] chore(package): update fs-extra to version 0.27.0 http://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3e89b30b..334dd862 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "dependencies": { "commander": "^2.8.1", "eslint": "^2.7.0", - "fs-extra": "^0.26.0", + "fs-extra": "^0.27.0", "glob": "^7.0.0", "gonzales-pe-sl": "3.2.6", "js-yaml": "^3.5.4", From d7be720276798999da514e612b4ba975777d45dd Mon Sep 17 00:00:00 2001 From: Nicolas Coden Date: Sun, 17 Apr 2016 18:28:02 +0200 Subject: [PATCH 112/137] :mag: Add BEM conventions for all naming rules **Fix**: https://github.com/sasstools/sass-lint/issues/598 **Changes**: Add `strictbem ` and `hyphenatedbem` conventions for all naming rules: - `function-name-format` - `mixin-name-format` - `placeholder-name-format` - `function-name-format` Add tests and examples in documentation for these naming conventions. --- bin/sass-lint.js | 9 +-- docs/cli/readme.md | 1 - docs/rules/function-name-format.md | 69 +++++++++++++++++++++- docs/rules/mixin-name-format.md | 70 ++++++++++++++++++++++- docs/rules/placeholder-name-format.md | 69 +++++++++++++++++++++- docs/rules/variable-name-format.md | 57 ++++++++++++++++++- lib/rules/function-name-format.js | 10 ++++ lib/rules/mixin-name-format.js | 10 ++++ lib/rules/placeholder-name-format.js | 10 ++++ lib/rules/variable-name-format.js | 10 ++++ package.json | 2 +- tests/cli.js | 12 ---- tests/rules/function-name-format.js | 76 +++++++++++++++++++++---- tests/rules/mixin-name-format.js | 76 +++++++++++++++++++++---- tests/rules/placeholder-name-format.js | 76 +++++++++++++++++++++---- tests/rules/variable-name-format.js | 76 +++++++++++++++++++++---- tests/sass/function-name-format.sass | 21 +++++++ tests/sass/function-name-format.scss | 28 +++++++++ tests/sass/mixin-name-format.sass | 27 +++++++++ tests/sass/mixin-name-format.scss | 36 ++++++++++++ tests/sass/placeholder-name-format.sass | 27 +++++++++ tests/sass/placeholder-name-format.scss | 36 ++++++++++++ tests/sass/variable-name-format.sass | 10 ++++ tests/sass/variable-name-format.scss | 10 ++++ 24 files changed, 762 insertions(+), 66 deletions(-) diff --git a/bin/sass-lint.js b/bin/sass-lint.js index 787e9330..33bea9dd 100755 --- a/bin/sass-lint.js +++ b/bin/sass-lint.js @@ -10,12 +10,6 @@ var configPath, configOptions = {}, exitCode = 0; -var tooManyWarnings = function (detects) { - var warningCount = lint.warningCount(detects).count; - - return warningCount > 0 && warningCount > program.maxWarnings; -}; - var detectPattern = function (pattern) { var detects; @@ -25,7 +19,7 @@ var detectPattern = function (pattern) { lint.outputResults(detects, configOptions, configPath); } - if (lint.errorCount(detects).count || tooManyWarnings(detects)) { + if (lint.errorCount(detects).count) { exitCode = 1; } @@ -44,7 +38,6 @@ program .option('-f, --format [format]', 'pass one of the available eslint formats') .option('-o, --output [output]', 'the path and filename where you would like output to be written') .option('-s, --syntax [syntax]', 'syntax to evaluate the file(s) with (either sass or scss)') - .option('--max-warnings [integer]', 'Number of warnings to trigger nonzero exit code') .parse(process.argv); diff --git a/docs/cli/readme.md b/docs/cli/readme.md index 739124df..160ed347 100644 --- a/docs/cli/readme.md +++ b/docs/cli/readme.md @@ -16,7 +16,6 @@ Command Line Flag | Description `-f`,`--format [format]` | Pass one of the available [Eslint formats](https://github.com/eslint/eslint/tree/master/lib/formatters) to format the output of sass-lint results. `-h`,`--help` | Outputs usage information for the CLI `-i`,`--ignore [pattern]` | A pattern that should be ignored from linting. Multiple patterns can be used by separating each pattern by `, `. Patterns should be wrapped in quotes (will be merged with other ignore options) -`--max-warnings [integer]`| Normally, if SassLint runs and finds no errors (only warnings), it will exit with a success exit status. However, if this option is specified and the total warning count is greater than the specified threshold, SassLint will exit with an error status. `-o`,`--output [output]` | The path plus file name relative to where Sass Lint is being run from where the output should be written to. `-q`,`--no-exit` | Prevents the CLI from throwing an error if there is one (useful for development work) `-s`,`--syntax` | Syntax to evaluate the given file(s) with, either sass or scss. Use with care: overrides filename extension-based syntax detection. diff --git a/docs/rules/function-name-format.md b/docs/rules/function-name-format.md index 2f702633..054dfaa3 100644 --- a/docs/rules/function-name-format.md +++ b/docs/rules/function-name-format.md @@ -5,7 +5,8 @@ Rule `function-name-format` will enforce a convention for function names. ## Options * `allow-leading-underscore`: `true`/`false` (defaults to `true`) -* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, or a Regular Expression that the function name must match (e.g. `^[_A-Z]+$`) +* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, [`strictbem`](https://en.bem.info/method/definitions/), +[`hyphenatedbem`](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/), or a Regular Expression that the function name must match (e.g. `^[_A-Z]+$`) * `convention-explanation`: Custom explanation to display to the user if a function doesn't adhere to the convention ## Example 1 @@ -116,6 +117,72 @@ When enabled, the following are disallowed: ## Example 4 +Settings: +- `convention: strictbem` + +When enabled, the following are allowed: + +```scss +@function namespace__function { + @return "foo"; +} + +@function namespace__function_mod-name { + @return "foo"; +} + +@function namespace_mod-name__function { + @return "foo"; +} +``` + +When enabled, the following are disallowed: + +```scss +@function HYPHENATED-UPPERCASE { + @return "foo"; +} + +.foo { + content: camelCase(); +} +``` + +## Example 5 + +Settings: +- `convention: hyphenatedbem` + +When enabled, the following are allowed: + +```scss +@function namespace__function { + @return "foo"; +} + +@function namespace__function--mod-name { + @return "foo"; +} + +@function namespace--mod-name__function { + @return "foo"; +} +``` + +When enabled, the following are disallowed: + +```scss +@function HYPHENATED-UPPERCASE { + @return "foo"; +} + +.foo { + content: camelCase(); +} +``` + +## Example 6 + Settings: - `allow-leading-underscore: true` - `convention: '^[_A-Z]+$'` diff --git a/docs/rules/mixin-name-format.md b/docs/rules/mixin-name-format.md index 537e11f9..2bbbbe3f 100644 --- a/docs/rules/mixin-name-format.md +++ b/docs/rules/mixin-name-format.md @@ -5,7 +5,8 @@ Rule `mixin-name-format` will enforce a convention for mixin names. ## Options * `allow-leading-underscore`: `true`/`false` (defaults to `true`) -* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) +* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, [`strictbem`](https://en.bem.info/method/definitions/), +[`hyphenatedbem`](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/), or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) * `convention-explanation`: Custom explanation to display to the user if a mixin doesn't adhere to the convention ## Example 1 @@ -117,6 +118,73 @@ When enabled, the following are disallowed: ## Example 4 +Settings: +- `convention: strictbem` + +When enabled, the following are allowed: + +```scss +@mixin block-name { + content: ''; +} + +@mixin block-name__mixin { + content: ''; +} + +@mixin block-name_mod-name { + content: ''; +} +``` + +When enabled, the following are disallowed: + +```scss +@mixin HYPHENATED-UPPERCASE { + content: ''; +} + +.foo { + @include camelCase(); +} +``` + +## Example 5 + +Settings: +- `convention: hyphenatedbem` + +When enabled, the following are allowed: + +```scss +@mixin block-name { + content: ''; +} + +@mixin block-name__mixin { + content: ''; +} + +@mixin block-name--mod-name { + content: ''; +} +``` + +When enabled, the following are disallowed: + +```scss +@mixin HYPHENATED-UPPERCASE { + content: ''; +} + +.foo { + @include camelCase(); +} +``` + + +## Example 6 + Settings: - `allow-leading-underscore: true` - `convention: ^[_A-Z]+$` diff --git a/docs/rules/placeholder-name-format.md b/docs/rules/placeholder-name-format.md index 6da7868d..9cb3a682 100644 --- a/docs/rules/placeholder-name-format.md +++ b/docs/rules/placeholder-name-format.md @@ -5,7 +5,8 @@ Rule `placeholder-name-format` will enforce a convention for placeholder names. ## Options * `allow-leading-underscore`: `true`/`false` (defaults to `true`) -* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) +* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, [`strictbem`](https://en.bem.info/method/definitions/), +[`hyphenatedbem`](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/), or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) * `convention-explanation`: Custom explanation to display to the user if a placeholder doesn't adhere to the convention ## Example 1 @@ -117,6 +118,72 @@ When enabled, the following are disallowed: ## Example 4 +Settings: +- `convention: strictbem` + +When enabled, the following are allowed: + +```scss +%block-name { + content: ''; +} + +%block-name__mixin { + content: ''; +} + +%block-name_mod-name { + content: ''; +} +``` + +When enabled, the following are disallowed: + +```scss +%HYPHENATED-UPPERCASE { + content: ''; +} + +.foo { + @extend %camelCase; +} +``` + +## Example 5 + +Settings: +- `convention: hyphenatedbem` + +When enabled, the following are allowed: + +```scss +%block-name { + content: ''; +} + +%block-name__mixin { + content: ''; +} + +%block-name--mod-name { + content: ''; +} +``` + +When enabled, the following are disallowed: + +```scss +%HYPHENATED-UPPERCASE { + content: ''; +} + +.foo { + @extend %camelCase; +} +``` + +## Example 6 + Settings: - `allow-leading-underscore: true` - `convention: ^[_A-Z]+$` diff --git a/docs/rules/variable-name-format.md b/docs/rules/variable-name-format.md index ac063242..df819ef0 100644 --- a/docs/rules/variable-name-format.md +++ b/docs/rules/variable-name-format.md @@ -5,7 +5,8 @@ Rule `variable-name-format` will enforce a convention for variable names. ## Options * `allow-leading-underscore`: `true`/`false` (defaults to `true`) -* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) +* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, [`strictbem`](https://en.bem.info/method/definitions/), +[`hyphenatedbem`](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/), or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) * `convention-explanation`: Custom explanation to display to the user if a variable doesn't adhere to the convention ## Example 1 @@ -93,6 +94,60 @@ $_snake_case_with_leading_underscore: 1px; ## Example 4 +Settings: +- `convention: strictbem` + +When enabled, the following are allowed: + +```scss +$block-name__variable: 1px; +$block-name__variable_mod-name: 1px; +$block-name_mod-name__variable: 1px; + +.foo { + width: $block-name__variable; +} +``` + +When enabled, the following are disallowed: + +```scss +$HYPHENATED-UPPERCASE: 1px; + +.foo { + width: $camelCase; +} +``` + +## Example 5 + +Settings: +- `convention: hyphenatedbem` + +When enabled, the following are allowed: + +```scss +$block-name__variable: 1px; +$block-name__variable--mod-name: 1px; +$block-name--mod-name__variable: 1px; + +.foo { + width: $block-name__variable; +} +``` + +When enabled, the following are disallowed: + +```scss +$HYPHENATED-UPPERCASE: 1px; + +.foo { + width: $camelCase; +} +``` + +## Example 6 + Settings: - `allow-leading-underscore: true` - `convention: ^[_A-Z]+$` diff --git a/lib/rules/function-name-format.js b/lib/rules/function-name-format.js index 163993d9..2bdccb97 100644 --- a/lib/rules/function-name-format.js +++ b/lib/rules/function-name-format.js @@ -50,6 +50,16 @@ module.exports = { violationMessage = 'Function \'' + name + '\' should be written in snake_case'; } break; + case 'strictbem': + if (!helpers.isStrictBEM(strippedName)) { + violationMessage = 'Function \'.' + name + '\' should be written in BEM (Block Element Modifier) format'; + } + break; + case 'hyphenatedbem': + if (!helpers.isHyphenatedBEM(strippedName)) { + violationMessage = 'Function \'.' + name + '\' should be written in hyphenated BEM (Block Element Modifier) format'; + } + break; default: if (!(new RegExp(parser.options.convention).test(strippedName))) { violationMessage = 'Function \'' + name + '\' should match regular expression /' + parser.options.convention + '/'; diff --git a/lib/rules/mixin-name-format.js b/lib/rules/mixin-name-format.js index d2940ebc..3f10ef50 100644 --- a/lib/rules/mixin-name-format.js +++ b/lib/rules/mixin-name-format.js @@ -60,6 +60,16 @@ module.exports = { violationMessage = 'Mixin \'' + name + '\' should be written in snake_case'; } break; + case 'strictbem': + if (!helpers.isStrictBEM(strippedName)) { + violationMessage = 'Mixin \'.' + name + '\' should be written in BEM (Block Element Modifier) format'; + } + break; + case 'hyphenatedbem': + if (!helpers.isHyphenatedBEM(strippedName)) { + violationMessage = 'Mixin \'.' + name + '\' should be written in hyphenated BEM (Block Element Modifier) format'; + } + break; default: if (!(new RegExp(parser.options.convention).test(strippedName))) { violationMessage = 'Mixin \'' + name + '\' should match regular expression /' + parser.options.convention + '/'; diff --git a/lib/rules/placeholder-name-format.js b/lib/rules/placeholder-name-format.js index f6333054..c2ef92e0 100644 --- a/lib/rules/placeholder-name-format.js +++ b/lib/rules/placeholder-name-format.js @@ -40,6 +40,16 @@ module.exports = { violationMessage = 'Placeholder \'%' + name + '\' should be written in snake_case'; } break; + case 'strictbem': + if (!helpers.isStrictBEM(strippedName)) { + violationMessage = 'Placeholder \'.' + name + '\' should be written in BEM (Block Element Modifier) format'; + } + break; + case 'hyphenatedbem': + if (!helpers.isHyphenatedBEM(strippedName)) { + violationMessage = 'Placeholder \'.' + name + '\' should be written in hyphenated BEM (Block Element Modifier) format'; + } + break; default: if (!(new RegExp(parser.options.convention).test(strippedName))) { violationMessage = 'Placeholder \'%' + name + '\' should match regular expression /' + parser.options.convention + '/'; diff --git a/lib/rules/variable-name-format.js b/lib/rules/variable-name-format.js index f79a7418..9ab4d6c0 100644 --- a/lib/rules/variable-name-format.js +++ b/lib/rules/variable-name-format.js @@ -40,6 +40,16 @@ module.exports = { violationMessage = 'Variable \'' + name + '\' should be written in snake_case'; } break; + case 'strictbem': + if (!helpers.isStrictBEM(strippedName)) { + violationMessage = 'Variable \'.' + name + '\' should be written in BEM (Block Element Modifier) format'; + } + break; + case 'hyphenatedbem': + if (!helpers.isHyphenatedBEM(strippedName)) { + violationMessage = 'Variable \'.' + name + '\' should be written in hyphenated BEM (Block Element Modifier) format'; + } + break; default: if (!(new RegExp(parser.options.convention).test(strippedName))) { violationMessage = 'Variable \'' + name + '\' should match regular expression /' + parser.options.convention + '/'; diff --git a/package.json b/package.json index 334dd862..3e89b30b 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "dependencies": { "commander": "^2.8.1", "eslint": "^2.7.0", - "fs-extra": "^0.27.0", + "fs-extra": "^0.26.0", "glob": "^7.0.0", "gonzales-pe-sl": "3.2.6", "js-yaml": "^3.5.4", diff --git a/tests/cli.js b/tests/cli.js index 29bee934..fc4b6943 100644 --- a/tests/cli.js +++ b/tests/cli.js @@ -320,18 +320,6 @@ describe('cli', function () { }); }); - it('should exit with exit code 1 when more warnings than --max-warnings', function (done) { - var command = 'sass-lint -c tests/yml/.color-keyword-errors.yml tests/sass/cli.scss --max-warnings 0'; - - exec(command, function (err) { - if (err && err.code === 1) { - return done(); - } - - return done(new Error('Error code not 1')); - }); - }); - it('should not exit with an error if no config is specified', function (done) { var command = 'sass-lint tests/sass/cli-clean.scss --verbose --no-exit'; diff --git a/tests/rules/function-name-format.js b/tests/rules/function-name-format.js index cf121035..e797f6a7 100644 --- a/tests/rules/function-name-format.js +++ b/tests/rules/function-name-format.js @@ -9,7 +9,7 @@ describe('function name format - scss', function () { lint.test(file, { 'function-name-format': 1 }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); @@ -23,7 +23,7 @@ describe('function name format - scss', function () { } ] }, function (data) { - lint.assert.equal(10, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -37,7 +37,35 @@ describe('function name format - scss', function () { } ] }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(11, data.warningCount); + done(); + }); + }); + + it('[convention: strictbem]', function (done) { + lint.test(file, { + 'function-name-format': [ + 1, + { + 'convention': 'strictbem' + } + ] + }, function (data) { + lint.assert.equal(13, data.warningCount); + done(); + }); + }); + + it('[convention: hyphenatedbem]', function (done) { + lint.test(file, { + 'function-name-format': [ + 1, + { + 'convention': 'hyphenatedbem' + } + ] + }, function (data) { + lint.assert.equal(11, data.warningCount); done(); }); }); @@ -52,7 +80,7 @@ describe('function name format - scss', function () { } ] }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -66,7 +94,7 @@ describe('function name format - scss', function () { } ] }, function (data) { - lint.assert.equal(10, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -79,7 +107,7 @@ describe('function name format - sass', function () { lint.test(file, { 'function-name-format': 1 }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); @@ -93,7 +121,7 @@ describe('function name format - sass', function () { } ] }, function (data) { - lint.assert.equal(10, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -107,7 +135,35 @@ describe('function name format - sass', function () { } ] }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(11, data.warningCount); + done(); + }); + }); + + it('[convention: strictbem]', function (done) { + lint.test(file, { + 'function-name-format': [ + 1, + { + 'convention': 'strictbem' + } + ] + }, function (data) { + lint.assert.equal(13, data.warningCount); + done(); + }); + }); + + it('[convention: hyphenatedbem]', function (done) { + lint.test(file, { + 'function-name-format': [ + 1, + { + 'convention': 'hyphenatedbem' + } + ] + }, function (data) { + lint.assert.equal(11, data.warningCount); done(); }); }); @@ -122,7 +178,7 @@ describe('function name format - sass', function () { } ] }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -136,7 +192,7 @@ describe('function name format - sass', function () { } ] }, function (data) { - lint.assert.equal(10, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); diff --git a/tests/rules/mixin-name-format.js b/tests/rules/mixin-name-format.js index 09b51874..f56bb146 100644 --- a/tests/rules/mixin-name-format.js +++ b/tests/rules/mixin-name-format.js @@ -9,7 +9,7 @@ describe('mixin name format - scss', function () { lint.test(file, { 'mixin-name-format': 1 }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(14, data.warningCount); done(); }); }); @@ -23,7 +23,7 @@ describe('mixin name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -37,7 +37,35 @@ describe('mixin name format - scss', function () { } ] }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(10, data.warningCount); + done(); + }); + }); + + it('[convention: strictbem]', function (done) { + lint.test(file, { + 'mixin-name-format': [ + 1, + { + 'convention': 'strictbem' + } + ] + }, function (data) { + lint.assert.equal(13, data.warningCount); + done(); + }); + }); + + it('[convention: hyphenatedbem]', function (done) { + lint.test(file, { + 'mixin-name-format': [ + 1, + { + 'convention': 'hyphenatedbem' + } + ] + }, function (data) { + lint.assert.equal(10, data.warningCount); done(); }); }); @@ -52,7 +80,7 @@ describe('mixin name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(17, data.warningCount); done(); }); }); @@ -66,7 +94,7 @@ describe('mixin name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); @@ -79,7 +107,7 @@ describe('mixin name format - sass', function () { lint.test(file, { 'mixin-name-format': 1 }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(14, data.warningCount); done(); }); }); @@ -93,7 +121,7 @@ describe('mixin name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -107,7 +135,35 @@ describe('mixin name format - sass', function () { } ] }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(10, data.warningCount); + done(); + }); + }); + + it('[convention: strictbem]', function (done) { + lint.test(file, { + 'mixin-name-format': [ + 1, + { + 'convention': 'strictbem' + } + ] + }, function (data) { + lint.assert.equal(13, data.warningCount); + done(); + }); + }); + + it('[convention: hyphenatedbem]', function (done) { + lint.test(file, { + 'mixin-name-format': [ + 1, + { + 'convention': 'hyphenatedbem' + } + ] + }, function (data) { + lint.assert.equal(10, data.warningCount); done(); }); }); @@ -122,7 +178,7 @@ describe('mixin name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(17, data.warningCount); done(); }); }); @@ -136,7 +192,7 @@ describe('mixin name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); diff --git a/tests/rules/placeholder-name-format.js b/tests/rules/placeholder-name-format.js index f102700e..f0f20355 100644 --- a/tests/rules/placeholder-name-format.js +++ b/tests/rules/placeholder-name-format.js @@ -9,7 +9,7 @@ describe('placeholder name format - scss', function () { lint.test(file, { 'placeholder-name-format': 1 }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(14, data.warningCount); done(); }); }); @@ -23,7 +23,7 @@ describe('placeholder name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -37,7 +37,35 @@ describe('placeholder name format - scss', function () { } ] }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(10, data.warningCount); + done(); + }); + }); + + it('[convention: strictbem]', function (done) { + lint.test(file, { + 'placeholder-name-format': [ + 1, + { + 'convention': 'strictbem' + } + ] + }, function (data) { + lint.assert.equal(13, data.warningCount); + done(); + }); + }); + + it('[convention: hyphenatedbem]', function (done) { + lint.test(file, { + 'placeholder-name-format': [ + 1, + { + 'convention': 'hyphenatedbem' + } + ] + }, function (data) { + lint.assert.equal(10, data.warningCount); done(); }); }); @@ -52,7 +80,7 @@ describe('placeholder name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(17, data.warningCount); done(); }); }); @@ -66,7 +94,7 @@ describe('placeholder name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); @@ -79,7 +107,7 @@ describe('placeholder name format - sass', function () { lint.test(file, { 'placeholder-name-format': 1 }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(14, data.warningCount); done(); }); }); @@ -93,7 +121,7 @@ describe('placeholder name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -107,7 +135,35 @@ describe('placeholder name format - sass', function () { } ] }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(10, data.warningCount); + done(); + }); + }); + + it('[convention: strictbem]', function (done) { + lint.test(file, { + 'placeholder-name-format': [ + 1, + { + 'convention': 'strictbem' + } + ] + }, function (data) { + lint.assert.equal(13, data.warningCount); + done(); + }); + }); + + it('[convention: hyphenatedbem]', function (done) { + lint.test(file, { + 'placeholder-name-format': [ + 1, + { + 'convention': 'hyphenatedbem' + } + ] + }, function (data) { + lint.assert.equal(10, data.warningCount); done(); }); }); @@ -122,7 +178,7 @@ describe('placeholder name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(17, data.warningCount); done(); }); }); @@ -136,7 +192,7 @@ describe('placeholder name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); diff --git a/tests/rules/variable-name-format.js b/tests/rules/variable-name-format.js index c72dde11..67168905 100644 --- a/tests/rules/variable-name-format.js +++ b/tests/rules/variable-name-format.js @@ -9,7 +9,7 @@ describe('variable name format - scss', function () { lint.test(file, { 'variable-name-format': 1 }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(13, data.warningCount); done(); }); }); @@ -23,7 +23,7 @@ describe('variable name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); @@ -37,7 +37,35 @@ describe('variable name format - scss', function () { } ] }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(10, data.warningCount); + done(); + }); + }); + + it('[convention: strictbem]', function (done) { + lint.test(file, { + 'variable-name-format': [ + 1, + { + 'convention': 'strictbem' + } + ] + }, function (data) { + lint.assert.equal(12, data.warningCount); + done(); + }); + }); + + it('[convention: hyphenatedbem]', function (done) { + lint.test(file, { + 'variable-name-format': [ + 1, + { + 'convention': 'hyphenatedbem' + } + ] + }, function (data) { + lint.assert.equal(9, data.warningCount); done(); }); }); @@ -52,7 +80,7 @@ describe('variable name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -66,7 +94,7 @@ describe('variable name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(14, data.warningCount); done(); }); }); @@ -79,7 +107,7 @@ describe('variable name format - sass', function () { lint.test(file, { 'variable-name-format': 1 }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(13, data.warningCount); done(); }); }); @@ -93,7 +121,7 @@ describe('variable name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); @@ -107,7 +135,35 @@ describe('variable name format - sass', function () { } ] }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(10, data.warningCount); + done(); + }); + }); + + it('[convention: strictbem]', function (done) { + lint.test(file, { + 'variable-name-format': [ + 1, + { + 'convention': 'strictbem' + } + ] + }, function (data) { + lint.assert.equal(12, data.warningCount); + done(); + }); + }); + + it('[convention: hyphenatedbem]', function (done) { + lint.test(file, { + 'variable-name-format': [ + 1, + { + 'convention': 'hyphenatedbem' + } + ] + }, function (data) { + lint.assert.equal(9, data.warningCount); done(); }); }); @@ -122,7 +178,7 @@ describe('variable name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -136,7 +192,7 @@ describe('variable name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(14, data.warningCount); done(); }); }); diff --git a/tests/sass/function-name-format.sass b/tests/sass/function-name-format.sass index d5ceca1c..7f79e981 100644 --- a/tests/sass/function-name-format.sass +++ b/tests/sass/function-name-format.sass @@ -19,6 +19,27 @@ @function _with-leading-underscore() @return 'foo' +@function strictbem() + @return 'foo' + +@function strictbem__function() + @return 'foo' + +@function strictbem__function_modifier() + @return 'foo' + +@function strictbem_modifier__function() + @return 'foo' + +@function hyphenatedbem__function() + @return 'foo' + +@function hyphenatedbem__function--modifier() + @return 'foo' + +@function hyphenatedbem--modifier__function() + @return 'foo' + @function _does_NOT-fitSTANDARD($x) @return $x diff --git a/tests/sass/function-name-format.scss b/tests/sass/function-name-format.scss index e0609943..3148fab0 100644 --- a/tests/sass/function-name-format.scss +++ b/tests/sass/function-name-format.scss @@ -26,6 +26,34 @@ @return 'foo'; } +@function strictbem() { + @return 'foo'; +} + +@function strictbem__function() { + @return 'foo'; +} + +@function strictbem__function_modifier() { + @return 'foo'; +} + +@function strictbem_modifier__function() { + @return 'foo'; +} + +@function hyphenatedbem__function() { + @return 'foo'; +} + +@function hyphenatedbem__function--modifier() { + @return 'foo'; +} + +@function hyphenatedbem--modifier__function() { + @return 'foo'; +} + @function _does_NOT-fitSTANDARD($x) { @return $x; } diff --git a/tests/sass/mixin-name-format.sass b/tests/sass/mixin-name-format.sass index b6648f36..b8f3c951 100644 --- a/tests/sass/mixin-name-format.sass +++ b/tests/sass/mixin-name-format.sass @@ -19,6 +19,33 @@ =_with-leading-underscore() content: '' +=strictbem() + content: '' + +=strictbem_modifier() + content: '' + +=strictbem__mixin() + content: '' + +=strictbem__mixin_modifier() + content: '' + +=strictbem_modifier__mixin() + content: '' + +=hyphenatedbem--modifier() + content: '' + +=hyphenatedbem__mixin() + content: '' + +=hyphenatedbem__mixin--modifier() + content: '' + +=hyphenatedbem--modifier__mixin() + content: '' + =_does_NOT-fitSTANDARD() content: '' diff --git a/tests/sass/mixin-name-format.scss b/tests/sass/mixin-name-format.scss index eb3bad1b..bd09d668 100644 --- a/tests/sass/mixin-name-format.scss +++ b/tests/sass/mixin-name-format.scss @@ -26,6 +26,42 @@ content: ''; } +@mixin strictbem() { + content: ''; +} + +@mixin strictbem_modifier() { + content: ''; +} + +@mixin strictbem__mixin() { + content: ''; +} + +@mixin strictbem__mixin_modifier() { + content: ''; +} + +@mixin strictbem_modifier__mixin() { + content: ''; +} + +@mixin hyphenatedbem--modifier() { + content: ''; +} + +@mixin hyphenatedbem__mixin() { + content: ''; +} + +@mixin hyphenatedbem__mixin--modifier() { + content: ''; +} + +@mixin hyphenatedbem--modifier__mixin() { + content: ''; +} + @mixin _does_NOT-fitSTANDARD() { content: ''; } diff --git a/tests/sass/placeholder-name-format.sass b/tests/sass/placeholder-name-format.sass index 914e5a51..3bef2f27 100644 --- a/tests/sass/placeholder-name-format.sass +++ b/tests/sass/placeholder-name-format.sass @@ -19,6 +19,33 @@ %_with-leading-underscore content: '' +%strictbem + content: '' + +%strictbem_modifier + content: '' + +%strictbem__placeholder + content: '' + +%strictbem__placeholder_modifier + content: '' + +%strictbem_modifier__placeholder + content: '' + +%hyphenatedbem--modifier + content: '' + +%hyphenatedbem__placeholder + content: '' + +%hyphenatedbem__placeholder--modifier + content: '' + +%hyphenatedbem--modifier__placeholder + content: '' + %_does_NOT-fitSTANDARD content: '' diff --git a/tests/sass/placeholder-name-format.scss b/tests/sass/placeholder-name-format.scss index e6348a46..ee7b7550 100644 --- a/tests/sass/placeholder-name-format.scss +++ b/tests/sass/placeholder-name-format.scss @@ -26,6 +26,42 @@ content: ''; } +%strictbem { + content: ''; +} + +%strictbem_placeholder { + content: ''; +} + +%strictbem__mixin { + content: ''; +} + +%strictbem__mixin_placeholder { + content: ''; +} + +%strictbem_placeholder__mixin { + content: ''; +} + +%hyphenatedbem--placeholder { + content: ''; +} + +%hyphenatedbem__mixin { + content: ''; +} + +%hyphenatedbem__mixin--placeholder { + content: ''; +} + +%hyphenatedbem--placeholder__mixin { + content: ''; +} + %_does_NOT-fitSTANDARD { content: ''; } diff --git a/tests/sass/variable-name-format.sass b/tests/sass/variable-name-format.sass index 16e33225..3c3ce96f 100644 --- a/tests/sass/variable-name-format.sass +++ b/tests/sass/variable-name-format.sass @@ -5,6 +5,16 @@ $PascalCase: 1 $Camel_Snake_Case: 1 $SCREAMING_SNAKE_CASE: 1 $_with-leading-underscore: 1 + +$strictbem: 1 +$strictbem__variable: 1 +$strictbem__variable_modifier: 1 +$strictbem_modifier__variable: 1 +$hyphenatedbem--modifier: 1 +$hyphenatedbem__variable: 1 +$hyphenatedbem__variable--modifier: 1 +$hyphenatedbem--modifier__variable: 1 + $_does_NOT-fitSTANDARD: 1 .class diff --git a/tests/sass/variable-name-format.scss b/tests/sass/variable-name-format.scss index 3a905623..fd8df367 100644 --- a/tests/sass/variable-name-format.scss +++ b/tests/sass/variable-name-format.scss @@ -5,6 +5,16 @@ $PascalCase: 1; $Camel_Snake_Case: 1; $SCREAMING_SNAKE_CASE: 1; $_with-leading-underscore: 1; + +$strictbem: 1; +$strictbem__variable: 1; +$strictbem__variable_modifier: 1; +$strictbem_modifier__variable: 1; +$hyphenatedbem--modifier: 1; +$hyphenatedbem__variable: 1; +$hyphenatedbem__variable--modifier: 1; +$hyphenatedbem--modifier__variable: 1; + $_does_NOT-fitSTANDARD: 1; .class { From 00a894e77927ec2beb9d33c905d17b25e8a8634a Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Sun, 17 Apr 2016 17:45:28 +0100 Subject: [PATCH 113/137] Revert "Add BEM conventions for all naming rules #598" --- bin/sass-lint.js | 9 ++- docs/cli/readme.md | 1 + docs/rules/function-name-format.md | 69 +--------------------- docs/rules/mixin-name-format.md | 70 +---------------------- docs/rules/placeholder-name-format.md | 69 +--------------------- docs/rules/variable-name-format.md | 57 +------------------ lib/rules/function-name-format.js | 10 ---- lib/rules/mixin-name-format.js | 10 ---- lib/rules/placeholder-name-format.js | 10 ---- lib/rules/variable-name-format.js | 10 ---- package.json | 2 +- tests/cli.js | 12 ++++ tests/rules/function-name-format.js | 76 ++++--------------------- tests/rules/mixin-name-format.js | 76 ++++--------------------- tests/rules/placeholder-name-format.js | 76 ++++--------------------- tests/rules/variable-name-format.js | 76 ++++--------------------- tests/sass/function-name-format.sass | 21 ------- tests/sass/function-name-format.scss | 28 --------- tests/sass/mixin-name-format.sass | 27 --------- tests/sass/mixin-name-format.scss | 36 ------------ tests/sass/placeholder-name-format.sass | 27 --------- tests/sass/placeholder-name-format.scss | 36 ------------ tests/sass/variable-name-format.sass | 10 ---- tests/sass/variable-name-format.scss | 10 ---- 24 files changed, 66 insertions(+), 762 deletions(-) diff --git a/bin/sass-lint.js b/bin/sass-lint.js index 33bea9dd..787e9330 100755 --- a/bin/sass-lint.js +++ b/bin/sass-lint.js @@ -10,6 +10,12 @@ var configPath, configOptions = {}, exitCode = 0; +var tooManyWarnings = function (detects) { + var warningCount = lint.warningCount(detects).count; + + return warningCount > 0 && warningCount > program.maxWarnings; +}; + var detectPattern = function (pattern) { var detects; @@ -19,7 +25,7 @@ var detectPattern = function (pattern) { lint.outputResults(detects, configOptions, configPath); } - if (lint.errorCount(detects).count) { + if (lint.errorCount(detects).count || tooManyWarnings(detects)) { exitCode = 1; } @@ -38,6 +44,7 @@ program .option('-f, --format [format]', 'pass one of the available eslint formats') .option('-o, --output [output]', 'the path and filename where you would like output to be written') .option('-s, --syntax [syntax]', 'syntax to evaluate the file(s) with (either sass or scss)') + .option('--max-warnings [integer]', 'Number of warnings to trigger nonzero exit code') .parse(process.argv); diff --git a/docs/cli/readme.md b/docs/cli/readme.md index 160ed347..739124df 100644 --- a/docs/cli/readme.md +++ b/docs/cli/readme.md @@ -16,6 +16,7 @@ Command Line Flag | Description `-f`,`--format [format]` | Pass one of the available [Eslint formats](https://github.com/eslint/eslint/tree/master/lib/formatters) to format the output of sass-lint results. `-h`,`--help` | Outputs usage information for the CLI `-i`,`--ignore [pattern]` | A pattern that should be ignored from linting. Multiple patterns can be used by separating each pattern by `, `. Patterns should be wrapped in quotes (will be merged with other ignore options) +`--max-warnings [integer]`| Normally, if SassLint runs and finds no errors (only warnings), it will exit with a success exit status. However, if this option is specified and the total warning count is greater than the specified threshold, SassLint will exit with an error status. `-o`,`--output [output]` | The path plus file name relative to where Sass Lint is being run from where the output should be written to. `-q`,`--no-exit` | Prevents the CLI from throwing an error if there is one (useful for development work) `-s`,`--syntax` | Syntax to evaluate the given file(s) with, either sass or scss. Use with care: overrides filename extension-based syntax detection. diff --git a/docs/rules/function-name-format.md b/docs/rules/function-name-format.md index 054dfaa3..2f702633 100644 --- a/docs/rules/function-name-format.md +++ b/docs/rules/function-name-format.md @@ -5,8 +5,7 @@ Rule `function-name-format` will enforce a convention for function names. ## Options * `allow-leading-underscore`: `true`/`false` (defaults to `true`) -* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, [`strictbem`](https://en.bem.info/method/definitions/), -[`hyphenatedbem`](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/), or a Regular Expression that the function name must match (e.g. `^[_A-Z]+$`) +* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, or a Regular Expression that the function name must match (e.g. `^[_A-Z]+$`) * `convention-explanation`: Custom explanation to display to the user if a function doesn't adhere to the convention ## Example 1 @@ -117,72 +116,6 @@ When enabled, the following are disallowed: ## Example 4 -Settings: -- `convention: strictbem` - -When enabled, the following are allowed: - -```scss -@function namespace__function { - @return "foo"; -} - -@function namespace__function_mod-name { - @return "foo"; -} - -@function namespace_mod-name__function { - @return "foo"; -} -``` - -When enabled, the following are disallowed: - -```scss -@function HYPHENATED-UPPERCASE { - @return "foo"; -} - -.foo { - content: camelCase(); -} -``` - -## Example 5 - -Settings: -- `convention: hyphenatedbem` - -When enabled, the following are allowed: - -```scss -@function namespace__function { - @return "foo"; -} - -@function namespace__function--mod-name { - @return "foo"; -} - -@function namespace--mod-name__function { - @return "foo"; -} -``` - -When enabled, the following are disallowed: - -```scss -@function HYPHENATED-UPPERCASE { - @return "foo"; -} - -.foo { - content: camelCase(); -} -``` - -## Example 6 - Settings: - `allow-leading-underscore: true` - `convention: '^[_A-Z]+$'` diff --git a/docs/rules/mixin-name-format.md b/docs/rules/mixin-name-format.md index 2bbbbe3f..537e11f9 100644 --- a/docs/rules/mixin-name-format.md +++ b/docs/rules/mixin-name-format.md @@ -5,8 +5,7 @@ Rule `mixin-name-format` will enforce a convention for mixin names. ## Options * `allow-leading-underscore`: `true`/`false` (defaults to `true`) -* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, [`strictbem`](https://en.bem.info/method/definitions/), -[`hyphenatedbem`](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/), or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) +* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) * `convention-explanation`: Custom explanation to display to the user if a mixin doesn't adhere to the convention ## Example 1 @@ -118,73 +117,6 @@ When enabled, the following are disallowed: ## Example 4 -Settings: -- `convention: strictbem` - -When enabled, the following are allowed: - -```scss -@mixin block-name { - content: ''; -} - -@mixin block-name__mixin { - content: ''; -} - -@mixin block-name_mod-name { - content: ''; -} -``` - -When enabled, the following are disallowed: - -```scss -@mixin HYPHENATED-UPPERCASE { - content: ''; -} - -.foo { - @include camelCase(); -} -``` - -## Example 5 - -Settings: -- `convention: hyphenatedbem` - -When enabled, the following are allowed: - -```scss -@mixin block-name { - content: ''; -} - -@mixin block-name__mixin { - content: ''; -} - -@mixin block-name--mod-name { - content: ''; -} -``` - -When enabled, the following are disallowed: - -```scss -@mixin HYPHENATED-UPPERCASE { - content: ''; -} - -.foo { - @include camelCase(); -} -``` - - -## Example 6 - Settings: - `allow-leading-underscore: true` - `convention: ^[_A-Z]+$` diff --git a/docs/rules/placeholder-name-format.md b/docs/rules/placeholder-name-format.md index 9cb3a682..6da7868d 100644 --- a/docs/rules/placeholder-name-format.md +++ b/docs/rules/placeholder-name-format.md @@ -5,8 +5,7 @@ Rule `placeholder-name-format` will enforce a convention for placeholder names. ## Options * `allow-leading-underscore`: `true`/`false` (defaults to `true`) -* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, [`strictbem`](https://en.bem.info/method/definitions/), -[`hyphenatedbem`](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/), or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) +* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) * `convention-explanation`: Custom explanation to display to the user if a placeholder doesn't adhere to the convention ## Example 1 @@ -118,72 +117,6 @@ When enabled, the following are disallowed: ## Example 4 -Settings: -- `convention: strictbem` - -When enabled, the following are allowed: - -```scss -%block-name { - content: ''; -} - -%block-name__mixin { - content: ''; -} - -%block-name_mod-name { - content: ''; -} -``` - -When enabled, the following are disallowed: - -```scss -%HYPHENATED-UPPERCASE { - content: ''; -} - -.foo { - @extend %camelCase; -} -``` - -## Example 5 - -Settings: -- `convention: hyphenatedbem` - -When enabled, the following are allowed: - -```scss -%block-name { - content: ''; -} - -%block-name__mixin { - content: ''; -} - -%block-name--mod-name { - content: ''; -} -``` - -When enabled, the following are disallowed: - -```scss -%HYPHENATED-UPPERCASE { - content: ''; -} - -.foo { - @extend %camelCase; -} -``` - -## Example 6 - Settings: - `allow-leading-underscore: true` - `convention: ^[_A-Z]+$` diff --git a/docs/rules/variable-name-format.md b/docs/rules/variable-name-format.md index df819ef0..ac063242 100644 --- a/docs/rules/variable-name-format.md +++ b/docs/rules/variable-name-format.md @@ -5,8 +5,7 @@ Rule `variable-name-format` will enforce a convention for variable names. ## Options * `allow-leading-underscore`: `true`/`false` (defaults to `true`) -* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, [`strictbem`](https://en.bem.info/method/definitions/), -[`hyphenatedbem`](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/), or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) +* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) * `convention-explanation`: Custom explanation to display to the user if a variable doesn't adhere to the convention ## Example 1 @@ -94,60 +93,6 @@ $_snake_case_with_leading_underscore: 1px; ## Example 4 -Settings: -- `convention: strictbem` - -When enabled, the following are allowed: - -```scss -$block-name__variable: 1px; -$block-name__variable_mod-name: 1px; -$block-name_mod-name__variable: 1px; - -.foo { - width: $block-name__variable; -} -``` - -When enabled, the following are disallowed: - -```scss -$HYPHENATED-UPPERCASE: 1px; - -.foo { - width: $camelCase; -} -``` - -## Example 5 - -Settings: -- `convention: hyphenatedbem` - -When enabled, the following are allowed: - -```scss -$block-name__variable: 1px; -$block-name__variable--mod-name: 1px; -$block-name--mod-name__variable: 1px; - -.foo { - width: $block-name__variable; -} -``` - -When enabled, the following are disallowed: - -```scss -$HYPHENATED-UPPERCASE: 1px; - -.foo { - width: $camelCase; -} -``` - -## Example 6 - Settings: - `allow-leading-underscore: true` - `convention: ^[_A-Z]+$` diff --git a/lib/rules/function-name-format.js b/lib/rules/function-name-format.js index 2bdccb97..163993d9 100644 --- a/lib/rules/function-name-format.js +++ b/lib/rules/function-name-format.js @@ -50,16 +50,6 @@ module.exports = { violationMessage = 'Function \'' + name + '\' should be written in snake_case'; } break; - case 'strictbem': - if (!helpers.isStrictBEM(strippedName)) { - violationMessage = 'Function \'.' + name + '\' should be written in BEM (Block Element Modifier) format'; - } - break; - case 'hyphenatedbem': - if (!helpers.isHyphenatedBEM(strippedName)) { - violationMessage = 'Function \'.' + name + '\' should be written in hyphenated BEM (Block Element Modifier) format'; - } - break; default: if (!(new RegExp(parser.options.convention).test(strippedName))) { violationMessage = 'Function \'' + name + '\' should match regular expression /' + parser.options.convention + '/'; diff --git a/lib/rules/mixin-name-format.js b/lib/rules/mixin-name-format.js index 3f10ef50..d2940ebc 100644 --- a/lib/rules/mixin-name-format.js +++ b/lib/rules/mixin-name-format.js @@ -60,16 +60,6 @@ module.exports = { violationMessage = 'Mixin \'' + name + '\' should be written in snake_case'; } break; - case 'strictbem': - if (!helpers.isStrictBEM(strippedName)) { - violationMessage = 'Mixin \'.' + name + '\' should be written in BEM (Block Element Modifier) format'; - } - break; - case 'hyphenatedbem': - if (!helpers.isHyphenatedBEM(strippedName)) { - violationMessage = 'Mixin \'.' + name + '\' should be written in hyphenated BEM (Block Element Modifier) format'; - } - break; default: if (!(new RegExp(parser.options.convention).test(strippedName))) { violationMessage = 'Mixin \'' + name + '\' should match regular expression /' + parser.options.convention + '/'; diff --git a/lib/rules/placeholder-name-format.js b/lib/rules/placeholder-name-format.js index c2ef92e0..f6333054 100644 --- a/lib/rules/placeholder-name-format.js +++ b/lib/rules/placeholder-name-format.js @@ -40,16 +40,6 @@ module.exports = { violationMessage = 'Placeholder \'%' + name + '\' should be written in snake_case'; } break; - case 'strictbem': - if (!helpers.isStrictBEM(strippedName)) { - violationMessage = 'Placeholder \'.' + name + '\' should be written in BEM (Block Element Modifier) format'; - } - break; - case 'hyphenatedbem': - if (!helpers.isHyphenatedBEM(strippedName)) { - violationMessage = 'Placeholder \'.' + name + '\' should be written in hyphenated BEM (Block Element Modifier) format'; - } - break; default: if (!(new RegExp(parser.options.convention).test(strippedName))) { violationMessage = 'Placeholder \'%' + name + '\' should match regular expression /' + parser.options.convention + '/'; diff --git a/lib/rules/variable-name-format.js b/lib/rules/variable-name-format.js index 9ab4d6c0..f79a7418 100644 --- a/lib/rules/variable-name-format.js +++ b/lib/rules/variable-name-format.js @@ -40,16 +40,6 @@ module.exports = { violationMessage = 'Variable \'' + name + '\' should be written in snake_case'; } break; - case 'strictbem': - if (!helpers.isStrictBEM(strippedName)) { - violationMessage = 'Variable \'.' + name + '\' should be written in BEM (Block Element Modifier) format'; - } - break; - case 'hyphenatedbem': - if (!helpers.isHyphenatedBEM(strippedName)) { - violationMessage = 'Variable \'.' + name + '\' should be written in hyphenated BEM (Block Element Modifier) format'; - } - break; default: if (!(new RegExp(parser.options.convention).test(strippedName))) { violationMessage = 'Variable \'' + name + '\' should match regular expression /' + parser.options.convention + '/'; diff --git a/package.json b/package.json index 3e89b30b..334dd862 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "dependencies": { "commander": "^2.8.1", "eslint": "^2.7.0", - "fs-extra": "^0.26.0", + "fs-extra": "^0.27.0", "glob": "^7.0.0", "gonzales-pe-sl": "3.2.6", "js-yaml": "^3.5.4", diff --git a/tests/cli.js b/tests/cli.js index fc4b6943..29bee934 100644 --- a/tests/cli.js +++ b/tests/cli.js @@ -320,6 +320,18 @@ describe('cli', function () { }); }); + it('should exit with exit code 1 when more warnings than --max-warnings', function (done) { + var command = 'sass-lint -c tests/yml/.color-keyword-errors.yml tests/sass/cli.scss --max-warnings 0'; + + exec(command, function (err) { + if (err && err.code === 1) { + return done(); + } + + return done(new Error('Error code not 1')); + }); + }); + it('should not exit with an error if no config is specified', function (done) { var command = 'sass-lint tests/sass/cli-clean.scss --verbose --no-exit'; diff --git a/tests/rules/function-name-format.js b/tests/rules/function-name-format.js index e797f6a7..cf121035 100644 --- a/tests/rules/function-name-format.js +++ b/tests/rules/function-name-format.js @@ -9,7 +9,7 @@ describe('function name format - scss', function () { lint.test(file, { 'function-name-format': 1 }, function (data) { - lint.assert.equal(15, data.warningCount); + lint.assert.equal(9, data.warningCount); done(); }); }); @@ -23,7 +23,7 @@ describe('function name format - scss', function () { } ] }, function (data) { - lint.assert.equal(16, data.warningCount); + lint.assert.equal(10, data.warningCount); done(); }); }); @@ -37,35 +37,7 @@ describe('function name format - scss', function () { } ] }, function (data) { - lint.assert.equal(11, data.warningCount); - done(); - }); - }); - - it('[convention: strictbem]', function (done) { - lint.test(file, { - 'function-name-format': [ - 1, - { - 'convention': 'strictbem' - } - ] - }, function (data) { - lint.assert.equal(13, data.warningCount); - done(); - }); - }); - - it('[convention: hyphenatedbem]', function (done) { - lint.test(file, { - 'function-name-format': [ - 1, - { - 'convention': 'hyphenatedbem' - } - ] - }, function (data) { - lint.assert.equal(11, data.warningCount); + lint.assert.equal(9, data.warningCount); done(); }); }); @@ -80,7 +52,7 @@ describe('function name format - scss', function () { } ] }, function (data) { - lint.assert.equal(16, data.warningCount); + lint.assert.equal(9, data.warningCount); done(); }); }); @@ -94,7 +66,7 @@ describe('function name format - scss', function () { } ] }, function (data) { - lint.assert.equal(16, data.warningCount); + lint.assert.equal(10, data.warningCount); done(); }); }); @@ -107,7 +79,7 @@ describe('function name format - sass', function () { lint.test(file, { 'function-name-format': 1 }, function (data) { - lint.assert.equal(15, data.warningCount); + lint.assert.equal(9, data.warningCount); done(); }); }); @@ -121,7 +93,7 @@ describe('function name format - sass', function () { } ] }, function (data) { - lint.assert.equal(16, data.warningCount); + lint.assert.equal(10, data.warningCount); done(); }); }); @@ -135,35 +107,7 @@ describe('function name format - sass', function () { } ] }, function (data) { - lint.assert.equal(11, data.warningCount); - done(); - }); - }); - - it('[convention: strictbem]', function (done) { - lint.test(file, { - 'function-name-format': [ - 1, - { - 'convention': 'strictbem' - } - ] - }, function (data) { - lint.assert.equal(13, data.warningCount); - done(); - }); - }); - - it('[convention: hyphenatedbem]', function (done) { - lint.test(file, { - 'function-name-format': [ - 1, - { - 'convention': 'hyphenatedbem' - } - ] - }, function (data) { - lint.assert.equal(11, data.warningCount); + lint.assert.equal(9, data.warningCount); done(); }); }); @@ -178,7 +122,7 @@ describe('function name format - sass', function () { } ] }, function (data) { - lint.assert.equal(16, data.warningCount); + lint.assert.equal(9, data.warningCount); done(); }); }); @@ -192,7 +136,7 @@ describe('function name format - sass', function () { } ] }, function (data) { - lint.assert.equal(16, data.warningCount); + lint.assert.equal(10, data.warningCount); done(); }); }); diff --git a/tests/rules/mixin-name-format.js b/tests/rules/mixin-name-format.js index f56bb146..09b51874 100644 --- a/tests/rules/mixin-name-format.js +++ b/tests/rules/mixin-name-format.js @@ -9,7 +9,7 @@ describe('mixin name format - scss', function () { lint.test(file, { 'mixin-name-format': 1 }, function (data) { - lint.assert.equal(14, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -23,7 +23,7 @@ describe('mixin name format - scss', function () { } ] }, function (data) { - lint.assert.equal(16, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); @@ -37,35 +37,7 @@ describe('mixin name format - scss', function () { } ] }, function (data) { - lint.assert.equal(10, data.warningCount); - done(); - }); - }); - - it('[convention: strictbem]', function (done) { - lint.test(file, { - 'mixin-name-format': [ - 1, - { - 'convention': 'strictbem' - } - ] - }, function (data) { - lint.assert.equal(13, data.warningCount); - done(); - }); - }); - - it('[convention: hyphenatedbem]', function (done) { - lint.test(file, { - 'mixin-name-format': [ - 1, - { - 'convention': 'hyphenatedbem' - } - ] - }, function (data) { - lint.assert.equal(10, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -80,7 +52,7 @@ describe('mixin name format - scss', function () { } ] }, function (data) { - lint.assert.equal(17, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); @@ -94,7 +66,7 @@ describe('mixin name format - scss', function () { } ] }, function (data) { - lint.assert.equal(15, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); @@ -107,7 +79,7 @@ describe('mixin name format - sass', function () { lint.test(file, { 'mixin-name-format': 1 }, function (data) { - lint.assert.equal(14, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -121,7 +93,7 @@ describe('mixin name format - sass', function () { } ] }, function (data) { - lint.assert.equal(16, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); @@ -135,35 +107,7 @@ describe('mixin name format - sass', function () { } ] }, function (data) { - lint.assert.equal(10, data.warningCount); - done(); - }); - }); - - it('[convention: strictbem]', function (done) { - lint.test(file, { - 'mixin-name-format': [ - 1, - { - 'convention': 'strictbem' - } - ] - }, function (data) { - lint.assert.equal(13, data.warningCount); - done(); - }); - }); - - it('[convention: hyphenatedbem]', function (done) { - lint.test(file, { - 'mixin-name-format': [ - 1, - { - 'convention': 'hyphenatedbem' - } - ] - }, function (data) { - lint.assert.equal(10, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -178,7 +122,7 @@ describe('mixin name format - sass', function () { } ] }, function (data) { - lint.assert.equal(17, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); @@ -192,7 +136,7 @@ describe('mixin name format - sass', function () { } ] }, function (data) { - lint.assert.equal(15, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); diff --git a/tests/rules/placeholder-name-format.js b/tests/rules/placeholder-name-format.js index f0f20355..f102700e 100644 --- a/tests/rules/placeholder-name-format.js +++ b/tests/rules/placeholder-name-format.js @@ -9,7 +9,7 @@ describe('placeholder name format - scss', function () { lint.test(file, { 'placeholder-name-format': 1 }, function (data) { - lint.assert.equal(14, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -23,7 +23,7 @@ describe('placeholder name format - scss', function () { } ] }, function (data) { - lint.assert.equal(16, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); @@ -37,35 +37,7 @@ describe('placeholder name format - scss', function () { } ] }, function (data) { - lint.assert.equal(10, data.warningCount); - done(); - }); - }); - - it('[convention: strictbem]', function (done) { - lint.test(file, { - 'placeholder-name-format': [ - 1, - { - 'convention': 'strictbem' - } - ] - }, function (data) { - lint.assert.equal(13, data.warningCount); - done(); - }); - }); - - it('[convention: hyphenatedbem]', function (done) { - lint.test(file, { - 'placeholder-name-format': [ - 1, - { - 'convention': 'hyphenatedbem' - } - ] - }, function (data) { - lint.assert.equal(10, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -80,7 +52,7 @@ describe('placeholder name format - scss', function () { } ] }, function (data) { - lint.assert.equal(17, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); @@ -94,7 +66,7 @@ describe('placeholder name format - scss', function () { } ] }, function (data) { - lint.assert.equal(15, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); @@ -107,7 +79,7 @@ describe('placeholder name format - sass', function () { lint.test(file, { 'placeholder-name-format': 1 }, function (data) { - lint.assert.equal(14, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -121,7 +93,7 @@ describe('placeholder name format - sass', function () { } ] }, function (data) { - lint.assert.equal(16, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); @@ -135,35 +107,7 @@ describe('placeholder name format - sass', function () { } ] }, function (data) { - lint.assert.equal(10, data.warningCount); - done(); - }); - }); - - it('[convention: strictbem]', function (done) { - lint.test(file, { - 'placeholder-name-format': [ - 1, - { - 'convention': 'strictbem' - } - ] - }, function (data) { - lint.assert.equal(13, data.warningCount); - done(); - }); - }); - - it('[convention: hyphenatedbem]', function (done) { - lint.test(file, { - 'placeholder-name-format': [ - 1, - { - 'convention': 'hyphenatedbem' - } - ] - }, function (data) { - lint.assert.equal(10, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -178,7 +122,7 @@ describe('placeholder name format - sass', function () { } ] }, function (data) { - lint.assert.equal(17, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); @@ -192,7 +136,7 @@ describe('placeholder name format - sass', function () { } ] }, function (data) { - lint.assert.equal(15, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); diff --git a/tests/rules/variable-name-format.js b/tests/rules/variable-name-format.js index 67168905..c72dde11 100644 --- a/tests/rules/variable-name-format.js +++ b/tests/rules/variable-name-format.js @@ -9,7 +9,7 @@ describe('variable name format - scss', function () { lint.test(file, { 'variable-name-format': 1 }, function (data) { - lint.assert.equal(13, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -23,7 +23,7 @@ describe('variable name format - scss', function () { } ] }, function (data) { - lint.assert.equal(15, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); @@ -37,35 +37,7 @@ describe('variable name format - scss', function () { } ] }, function (data) { - lint.assert.equal(10, data.warningCount); - done(); - }); - }); - - it('[convention: strictbem]', function (done) { - lint.test(file, { - 'variable-name-format': [ - 1, - { - 'convention': 'strictbem' - } - ] - }, function (data) { - lint.assert.equal(12, data.warningCount); - done(); - }); - }); - - it('[convention: hyphenatedbem]', function (done) { - lint.test(file, { - 'variable-name-format': [ - 1, - { - 'convention': 'hyphenatedbem' - } - ] - }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -80,7 +52,7 @@ describe('variable name format - scss', function () { } ] }, function (data) { - lint.assert.equal(16, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); @@ -94,7 +66,7 @@ describe('variable name format - scss', function () { } ] }, function (data) { - lint.assert.equal(14, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); @@ -107,7 +79,7 @@ describe('variable name format - sass', function () { lint.test(file, { 'variable-name-format': 1 }, function (data) { - lint.assert.equal(13, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -121,7 +93,7 @@ describe('variable name format - sass', function () { } ] }, function (data) { - lint.assert.equal(15, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); @@ -135,35 +107,7 @@ describe('variable name format - sass', function () { } ] }, function (data) { - lint.assert.equal(10, data.warningCount); - done(); - }); - }); - - it('[convention: strictbem]', function (done) { - lint.test(file, { - 'variable-name-format': [ - 1, - { - 'convention': 'strictbem' - } - ] - }, function (data) { - lint.assert.equal(12, data.warningCount); - done(); - }); - }); - - it('[convention: hyphenatedbem]', function (done) { - lint.test(file, { - 'variable-name-format': [ - 1, - { - 'convention': 'hyphenatedbem' - } - ] - }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(7, data.warningCount); done(); }); }); @@ -178,7 +122,7 @@ describe('variable name format - sass', function () { } ] }, function (data) { - lint.assert.equal(16, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); @@ -192,7 +136,7 @@ describe('variable name format - sass', function () { } ] }, function (data) { - lint.assert.equal(14, data.warningCount); + lint.assert.equal(8, data.warningCount); done(); }); }); diff --git a/tests/sass/function-name-format.sass b/tests/sass/function-name-format.sass index 7f79e981..d5ceca1c 100644 --- a/tests/sass/function-name-format.sass +++ b/tests/sass/function-name-format.sass @@ -19,27 +19,6 @@ @function _with-leading-underscore() @return 'foo' -@function strictbem() - @return 'foo' - -@function strictbem__function() - @return 'foo' - -@function strictbem__function_modifier() - @return 'foo' - -@function strictbem_modifier__function() - @return 'foo' - -@function hyphenatedbem__function() - @return 'foo' - -@function hyphenatedbem__function--modifier() - @return 'foo' - -@function hyphenatedbem--modifier__function() - @return 'foo' - @function _does_NOT-fitSTANDARD($x) @return $x diff --git a/tests/sass/function-name-format.scss b/tests/sass/function-name-format.scss index 3148fab0..e0609943 100644 --- a/tests/sass/function-name-format.scss +++ b/tests/sass/function-name-format.scss @@ -26,34 +26,6 @@ @return 'foo'; } -@function strictbem() { - @return 'foo'; -} - -@function strictbem__function() { - @return 'foo'; -} - -@function strictbem__function_modifier() { - @return 'foo'; -} - -@function strictbem_modifier__function() { - @return 'foo'; -} - -@function hyphenatedbem__function() { - @return 'foo'; -} - -@function hyphenatedbem__function--modifier() { - @return 'foo'; -} - -@function hyphenatedbem--modifier__function() { - @return 'foo'; -} - @function _does_NOT-fitSTANDARD($x) { @return $x; } diff --git a/tests/sass/mixin-name-format.sass b/tests/sass/mixin-name-format.sass index b8f3c951..b6648f36 100644 --- a/tests/sass/mixin-name-format.sass +++ b/tests/sass/mixin-name-format.sass @@ -19,33 +19,6 @@ =_with-leading-underscore() content: '' -=strictbem() - content: '' - -=strictbem_modifier() - content: '' - -=strictbem__mixin() - content: '' - -=strictbem__mixin_modifier() - content: '' - -=strictbem_modifier__mixin() - content: '' - -=hyphenatedbem--modifier() - content: '' - -=hyphenatedbem__mixin() - content: '' - -=hyphenatedbem__mixin--modifier() - content: '' - -=hyphenatedbem--modifier__mixin() - content: '' - =_does_NOT-fitSTANDARD() content: '' diff --git a/tests/sass/mixin-name-format.scss b/tests/sass/mixin-name-format.scss index bd09d668..eb3bad1b 100644 --- a/tests/sass/mixin-name-format.scss +++ b/tests/sass/mixin-name-format.scss @@ -26,42 +26,6 @@ content: ''; } -@mixin strictbem() { - content: ''; -} - -@mixin strictbem_modifier() { - content: ''; -} - -@mixin strictbem__mixin() { - content: ''; -} - -@mixin strictbem__mixin_modifier() { - content: ''; -} - -@mixin strictbem_modifier__mixin() { - content: ''; -} - -@mixin hyphenatedbem--modifier() { - content: ''; -} - -@mixin hyphenatedbem__mixin() { - content: ''; -} - -@mixin hyphenatedbem__mixin--modifier() { - content: ''; -} - -@mixin hyphenatedbem--modifier__mixin() { - content: ''; -} - @mixin _does_NOT-fitSTANDARD() { content: ''; } diff --git a/tests/sass/placeholder-name-format.sass b/tests/sass/placeholder-name-format.sass index 3bef2f27..914e5a51 100644 --- a/tests/sass/placeholder-name-format.sass +++ b/tests/sass/placeholder-name-format.sass @@ -19,33 +19,6 @@ %_with-leading-underscore content: '' -%strictbem - content: '' - -%strictbem_modifier - content: '' - -%strictbem__placeholder - content: '' - -%strictbem__placeholder_modifier - content: '' - -%strictbem_modifier__placeholder - content: '' - -%hyphenatedbem--modifier - content: '' - -%hyphenatedbem__placeholder - content: '' - -%hyphenatedbem__placeholder--modifier - content: '' - -%hyphenatedbem--modifier__placeholder - content: '' - %_does_NOT-fitSTANDARD content: '' diff --git a/tests/sass/placeholder-name-format.scss b/tests/sass/placeholder-name-format.scss index ee7b7550..e6348a46 100644 --- a/tests/sass/placeholder-name-format.scss +++ b/tests/sass/placeholder-name-format.scss @@ -26,42 +26,6 @@ content: ''; } -%strictbem { - content: ''; -} - -%strictbem_placeholder { - content: ''; -} - -%strictbem__mixin { - content: ''; -} - -%strictbem__mixin_placeholder { - content: ''; -} - -%strictbem_placeholder__mixin { - content: ''; -} - -%hyphenatedbem--placeholder { - content: ''; -} - -%hyphenatedbem__mixin { - content: ''; -} - -%hyphenatedbem__mixin--placeholder { - content: ''; -} - -%hyphenatedbem--placeholder__mixin { - content: ''; -} - %_does_NOT-fitSTANDARD { content: ''; } diff --git a/tests/sass/variable-name-format.sass b/tests/sass/variable-name-format.sass index 3c3ce96f..16e33225 100644 --- a/tests/sass/variable-name-format.sass +++ b/tests/sass/variable-name-format.sass @@ -5,16 +5,6 @@ $PascalCase: 1 $Camel_Snake_Case: 1 $SCREAMING_SNAKE_CASE: 1 $_with-leading-underscore: 1 - -$strictbem: 1 -$strictbem__variable: 1 -$strictbem__variable_modifier: 1 -$strictbem_modifier__variable: 1 -$hyphenatedbem--modifier: 1 -$hyphenatedbem__variable: 1 -$hyphenatedbem__variable--modifier: 1 -$hyphenatedbem--modifier__variable: 1 - $_does_NOT-fitSTANDARD: 1 .class diff --git a/tests/sass/variable-name-format.scss b/tests/sass/variable-name-format.scss index fd8df367..3a905623 100644 --- a/tests/sass/variable-name-format.scss +++ b/tests/sass/variable-name-format.scss @@ -5,16 +5,6 @@ $PascalCase: 1; $Camel_Snake_Case: 1; $SCREAMING_SNAKE_CASE: 1; $_with-leading-underscore: 1; - -$strictbem: 1; -$strictbem__variable: 1; -$strictbem__variable_modifier: 1; -$strictbem_modifier__variable: 1; -$hyphenatedbem--modifier: 1; -$hyphenatedbem__variable: 1; -$hyphenatedbem__variable--modifier: 1; -$hyphenatedbem--modifier__variable: 1; - $_does_NOT-fitSTANDARD: 1; .class { From ecccaa472342384097139ebe73a530c97ee365db Mon Sep 17 00:00:00 2001 From: Nicolas Coden Date: Sun, 17 Apr 2016 19:01:18 +0200 Subject: [PATCH 114/137] :mag: Add BEM conventions for all naming rules **Fix**: sasstools#598 **Changes**: Add `strictbem ` and `hyphenatedbem` conventions for all naming rules: - `function-name-format` - `mixin-name-format` - `placeholder-name-format` - `function-name-format` Add tests and examples in documentation for these naming conventions. --- docs/rules/function-name-format.md | 69 +++++++++++++++++++++- docs/rules/mixin-name-format.md | 70 ++++++++++++++++++++++- docs/rules/placeholder-name-format.md | 69 +++++++++++++++++++++- docs/rules/variable-name-format.md | 57 ++++++++++++++++++- lib/rules/function-name-format.js | 10 ++++ lib/rules/mixin-name-format.js | 10 ++++ lib/rules/placeholder-name-format.js | 10 ++++ lib/rules/variable-name-format.js | 10 ++++ tests/rules/function-name-format.js | 76 +++++++++++++++++++++---- tests/rules/mixin-name-format.js | 76 +++++++++++++++++++++---- tests/rules/placeholder-name-format.js | 76 +++++++++++++++++++++---- tests/rules/variable-name-format.js | 76 +++++++++++++++++++++---- tests/sass/function-name-format.sass | 21 +++++++ tests/sass/function-name-format.scss | 28 +++++++++ tests/sass/mixin-name-format.sass | 27 +++++++++ tests/sass/mixin-name-format.scss | 36 ++++++++++++ tests/sass/placeholder-name-format.sass | 27 +++++++++ tests/sass/placeholder-name-format.scss | 36 ++++++++++++ tests/sass/variable-name-format.sass | 10 ++++ tests/sass/variable-name-format.scss | 10 ++++ 20 files changed, 760 insertions(+), 44 deletions(-) diff --git a/docs/rules/function-name-format.md b/docs/rules/function-name-format.md index 2f702633..054dfaa3 100644 --- a/docs/rules/function-name-format.md +++ b/docs/rules/function-name-format.md @@ -5,7 +5,8 @@ Rule `function-name-format` will enforce a convention for function names. ## Options * `allow-leading-underscore`: `true`/`false` (defaults to `true`) -* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, or a Regular Expression that the function name must match (e.g. `^[_A-Z]+$`) +* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, [`strictbem`](https://en.bem.info/method/definitions/), +[`hyphenatedbem`](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/), or a Regular Expression that the function name must match (e.g. `^[_A-Z]+$`) * `convention-explanation`: Custom explanation to display to the user if a function doesn't adhere to the convention ## Example 1 @@ -116,6 +117,72 @@ When enabled, the following are disallowed: ## Example 4 +Settings: +- `convention: strictbem` + +When enabled, the following are allowed: + +```scss +@function namespace__function { + @return "foo"; +} + +@function namespace__function_mod-name { + @return "foo"; +} + +@function namespace_mod-name__function { + @return "foo"; +} +``` + +When enabled, the following are disallowed: + +```scss +@function HYPHENATED-UPPERCASE { + @return "foo"; +} + +.foo { + content: camelCase(); +} +``` + +## Example 5 + +Settings: +- `convention: hyphenatedbem` + +When enabled, the following are allowed: + +```scss +@function namespace__function { + @return "foo"; +} + +@function namespace__function--mod-name { + @return "foo"; +} + +@function namespace--mod-name__function { + @return "foo"; +} +``` + +When enabled, the following are disallowed: + +```scss +@function HYPHENATED-UPPERCASE { + @return "foo"; +} + +.foo { + content: camelCase(); +} +``` + +## Example 6 + Settings: - `allow-leading-underscore: true` - `convention: '^[_A-Z]+$'` diff --git a/docs/rules/mixin-name-format.md b/docs/rules/mixin-name-format.md index 537e11f9..2bbbbe3f 100644 --- a/docs/rules/mixin-name-format.md +++ b/docs/rules/mixin-name-format.md @@ -5,7 +5,8 @@ Rule `mixin-name-format` will enforce a convention for mixin names. ## Options * `allow-leading-underscore`: `true`/`false` (defaults to `true`) -* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) +* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, [`strictbem`](https://en.bem.info/method/definitions/), +[`hyphenatedbem`](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/), or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) * `convention-explanation`: Custom explanation to display to the user if a mixin doesn't adhere to the convention ## Example 1 @@ -117,6 +118,73 @@ When enabled, the following are disallowed: ## Example 4 +Settings: +- `convention: strictbem` + +When enabled, the following are allowed: + +```scss +@mixin block-name { + content: ''; +} + +@mixin block-name__mixin { + content: ''; +} + +@mixin block-name_mod-name { + content: ''; +} +``` + +When enabled, the following are disallowed: + +```scss +@mixin HYPHENATED-UPPERCASE { + content: ''; +} + +.foo { + @include camelCase(); +} +``` + +## Example 5 + +Settings: +- `convention: hyphenatedbem` + +When enabled, the following are allowed: + +```scss +@mixin block-name { + content: ''; +} + +@mixin block-name__mixin { + content: ''; +} + +@mixin block-name--mod-name { + content: ''; +} +``` + +When enabled, the following are disallowed: + +```scss +@mixin HYPHENATED-UPPERCASE { + content: ''; +} + +.foo { + @include camelCase(); +} +``` + + +## Example 6 + Settings: - `allow-leading-underscore: true` - `convention: ^[_A-Z]+$` diff --git a/docs/rules/placeholder-name-format.md b/docs/rules/placeholder-name-format.md index 6da7868d..9cb3a682 100644 --- a/docs/rules/placeholder-name-format.md +++ b/docs/rules/placeholder-name-format.md @@ -5,7 +5,8 @@ Rule `placeholder-name-format` will enforce a convention for placeholder names. ## Options * `allow-leading-underscore`: `true`/`false` (defaults to `true`) -* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) +* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, [`strictbem`](https://en.bem.info/method/definitions/), +[`hyphenatedbem`](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/), or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) * `convention-explanation`: Custom explanation to display to the user if a placeholder doesn't adhere to the convention ## Example 1 @@ -117,6 +118,72 @@ When enabled, the following are disallowed: ## Example 4 +Settings: +- `convention: strictbem` + +When enabled, the following are allowed: + +```scss +%block-name { + content: ''; +} + +%block-name__mixin { + content: ''; +} + +%block-name_mod-name { + content: ''; +} +``` + +When enabled, the following are disallowed: + +```scss +%HYPHENATED-UPPERCASE { + content: ''; +} + +.foo { + @extend %camelCase; +} +``` + +## Example 5 + +Settings: +- `convention: hyphenatedbem` + +When enabled, the following are allowed: + +```scss +%block-name { + content: ''; +} + +%block-name__mixin { + content: ''; +} + +%block-name--mod-name { + content: ''; +} +``` + +When enabled, the following are disallowed: + +```scss +%HYPHENATED-UPPERCASE { + content: ''; +} + +.foo { + @extend %camelCase; +} +``` + +## Example 6 + Settings: - `allow-leading-underscore: true` - `convention: ^[_A-Z]+$` diff --git a/docs/rules/variable-name-format.md b/docs/rules/variable-name-format.md index ac063242..df819ef0 100644 --- a/docs/rules/variable-name-format.md +++ b/docs/rules/variable-name-format.md @@ -5,7 +5,8 @@ Rule `variable-name-format` will enforce a convention for variable names. ## Options * `allow-leading-underscore`: `true`/`false` (defaults to `true`) -* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) +* `convention`: `'hyphenatedlowercase'` (default), `camelcase`, `snakecase`, [`strictbem`](https://en.bem.info/method/definitions/), +[`hyphenatedbem`](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/), or a Regular Expression that the variable name must match (e.g. `^[_A-Z]+$`) * `convention-explanation`: Custom explanation to display to the user if a variable doesn't adhere to the convention ## Example 1 @@ -93,6 +94,60 @@ $_snake_case_with_leading_underscore: 1px; ## Example 4 +Settings: +- `convention: strictbem` + +When enabled, the following are allowed: + +```scss +$block-name__variable: 1px; +$block-name__variable_mod-name: 1px; +$block-name_mod-name__variable: 1px; + +.foo { + width: $block-name__variable; +} +``` + +When enabled, the following are disallowed: + +```scss +$HYPHENATED-UPPERCASE: 1px; + +.foo { + width: $camelCase; +} +``` + +## Example 5 + +Settings: +- `convention: hyphenatedbem` + +When enabled, the following are allowed: + +```scss +$block-name__variable: 1px; +$block-name__variable--mod-name: 1px; +$block-name--mod-name__variable: 1px; + +.foo { + width: $block-name__variable; +} +``` + +When enabled, the following are disallowed: + +```scss +$HYPHENATED-UPPERCASE: 1px; + +.foo { + width: $camelCase; +} +``` + +## Example 6 + Settings: - `allow-leading-underscore: true` - `convention: ^[_A-Z]+$` diff --git a/lib/rules/function-name-format.js b/lib/rules/function-name-format.js index 163993d9..2bdccb97 100644 --- a/lib/rules/function-name-format.js +++ b/lib/rules/function-name-format.js @@ -50,6 +50,16 @@ module.exports = { violationMessage = 'Function \'' + name + '\' should be written in snake_case'; } break; + case 'strictbem': + if (!helpers.isStrictBEM(strippedName)) { + violationMessage = 'Function \'.' + name + '\' should be written in BEM (Block Element Modifier) format'; + } + break; + case 'hyphenatedbem': + if (!helpers.isHyphenatedBEM(strippedName)) { + violationMessage = 'Function \'.' + name + '\' should be written in hyphenated BEM (Block Element Modifier) format'; + } + break; default: if (!(new RegExp(parser.options.convention).test(strippedName))) { violationMessage = 'Function \'' + name + '\' should match regular expression /' + parser.options.convention + '/'; diff --git a/lib/rules/mixin-name-format.js b/lib/rules/mixin-name-format.js index d2940ebc..3f10ef50 100644 --- a/lib/rules/mixin-name-format.js +++ b/lib/rules/mixin-name-format.js @@ -60,6 +60,16 @@ module.exports = { violationMessage = 'Mixin \'' + name + '\' should be written in snake_case'; } break; + case 'strictbem': + if (!helpers.isStrictBEM(strippedName)) { + violationMessage = 'Mixin \'.' + name + '\' should be written in BEM (Block Element Modifier) format'; + } + break; + case 'hyphenatedbem': + if (!helpers.isHyphenatedBEM(strippedName)) { + violationMessage = 'Mixin \'.' + name + '\' should be written in hyphenated BEM (Block Element Modifier) format'; + } + break; default: if (!(new RegExp(parser.options.convention).test(strippedName))) { violationMessage = 'Mixin \'' + name + '\' should match regular expression /' + parser.options.convention + '/'; diff --git a/lib/rules/placeholder-name-format.js b/lib/rules/placeholder-name-format.js index f6333054..c2ef92e0 100644 --- a/lib/rules/placeholder-name-format.js +++ b/lib/rules/placeholder-name-format.js @@ -40,6 +40,16 @@ module.exports = { violationMessage = 'Placeholder \'%' + name + '\' should be written in snake_case'; } break; + case 'strictbem': + if (!helpers.isStrictBEM(strippedName)) { + violationMessage = 'Placeholder \'.' + name + '\' should be written in BEM (Block Element Modifier) format'; + } + break; + case 'hyphenatedbem': + if (!helpers.isHyphenatedBEM(strippedName)) { + violationMessage = 'Placeholder \'.' + name + '\' should be written in hyphenated BEM (Block Element Modifier) format'; + } + break; default: if (!(new RegExp(parser.options.convention).test(strippedName))) { violationMessage = 'Placeholder \'%' + name + '\' should match regular expression /' + parser.options.convention + '/'; diff --git a/lib/rules/variable-name-format.js b/lib/rules/variable-name-format.js index f79a7418..9ab4d6c0 100644 --- a/lib/rules/variable-name-format.js +++ b/lib/rules/variable-name-format.js @@ -40,6 +40,16 @@ module.exports = { violationMessage = 'Variable \'' + name + '\' should be written in snake_case'; } break; + case 'strictbem': + if (!helpers.isStrictBEM(strippedName)) { + violationMessage = 'Variable \'.' + name + '\' should be written in BEM (Block Element Modifier) format'; + } + break; + case 'hyphenatedbem': + if (!helpers.isHyphenatedBEM(strippedName)) { + violationMessage = 'Variable \'.' + name + '\' should be written in hyphenated BEM (Block Element Modifier) format'; + } + break; default: if (!(new RegExp(parser.options.convention).test(strippedName))) { violationMessage = 'Variable \'' + name + '\' should match regular expression /' + parser.options.convention + '/'; diff --git a/tests/rules/function-name-format.js b/tests/rules/function-name-format.js index cf121035..e797f6a7 100644 --- a/tests/rules/function-name-format.js +++ b/tests/rules/function-name-format.js @@ -9,7 +9,7 @@ describe('function name format - scss', function () { lint.test(file, { 'function-name-format': 1 }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); @@ -23,7 +23,7 @@ describe('function name format - scss', function () { } ] }, function (data) { - lint.assert.equal(10, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -37,7 +37,35 @@ describe('function name format - scss', function () { } ] }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(11, data.warningCount); + done(); + }); + }); + + it('[convention: strictbem]', function (done) { + lint.test(file, { + 'function-name-format': [ + 1, + { + 'convention': 'strictbem' + } + ] + }, function (data) { + lint.assert.equal(13, data.warningCount); + done(); + }); + }); + + it('[convention: hyphenatedbem]', function (done) { + lint.test(file, { + 'function-name-format': [ + 1, + { + 'convention': 'hyphenatedbem' + } + ] + }, function (data) { + lint.assert.equal(11, data.warningCount); done(); }); }); @@ -52,7 +80,7 @@ describe('function name format - scss', function () { } ] }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -66,7 +94,7 @@ describe('function name format - scss', function () { } ] }, function (data) { - lint.assert.equal(10, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -79,7 +107,7 @@ describe('function name format - sass', function () { lint.test(file, { 'function-name-format': 1 }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); @@ -93,7 +121,7 @@ describe('function name format - sass', function () { } ] }, function (data) { - lint.assert.equal(10, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -107,7 +135,35 @@ describe('function name format - sass', function () { } ] }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(11, data.warningCount); + done(); + }); + }); + + it('[convention: strictbem]', function (done) { + lint.test(file, { + 'function-name-format': [ + 1, + { + 'convention': 'strictbem' + } + ] + }, function (data) { + lint.assert.equal(13, data.warningCount); + done(); + }); + }); + + it('[convention: hyphenatedbem]', function (done) { + lint.test(file, { + 'function-name-format': [ + 1, + { + 'convention': 'hyphenatedbem' + } + ] + }, function (data) { + lint.assert.equal(11, data.warningCount); done(); }); }); @@ -122,7 +178,7 @@ describe('function name format - sass', function () { } ] }, function (data) { - lint.assert.equal(9, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -136,7 +192,7 @@ describe('function name format - sass', function () { } ] }, function (data) { - lint.assert.equal(10, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); diff --git a/tests/rules/mixin-name-format.js b/tests/rules/mixin-name-format.js index 09b51874..f56bb146 100644 --- a/tests/rules/mixin-name-format.js +++ b/tests/rules/mixin-name-format.js @@ -9,7 +9,7 @@ describe('mixin name format - scss', function () { lint.test(file, { 'mixin-name-format': 1 }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(14, data.warningCount); done(); }); }); @@ -23,7 +23,7 @@ describe('mixin name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -37,7 +37,35 @@ describe('mixin name format - scss', function () { } ] }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(10, data.warningCount); + done(); + }); + }); + + it('[convention: strictbem]', function (done) { + lint.test(file, { + 'mixin-name-format': [ + 1, + { + 'convention': 'strictbem' + } + ] + }, function (data) { + lint.assert.equal(13, data.warningCount); + done(); + }); + }); + + it('[convention: hyphenatedbem]', function (done) { + lint.test(file, { + 'mixin-name-format': [ + 1, + { + 'convention': 'hyphenatedbem' + } + ] + }, function (data) { + lint.assert.equal(10, data.warningCount); done(); }); }); @@ -52,7 +80,7 @@ describe('mixin name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(17, data.warningCount); done(); }); }); @@ -66,7 +94,7 @@ describe('mixin name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); @@ -79,7 +107,7 @@ describe('mixin name format - sass', function () { lint.test(file, { 'mixin-name-format': 1 }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(14, data.warningCount); done(); }); }); @@ -93,7 +121,7 @@ describe('mixin name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -107,7 +135,35 @@ describe('mixin name format - sass', function () { } ] }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(10, data.warningCount); + done(); + }); + }); + + it('[convention: strictbem]', function (done) { + lint.test(file, { + 'mixin-name-format': [ + 1, + { + 'convention': 'strictbem' + } + ] + }, function (data) { + lint.assert.equal(13, data.warningCount); + done(); + }); + }); + + it('[convention: hyphenatedbem]', function (done) { + lint.test(file, { + 'mixin-name-format': [ + 1, + { + 'convention': 'hyphenatedbem' + } + ] + }, function (data) { + lint.assert.equal(10, data.warningCount); done(); }); }); @@ -122,7 +178,7 @@ describe('mixin name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(17, data.warningCount); done(); }); }); @@ -136,7 +192,7 @@ describe('mixin name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); diff --git a/tests/rules/placeholder-name-format.js b/tests/rules/placeholder-name-format.js index f102700e..f0f20355 100644 --- a/tests/rules/placeholder-name-format.js +++ b/tests/rules/placeholder-name-format.js @@ -9,7 +9,7 @@ describe('placeholder name format - scss', function () { lint.test(file, { 'placeholder-name-format': 1 }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(14, data.warningCount); done(); }); }); @@ -23,7 +23,7 @@ describe('placeholder name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -37,7 +37,35 @@ describe('placeholder name format - scss', function () { } ] }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(10, data.warningCount); + done(); + }); + }); + + it('[convention: strictbem]', function (done) { + lint.test(file, { + 'placeholder-name-format': [ + 1, + { + 'convention': 'strictbem' + } + ] + }, function (data) { + lint.assert.equal(13, data.warningCount); + done(); + }); + }); + + it('[convention: hyphenatedbem]', function (done) { + lint.test(file, { + 'placeholder-name-format': [ + 1, + { + 'convention': 'hyphenatedbem' + } + ] + }, function (data) { + lint.assert.equal(10, data.warningCount); done(); }); }); @@ -52,7 +80,7 @@ describe('placeholder name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(17, data.warningCount); done(); }); }); @@ -66,7 +94,7 @@ describe('placeholder name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); @@ -79,7 +107,7 @@ describe('placeholder name format - sass', function () { lint.test(file, { 'placeholder-name-format': 1 }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(14, data.warningCount); done(); }); }); @@ -93,7 +121,7 @@ describe('placeholder name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -107,7 +135,35 @@ describe('placeholder name format - sass', function () { } ] }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(10, data.warningCount); + done(); + }); + }); + + it('[convention: strictbem]', function (done) { + lint.test(file, { + 'placeholder-name-format': [ + 1, + { + 'convention': 'strictbem' + } + ] + }, function (data) { + lint.assert.equal(13, data.warningCount); + done(); + }); + }); + + it('[convention: hyphenatedbem]', function (done) { + lint.test(file, { + 'placeholder-name-format': [ + 1, + { + 'convention': 'hyphenatedbem' + } + ] + }, function (data) { + lint.assert.equal(10, data.warningCount); done(); }); }); @@ -122,7 +178,7 @@ describe('placeholder name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(17, data.warningCount); done(); }); }); @@ -136,7 +192,7 @@ describe('placeholder name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); diff --git a/tests/rules/variable-name-format.js b/tests/rules/variable-name-format.js index c72dde11..67168905 100644 --- a/tests/rules/variable-name-format.js +++ b/tests/rules/variable-name-format.js @@ -9,7 +9,7 @@ describe('variable name format - scss', function () { lint.test(file, { 'variable-name-format': 1 }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(13, data.warningCount); done(); }); }); @@ -23,7 +23,7 @@ describe('variable name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); @@ -37,7 +37,35 @@ describe('variable name format - scss', function () { } ] }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(10, data.warningCount); + done(); + }); + }); + + it('[convention: strictbem]', function (done) { + lint.test(file, { + 'variable-name-format': [ + 1, + { + 'convention': 'strictbem' + } + ] + }, function (data) { + lint.assert.equal(12, data.warningCount); + done(); + }); + }); + + it('[convention: hyphenatedbem]', function (done) { + lint.test(file, { + 'variable-name-format': [ + 1, + { + 'convention': 'hyphenatedbem' + } + ] + }, function (data) { + lint.assert.equal(9, data.warningCount); done(); }); }); @@ -52,7 +80,7 @@ describe('variable name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -66,7 +94,7 @@ describe('variable name format - scss', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(14, data.warningCount); done(); }); }); @@ -79,7 +107,7 @@ describe('variable name format - sass', function () { lint.test(file, { 'variable-name-format': 1 }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(13, data.warningCount); done(); }); }); @@ -93,7 +121,7 @@ describe('variable name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(15, data.warningCount); done(); }); }); @@ -107,7 +135,35 @@ describe('variable name format - sass', function () { } ] }, function (data) { - lint.assert.equal(7, data.warningCount); + lint.assert.equal(10, data.warningCount); + done(); + }); + }); + + it('[convention: strictbem]', function (done) { + lint.test(file, { + 'variable-name-format': [ + 1, + { + 'convention': 'strictbem' + } + ] + }, function (data) { + lint.assert.equal(12, data.warningCount); + done(); + }); + }); + + it('[convention: hyphenatedbem]', function (done) { + lint.test(file, { + 'variable-name-format': [ + 1, + { + 'convention': 'hyphenatedbem' + } + ] + }, function (data) { + lint.assert.equal(9, data.warningCount); done(); }); }); @@ -122,7 +178,7 @@ describe('variable name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(16, data.warningCount); done(); }); }); @@ -136,7 +192,7 @@ describe('variable name format - sass', function () { } ] }, function (data) { - lint.assert.equal(8, data.warningCount); + lint.assert.equal(14, data.warningCount); done(); }); }); diff --git a/tests/sass/function-name-format.sass b/tests/sass/function-name-format.sass index d5ceca1c..7f79e981 100644 --- a/tests/sass/function-name-format.sass +++ b/tests/sass/function-name-format.sass @@ -19,6 +19,27 @@ @function _with-leading-underscore() @return 'foo' +@function strictbem() + @return 'foo' + +@function strictbem__function() + @return 'foo' + +@function strictbem__function_modifier() + @return 'foo' + +@function strictbem_modifier__function() + @return 'foo' + +@function hyphenatedbem__function() + @return 'foo' + +@function hyphenatedbem__function--modifier() + @return 'foo' + +@function hyphenatedbem--modifier__function() + @return 'foo' + @function _does_NOT-fitSTANDARD($x) @return $x diff --git a/tests/sass/function-name-format.scss b/tests/sass/function-name-format.scss index e0609943..3148fab0 100644 --- a/tests/sass/function-name-format.scss +++ b/tests/sass/function-name-format.scss @@ -26,6 +26,34 @@ @return 'foo'; } +@function strictbem() { + @return 'foo'; +} + +@function strictbem__function() { + @return 'foo'; +} + +@function strictbem__function_modifier() { + @return 'foo'; +} + +@function strictbem_modifier__function() { + @return 'foo'; +} + +@function hyphenatedbem__function() { + @return 'foo'; +} + +@function hyphenatedbem__function--modifier() { + @return 'foo'; +} + +@function hyphenatedbem--modifier__function() { + @return 'foo'; +} + @function _does_NOT-fitSTANDARD($x) { @return $x; } diff --git a/tests/sass/mixin-name-format.sass b/tests/sass/mixin-name-format.sass index b6648f36..b8f3c951 100644 --- a/tests/sass/mixin-name-format.sass +++ b/tests/sass/mixin-name-format.sass @@ -19,6 +19,33 @@ =_with-leading-underscore() content: '' +=strictbem() + content: '' + +=strictbem_modifier() + content: '' + +=strictbem__mixin() + content: '' + +=strictbem__mixin_modifier() + content: '' + +=strictbem_modifier__mixin() + content: '' + +=hyphenatedbem--modifier() + content: '' + +=hyphenatedbem__mixin() + content: '' + +=hyphenatedbem__mixin--modifier() + content: '' + +=hyphenatedbem--modifier__mixin() + content: '' + =_does_NOT-fitSTANDARD() content: '' diff --git a/tests/sass/mixin-name-format.scss b/tests/sass/mixin-name-format.scss index eb3bad1b..bd09d668 100644 --- a/tests/sass/mixin-name-format.scss +++ b/tests/sass/mixin-name-format.scss @@ -26,6 +26,42 @@ content: ''; } +@mixin strictbem() { + content: ''; +} + +@mixin strictbem_modifier() { + content: ''; +} + +@mixin strictbem__mixin() { + content: ''; +} + +@mixin strictbem__mixin_modifier() { + content: ''; +} + +@mixin strictbem_modifier__mixin() { + content: ''; +} + +@mixin hyphenatedbem--modifier() { + content: ''; +} + +@mixin hyphenatedbem__mixin() { + content: ''; +} + +@mixin hyphenatedbem__mixin--modifier() { + content: ''; +} + +@mixin hyphenatedbem--modifier__mixin() { + content: ''; +} + @mixin _does_NOT-fitSTANDARD() { content: ''; } diff --git a/tests/sass/placeholder-name-format.sass b/tests/sass/placeholder-name-format.sass index 914e5a51..3bef2f27 100644 --- a/tests/sass/placeholder-name-format.sass +++ b/tests/sass/placeholder-name-format.sass @@ -19,6 +19,33 @@ %_with-leading-underscore content: '' +%strictbem + content: '' + +%strictbem_modifier + content: '' + +%strictbem__placeholder + content: '' + +%strictbem__placeholder_modifier + content: '' + +%strictbem_modifier__placeholder + content: '' + +%hyphenatedbem--modifier + content: '' + +%hyphenatedbem__placeholder + content: '' + +%hyphenatedbem__placeholder--modifier + content: '' + +%hyphenatedbem--modifier__placeholder + content: '' + %_does_NOT-fitSTANDARD content: '' diff --git a/tests/sass/placeholder-name-format.scss b/tests/sass/placeholder-name-format.scss index e6348a46..ee7b7550 100644 --- a/tests/sass/placeholder-name-format.scss +++ b/tests/sass/placeholder-name-format.scss @@ -26,6 +26,42 @@ content: ''; } +%strictbem { + content: ''; +} + +%strictbem_placeholder { + content: ''; +} + +%strictbem__mixin { + content: ''; +} + +%strictbem__mixin_placeholder { + content: ''; +} + +%strictbem_placeholder__mixin { + content: ''; +} + +%hyphenatedbem--placeholder { + content: ''; +} + +%hyphenatedbem__mixin { + content: ''; +} + +%hyphenatedbem__mixin--placeholder { + content: ''; +} + +%hyphenatedbem--placeholder__mixin { + content: ''; +} + %_does_NOT-fitSTANDARD { content: ''; } diff --git a/tests/sass/variable-name-format.sass b/tests/sass/variable-name-format.sass index 16e33225..3c3ce96f 100644 --- a/tests/sass/variable-name-format.sass +++ b/tests/sass/variable-name-format.sass @@ -5,6 +5,16 @@ $PascalCase: 1 $Camel_Snake_Case: 1 $SCREAMING_SNAKE_CASE: 1 $_with-leading-underscore: 1 + +$strictbem: 1 +$strictbem__variable: 1 +$strictbem__variable_modifier: 1 +$strictbem_modifier__variable: 1 +$hyphenatedbem--modifier: 1 +$hyphenatedbem__variable: 1 +$hyphenatedbem__variable--modifier: 1 +$hyphenatedbem--modifier__variable: 1 + $_does_NOT-fitSTANDARD: 1 .class diff --git a/tests/sass/variable-name-format.scss b/tests/sass/variable-name-format.scss index 3a905623..fd8df367 100644 --- a/tests/sass/variable-name-format.scss +++ b/tests/sass/variable-name-format.scss @@ -5,6 +5,16 @@ $PascalCase: 1; $Camel_Snake_Case: 1; $SCREAMING_SNAKE_CASE: 1; $_with-leading-underscore: 1; + +$strictbem: 1; +$strictbem__variable: 1; +$strictbem__variable_modifier: 1; +$strictbem_modifier__variable: 1; +$hyphenatedbem--modifier: 1; +$hyphenatedbem__variable: 1; +$hyphenatedbem__variable--modifier: 1; +$hyphenatedbem--modifier__variable: 1; + $_does_NOT-fitSTANDARD: 1; .class { From 49ac1096cc144700175e98e511b6a59aae38fdfd Mon Sep 17 00:00:00 2001 From: greenkeeperio-bot Date: Mon, 18 Apr 2016 05:38:27 -0400 Subject: [PATCH 115/137] chore(package): update fs-extra to version 0.28.0 http://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 334dd862..61bda2e8 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "dependencies": { "commander": "^2.8.1", "eslint": "^2.7.0", - "fs-extra": "^0.27.0", + "fs-extra": "^0.28.0", "glob": "^7.0.0", "gonzales-pe-sl": "3.2.6", "js-yaml": "^3.5.4", From b35ff4ee6d2be5f78a379d9ca354089d5e024224 Mon Sep 17 00:00:00 2001 From: Todd Christensen Date: Fri, 15 Apr 2016 08:05:53 -0700 Subject: [PATCH 116/137] Correct indentation checks when using CRLFs. Otherwise we think there's an extra indent and mixed tabs. --- lib/rules/indentation.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/rules/indentation.js b/lib/rules/indentation.js index 0d7d4fea..f1991990 100644 --- a/lib/rules/indentation.js +++ b/lib/rules/indentation.js @@ -16,7 +16,8 @@ module.exports = { prevNode, space, spaceLength, - count; + count, + newlineLength; level = level || 0; @@ -35,16 +36,18 @@ module.exports = { if (n.type === 'space') { // Test for CRLF first, since it includes LF space = n.content.lastIndexOf('\r\n'); + newlineLength = 2; if (space === -1) { space = n.content.lastIndexOf('\n'); + newlineLength = 1; } if (space >= 0) { - spaceLength = n.content.slice(space + 1).length; + spaceLength = n.content.slice(space + newlineLength).length; try { - count = n.content.slice(space + 1).match(/ /g).length; + count = n.content.slice(space + newlineLength).match(/ /g).length; } catch (e) { count = 0; From d84d16729b1b5f3b331304721704cd24deade3df Mon Sep 17 00:00:00 2001 From: Alex Gyoshev Date: Mon, 11 Apr 2016 14:51:45 +0300 Subject: [PATCH 117/137] :mag: Add no-trailing-whitespace rule For SCSS, the rule only needs to check 'space' nodes for trailing whitespace (\s\n). For SASS, it needs to check both space and delimeter nodes, so it accumulates their content internally. --- lib/rules/no-trailing-whitespace.js | 37 ++++++++++++++++++++++++++ tests/rules/no-trailing-whitespace.js | 35 ++++++++++++++++++++++++ tests/sass/no-trailing-whitespace.sass | 19 +++++++++++++ tests/sass/no-trailing-whitespace.scss | 24 +++++++++++++++++ 4 files changed, 115 insertions(+) create mode 100644 lib/rules/no-trailing-whitespace.js create mode 100644 tests/rules/no-trailing-whitespace.js create mode 100644 tests/sass/no-trailing-whitespace.sass create mode 100644 tests/sass/no-trailing-whitespace.scss diff --git a/lib/rules/no-trailing-whitespace.js b/lib/rules/no-trailing-whitespace.js new file mode 100644 index 00000000..95d3369e --- /dev/null +++ b/lib/rules/no-trailing-whitespace.js @@ -0,0 +1,37 @@ +'use strict'; + +var helpers = require('../helpers'); + +module.exports = { + 'name': 'no-trailing-whitespace', + 'defaults': {}, + 'detect': function (ast, parser) { + var result = []; + var trailing = (/( |\t)+\n/); + + ast.traverseByType('space', function (space, i, parent) { + var content = space.content; + var nextIndex = i + 1; + var next = parent.content[nextIndex]; + + while (next && (next.is('space') || next.is('declarationDelimeter'))) { + content += next.content; + nextIndex++; + next = parent.content[nextIndex]; + } + + if (trailing.test(content)) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'severity': parser.severity, + 'line': space.start.line, + 'column': space.start.column, + 'message': 'No trailing whitespace allowed' + }); + } + }); + + return result; + } +}; + diff --git a/tests/rules/no-trailing-whitespace.js b/tests/rules/no-trailing-whitespace.js new file mode 100644 index 00000000..3cfa81da --- /dev/null +++ b/tests/rules/no-trailing-whitespace.js @@ -0,0 +1,35 @@ +'use strict'; + +var lint = require('./_lint'); + +////////////////////////////// +// SCSS syntax tests +////////////////////////////// +describe('no trailing whitespace - scss', function () { + var file = lint.file('no-trailing-whitespace.scss'); + + it('enforce', function (done) { + lint.test(file, { + 'no-trailing-whitespace': 1 + }, function (data) { + lint.assert.equal(5, data.warningCount); + done(); + }); + }); +}); + +////////////////////////////// +// Sass syntax tests +////////////////////////////// +describe('no trailing whitespace - sass', function () { + var file = lint.file('no-trailing-whitespace.sass'); + + it('enforce', function (done) { + lint.test(file, { + 'no-trailing-whitespace': 1 + }, function (data) { + lint.assert.equal(5, data.warningCount); + done(); + }); + }); +}); diff --git a/tests/sass/no-trailing-whitespace.sass b/tests/sass/no-trailing-whitespace.sass new file mode 100644 index 00000000..baf3b31e --- /dev/null +++ b/tests/sass/no-trailing-whitespace.sass @@ -0,0 +1,19 @@ +.foo + margin: 1.5rem; + +.bar + margin: 1.5rem; + +.baz + margin: 1.5rem; + +.qux + margin: 1.5rem; + + +.cat + margin: 1.5rem; + +.dog + margin: 1.5rem; + diff --git a/tests/sass/no-trailing-whitespace.scss b/tests/sass/no-trailing-whitespace.scss new file mode 100644 index 00000000..30f17dd7 --- /dev/null +++ b/tests/sass/no-trailing-whitespace.scss @@ -0,0 +1,24 @@ +.foo { + margin: 1.5rem; +} + +.bar { + margin: 1.5rem; +} + +.baz { + margin: 1.5rem; +} + +.qux { + margin: 1.5rem; +} + +.cat { + margin: 1.5rem; +} + +.dog { + margin: 1.5rem; +} + From 6a3e797d102f34811fcd755420f45a7bc1780d60 Mon Sep 17 00:00:00 2001 From: Alex Gyoshev Date: Tue, 12 Apr 2016 14:32:49 +0300 Subject: [PATCH 118/137] Document no-trailing-whitespace rule --- docs/rules/no-trailing-whitespace.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 docs/rules/no-trailing-whitespace.md diff --git a/docs/rules/no-trailing-whitespace.md b/docs/rules/no-trailing-whitespace.md new file mode 100644 index 00000000..19858652 --- /dev/null +++ b/docs/rules/no-trailing-whitespace.md @@ -0,0 +1,21 @@ +# No Trailing Whitespace + +Rule `no-trailing-whitespace` will enforce that trailing whitespace is not allowed. + +## Examples + +When enabled, the following are disallowed (\s denotes spaces or tabs): + +```scss +.foo {\s + margin: 1.5rem; +} + +.foo { + margin: .5rem;\s +} + +.foo { + margin: .4rem; +}\s +``` From a20b9b144e444a616e0199d0b7bd788191ccc220 Mon Sep 17 00:00:00 2001 From: Alex Gyoshev Date: Thu, 14 Apr 2016 09:44:34 +0300 Subject: [PATCH 119/137] Add no-trailing-whitespace rule to config --- lib/config/sass-lint.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/config/sass-lint.yml b/lib/config/sass-lint.yml index 5acfaedf..85441fe1 100644 --- a/lib/config/sass-lint.yml +++ b/lib/config/sass-lint.yml @@ -30,6 +30,7 @@ rules: no-mergeable-selectors: 1 no-misspelled-properties: 1 no-qualifying-elements: 1 + no-trailing-whitespace: 1 no-trailing-zero: 1 no-transition-all: 1 no-url-protocols: 1 From 3523f529f6bc70d830cb88cdb5e99ffb509f11ea Mon Sep 17 00:00:00 2001 From: Alex Gyoshev Date: Tue, 19 Apr 2016 11:55:59 +0300 Subject: [PATCH 120/137] Add comments to no-trailing-whitespace tests --- tests/sass/no-trailing-whitespace.sass | 5 +++++ tests/sass/no-trailing-whitespace.scss | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/tests/sass/no-trailing-whitespace.sass b/tests/sass/no-trailing-whitespace.sass index baf3b31e..58fefba9 100644 --- a/tests/sass/no-trailing-whitespace.sass +++ b/tests/sass/no-trailing-whitespace.sass @@ -1,19 +1,24 @@ .foo margin: 1.5rem; +// Two trailing spaces after selector .bar margin: 1.5rem; +// Two trailing spaces after property .baz margin: 1.5rem; +// Two trailing spaces between rules .qux margin: 1.5rem; +// Trailing tab after selector .cat margin: 1.5rem; +// Trailing tab after property .dog margin: 1.5rem; diff --git a/tests/sass/no-trailing-whitespace.scss b/tests/sass/no-trailing-whitespace.scss index 30f17dd7..37bce92f 100644 --- a/tests/sass/no-trailing-whitespace.scss +++ b/tests/sass/no-trailing-whitespace.scss @@ -2,22 +2,27 @@ margin: 1.5rem; } +// Two trailing spaces after selector .bar { margin: 1.5rem; } +// Two trailing spaces after property .baz { margin: 1.5rem; } +// Two trailing spaces after rule .qux { margin: 1.5rem; } +// Trailing tab after selector .cat { margin: 1.5rem; } +// Trailing tab after property .dog { margin: 1.5rem; } From 0ad847e321887bb88ef7342e093c2aaf651ccbe9 Mon Sep 17 00:00:00 2001 From: Sam Richard Date: Tue, 19 Apr 2016 10:18:28 -0400 Subject: [PATCH 121/137] :heartbeat: Add Appveyor config --- appveyor.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..ef3f48c8 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,29 @@ +# Test against this version of Node.js +environment: + matrix: + - nodejs_version: '0.10' + - nodejs_version: '0.12' + - nodejs_version: '4' + # Latest version of Node + - nodejs_version: '5' + - nodejs_version: '1' + +# Install scripts. (runs after repo cloning) +install: + # Get the latest stable version of Node.js or io.js + - ps: Install-Product node $env:nodejs_version + # install modules + - npm install + # link + - npm link + +# Post-install test scripts. +test_script: + # Output useful info for debugging. + - node --version + - npm --version + # run tests + - npm test + +# Don't actually build. +build: off From 8703d6fcaf96ef043c16979f08c5efd06877b199 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Wed, 20 Apr 2016 11:18:16 +0100 Subject: [PATCH 122/137] :bug: Ignore unicode syntax --- lib/rules/space-around-operator.js | 13 +++++++++---- tests/sass/space-around-operator.sass | 4 ++++ tests/sass/space-around-operator.scss | 5 +++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/rules/space-around-operator.js b/lib/rules/space-around-operator.js index f01813b1..8d8cb1ae 100644 --- a/lib/rules/space-around-operator.js +++ b/lib/rules/space-around-operator.js @@ -44,7 +44,6 @@ var checkSpacing = function (node, i, parent, parser, result) { ////////////////////////// if (parent && operator && next) { - // The parent ofthe exceptions are always going to be of type value if (parent.is('value')) { // 1. We should allow valid CSS use of leading - operator when @@ -53,9 +52,10 @@ var checkSpacing = function (node, i, parent, parser, result) { return false; } - // 2. We should allow valid CSS use of / operators when defining - // font size/line-height if (previous) { + // 2. We should allow valid CSS use of / operators when defining + // font size/line-height + // The first value is the font-size and must have a unit, therefore // a node of type dimension if (operator === '/' && previous.is('dimension') @@ -64,9 +64,14 @@ var checkSpacing = function (node, i, parent, parser, result) { ) { return false; } + + // 3. Ignore if Unicode + if (previous.is('ident') && previous.content === 'U' && operator === '+') { + return false; + } } - // 3. If there is nothing leading the minus, it's a negative value so we + // 4. If there is nothing leading the minus, it's a negative value so we // shouldn't have a following space if (!previous && operator === '-' && !next.is('space')) { return false; diff --git a/tests/sass/space-around-operator.sass b/tests/sass/space-around-operator.sass index 2fbc4ebd..0803ec8a 100644 --- a/tests/sass/space-around-operator.sass +++ b/tests/sass/space-around-operator.sass @@ -466,3 +466,7 @@ h1 // Should flag this if rule is false .foo $val: 1 - ($foo * 2) + +@font-face + font-family: 'Proxima Nova' + unicode-range: U+1F00-1FFF diff --git a/tests/sass/space-around-operator.scss b/tests/sass/space-around-operator.scss index 1fe75a39..9f506993 100644 --- a/tests/sass/space-around-operator.scss +++ b/tests/sass/space-around-operator.scss @@ -480,3 +480,8 @@ $qux: (2 * 1); // FIXME: Gonzales - FIXED IN BETA // .foo { // $val: - ($foo * 2); // } + +@font-face { + font-family: 'Proxima Nova'; + unicode-range: U+1F00-1FFF; +} From 9c1a8d340e9d3302f04cb372107e0622ce80f879 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Wed, 20 Apr 2016 11:25:26 +0100 Subject: [PATCH 123/137] Expand unicode tests and flag wildcard issue --- tests/sass/space-around-operator.sass | 7 ++++++- tests/sass/space-around-operator.scss | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/sass/space-around-operator.sass b/tests/sass/space-around-operator.sass index 0803ec8a..967afebc 100644 --- a/tests/sass/space-around-operator.sass +++ b/tests/sass/space-around-operator.sass @@ -469,4 +469,9 @@ h1 @font-face font-family: 'Proxima Nova' - unicode-range: U+1F00-1FFF + unicode-range: U+26 + unicode-range: U+0-7F + unicode-range: U+0025-00FF + // Wildcard currently causes gonzales to fail + // unicode-range: U+4??; + // unicode-range: U+0025-00FF, U+4??; diff --git a/tests/sass/space-around-operator.scss b/tests/sass/space-around-operator.scss index 9f506993..3b9c458e 100644 --- a/tests/sass/space-around-operator.scss +++ b/tests/sass/space-around-operator.scss @@ -483,5 +483,10 @@ $qux: (2 * 1); // FIXME: Gonzales - FIXED IN BETA @font-face { font-family: 'Proxima Nova'; - unicode-range: U+1F00-1FFF; + unicode-range: U+26; + unicode-range: U+0-7F; + unicode-range: U+0025-00FF; + // Wildcard currently causes gonzales to fail + // unicode-range: U+4??; + // unicode-range: U+0025-00FF, U+4??; } From 11c994c8d689be9d13f0fd24383b64d26916d5e7 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Wed, 20 Apr 2016 13:06:32 +0100 Subject: [PATCH 124/137] :bug: Fix negative number issue with space-around-operator --- lib/rules/space-around-operator.js | 157 ++++++++++++++++++++++------- 1 file changed, 123 insertions(+), 34 deletions(-) diff --git a/lib/rules/space-around-operator.js b/lib/rules/space-around-operator.js index a0920dbb..acbef621 100644 --- a/lib/rules/space-around-operator.js +++ b/lib/rules/space-around-operator.js @@ -21,6 +21,127 @@ var getRelationalOperator = function (node) { return false; }; +/** + * Determine if operator is negative number + * + * @param {string} operator - The operator + * @param {Object} next - The next node + * @param {Object} previous - The previous node +* @param {Object} doublePrevious - The double previous node (back two) + * @returns {bool} true / false + */ +var isNegativeNumber = function (operator, next, previous, doublePrevious) { + if (operator === '-') { + + // Catch the following: + // $foo: -20; + if (!previous && !next.is('space')) { + return true; + } + + // Catch the following: + // .foo { + // property: -16px; + // } + if (next.is('dimension') || next.is('percentage')) { + return true; + } + + // Catch the following: + // .foo { + // propery: 2 / -16; + // } + if (doublePrevious && doublePrevious.is('operator')) { + return true; + } + + // Catch the following: + // .foo { + // property: 2 /-16px; + // } + if (previous && previous.is('operator')) { + return true; + } + } + + return false; +}; + +/** + * Determine if operator is divider + * + * @param {string} operator - The operator + * @param {Object} next - The next node + * @param {Object} previous - The previous node + * @returns {bool} true / false + */ +var isDivider = function (operator, next, previous) { + if (operator === '/') { + + // Catch the following: + // .foo { + // property: calc(100% / 2); + // } + if (previous && next) { + if (previous.is('dimension') && (next.is('dimension') || next.is('number'))) { + return true; + } + } + } + + return false; +}; + +/** + * Determine if operator is part of unicode + * + * @param {string} operator - The operator + * @param {Object} previous - The previous node + * @returns {bool} true / false + */ +var isUnicode = function (operator, previous) { + if (operator === '+') { + + // Catch the following: + // @font-family { + // + // } + if (previous.is('ident') && previous.content === 'U') { + return true; + } + } + + return false; +} + +/** + * Determine if operator is exception + * + * @param {string} operator - The operator + * @param {Object} parent - The parent node + * @param {Object} i - The node index + * @returns {bool} true / false + */ +var isException = function (operator, parent, i) { + var previous = parent.content[i - 1] || false, + doublePrevious = parent.content[i - 2] || false, + next = parent.content[i + 1] || false; + + if (isNegativeNumber(operator, next, previous, doublePrevious)) { + return true; + } + + if (isDivider(operator, next, previous)) { + return true; + } + + if (isUnicode(operator, previous)) { + return true; + } + + return false; +}; + /** * Check the spacing around an operator * @@ -60,40 +181,8 @@ var checkSpacing = function (node, i, parent, parser, result) { // Exceptions ////////////////////////// - if (parent && operator && next) { - // The parent ofthe exceptions are always going to be of type value - if (parent.is('value')) { - // 1. We should allow valid CSS use of leading - operator when - // defining negative values (margin, top, etc) - if (parent.is('value') && operator === '-' && (next.is('dimension') || next.is('percentage'))) { - return false; - } - - if (previous) { - // 2. We should allow valid CSS use of / operators when defining - // font size/line-height - - // The first value is the font-size and must have a unit, therefore - // a node of type dimension - if (operator === '/' && previous.is('dimension') - // Line-height - can be a number with our without units - && (next.is('dimension') || next.is('number')) - ) { - return false; - } - - // 3. Ignore if Unicode - if (previous.is('ident') && previous.content === 'U' && operator === '+') { - return false; - } - } - - // 4. If there is nothing leading the minus, it's a negative value so we - // shouldn't have a following space - if (!previous && operator === '-' && !next.is('space')) { - return false; - } - } + if (isException(operator, parent, i)) { + return false; } // If the operator checks out in our valid operator list From a4a5017d3738a0abe96ab8e9b8e1bd152f5b2a01 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Wed, 20 Apr 2016 13:07:13 +0100 Subject: [PATCH 125/137] :white_check_mark: Add test coverage for fix --- tests/rules/space-around-operator.js | 8 ++++---- tests/sass/space-around-operator.sass | 10 ++++++++++ tests/sass/space-around-operator.scss | 10 ++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/tests/rules/space-around-operator.js b/tests/rules/space-around-operator.js index 957c1599..b87ca3cf 100644 --- a/tests/rules/space-around-operator.js +++ b/tests/rules/space-around-operator.js @@ -12,7 +12,7 @@ describe('space around operator - scss', function () { lint.test(file, { 'space-around-operator': 1 }, function (data) { - lint.assert.equal(91, data.warningCount); + lint.assert.equal(92, data.warningCount); done(); }); }); @@ -26,7 +26,7 @@ describe('space around operator - scss', function () { } ] }, function (data) { - lint.assert.equal(90, data.warningCount); + lint.assert.equal(92, data.warningCount); done(); }); }); @@ -42,7 +42,7 @@ describe('space around operator - sass', function () { lint.test(file, { 'space-around-operator': 1 }, function (data) { - lint.assert.equal(87, data.warningCount); + lint.assert.equal(88, data.warningCount); done(); }); }); @@ -56,7 +56,7 @@ describe('space around operator - sass', function () { } ] }, function (data) { - lint.assert.equal(84, data.warningCount); + lint.assert.equal(86, data.warningCount); done(); }); }); diff --git a/tests/sass/space-around-operator.sass b/tests/sass/space-around-operator.sass index a1bb3c3b..1937ca77 100644 --- a/tests/sass/space-around-operator.sass +++ b/tests/sass/space-around-operator.sass @@ -314,6 +314,11 @@ $qux: (2 +1) $baz: 1 +// Negative numbers + +.foo + bottom: $var /-2 + //==================== // Should be ok @@ -473,3 +478,8 @@ h1 // Wildcard currently causes gonzales to fail // unicode-range: U+4??; // unicode-range: U+0025-00FF, U+4??; + +// Negative numbers + +.foo + bottom: $var / -2 diff --git a/tests/sass/space-around-operator.scss b/tests/sass/space-around-operator.scss index a0d59497..65499b48 100644 --- a/tests/sass/space-around-operator.scss +++ b/tests/sass/space-around-operator.scss @@ -321,6 +321,11 @@ $norf: (5 % 2); $baz: 1; } +// Negative numbers + +.foo { + bottom: $var /-2; +} //==================== // Should be ok @@ -487,4 +492,9 @@ $norf: (5 % 2); // Wildcard currently causes gonzales to fail // unicode-range: U+4??; // unicode-range: U+0025-00FF, U+4??; + +// Negative numbers + +.foo { + bottom: $var / -2; } From 96126d4289c0375085fd70e0ec212a0abeaffa93 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Wed, 20 Apr 2016 13:20:21 +0100 Subject: [PATCH 126/137] Fix eslint issues --- lib/rules/space-around-operator.js | 6 +++--- tests/sass/space-around-operator.scss | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/rules/space-around-operator.js b/lib/rules/space-around-operator.js index acbef621..6c44d556 100644 --- a/lib/rules/space-around-operator.js +++ b/lib/rules/space-around-operator.js @@ -103,8 +103,8 @@ var isUnicode = function (operator, previous) { if (operator === '+') { // Catch the following: - // @font-family { - // + // @font-face { + // unicode-range: U+26; // } if (previous.is('ident') && previous.content === 'U') { return true; @@ -112,7 +112,7 @@ var isUnicode = function (operator, previous) { } return false; -} +}; /** * Determine if operator is exception diff --git a/tests/sass/space-around-operator.scss b/tests/sass/space-around-operator.scss index 65499b48..5966929e 100644 --- a/tests/sass/space-around-operator.scss +++ b/tests/sass/space-around-operator.scss @@ -492,6 +492,7 @@ $norf: (5 % 2); // Wildcard currently causes gonzales to fail // unicode-range: U+4??; // unicode-range: U+0025-00FF, U+4??; +} // Negative numbers From c12df2ab9a283628ad918df24a9a0eb4b82001ad Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Wed, 20 Apr 2016 13:22:39 +0100 Subject: [PATCH 127/137] Correct indentation --- lib/rules/space-around-operator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/space-around-operator.js b/lib/rules/space-around-operator.js index 6c44d556..e6960bf8 100644 --- a/lib/rules/space-around-operator.js +++ b/lib/rules/space-around-operator.js @@ -27,7 +27,7 @@ var getRelationalOperator = function (node) { * @param {string} operator - The operator * @param {Object} next - The next node * @param {Object} previous - The previous node -* @param {Object} doublePrevious - The double previous node (back two) + * @param {Object} doublePrevious - The double previous node (back two) * @returns {bool} true / false */ var isNegativeNumber = function (operator, next, previous, doublePrevious) { From 50b86b09350cd9159f8ca0f94cc354367024f2ea Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Wed, 20 Apr 2016 13:59:50 +0100 Subject: [PATCH 128/137] :green_heart: Fix issues with rules and cli tests --- lib/rules/brace-style.js | 2 +- lib/rules/final-newline.js | 2 +- tests/cli.js | 23 ++++++++++++++--------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index 2d35a134..af1d6273 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -89,7 +89,7 @@ var isClosingBraceOnNewLine = function (node) { contentLength = content.length - 1, lastNode = content.get(contentLength); - if (lastNode.is('space') && helpers.hasEOL(lastNode.content)) { + if (lastNode && lastNode.is('space') && helpers.hasEOL(lastNode.content)) { return true; } return false; diff --git a/lib/rules/final-newline.js b/lib/rules/final-newline.js index 404d1afb..2a173fc6 100644 --- a/lib/rules/final-newline.js +++ b/lib/rules/final-newline.js @@ -19,7 +19,7 @@ module.exports = { if (typeof last.last('block') === 'object') { var lastBlock = last.last('block'); - if (lastBlock.content.length > 0) { + if (lastBlock && lastBlock.content.length > 0) { if (lastBlock.content[lastBlock.length - 1]) { last = lastBlock.content[lastBlock.length - 1]; } diff --git a/tests/cli.js b/tests/cli.js index 29bee934..e8279a65 100644 --- a/tests/cli.js +++ b/tests/cli.js @@ -237,12 +237,14 @@ describe('cli', function () { it('should not include ignored paths', function (done) { - var sassTestsPath = path.join(__dirname, '/sass/'), - files = []; + var sassTestsPath = 'tests/sass/', + files = [], + dirContents = fs.readdirSync(sassTestsPath); - files.push(sassTestsPath + fs.readdirSync(sassTestsPath)); - - var command = 'sass-lint -i \'**/*.s+(a|c)ss\''; + dirContents.forEach(function (file) { + files.push(sassTestsPath + file); + }); + var command = 'sass-lint -i \'**/*.s*\' -v -q'; exec(command, function (err, stdout) { @@ -260,12 +262,15 @@ describe('cli', function () { it('should not include multiple ignored paths', function (done) { - var sassTestsPath = path.join(__dirname, '/sass/'), - files = []; + var sassTestsPath = 'tests/sass/', + files = [], + dirContents = fs.readdirSync(sassTestsPath); - files.push(sassTestsPath + fs.readdirSync(sassTestsPath)); + dirContents.forEach(function (file) { + files.push(sassTestsPath + file); + }); - var command = 'sass-lint -i \'**/*.scss, **/*.sass \''; + var command = 'sass-lint -i \'**/*.scss, **/*.sass \' -q -v'; exec(command, function (err, stdout) { From 4b1d98d633c258d8948a1072b1d88ffec9e67cd0 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Wed, 20 Apr 2016 15:21:03 +0100 Subject: [PATCH 129/137] :green_heart: Tidy up cli tests --- tests/cli.js | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/tests/cli.js b/tests/cli.js index e8279a65..d85c6578 100644 --- a/tests/cli.js +++ b/tests/cli.js @@ -4,8 +4,19 @@ var assert = require('assert'), path = require('path'), exec = require('child_process').exec; +var sassTestsPath = 'tests/sass/', + files = []; describe('cli', function () { + + before(function () { + var testDirContents = fs.readdirSync(sassTestsPath); + + testDirContents.forEach(function (file) { + files.push(sassTestsPath + file); + }); + }); + it('should return help instructions', function (done) { var command = 'sass-lint -h'; @@ -236,14 +247,6 @@ describe('cli', function () { }); it('should not include ignored paths', function (done) { - - var sassTestsPath = 'tests/sass/', - files = [], - dirContents = fs.readdirSync(sassTestsPath); - - dirContents.forEach(function (file) { - files.push(sassTestsPath + file); - }); var command = 'sass-lint -i \'**/*.s*\' -v -q'; exec(command, function (err, stdout) { @@ -261,15 +264,6 @@ describe('cli', function () { }); it('should not include multiple ignored paths', function (done) { - - var sassTestsPath = 'tests/sass/', - files = [], - dirContents = fs.readdirSync(sassTestsPath); - - dirContents.forEach(function (file) { - files.push(sassTestsPath + file); - }); - var command = 'sass-lint -i \'**/*.scss, **/*.sass \' -q -v'; exec(command, function (err, stdout) { @@ -287,12 +281,6 @@ describe('cli', function () { }); it('should override filename convention if a valid --syntax is provided', function (done) { - - var sassTestsPath = path.join(__dirname, '/sass/'), - files = []; - - files.push(sassTestsPath + fs.readdirSync(sassTestsPath)); - var command = 'sass-lint --syntax scss tests/sass/cli.txt --verbose'; exec(command, function (err, stdout) { From c97ee3e20ad610bfbc546abb5dad578d69cfb2d3 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Wed, 20 Apr 2016 15:28:14 +0100 Subject: [PATCH 130/137] :green_heart: Speed up tests for appveyor --- tests/cli.js | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/tests/cli.js b/tests/cli.js index d85c6578..4f140e12 100644 --- a/tests/cli.js +++ b/tests/cli.js @@ -4,19 +4,8 @@ var assert = require('assert'), path = require('path'), exec = require('child_process').exec; -var sassTestsPath = 'tests/sass/', - files = []; - describe('cli', function () { - before(function () { - var testDirContents = fs.readdirSync(sassTestsPath); - - testDirContents.forEach(function (file) { - files.push(sassTestsPath + file); - }); - }); - it('should return help instructions', function (done) { var command = 'sass-lint -h'; @@ -255,9 +244,8 @@ describe('cli', function () { return done(err); } - files.forEach(function (file) { - assert(stdout.indexOf(file) === -1); - }); + assert(stdout.indexOf('.scss') === -1); + assert(stdout.indexOf('.sass') === -1); return done(); }); @@ -272,9 +260,8 @@ describe('cli', function () { return done(err); } - files.forEach(function (file) { - assert(stdout.indexOf(file) === -1); - }); + assert(stdout.indexOf('.scss') === -1); + assert(stdout.indexOf('.sass') === -1); return done(); }); From a6a661e51d9bcf29676df617469281cb490ea064 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Wed, 20 Apr 2016 16:55:21 +0100 Subject: [PATCH 131/137] :green_heart: Separate cli test files --- tests/cli.js | 31 +++++++++++++++--------------- tests/{sass => cli}/cli-clean.scss | 0 tests/cli/cli-error.sass | 2 ++ tests/{sass => cli}/cli-error.scss | 0 tests/{sass => cli}/cli.scss | 0 tests/{sass => cli}/cli.txt | 0 6 files changed, 17 insertions(+), 16 deletions(-) rename tests/{sass => cli}/cli-clean.scss (100%) create mode 100644 tests/cli/cli-error.sass rename tests/{sass => cli}/cli-error.scss (100%) rename tests/{sass => cli}/cli.scss (100%) rename tests/{sass => cli}/cli.txt (100%) diff --git a/tests/cli.js b/tests/cli.js index 4f140e12..cf2dc614 100644 --- a/tests/cli.js +++ b/tests/cli.js @@ -35,7 +35,7 @@ describe('cli', function () { }); it('CLI format option should output JSON', function (done) { - var command = 'sass-lint -c tests/yml/.stylish-output.yml tests/sass/cli.scss --verbose --format json'; + var command = 'sass-lint -c tests/yml/.stylish-output.yml tests/cli/cli.scss --verbose --format json'; exec(command, function (err, stdout) { @@ -55,7 +55,7 @@ describe('cli', function () { }); it('CLI output option should write to test file', function (done) { - var command = 'sass-lint -c tests/yml/.stylish-output.yml tests/sass/cli.scss --verbose --format json --output tests/cli-output.json', + var command = 'sass-lint -c tests/yml/.stylish-output.yml tests/cli/cli.scss --verbose --format json --output tests/cli-output.json', outputFile = path.resolve(process.cwd(), 'tests/cli-output.json'); exec(command, function (err) { @@ -78,7 +78,7 @@ describe('cli', function () { }); it('CLI output option should write JSON to test file', function (done) { - var command = 'sass-lint -c tests/yml/.stylish-output.yml tests/sass/cli.scss --verbose --format json --output tests/cli-output.json', + var command = 'sass-lint -c tests/yml/.stylish-output.yml tests/cli/cli.scss --verbose --format json --output tests/cli-output.json', outputFile = path.resolve(process.cwd(), 'tests/cli-output.json'); exec(command, function (err) { @@ -111,7 +111,7 @@ describe('cli', function () { }); it('CLI output option should write JSON to test file when upper case format is used', function (done) { - var command = 'sass-lint -c tests/yml/.stylish-output.yml tests/sass/cli.scss --verbose --format JSON --output tests/cli-output.json', + var command = 'sass-lint -c tests/yml/.stylish-output.yml tests/cli/cli.scss --verbose --format JSON --output tests/cli-output.json', outputFile = path.resolve(process.cwd(), 'tests/cli-output.json'); exec(command, function (err) { @@ -146,7 +146,7 @@ describe('cli', function () { // Test custom config path it('should return JSON from a custom config', function (done) { - var command = 'sass-lint -c tests/yml/.color-keyword-errors.yml tests/sass/cli.scss --verbose'; + var command = 'sass-lint -c tests/yml/.color-keyword-errors.yml tests/cli/cli.scss --verbose'; exec(command, function (err, stdout) { @@ -168,7 +168,7 @@ describe('cli', function () { // Test 0 errors/warnings when rules set to 0 in config it('output should return no errors/warnings', function (done) { - var command = 'sass-lint -c tests/yml/.json-lint.yml tests/sass/cli.scss --verbose'; + var command = 'sass-lint -c tests/yml/.json-lint.yml tests/cli/cli.scss --verbose'; exec(command, function (err, stdout) { @@ -190,7 +190,7 @@ describe('cli', function () { // Test 1 warning when rules set to 0 in config it('should return a warning', function (done) { - var command = 'sass-lint -c tests/yml/.color-keyword-errors.yml tests/sass/cli.scss --verbose'; + var command = 'sass-lint -c tests/yml/.color-keyword-errors.yml tests/cli/cli.scss --verbose'; exec(command, function (err, stdout) { @@ -219,8 +219,8 @@ describe('cli', function () { }); it('should return a warning - stylish', function (done) { - var command = 'sass-lint -c tests/yml/.stylish-errors.yml tests/sass/cli.scss --verbose', - expectedOutputLength = 155; + var command = 'sass-lint -c tests/yml/.stylish-errors.yml tests/cli/cli.scss --verbose', + expectedOutputLength = 154; exec(command, function (err, stdout) { @@ -236,7 +236,7 @@ describe('cli', function () { }); it('should not include ignored paths', function (done) { - var command = 'sass-lint -i \'**/*.s*\' -v -q'; + var command = 'sass-lint -i **/*.scss -v -q --format json **/cli/*.scss'; exec(command, function (err, stdout) { @@ -245,14 +245,13 @@ describe('cli', function () { } assert(stdout.indexOf('.scss') === -1); - assert(stdout.indexOf('.sass') === -1); return done(); }); }); it('should not include multiple ignored paths', function (done) { - var command = 'sass-lint -i \'**/*.scss, **/*.sass \' -q -v'; + var command = 'sass-lint -i \'**/*.scss, **/*.sass\' -q -v --format json'; exec(command, function (err, stdout) { @@ -268,7 +267,7 @@ describe('cli', function () { }); it('should override filename convention if a valid --syntax is provided', function (done) { - var command = 'sass-lint --syntax scss tests/sass/cli.txt --verbose'; + var command = 'sass-lint --syntax scss tests/cli/cli.txt --verbose --format json'; exec(command, function (err, stdout) { @@ -289,7 +288,7 @@ describe('cli', function () { }); it('should exit with exit code 1 when quiet', function (done) { - var command = 'sass-lint -c tests/yml/.error-output.yml tests/sass/cli-error.scss --verbose --no-exit'; + var command = 'sass-lint -c tests/yml/.error-output.yml tests/cli/cli-error.scss --verbose --no-exit'; exec(command, function (err) { if (err.code === 1) { @@ -301,7 +300,7 @@ describe('cli', function () { }); it('should exit with exit code 1 when more warnings than --max-warnings', function (done) { - var command = 'sass-lint -c tests/yml/.color-keyword-errors.yml tests/sass/cli.scss --max-warnings 0'; + var command = 'sass-lint -c tests/yml/.color-keyword-errors.yml tests/cli/cli.scss --max-warnings 0'; exec(command, function (err) { if (err && err.code === 1) { @@ -313,7 +312,7 @@ describe('cli', function () { }); it('should not exit with an error if no config is specified', function (done) { - var command = 'sass-lint tests/sass/cli-clean.scss --verbose --no-exit'; + var command = 'sass-lint tests/cli/cli-clean.scss --verbose --no-exit'; exec(command, function (err) { if (!err) { diff --git a/tests/sass/cli-clean.scss b/tests/cli/cli-clean.scss similarity index 100% rename from tests/sass/cli-clean.scss rename to tests/cli/cli-clean.scss diff --git a/tests/cli/cli-error.sass b/tests/cli/cli-error.sass new file mode 100644 index 00000000..44acc58a --- /dev/null +++ b/tests/cli/cli-error.sass @@ -0,0 +1,2 @@ +#cli + color: red diff --git a/tests/sass/cli-error.scss b/tests/cli/cli-error.scss similarity index 100% rename from tests/sass/cli-error.scss rename to tests/cli/cli-error.scss diff --git a/tests/sass/cli.scss b/tests/cli/cli.scss similarity index 100% rename from tests/sass/cli.scss rename to tests/cli/cli.scss diff --git a/tests/sass/cli.txt b/tests/cli/cli.txt similarity index 100% rename from tests/sass/cli.txt rename to tests/cli/cli.txt From 8c746146c05da8177a8a7a0ddba67245ca7b9d23 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Wed, 20 Apr 2016 17:18:25 +0100 Subject: [PATCH 132/137] :green_heart: Update Node test matrices --- .travis.yml | 4 ++-- appveyor.yml | 1 - tests/cli.js | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 98453264..420d2edc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: node_js node_js: - - '0.10' - '0.12' - - '4.2' + - '4' + - '5' - node - iojs before_script: npm link diff --git a/appveyor.yml b/appveyor.yml index ef3f48c8..148b11d4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,6 @@ # Test against this version of Node.js environment: matrix: - - nodejs_version: '0.10' - nodejs_version: '0.12' - nodejs_version: '4' # Latest version of Node diff --git a/tests/cli.js b/tests/cli.js index cf2dc614..ecb4a05c 100644 --- a/tests/cli.js +++ b/tests/cli.js @@ -16,7 +16,7 @@ describe('cli', function () { assert(stdout.indexOf('Usage') > 0); - return done(null); + return done(); }); }); @@ -30,7 +30,7 @@ describe('cli', function () { should(stdout).match(/^[0-9]+.[0-9]+(.[0-9]+)?/); - return done(null); + return done(); }); }); From 88b37763928ad0304bb028d66c34264666b3c3c3 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Thu, 21 Apr 2016 11:11:02 +0100 Subject: [PATCH 133/137] Add import exception check and tests - fix issue with sass --- lib/rules/space-around-operator.js | 30 +++++++++++++++++++++++++++ tests/sass/space-around-operator.sass | 21 ++++++++++--------- tests/sass/space-around-operator.scss | 23 ++++++++++++-------- 3 files changed, 55 insertions(+), 19 deletions(-) diff --git a/lib/rules/space-around-operator.js b/lib/rules/space-around-operator.js index e6960bf8..8a4bc9c7 100644 --- a/lib/rules/space-around-operator.js +++ b/lib/rules/space-around-operator.js @@ -114,6 +114,32 @@ var isUnicode = function (operator, previous) { return false; }; +/** + * Determine if operator is part of import path + * + * @param {string} operator - The operator + * @param {Object} parent - The parent node + * @returns {bool} true / false + */ +var isImport = function (operator, parent) { + if (operator === '/') { + + if (parent.is('atrule') && parent.contains('atkeyword')) { + var keyword = parent.first('atkeyword'); + + if (keyword.contains('ident')) { + var ident = keyword.first('ident'); + + if (ident.content === 'import') { + return true; + } + } + } + } + + return false; +}; + /** * Determine if operator is exception * @@ -139,6 +165,10 @@ var isException = function (operator, parent, i) { return true; } + if (isImport(operator, parent)) { + return true; + } + return false; }; diff --git a/tests/sass/space-around-operator.sass b/tests/sass/space-around-operator.sass index 1937ca77..ef44fe67 100644 --- a/tests/sass/space-around-operator.sass +++ b/tests/sass/space-around-operator.sass @@ -314,6 +314,13 @@ $qux: (2 +1) $baz: 1 +// ======================== +// Interesting user cases +// ======================== + +.foo + $val: 1-($foo * 2) + // Negative numbers .foo @@ -372,8 +379,6 @@ ul li:nth-child(2n+1) content: '' - - // Invalid CSS .foo @@ -386,8 +391,6 @@ ul content: '' - - // Values no parens $foo: 1 + 1 @@ -462,10 +465,6 @@ h1 .foo $val: -($foo * 2) -// Should flag this if rule is true -.foo - $val: 1-($foo * 2) - // Should flag this if rule is false .foo $val: 1 - ($foo * 2) @@ -475,11 +474,13 @@ h1 unicode-range: U+26 unicode-range: U+0-7F unicode-range: U+0025-00FF - // Wildcard currently causes gonzales to fail + // TODO: Wildcard currently causes gonzales to fail // unicode-range: U+4??; // unicode-range: U+0025-00FF, U+4??; // Negative numbers - .foo bottom: $var / -2 + +// Imports +@import bar/foo diff --git a/tests/sass/space-around-operator.scss b/tests/sass/space-around-operator.scss index 5966929e..f3ee6116 100644 --- a/tests/sass/space-around-operator.scss +++ b/tests/sass/space-around-operator.scss @@ -321,12 +321,22 @@ $norf: (5 % 2); $baz: 1; } +// ======================== +// Interesting user cases +// ======================== + +// Should flag this if rule is true +.foo { + $val: 1-($foo * 2); +} + // Negative numbers .foo { bottom: $var /-2; } + //==================== // Should be ok //==================== @@ -464,17 +474,10 @@ $norf: (5 % 2); // Interesting user cases // ======================== -// Should ignore the this .foo { $val: -($foo * 2); } -// Should flag this if rule is true -.foo { - $val: 1-($foo * 2); -} - -// Should flag this if rule is false .foo { $val: 1 - ($foo * 2); } @@ -489,13 +492,15 @@ $norf: (5 % 2); unicode-range: U+26; unicode-range: U+0-7F; unicode-range: U+0025-00FF; - // Wildcard currently causes gonzales to fail + // TODO: Wildcard currently causes gonzales to fail // unicode-range: U+4??; // unicode-range: U+0025-00FF, U+4??; } // Negative numbers - .foo { bottom: $var / -2; } + +// Imports +@import 'bar/foo' From db6fedd394dce056dba82bf73b1535d17e4be816 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Thu, 21 Apr 2016 11:44:13 +0100 Subject: [PATCH 134/137] :bug: Fix CRLF issues and fix sass issues --- lib/rules/no-trailing-whitespace.js | 5 +++-- tests/sass/no-trailing-whitespace.sass | 12 ++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/rules/no-trailing-whitespace.js b/lib/rules/no-trailing-whitespace.js index 95d3369e..be54bf44 100644 --- a/lib/rules/no-trailing-whitespace.js +++ b/lib/rules/no-trailing-whitespace.js @@ -8,19 +8,20 @@ module.exports = { 'detect': function (ast, parser) { var result = []; var trailing = (/( |\t)+\n/); + var trailingCRLF = (/( |\t)+\r\n/); ast.traverseByType('space', function (space, i, parent) { var content = space.content; var nextIndex = i + 1; var next = parent.content[nextIndex]; - while (next && (next.is('space') || next.is('declarationDelimeter'))) { + while (next && (next.is('space') || next.is('declarationDelimiter'))) { content += next.content; nextIndex++; next = parent.content[nextIndex]; } - if (trailing.test(content)) { + if (trailing.test(content) || trailingCRLF.test(content)) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'severity': parser.severity, diff --git a/tests/sass/no-trailing-whitespace.sass b/tests/sass/no-trailing-whitespace.sass index 58fefba9..c3f9c3c2 100644 --- a/tests/sass/no-trailing-whitespace.sass +++ b/tests/sass/no-trailing-whitespace.sass @@ -1,24 +1,24 @@ .foo - margin: 1.5rem; + margin: 1.5rem // Two trailing spaces after selector .bar - margin: 1.5rem; + margin: 1.5rem // Two trailing spaces after property .baz - margin: 1.5rem; + margin: 1.5rem // Two trailing spaces between rules .qux - margin: 1.5rem; + margin: 1.5rem // Trailing tab after selector .cat - margin: 1.5rem; + margin: 1.5rem // Trailing tab after property .dog - margin: 1.5rem; + margin: 1.5rem From c32533cb08421c872da22be1a1e4b28cfed52e20 Mon Sep 17 00:00:00 2001 From: greenkeeperio-bot Date: Thu, 21 Apr 2016 08:45:58 -0400 Subject: [PATCH 135/137] chore(package): update gonzales-pe-sl to version 3.2.7 https://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 61bda2e8..35e813b2 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "eslint": "^2.7.0", "fs-extra": "^0.28.0", "glob": "^7.0.0", - "gonzales-pe-sl": "3.2.6", + "gonzales-pe-sl": "3.2.7", "js-yaml": "^3.5.4", "lodash.capitalize": "^4.1.0", "lodash.kebabcase": "^4.0.0", From b7438ecdca40df74cf859c767c1ad6e59247c55d Mon Sep 17 00:00:00 2001 From: Sam Richard Date: Thu, 21 Apr 2016 09:14:28 -0400 Subject: [PATCH 136/137] Add CRLF line endings for Appveyor --- appveyor.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 148b11d4..871df024 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,3 +1,7 @@ +# CRLF Line Endings +init: + - git config --global core.autocrlf true + # Test against this version of Node.js environment: matrix: From 29612fbbecd505a9c0e1636aebb909337991bf69 Mon Sep 17 00:00:00 2001 From: Dan Purdy Date: Thu, 21 Apr 2016 15:31:09 +0100 Subject: [PATCH 137/137] Prepare v1.6.0 --- CHANGELOG.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecc4c8d1..9e9a3350 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,54 @@ # Sass Lint Changelog +## v1.6.0 + +**April 21, 2016** + +The long lost 1.6 update + +**WARNING** +* We've moved to the latest version of gonzales-pe and then onto our own fork in which we've fixed our issues with CRLF etc. All of our tests are passing but there may be unforeseen regressions in gonzales that we will aim to fix. If you find a problem like this please report it to us and we'll investigate further. You can then decide to keep your sass-lint dependency to 1.5.1 if you so choose. + +**Changes** +* Updated all rules to work with the new Gonzales-pe 3.2.x release [#495](https://github.com/sasstools/sass-lint/issues/495) +> No breaking changes in the sense that all the rules are the same and pass the same tests or more BUT many did involve a complete re write. + +* Now using gonzales-pe-sl 3.2.7 fork +* Update the no-mergeable-selectors rule to ignore certain situations and work a little more reliably across codebases +* Added BEM conventions to all naming rules [#614](https://github.com/sasstools/sass-lint/pull/614) +* Added Appveyor CI for testing against CRLF line endings on Windows + + +**CLI** +* Add max warnings option to the CLI --max-warnings [#568](https://github.com/sasstools/sass-lint/pull/568) + +**New Rules** +* `no-trailing-whitespace` rule added [#605](https://github.com/sasstools/sass-lint/pull/605) + +**Fixes** +* Fixed parsing error when using interpolated values [#44](https://github.com/sasstools/sass-lint/issues/44), [#184](https://github.com/sasstools/sass-lint/issues/184), [#210](https://github.com/sasstools/sass-lint/issues/210), [#222](https://github.com/sasstools/sass-lint/issues/222), +[#321](https://github.com/sasstools/sass-lint/issues/321), +[#486](https://github.com/sasstools/sass-lint/issues/486), +* Fixed parsing error when using the `!global` flag [#56](https://github.com/sasstools/sass-lint/issues/56) +* Having `-` within a class name will no longer return a parse error [#229](https://github.com/sasstools/sass-lint/issues/229) +* Fixed parsing error when using extrapolated variable as extend name [#313](https://github.com/sasstools/sass-lint/issues/313) +* Fixed an un-handled error thrown from the indentation rule [#389](https://github.com/sasstools/sass-lint/issues/389) +* Fixed an issue with final newline rule for sass [#445](https://github.com/sasstools/sass-lint/issues/445) +* Updated indentation rule to work with CRLF (indentation is mainly scss for the moment) [#524](https://github.com/sasstools/sass-lint/pull/524) +* Fixed parsing error when using nested maps [#531](https://github.com/sasstools/sass-lint/issues/531) +* Fixed parsing error when using variables for placeholder name [#532](https://github.com/sasstools/sass-lint/issues/532) +* Fixed issue with dots in filenames [#541](https://github.com/sasstools/sass-lint/pull/541) +* Fixed use of modulo operator in SCSS syntax [#565](https://github.com/sasstools/sass-lint/issues/565) +* Fixed an issue with space-around-operator and unicode [#620](https://github.com/sasstools/sass-lint/pull/620) +* Fixed an issue with CRLF line endings in the no-trailing-whitespace rule [#623](https://github.com/sasstools/sass-lint/pull/623) + +**A big thank you to everyone who reported issues or contributed to the discussion around issues and also for everyone bearing with us while we go this monster update ready for you.** + +## v1.5.1 +**February 26, 2016** + +* Locked down our dependency to lodash due to npm/unpublish issues + ## v1.5.0 **January 28, 2016** diff --git a/package.json b/package.json index 35e813b2..0dbc2a8c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sass-lint", - "version": "1.5.0", + "version": "1.6.0", "description": "All Node Sass linter!", "main": "index.js", "scripts": {