Skip to content

Commit

Permalink
fix: Discarding used font-families due to mixed quotation types
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonathan Felchlin committed Mar 28, 2019
1 parent 8d4610a commit b7dbc23
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 17 deletions.
30 changes: 27 additions & 3 deletions packages/postcss-discard-unused/src/__tests__/index.js
Expand Up @@ -86,10 +86,34 @@ test(
);

test(
'should not be responsible for normalising fonts',
processCSS,
'should normalize fonts',
passthroughCSS,
'@font-face {font-family:"Does Exist";src:url("fonts/does-exist.ttf") format("truetype")}body{font-family:Does Exist}',
'body{font-family:Does Exist}'
);

test(
'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 work spaces in font lists',
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", Helvetica, Arial, sans-serif; font: 10px/1.5 DoesExist, Helvetica, Arial, sans-serif }'
);

test(
'should work with weird font names',
passthroughCSS,
'@font-face {font-family:"ൠ😳ฬ𝔢IяĎ 🐉💩👍Iñtërnâtiônàlizætiøn☃💩";src: url("fonts/weird.woff") format("truetype")}body {font-family: "ൠ😳ฬ𝔢IяĎ 🐉💩👍Iñtërnâtiônàlizætiøn☃💩"}'
);

test(
"should handle font shorthands that don't include a font-family",
processCSS,
'@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(
Expand Down
56 changes: 42 additions & 14 deletions packages/postcss-discard-unused/src/index.js
Expand Up @@ -8,6 +8,40 @@ const atrule = 'atrule';
const decl = 'decl';
const rule = 'rule';

const FONT_SIZE_KEYWORDS = [
'(x{1,2}-)?(small|large)',
'(larg|small)er',
'medium',
'auto',
'inherit',
'initial',
'unset',
];
const SYSTEM_FONTS = [
'caption',
'icon',
'menu',
'message-box',
'small-caption',
'status-bar',
];
const CSS_UNITS_RE = '[+-]?\\d+(\\.\\d*)?(px|em|ex|%|in|cm|mm|pt|pc)?';
// Matches all properties in a font shorthand value through the font-size,
// which is required and is always the last value before the font-family
const MATCH_NON_FONT_FAMILY_PROPERTIES = new RegExp(
`(.*(^| )(${FONT_SIZE_KEYWORDS.join('|')}|${CSS_UNITS_RE})( *\\/ *${CSS_UNITS_RE})?|` +
`(${SYSTEM_FONTS.join('|')})?( |$))`
);

function getFontFamilyFromShorthand (font) {
return font.replace(MATCH_NON_FONT_FAMILY_PROPERTIES, '');
}

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);
}
Expand Down Expand Up @@ -36,24 +70,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();
}
});
});
}

Expand Down Expand Up @@ -94,7 +119,10 @@ 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 = comma(
prop === 'font' ? getFontFamilyFromShorthand(node.value) : node.value
);
fontCache.values.push(...fontFamilies.map(normalizeFontName));
}
if (keyframes && /animation/.test(prop)) {
keyframesCache.values = addValues(keyframesCache.values, node);
Expand Down

0 comments on commit b7dbc23

Please sign in to comment.