From 30a47e199a990c2fb6f7371490170dc7231d875c Mon Sep 17 00:00:00 2001 From: Jonathan Felchlin Date: Tue, 26 Mar 2019 22:30:48 -0700 Subject: [PATCH] fix: Discarding used font-families due to mixed quotation types --- .../src/__tests__/index.js | 18 ++++++++----- packages/postcss-discard-unused/src/index.js | 26 +++++++++---------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/packages/postcss-discard-unused/src/__tests__/index.js b/packages/postcss-discard-unused/src/__tests__/index.js index 56bb24be5..dc31214ed 100644 --- a/packages/postcss-discard-unused/src/__tests__/index.js +++ b/packages/postcss-discard-unused/src/__tests__/index.js @@ -75,21 +75,27 @@ test( test( 'should remove unused fonts & keep used fonts', processCSS, - '@font-face {font-family:"Does Not Exist";src:url("fonts/does-not-exist.ttf") format("truetype")}@font-face {font-family:"Does Exist";src: url("fonts/does-exist.ttf") format("truetype")}body{font-family:"Does Exist",Helvetica,Arial,sans-serif}', - '@font-face {font-family:"Does Exist";src: url("fonts/does-exist.ttf") format("truetype")}body{font-family:"Does Exist",Helvetica,Arial,sans-serif}' + '@font-face {font-family:"Does Not Exist";src:url("fonts/does-not-exist.ttf") format("truetype")}@font-face {font-family:"Does Exist";src: url("fonts/does-exist.ttf") format("truetype")}body{font-family: "Does Exist", Helvetica, Arial, sans-serif}', + '@font-face {font-family:"Does Exist";src: url("fonts/does-exist.ttf") format("truetype")}body{font-family: "Does Exist", Helvetica, Arial, sans-serif}' ); test( 'should work with the font shorthand', passthroughCSS, - '@font-face {font-family:"Does Exist";src: url("fonts/does-exist.ttf") format("truetype")}body{font: 10px/1.5 "Does Exist",Helvetica,Arial,sans-serif}' + '@font-face {font-family:"Does Exist";src: url("fonts/does-exist.ttf") format("truetype")}body{font: 10px/1.5 "Does Exist", Helvetica, Arial, sans-serif}' ); test( - 'should not be responsible for normalising fonts', + 'should work with mixed quote styles', + passthroughCSS, + '@font-face {font-family:"Does Exist";src:url("fonts/does-exist.ttf") format("truetype")}@font-face {font-family:\'DoesExist\';src:url("fonts/does-exist.ttf") format("truetype")}body{font-family: Does Exist; font: 10px DoesExist}' +); + +test( + "should handle font properties that don't include a font-family", processCSS, - '@font-face {font-family:"Does Exist";src:url("fonts/does-exist.ttf") format("truetype")}body{font-family:Does Exist}', - 'body{font-family:Does Exist}' + '@font-face {font-family:"Does Exist";src: url("fonts/does-exist.ttf") format("truetype")}body{font: italic bold 10px/1.5}', + 'body{font: italic bold 10px/1.5}' ); test( diff --git a/packages/postcss-discard-unused/src/index.js b/packages/postcss-discard-unused/src/index.js index 7bc03b6ee..edca0a10f 100644 --- a/packages/postcss-discard-unused/src/index.js +++ b/packages/postcss-discard-unused/src/index.js @@ -8,6 +8,11 @@ const atrule = 'atrule'; const decl = 'decl'; const rule = 'rule'; +function normalizeFontName (value) { + // Ignore casing and wrapping quotes when checking for font use. + return value.toLowerCase().replace(/^\s*(['"])(.*?)(\1)\s*$/, '$2'); +} + function addValues (cache, {value}) { return comma(value).reduce((memo, val) => [...memo, ...space(val)], cache); } @@ -36,24 +41,15 @@ function filterNamespace ({atRules, rules}) { }); } -function hasFont (fontFamily, cache) { - return comma(fontFamily).some(font => cache.some(c => ~c.indexOf(font))); -} - // fonts have slightly different logic function filterFont ({atRules, values}) { values = uniqs(values); atRules.forEach(r => { - const families = r.nodes.filter(({prop}) => prop === 'font-family'); - // Discard the @font-face if it has no font-family - if (!families.length) { - return r.remove(); + const fontFamilyRule = r.nodes.find(({prop}) => prop === 'font-family'); + // Discard the @font-face if it has no font-family rule or if it is unused + if (!fontFamilyRule || !values.includes(normalizeFontName(fontFamilyRule.value))) { + r.remove(); } - families.forEach(family => { - if (!hasFont(family.value.toLowerCase(), values)) { - r.remove(); - } - }); }); } @@ -94,7 +90,9 @@ export default plugin('postcss-discard-unused', opts => { counterStyleCache.values = addValues(counterStyleCache.values, node); } if (fontFace && node.parent.type === rule && /font(|-family)/.test(prop)) { - fontCache.values = fontCache.values.concat(comma(node.value.toLowerCase())); + const fontFamilies = prop === 'font-family' ? comma(node.value) : + [].concat(...space(node.value).map(comma)); + fontCache.values.push(...fontFamilies.map(normalizeFontName)); } if (keyframes && /animation/.test(prop)) { keyframesCache.values = addValues(keyframesCache.values, node);