Fixes #449 - missing close brace in a selector.
authorJakub Pawlowicz <contact@jakubpawlowicz.com>
Sun, 1 Feb 2015 11:52:54 +0000 (11:52 +0000)
committerJakub Pawlowicz <contact@jakubpawlowicz.com>
Sun, 1 Feb 2015 19:01:23 +0000 (19:01 +0000)
Adds proper handling of missing closing brace in selectory body:

* warning is raised;
* extra content is ignored.

History.md
lib/selectors/tokenizer.js
lib/utils/extractors.js
test/integration-test.js
test/module-test.js
test/selectors/tokenizer-test.js

index e70067f..d279e01 100644 (file)
@@ -13,6 +13,7 @@
 * Fixed issue [#439](https://github.com/GoalSmashers/clean-css/issues/439) - `background-origin` in shorthand.
 * Fixed issue [#442](https://github.com/GoalSmashers/clean-css/issues/442) - space before adjacent `nav`.
 * Fixed issue [#445](https://github.com/GoalSmashers/clean-css/issues/445) - regression issue in url processor.
+* Fixed issue [#449](https://github.com/GoalSmashers/clean-css/issues/449) - warns of missing close braces.
 
 [3.0.8 / 2015-01-31](https://github.com/jakubpawlowicz/clean-css/compare/v3.0.7...v3.0.8)
 ==================
index 9839a5c..79e4947 100644 (file)
@@ -96,7 +96,11 @@ function tokenize(context) {
     if (!next) {
       var whatsLeft = context.chunk.substring(context.cursor);
       if (whatsLeft.trim().length > 0) {
-        tokenized.push({ kind: 'text', value: whatsLeft });
+        if (context.mode == 'body') {
+          context.outer.warnings.push('Missing \'}\' after \'' + whatsLeft + '\'. Ignoring.');
+        } else {
+          tokenized.push({ kind: 'text', value: whatsLeft });
+        }
         context.cursor += whatsLeft.length;
       }
       break;
index d8b4c47..e19c768 100644 (file)
@@ -50,12 +50,14 @@ var Extractors = {
           buffer.pop();
         if (buffer.length > 0) {
           property = buffer.join('');
-          token = { value: property };
-          tokenized.push(token);
-          list.push(property);
+          if (property.indexOf('{') === -1) {
+            token = { value: property };
+            tokenized.push(token);
+            list.push(property);
 
-          if (addSourceMap)
-            token.metadata = SourceMaps.saveAndTrack(all.join(''), context, !isEscape);
+            if (addSourceMap)
+              token.metadata = SourceMaps.saveAndTrack(all.join(''), context, !isEscape);
+          }
         }
         buffer = [];
         all = [];
@@ -89,12 +91,14 @@ var Extractors = {
       buffer.pop();
     if (buffer.length > 0) {
       property = buffer.join('');
-      token = { value: property };
-      tokenized.push(token);
-      list.push(property);
+      if (property.indexOf('{') === -1) {
+        token = { value: property };
+        tokenized.push(token);
+        list.push(property);
 
-      if (addSourceMap)
-        token.metadata = SourceMaps.saveAndTrack(all.join(''), context, false);
+        if (addSourceMap)
+          token.metadata = SourceMaps.saveAndTrack(all.join(''), context, false);
+      }
     } else if (all.indexOf('\n') > -1) {
       SourceMaps.track(all.join(''), context);
     }
index bf637b1..31ce082 100644 (file)
@@ -919,7 +919,7 @@ vows.describe('integration tests').addBatch({
     ],
     'cut off url content on top level': [
       '@font-face{src:url(data:application/x-font-woff;base64,d09GRk9UVE8AAENAAA0AAAAA',
-      '@font-face{src:url(data:application/x-font-woff;base64,d09GRk9UVE8AAENAAA0AAAAA}'
+      ''
     ],
     'strip single parentheses': [
       "a{background:url('/images/blank.png')}",
index ab4a823..3e32f2a 100644 (file)
@@ -116,6 +116,19 @@ vows.describe('module tests').addBatch({
       assert.equal(minified.warnings[0], 'Unexpected \'}\' in \'a{display:block}}\'. Ignoring.');
     }
   },
+  'warnings on missing closing brace': {
+    'topic': new CleanCSS().minify('a{display:block'),
+    'should minify correctly': function (error, minified) {
+      assert.equal(minified.styles, '');
+    },
+    'should raise no errors': function (error, minified) {
+      assert.isEmpty(minified.errors);
+    },
+    'should raise one warning': function (error, minified) {
+      assert.lengthOf(minified.warnings, 1);
+      assert.equal(minified.warnings[0], 'Missing \'}\' after \'display:block\'. Ignoring.');
+    }
+  },
   'warnings on unexpected body': {
     'topic': new CleanCSS().minify('a{display:block}color:#535353}p{color:red}'),
     'should minify correctly': function (error, minified) {
index 153e719..f3be522 100644 (file)
@@ -8,7 +8,7 @@ function tokenizerContext(name, specs, addMetadata) {
 
   function tokenized(target) {
     return function (source) {
-      var tokenized = new Tokenizer({ sourceTracker: new SourceTracker() }, addMetadata).toTokens(source);
+      var tokenized = new Tokenizer({ sourceTracker: new SourceTracker(), warnings: [] }, addMetadata).toTokens(source);
       assert.deepEqual(target, tokenized);
     };
   }
@@ -230,6 +230,26 @@ vows.describe(Tokenizer)
       ]
     })
   )
+  .addBatch(
+    tokenizerContext('broken', {
+      'missing end brace': [
+        'a{display:block',
+        [{
+          kind: 'selector',
+          value: [{ value: 'a' }],
+          body: []
+        }]
+      ],
+      'missing end brace in the middle': [
+        'body{color:red;a{color:blue;}',
+        [{
+          kind: 'selector',
+          value: [{ value: 'body' }],
+          body: [{ value: 'color:red' }]
+        }]
+      ]
+    })
+  )
   .addBatch(
     tokenizerContext('metadata', {
       'no content': [