| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 | var List = require('css-tree').List;var walk = require('css-tree').walk;var utils = require('./utils');function calcSelectorLength(list) {    var length = 0;    list.each(function(data) {        length += data.id.length + 1;    });    return length - 1;}function calcDeclarationsLength(tokens) {    var length = 0;    for (var i = 0; i < tokens.length; i++) {        length += tokens[i].length;    }    return (        length +          // declarations        tokens.length - 1 // delimeters    );}function processRule(node, item, list) {    var avoidRulesMerge = this.block !== null ? this.block.avoidRulesMerge : false;    var selectors = node.prelude.children;    var block = node.block;    var disallowDownMarkers = Object.create(null);    var allowMergeUp = true;    var allowMergeDown = true;    list.prevUntil(item.prev, function(prev, prevItem) {        var prevBlock = prev.block;        var prevType = prev.type;        if (prevType !== 'Rule') {            var unsafe = utils.unsafeToSkipNode.call(selectors, prev);            if (!unsafe && prevType === 'Atrule' && prevBlock) {                walk(prevBlock, {                    visit: 'Rule',                    enter: function(node) {                        node.prelude.children.each(function(data) {                            disallowDownMarkers[data.compareMarker] = true;                        });                    }                });            }            return unsafe;        }        var prevSelectors = prev.prelude.children;        if (node.pseudoSignature !== prev.pseudoSignature) {            return true;        }        allowMergeDown = !prevSelectors.some(function(selector) {            return selector.compareMarker in disallowDownMarkers;        });        // try prev ruleset if simpleselectors has no equal specifity and element selector        if (!allowMergeDown && !allowMergeUp) {            return true;        }        // try to join by selectors        if (allowMergeUp && utils.isEqualSelectors(prevSelectors, selectors)) {            prevBlock.children.appendList(block.children);            list.remove(item);            return true;        }        // try to join by properties        var diff = utils.compareDeclarations(block.children, prevBlock.children);        // console.log(diff.eq, diff.ne1, diff.ne2);        if (diff.eq.length) {            if (!diff.ne1.length && !diff.ne2.length) {                // equal blocks                if (allowMergeDown) {                    utils.addSelectors(selectors, prevSelectors);                    list.remove(prevItem);                }                return true;            } else if (!avoidRulesMerge) { /* probably we don't need to prevent those merges for @keyframes                                              TODO: need to be checked */                if (diff.ne1.length && !diff.ne2.length) {                    // prevBlock is subset block                    var selectorLength = calcSelectorLength(selectors);                    var blockLength = calcDeclarationsLength(diff.eq); // declarations length                    if (allowMergeUp && selectorLength < blockLength) {                        utils.addSelectors(prevSelectors, selectors);                        block.children = new List().fromArray(diff.ne1);                    }                } else if (!diff.ne1.length && diff.ne2.length) {                    // node is subset of prevBlock                    var selectorLength = calcSelectorLength(prevSelectors);                    var blockLength = calcDeclarationsLength(diff.eq); // declarations length                    if (allowMergeDown && selectorLength < blockLength) {                        utils.addSelectors(selectors, prevSelectors);                        prevBlock.children = new List().fromArray(diff.ne2);                    }                } else {                    // diff.ne1.length && diff.ne2.length                    // extract equal block                    var newSelector = {                        type: 'SelectorList',                        loc: null,                        children: utils.addSelectors(prevSelectors.copy(), selectors)                    };                    var newBlockLength = calcSelectorLength(newSelector.children) + 2; // selectors length + curly braces length                    var blockLength = calcDeclarationsLength(diff.eq); // declarations length                    // create new ruleset if declarations length greater than                    // ruleset description overhead                    if (blockLength >= newBlockLength) {                        var newItem = list.createItem({                            type: 'Rule',                            loc: null,                            prelude: newSelector,                            block: {                                type: 'Block',                                loc: null,                                children: new List().fromArray(diff.eq)                            },                            pseudoSignature: node.pseudoSignature                        });                        block.children = new List().fromArray(diff.ne1);                        prevBlock.children = new List().fromArray(diff.ne2overrided);                        if (allowMergeUp) {                            list.insert(newItem, prevItem);                        } else {                            list.insert(newItem, item);                        }                        return true;                    }                }            }        }        if (allowMergeUp) {            // TODO: disallow up merge only if any property interception only (i.e. diff.ne2overrided.length > 0);            // await property families to find property interception correctly            allowMergeUp = !prevSelectors.some(function(prevSelector) {                return selectors.some(function(selector) {                    return selector.compareMarker === prevSelector.compareMarker;                });            });        }        prevSelectors.each(function(data) {            disallowDownMarkers[data.compareMarker] = true;        });    });}module.exports = function restructRule(ast) {    walk(ast, {        visit: 'Rule',        reverse: true,        enter: processRule    });};
 |