From 1d535349a30522734ddb838ab51b60aeddb9a991 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowicz Date: Fri, 1 Apr 2016 22:39:43 +0200 Subject: [PATCH] Fixes #738 - edge case in comment processing. Second `/*` in `/*! comment */*{...}` was taken as a comment start which actually is not. --- History.md | 1 + lib/utils/quote-scanner.js | 21 +++++++++++++++++++-- test/integration-test.js | 6 +++++- test/text/comments-processor-test.js | 5 +++++ test/utils/quote-scanner-test.js | 15 ++++++++++++++- 5 files changed, 44 insertions(+), 4 deletions(-) diff --git a/History.md b/History.md index eaf435eb..9aaf2018 100644 --- a/History.md +++ b/History.md @@ -6,6 +6,7 @@ [3.4.11 / 2016-xx-xx](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.10...3.4) ================== +* Fixed issue [#738](https://github.com/jakubpawlowicz/clean-css/issues/738) - edge case in comment processing. * Fixed issue [#741](https://github.com/jakubpawlowicz/clean-css/issues/741) - HTTP proxy with HTTPS inlining. * Fixed issue [#743](https://github.com/jakubpawlowicz/clean-css/issues/743) - background shorthand and source maps. * Fixed issue [#745](https://github.com/jakubpawlowicz/clean-css/issues/745) - matching mixed case `!important`. diff --git a/lib/utils/quote-scanner.js b/lib/utils/quote-scanner.js index 69980f54..1810b9ec 100644 --- a/lib/utils/quote-scanner.js +++ b/lib/utils/quote-scanner.js @@ -1,15 +1,16 @@ +var COMMENT_START_MARK = '/*'; + function QuoteScanner(data) { this.data = data; } var findQuoteEnd = function (data, matched, cursor, oldCursor) { - var commentStartMark = '/*'; var commentEndMark = '*/'; var escapeMark = '\\'; var blockEndMark = '}'; var dataPrefix = data.substring(oldCursor, cursor); var commentEndedAt = dataPrefix.lastIndexOf(commentEndMark, cursor); - var commentStartedAt = dataPrefix.lastIndexOf(commentStartMark, cursor); + var commentStartedAt = findLastCommentStartedAt(dataPrefix, cursor); var commentStarted = false; if (commentEndedAt >= cursor && commentStartedAt > -1) @@ -38,6 +39,22 @@ var findQuoteEnd = function (data, matched, cursor, oldCursor) { return cursor; }; +function findLastCommentStartedAt(data, cursor) { + var position = cursor; + + while (position > -1) { + position = data.lastIndexOf(COMMENT_START_MARK, position); + + if (position > -1 && data[position - 1] != '*') { + break; + } else { + position--; + } + } + + return position; +} + function findNext(data, mark, startAt) { var escapeMark = '\\'; var candidate = startAt; diff --git a/test/integration-test.js b/test/integration-test.js index 4ea1d867..e2c902fb 100644 --- a/test/integration-test.js +++ b/test/integration-test.js @@ -425,6 +425,10 @@ vows.describe('integration tests') 'two important after value': [ 'div{color:red;/*!1*//*!2*/}', 'div{color:red/*!1*//*!2*/}' + ], + 'two comments, general selector right after first, and quotes': [ + '/*! comment */*{box-sizing:border-box}div:before{content:" "}/*! @comment */div{display:inline-block}', + '/*! comment */*{box-sizing:border-box}div:before{content:" "}/*! @comment */div{display:inline-block}' ] }) ) @@ -2231,7 +2235,7 @@ vows.describe('integration tests') 'a{' + lineBreak + '@apply(--rule1);' + lineBreak + ' @apply(--rule2);' + lineBreak + 'color:red;display:block}', 'a{@apply(--rule1);@apply(--rule2);color:red;display:block}' ], - '@apply another rule within :root context 123': [ + '@apply another rule within :root context': [ ':root{--layout:{display:flex};--layout-horizontal:{@apply(--layout)};}', ':root{--layout:{display:flex};--layout-horizontal:{@apply(--layout)};}' ] diff --git a/test/text/comments-processor-test.js b/test/text/comments-processor-test.js index 4e851f4f..2b92e3c7 100644 --- a/test/text/comments-processor-test.js +++ b/test/text/comments-processor-test.js @@ -67,6 +67,11 @@ vows.describe(CommentsProcessor) '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0____ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1__', '/*! one text *//*! another text */' ], + 'two special comments when second with @ sign and preceded by quote sign': [ + '/*! comment 1 */*{box-sizing: border-box}div:before{content:" "}/*! comment 2 */div{display:inline-block}', + '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__*{box-sizing: border-box}div:before{content:" "}__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1__div{display:inline-block}', + '/*! comment 1 */*{box-sizing: border-box}div:before{content:" "}/*! comment 2 */div{display:inline-block}' + ], 'commented selector': [ '/* a{color:red} */', '', diff --git a/test/utils/quote-scanner-test.js b/test/utils/quote-scanner-test.js index a1e47091..ac809725 100644 --- a/test/utils/quote-scanner-test.js +++ b/test/utils/quote-scanner-test.js @@ -115,6 +115,19 @@ vows.describe(QuoteScanner) assert.equal(index, 2); } - } + }, + 'between comments': { + topic: '/*! comment */*{box-sizing:border-box}div:before{content:" "}/*! @comment */', + iterator: function (topic) { + var index = 0; + new QuoteScanner(topic).each(function iterator(match) { + index++; + + assert.equal(match, '" "'); + }); + + assert.equal(index, 1); + } + }, }) .export(module); -- 2.34.1