Fixes #738 - edge case in comment processing.
authorJakub Pawlowicz <contact@jakubpawlowicz.com>
Fri, 1 Apr 2016 20:39:43 +0000 (22:39 +0200)
committerJakub Pawlowicz <contact@jakubpawlowicz.com>
Fri, 1 Apr 2016 20:49:48 +0000 (22:49 +0200)
Second `/*` in `/*! comment */*{...}` was taken as a comment start
which actually is not.

History.md
lib/utils/quote-scanner.js
test/integration-test.js
test/text/comments-processor-test.js
test/utils/quote-scanner-test.js

index eaf435e..9aaf201 100644 (file)
@@ -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`.
index 69980f5..1810b9e 100644 (file)
@@ -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;
index 4ea1d86..e2c902f 100644 (file)
@@ -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)};}'
       ]
index 4e851f4..2b92e3c 100644 (file)
@@ -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} */',
         '',
index a1e4709..ac80972 100644 (file)
@@ -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);