UNPKG

uselesscss

Version:

Trim bloat from your CSS by only including rules that will actually be active.

70 lines (65 loc) 2.16 kB
import cssLib from 'css' import { parseDOM } from 'htmlparser2' import select from 'css-select' import cssesc from 'cssesc' /** * Useless(html, css) -> css */ export default function Useless (html, css) { var dom = parseDOM(html) return reduceCss(css) function reduceCss (css) { let ast = cssLib.parse(css || '') let candidateRules = ast.stylesheet.rules let activeRules = candidateRules.map((rule) => { switch (rule.type) { case 'rule': rule.selectors = rule.selectors.filter(filterSelector) return rule.selectors.length === 0 ? null : rule case 'media': rule.rules = rule.rules.filter((rule) => { return rule.type === 'rule' }).map((rule) => { rule.selectors = rule.selectors.filter(filterSelector) return rule.selectors.length === 0 ? null : rule }).filter((rule) => { return rule !== null && rule !== undefined }) return rule.rules.length === 0 ? null : rule case 'comment': // curently not including comments break case 'keyframes': return rule case 'font-face': return rule default: console.error(`Unknown rule! Add ${rule.type} as case to switch on!`) return rule } }).filter((rule) => { return rule !== null && rule !== undefined }) ast.stylesheet.rules = activeRules return cssLib.stringify(ast) } function filterSelector (selector) { let result = false let override = /(@|(:link)|(:visited)|:(active)|(:hover)|(:focus)|(:target)|(:lang)|(:disable)|(:enabled)|(:checked)|(::)|(:before)|(:after))/ if (override.test(selector)) { if (/::/.test(selector)) { selector = selector.substring(0, selector.lastIndexOf('::')) } else { selector = selector.substring(0, selector.lastIndexOf(':')) } } let selection try { selection = select(selector, dom) } catch (e) { selection = select(cssesc(selector, { isIdentifier: true }), dom) } result = selection.length > 0 return result } }