From f9a1d0eea41cc569dd4e1cb88759d45577346e70 Mon Sep 17 00:00:00 2001 From: GoalSmashers Date: Sun, 27 Oct 2013 00:09:55 +0200 Subject: [PATCH] Adds much faster removal of empty selectors & @media elements. * Adds supports for removing empty nested @media elements. --- History.md | 1 + lib/clean.js | 9 ++++----- lib/selectors/empty-removal.js | 30 ++++++++++++++++++++++++++++++ test/unit-test.js | 8 ++++++++ 4 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 lib/selectors/empty-removal.js diff --git a/History.md b/History.md index 3325d15f..2019cd25 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,7 @@ ================== * Adds simplified and more advanced text escaping / restoring via `EscapeStore` class. +* Adds simplified and much faster empty elements removal. 1.1.6 / 2013-10-26 ================== diff --git a/lib/clean.js b/lib/clean.js index 7366e60c..27f4e6e8 100644 --- a/lib/clean.js +++ b/lib/clean.js @@ -13,6 +13,7 @@ var ColorLongToShortHex = require('./colors/long-to-short-hex'); var ShorthandNotations = require('./properties/shorthand-notations'); var ImportInliner = require('./imports/inliner'); var UrlRebase = require('./images/url-rebase'); +var EmptyRemoval = require('./selectors/empty-removal'); var CommentsProcessor = require('./text/comments'); var ExpressionsProcessor = require('./text/expressions'); @@ -274,11 +275,9 @@ var CleanCSS = { }); if (options.removeEmpty) { - // empty elements - replace(/[^\{\}]+\{\}/g, ''); - - // empty @media declarations - replace(/@media [^\{]+\{\}/g, ''); + replace(function removeEmptySelectors() { + data = new EmptyRemoval(data).process(); + }); } // trim spaces at beginning and end diff --git a/lib/selectors/empty-removal.js b/lib/selectors/empty-removal.js new file mode 100644 index 00000000..ee193c16 --- /dev/null +++ b/lib/selectors/empty-removal.js @@ -0,0 +1,30 @@ +module.exports = function EmptyRemoval(data) { + var stripEmpty = function(cssData) { + var tempData = []; + var nextEmpty = 0; + var cursor = 0; + + for (; nextEmpty < cssData.length; ) { + nextEmpty = cssData.indexOf('{}', cursor); + if (nextEmpty == -1) + break; + + var startsAt = nextEmpty - 1; + while (cssData[startsAt] && cssData[startsAt] != '}' && cssData[startsAt] != '{') + startsAt--; + + tempData.push(cssData.substring(cursor, startsAt + 1)); + cursor = nextEmpty + 2; + } + + return tempData.length > 0 ? + stripEmpty(tempData.join('') + cssData.substring(cursor, cssData.length)) : + cssData; + }; + + return { + process: function() { + return stripEmpty(data); + } + }; +}; diff --git a/test/unit-test.js b/test/unit-test.js index 17d74b13..0faf6730 100644 --- a/test/unit-test.js +++ b/test/unit-test.js @@ -852,9 +852,17 @@ title']", "@media screen { .test {} } .test1 { color: green; }", ".test1{color:green}" ], + 'inside nested @media': [ + '@media screen { @media (orientation:landscape) { @media (max-width:999px) { .test {} } } }', + '' + ], 'inside not empty @media': [ "@media screen { .test {} .some { display:none } }", "@media screen{.some{display:none}}" + ], + 'inside nested not empty @media': [ + '@media screen { @media (orientation:landscape) { @media (max-width:999px) { .test {} } a {color:red} } }', + '@media screen{@media (orientation:landscape){a{color:red}}}' ] }, { removeEmpty: true }), 'empty @media': cssContext({ -- 2.34.1