From b1cef400cc2c230394f0db879178fd9c01d5fcc3 Mon Sep 17 00:00:00 2001 From: Tony Ganch Date: Fri, 20 Dec 2013 20:11:31 +0400 Subject: [PATCH 1/2] Remove empty nested rulesets (#133) 1. Fix for rulesets with spaces only: `a { }` is now treated as an empty ruleset. 2. Fix for preprocessors: nested rulesets are removed if they are empty. Ruleset is now considered empty if it has nothing but spaces or another empty ruleset. Example: ```scss // Before: .parent1 { .child { .grandchild { } } } .parent2 { .child1 { @include 'panda'; } .child 2 { .grandchild { } } } // After: .parent2 { .child1 { @include 'panda'; } } ``` --- README.md | 2 +- lib/options/remove-empty-rulesets.js | 28 +++++++++++++-- test/remove-empty-rulesets-scss.js | 35 +++++++++++++++++++ .../empty-nested-rule.expected.scss | 1 + .../empty-nested-rule.scss | 5 +++ test/remove-empty-rulesets-scss/include.scss | 4 +++ .../nested-rule-1.scss | 6 ++++ .../nested-rule-2.expected.scss | 7 ++++ .../nested-rule-2.scss | 9 +++++ test/remove-empty-rulesets.js | 4 +++ 10 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 test/remove-empty-rulesets-scss.js create mode 100644 test/remove-empty-rulesets-scss/empty-nested-rule.expected.scss create mode 100644 test/remove-empty-rulesets-scss/empty-nested-rule.scss create mode 100644 test/remove-empty-rulesets-scss/include.scss create mode 100644 test/remove-empty-rulesets-scss/nested-rule-1.scss create mode 100644 test/remove-empty-rulesets-scss/nested-rule-2.expected.scss create mode 100644 test/remove-empty-rulesets-scss/nested-rule-2.scss diff --git a/README.md b/README.md index cf61220e..3efe60ff 100644 --- a/README.md +++ b/README.md @@ -522,7 +522,7 @@ p[href^='https://']:before { content: 'secure' } Acceptable values: `{Boolean}` `true` -Example: `{ "remove-empty-rulesets": true }` - remove rulesets that have no declarations or comments. +Example: `{ "remove-empty-rulesets": true }` - remove rulesets that have nothing but spaces. `a { color: red; } p { /* hey */ } b { }` → `a { color: red; } p { /* hey */ } ` diff --git a/lib/options/remove-empty-rulesets.js b/lib/options/remove-empty-rulesets.js index 5006bc0c..e2e3ed06 100644 --- a/lib/options/remove-empty-rulesets.js +++ b/lib/options/remove-empty-rulesets.js @@ -32,8 +32,26 @@ module.exports = { _removeEmptyRulesets: function(nodeContent) { var i = nodeContent.length; + // Loop through node and try to find a ruleset: while (i--) { - if (this._isRuleset(nodeContent[i]) && this._isEmptyRuleset(nodeContent[i])) { + var node = nodeContent[i]; + if (!this._isRuleset(node)) continue; + // If a ruleset is found, try to find its nested rulesets and remove + // all empty ones: + var j = node.length; + while (j--) { + // Nested rulesets are located inside blocks, that's why look + // for blocks only: + var blockNode = node[j]; + if (blockNode[0] !== 'block') continue; + blockNode.shift(); + this._processStylesheetContent(blockNode); + blockNode.unshift('block'); + node[j] = blockNode; + } + // If after removing all empty nested rulesets the parent has also + // become empty, remove it too: + if (this._isEmptyRuleset(node)) { nodeContent.splice(i, 1); } } @@ -60,10 +78,10 @@ module.exports = { }, /** - * Block considered empty when it has no declarations or comments. + * Block is considered empty when it has nothing but spaces. */ _isEmptyBlock: function(node) { - return !node.some(this._isDeclarationOrComment); + return node.length === 1 || !node.some(this._isNotWhitespace); }, _isDeclarationOrComment: function(node) { @@ -82,6 +100,10 @@ module.exports = { return node[0] === 's'; }, + _isNotWhitespace: function(node) { + return typeof node === 'object' && node[0] !== 's'; + }, + /** * Detects the value of an option at the tree node. * This option is treated as `true` by default, but any trailing space would invalidate it. diff --git a/test/remove-empty-rulesets-scss.js b/test/remove-empty-rulesets-scss.js new file mode 100644 index 00000000..6d2d85dc --- /dev/null +++ b/test/remove-empty-rulesets-scss.js @@ -0,0 +1,35 @@ +var Comb = require('../lib/csscomb'); +var assert = require('assert'); +var fs = require('fs'); + +describe('options/remove-empty-rulesets (scss)', function() { + var comb = new Comb({ 'remove-empty-rulesets': true }); + var input; + var expected; + + function readFile(path) { + return fs.readFileSync('test/remove-empty-rulesets-scss/' + path, 'utf8'); + } + + it('Should not remove rulesets which contain only includes', function() { + input = readFile('include.scss', 'utf-8'); + assert.equal(comb.processString(input, 'scss'), input); + }); + + it('Should remove rulesets with contain only empty nested rules', function() { + input = readFile('empty-nested-rule.scss', 'utf-8'); + expected = readFile('empty-nested-rule.expected.scss', 'utf-8'); + assert.equal(comb.processString(input, 'scss'), expected); + }); + + it('Should not remove rulesets with non-empty nested rules. Test 1', function() { + input = readFile('nested-rule-1.scss', 'utf-8'); + assert.equal(comb.processString(input, 'scss'), input); + }); + + it('Should not remove rulesets with non-empty nested rules. Test 2', function() { + input = readFile('nested-rule-2.scss', 'utf-8'); + expected = readFile('nested-rule-2.expected.scss', 'utf-8'); + assert.equal(comb.processString(input, 'scss'), expected); + }); +}); diff --git a/test/remove-empty-rulesets-scss/empty-nested-rule.expected.scss b/test/remove-empty-rulesets-scss/empty-nested-rule.expected.scss new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/test/remove-empty-rulesets-scss/empty-nested-rule.expected.scss @@ -0,0 +1 @@ + diff --git a/test/remove-empty-rulesets-scss/empty-nested-rule.scss b/test/remove-empty-rulesets-scss/empty-nested-rule.scss new file mode 100644 index 00000000..89b0d979 --- /dev/null +++ b/test/remove-empty-rulesets-scss/empty-nested-rule.scss @@ -0,0 +1,5 @@ +.parent { + .child { + .grandchild { } + } +} diff --git a/test/remove-empty-rulesets-scss/include.scss b/test/remove-empty-rulesets-scss/include.scss new file mode 100644 index 00000000..70d39a59 --- /dev/null +++ b/test/remove-empty-rulesets-scss/include.scss @@ -0,0 +1,4 @@ +.parent { + @include mix-all; + @include mix-top; +} diff --git a/test/remove-empty-rulesets-scss/nested-rule-1.scss b/test/remove-empty-rulesets-scss/nested-rule-1.scss new file mode 100644 index 00000000..f69f70a2 --- /dev/null +++ b/test/remove-empty-rulesets-scss/nested-rule-1.scss @@ -0,0 +1,6 @@ +.parent { + .child { + @include mix-all; + @include mix-top; + } +} diff --git a/test/remove-empty-rulesets-scss/nested-rule-2.expected.scss b/test/remove-empty-rulesets-scss/nested-rule-2.expected.scss new file mode 100644 index 00000000..b020e30f --- /dev/null +++ b/test/remove-empty-rulesets-scss/nested-rule-2.expected.scss @@ -0,0 +1,7 @@ +.parent { + .child1 { + @include mix-all; + @include mix-top; + } + +} diff --git a/test/remove-empty-rulesets-scss/nested-rule-2.scss b/test/remove-empty-rulesets-scss/nested-rule-2.scss new file mode 100644 index 00000000..f5eedd44 --- /dev/null +++ b/test/remove-empty-rulesets-scss/nested-rule-2.scss @@ -0,0 +1,9 @@ +.parent { + .child1 { + @include mix-all; + @include mix-top; + } + .child2 { + .grandchild { } + } +} diff --git a/test/remove-empty-rulesets.js b/test/remove-empty-rulesets.js index e6c4fd3c..d4fffb66 100644 --- a/test/remove-empty-rulesets.js +++ b/test/remove-empty-rulesets.js @@ -27,6 +27,10 @@ describe('options/remove-empty-rulesets', function() { assert.equal(comb.processString(' b {} '), ' '); }); + it('should remove ruleset with spaces', function() { + assert.equal(comb.processString(' b { } '), ' '); + }); + it('should leave ruleset with declarations', function() { assert.equal(comb.processString('a { width: 10px; }\nb {} '), 'a { width: 10px; }\n '); }); From 89a7f8abc427d06abbb2cdc1d22fee3c9f0eb03c Mon Sep 17 00:00:00 2001 From: Mikhail Troshev Date: Mon, 23 Dec 2013 17:40:24 +0400 Subject: [PATCH 2/2] v2.0.1 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ec174ee..09771fee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 2.0.1 - 2013-12-23 +- Fix for `remove-empty-rulesets` option (#133) + ## 2.0.0 - 2013-12-18 **Great thanks for @tonyganch and @kizu!** - Use Gonzales PE to parse *.scss and *.less files diff --git a/package.json b/package.json index 30a6dedb..90c1053d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "csscomb", "description": "CSS coding style formatter", - "version": "2.0.0", + "version": "2.0.1", "homepage": "http://csscomb.com/", "author": "Mikhail Troshev ", "repository": "https://github.com/csscomb/csscomb.js",