src/lib.js
function filterUnusedRules(pages, css, ignore, usedSelectors) {
    let ignoreNextRule = false,
        ignoreNextRulesStart = false,
        unusedRules = [],
        unusedRuleSelectors,
        usedRuleSelectors;
    /* Rule format:
     *  { selectors: [ '...', '...' ],
     *    declarations: [ { property: '...', value: '...' } ]
     *  },.
     * Two steps: filter the unused selectors for each rule,
     *            filter the rules with no selectors
     */
    ignoreNextRule = false;
    css.walk((rule) => {
        if (rule.type === 'comment') {
            if (/^!?\s?uncss:ignore start\s?$/.test(rule.text)) { // ignore next rules while using comment `/* uncss:ignore start */`
                ignoreNextRulesStart = true;
            } else if (/^!?\s?uncss:ignore end\s?$/.test(rule.text)) { // until `/* uncss:ignore end */` was found
                ignoreNextRulesStart = false;
            } else if (/^!?\s?uncss:ignore\s?$/.test(rule.text)) { // ignore next rule while using comment `/* uncss:ignore */`
                ignoreNextRule = true;
            }
        } else if (rule.type === 'rule') {
            if (rule.parent.type === 'atrule' && _.endsWith(rule.parent.name, 'keyframes')) {
                // Don't remove animation keyframes that have selector names of '30%' or 'to'
                return;
            }
            if (ignoreNextRulesStart) {
                ignore = ignore.concat(rule.selectors);
            } else if (ignoreNextRule) {
                ignoreNextRule = false;
                ignore = ignore.concat(rule.selectors);
            }

            usedRuleSelectors = filterUnusedSelectors(
                rule.selectors,
                ignore,
                usedSelectors
            );
            unusedRuleSelectors = rule.selectors.filter((selector) => usedRuleSelectors.indexOf(selector) < 0);
            if (unusedRuleSelectors && unusedRuleSelectors.length) {
                unusedRules.push({
                    type: 'rule',
                    selectors: unusedRuleSelectors,
                    position: rule.source
                });
            }
            if (usedRuleSelectors.length === 0) {
                rule.remove();
            } else {
                rule.selectors = usedRuleSelectors;
            }
        }
    });

    /* Filter the @media rules with no rules */
    filterEmptyAtRules(css);

    /* Filter unused @keyframes */
    filterKeyframes(css, unusedRules);

    return css;
}
function filterUnusedRules(pages, css, ignore, usedSelectors) {
    let ignoreNextRule = false,
        ignoreNextRulesStart = false,
        unusedRules = [],
        unusedRuleSelectors,
        usedRuleSelectors;
    /* Rule format:
     *  { selectors: [ '...', '...' ],
     *    declarations: [ { property: '...', value: '...' } ]
     *  },.
     * Two steps: filter the unused selectors for each rule,
     *            filter the rules with no selectors
     */
    ignoreNextRule = false;
    css.walk((rule) => {
        if (rule.type === 'comment') {
            if (/^!?\s?uncss:ignore start\s?$/.test(rule.text)) { // ignore next rules while using comment `/* uncss:ignore start */`
                ignoreNextRulesStart = true;
            } else if (/^!?\s?uncss:ignore end\s?$/.test(rule.text)) { // until `/* uncss:ignore end */` was found
                ignoreNextRulesStart = false;
            } else if (/^!?\s?uncss:ignore\s?$/.test(rule.text)) { // ignore next rule while using comment `/* uncss:ignore */`
                ignoreNextRule = true;
            }
        } else if (rule.type === 'rule') {
            if (rule.parent.type === 'atrule' && _.endsWith(rule.parent.name, 'keyframes')) {
                // Don't remove animation keyframes that have selector names of '30%' or 'to'
                return;
            }
            if (ignoreNextRulesStart) {
                ignore = ignore.concat(rule.selectors);
            } else if (ignoreNextRule) {
                ignoreNextRule = false;
                ignore = ignore.concat(rule.selectors);
            }

            usedRuleSelectors = filterUnusedSelectors(
                rule.selectors,
                ignore,
                usedSelectors
            );
            unusedRuleSelectors = rule.selectors.filter((selector) => usedRuleSelectors.indexOf(selector) < 0);
            if (unusedRuleSelectors && unusedRuleSelectors.length) {
                unusedRules.push({
                    type: 'rule',
                    selectors: unusedRuleSelectors,
                    position: rule.source
                });
            }
            if (usedRuleSelectors.length === 0) {
                rule.remove();
            } else {
                rule.selectors = usedRuleSelectors;
            }
        }
    });

    /* Filter the @media rules with no rules */
    filterEmptyAtRules(css);

    /* Filter unused @keyframes */
    filterKeyframes(css, unusedRules);

    return css;
}
Remove css rules not used in the dom
@param  {Array}  pages           List of jsdom pages
@param  {Object} css             The postcss.Root node
@param  {Array}  ignore          List of selectors to be ignored
@param  {Array}  usedSelectors   List of selectors that are found in {pages}
@return {Object}                 A css_parse-compatible stylesheet