Fixes #785 - adds `@font-face` de-duplication.
authorJakub Pawlowicz <contact@jakubpawlowicz.com>
Tue, 27 Dec 2016 14:12:28 +0000 (15:12 +0100)
committerJakub Pawlowicz <contact@jakubpawlowicz.com>
Tue, 27 Dec 2016 14:13:15 +0000 (15:13 +0100)
Why:

* Extra `@font-face` rules can be safely removed.
* There's no API/CLI switch to turn it off as it's pending
  a refactoring in v4 - see #842.

History.md
lib/optimizer/advanced.js
lib/optimizer/remove-duplicate-font-at-rules.js [new file with mode: 0644]
test/optimizer/remove-duplicate-font-at-rules-test.js [new file with mode: 0644]

index 9c2aeb5..3e85a38 100644 (file)
@@ -16,6 +16,7 @@
 * Fixed issue [#756](https://github.com/jakubpawlowicz/clean-css/issues/756) - adds disabling font-weight optimizations.
 * Fixed issue [#758](https://github.com/jakubpawlowicz/clean-css/issues/758) - ignores rules with empty selector.
 * Fixed issue [#767](https://github.com/jakubpawlowicz/clean-css/issues/767) - disables remote `@import` inlining by default.
+* Fixed issue [#785](https://github.com/jakubpawlowicz/clean-css/issues/785) - adds `@font-face` de-duplication.
 * Fixed issue [#791](https://github.com/jakubpawlowicz/clean-css/issues/791) - resolves imports in-memory if possible.
 * Fixed issue [#801](https://github.com/jakubpawlowicz/clean-css/issues/801) - smarter `@import` inlining.
 * Fixed issue [#817](https://github.com/jakubpawlowicz/clean-css/issues/817) - makes `off` disable rounding.
index d809147..8a74d8b 100644 (file)
@@ -6,6 +6,7 @@ var reduceNonAdjacent = require('./reduce-non-adjacent');
 var mergeNonAdjacentBySelector = require('./merge-non-adjacent-by-selector');
 var mergeNonAdjacentByBody = require('./merge-non-adjacent-by-body');
 var restructure = require('./restructure');
+var removeDuplicateFontAtRules = require('./remove-duplicate-font-at-rules');
 var removeDuplicateMediaQueries = require('./remove-duplicate-media-queries');
 var mergeMediaQueries = require('./merge-media-queries');
 
@@ -23,6 +24,9 @@ function removeEmpty(tokens) {
       case Token.BLOCK:
         removeEmpty(token[2]);
         isEmpty = token[2].length === 0;
+        break;
+      case Token.AT_RULE_BLOCK:
+        isEmpty = token[2].length === 0;
     }
 
     if (isEmpty) {
@@ -74,6 +78,8 @@ function optimize(tokens, context, withRestructuring) {
     mergeAdjacent(tokens, context);
   }
 
+  removeDuplicateFontAtRules(tokens, context);
+
   if (context.options.mediaMerging) {
     removeDuplicateMediaQueries(tokens, context);
     var reduced = mergeMediaQueries(tokens, context);
diff --git a/lib/optimizer/remove-duplicate-font-at-rules.js b/lib/optimizer/remove-duplicate-font-at-rules.js
new file mode 100644 (file)
index 0000000..5b0eb51
--- /dev/null
@@ -0,0 +1,30 @@
+var serializeAll = require('../writer/one-time').all;
+
+var Token = require('../tokenizer/token');
+
+var FONT_FACE_SCOPE = '@font-face';
+
+function removeDuplicateFontAtRules(tokens) {
+  var fontAtRules = [];
+  var token;
+  var key;
+  var i, l;
+
+  for (i = 0, l = tokens.length; i < l; i++) {
+    token = tokens[i];
+
+    if (token[0] != Token.AT_RULE_BLOCK && token[1][0][1] != FONT_FACE_SCOPE) {
+      continue;
+    }
+
+    key = serializeAll([token]);
+
+    if (fontAtRules.indexOf(key) > -1) {
+      token[2] = [];
+    } else {
+      fontAtRules.push(key);
+    }
+  }
+}
+
+module.exports = removeDuplicateFontAtRules;
diff --git a/test/optimizer/remove-duplicate-font-at-rules-test.js b/test/optimizer/remove-duplicate-font-at-rules-test.js
new file mode 100644 (file)
index 0000000..8f79ff1
--- /dev/null
@@ -0,0 +1,29 @@
+var vows = require('vows');
+var optimizerContext = require('../test-helper').optimizerContext;
+
+vows.describe('remove duplicate @font-face at-rules')
+  .addBatch(
+    optimizerContext('advanced on', {
+      'adjacent': [
+        '@font-face{font-family:test;src:url(fonts/test.woff2)}@font-face{font-family:test;src:url(fonts/test.woff2)}',
+        '@font-face{font-family:test;src:url(fonts/test.woff2)}'
+      ],
+      'non-adjacent': [
+        '@font-face{font-family:test;src:url(fonts/test.woff2)}.one{color:red}@font-face{font-family:test;src:url(fonts/test.woff2)}',
+        '@font-face{font-family:test;src:url(fonts/test.woff2)}.one{color:red}'
+      ],
+      'non-mergeable': [
+        '@font-face{font-family:test;src:url(fonts/test.woff2)}.one{color:#000}@font-face{font-family:test2;src:url(fonts/test.woff2)}',
+        '@font-face{font-family:test;src:url(fonts/test.woff2)}.one{color:#000}@font-face{font-family:test2;src:url(fonts/test.woff2)}'
+      ]
+    })
+  )
+  .addBatch(
+    optimizerContext('advanced off', {
+      'keeps content same': [
+        '@font-face{font-family:test;src:url(fonts/test.woff2)}@font-face{font-family:test;src:url(fonts/test.woff2)}',
+        '@font-face{font-family:test;src:url(fonts/test.woff2)}@font-face{font-family:test;src:url(fonts/test.woff2)}'
+      ]
+    }, { advanced: false })
+  )
+  .export(module);