From ccb8af4e1e1efed6c896066cdc55544e5ced0ecd Mon Sep 17 00:00:00 2001 From: Jakub Pawlowicz Date: Tue, 27 Dec 2016 15:12:28 +0100 Subject: [PATCH] Fixes #785 - adds `@font-face` de-duplication. 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 | 1 + lib/optimizer/advanced.js | 6 ++++ .../remove-duplicate-font-at-rules.js | 30 +++++++++++++++++++ .../remove-duplicate-font-at-rules-test.js | 29 ++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 lib/optimizer/remove-duplicate-font-at-rules.js create mode 100644 test/optimizer/remove-duplicate-font-at-rules-test.js diff --git a/History.md b/History.md index 9c2aeb57..3e85a38f 100644 --- a/History.md +++ b/History.md @@ -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. diff --git a/lib/optimizer/advanced.js b/lib/optimizer/advanced.js index d8091475..8a74d8b5 100644 --- a/lib/optimizer/advanced.js +++ b/lib/optimizer/advanced.js @@ -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 index 00000000..5b0eb515 --- /dev/null +++ b/lib/optimizer/remove-duplicate-font-at-rules.js @@ -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 index 00000000..8f79ff1b --- /dev/null +++ b/test/optimizer/remove-duplicate-font-at-rules-test.js @@ -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); -- 2.34.1