You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							157 lines
						
					
					
						
							5.8 KiB
						
					
					
				
			
		
		
	
	
							157 lines
						
					
					
						
							5.8 KiB
						
					
					
				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) { | 
						|
        // skip non-ruleset node if safe | 
						|
        if (prev.type !== 'Rule') { | 
						|
            return utils.unsafeToSkipNode.call(selectors, prev); | 
						|
        } | 
						|
 | 
						|
        var prevSelectors = prev.prelude.children; | 
						|
        var prevBlock = prev.block; | 
						|
 | 
						|
        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 (allowMergeDown && blockLength >= newBlockLength) { | 
						|
                        var newRule = { | 
						|
                            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.ne2.concat(diff.ne2overrided)); | 
						|
                        list.insert(list.createItem(newRule), prevItem); | 
						|
                        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 | 
						|
    }); | 
						|
};
 | 
						|
 |