[6a3a178] | 1 | var List = require('css-tree').List;
|
---|
| 2 | var walk = require('css-tree').walk;
|
---|
| 3 | var utils = require('./utils');
|
---|
| 4 |
|
---|
| 5 | function calcSelectorLength(list) {
|
---|
| 6 | var length = 0;
|
---|
| 7 |
|
---|
| 8 | list.each(function(data) {
|
---|
| 9 | length += data.id.length + 1;
|
---|
| 10 | });
|
---|
| 11 |
|
---|
| 12 | return length - 1;
|
---|
| 13 | }
|
---|
| 14 |
|
---|
| 15 | function calcDeclarationsLength(tokens) {
|
---|
| 16 | var length = 0;
|
---|
| 17 |
|
---|
| 18 | for (var i = 0; i < tokens.length; i++) {
|
---|
| 19 | length += tokens[i].length;
|
---|
| 20 | }
|
---|
| 21 |
|
---|
| 22 | return (
|
---|
| 23 | length + // declarations
|
---|
| 24 | tokens.length - 1 // delimeters
|
---|
| 25 | );
|
---|
| 26 | }
|
---|
| 27 |
|
---|
| 28 | function processRule(node, item, list) {
|
---|
| 29 | var avoidRulesMerge = this.block !== null ? this.block.avoidRulesMerge : false;
|
---|
| 30 | var selectors = node.prelude.children;
|
---|
| 31 | var block = node.block;
|
---|
| 32 | var disallowDownMarkers = Object.create(null);
|
---|
| 33 | var allowMergeUp = true;
|
---|
| 34 | var allowMergeDown = true;
|
---|
| 35 |
|
---|
| 36 | list.prevUntil(item.prev, function(prev, prevItem) {
|
---|
| 37 | var prevBlock = prev.block;
|
---|
| 38 | var prevType = prev.type;
|
---|
| 39 |
|
---|
| 40 | if (prevType !== 'Rule') {
|
---|
| 41 | var unsafe = utils.unsafeToSkipNode.call(selectors, prev);
|
---|
| 42 |
|
---|
| 43 | if (!unsafe && prevType === 'Atrule' && prevBlock) {
|
---|
| 44 | walk(prevBlock, {
|
---|
| 45 | visit: 'Rule',
|
---|
| 46 | enter: function(node) {
|
---|
| 47 | node.prelude.children.each(function(data) {
|
---|
| 48 | disallowDownMarkers[data.compareMarker] = true;
|
---|
| 49 | });
|
---|
| 50 | }
|
---|
| 51 | });
|
---|
| 52 | }
|
---|
| 53 |
|
---|
| 54 | return unsafe;
|
---|
| 55 | }
|
---|
| 56 |
|
---|
| 57 | var prevSelectors = prev.prelude.children;
|
---|
| 58 |
|
---|
| 59 | if (node.pseudoSignature !== prev.pseudoSignature) {
|
---|
| 60 | return true;
|
---|
| 61 | }
|
---|
| 62 |
|
---|
| 63 | allowMergeDown = !prevSelectors.some(function(selector) {
|
---|
| 64 | return selector.compareMarker in disallowDownMarkers;
|
---|
| 65 | });
|
---|
| 66 |
|
---|
| 67 | // try prev ruleset if simpleselectors has no equal specifity and element selector
|
---|
| 68 | if (!allowMergeDown && !allowMergeUp) {
|
---|
| 69 | return true;
|
---|
| 70 | }
|
---|
| 71 |
|
---|
| 72 | // try to join by selectors
|
---|
| 73 | if (allowMergeUp && utils.isEqualSelectors(prevSelectors, selectors)) {
|
---|
| 74 | prevBlock.children.appendList(block.children);
|
---|
| 75 | list.remove(item);
|
---|
| 76 | return true;
|
---|
| 77 | }
|
---|
| 78 |
|
---|
| 79 | // try to join by properties
|
---|
| 80 | var diff = utils.compareDeclarations(block.children, prevBlock.children);
|
---|
| 81 |
|
---|
| 82 | // console.log(diff.eq, diff.ne1, diff.ne2);
|
---|
| 83 |
|
---|
| 84 | if (diff.eq.length) {
|
---|
| 85 | if (!diff.ne1.length && !diff.ne2.length) {
|
---|
| 86 | // equal blocks
|
---|
| 87 | if (allowMergeDown) {
|
---|
| 88 | utils.addSelectors(selectors, prevSelectors);
|
---|
| 89 | list.remove(prevItem);
|
---|
| 90 | }
|
---|
| 91 |
|
---|
| 92 | return true;
|
---|
| 93 | } else if (!avoidRulesMerge) { /* probably we don't need to prevent those merges for @keyframes
|
---|
| 94 | TODO: need to be checked */
|
---|
| 95 |
|
---|
| 96 | if (diff.ne1.length && !diff.ne2.length) {
|
---|
| 97 | // prevBlock is subset block
|
---|
| 98 | var selectorLength = calcSelectorLength(selectors);
|
---|
| 99 | var blockLength = calcDeclarationsLength(diff.eq); // declarations length
|
---|
| 100 |
|
---|
| 101 | if (allowMergeUp && selectorLength < blockLength) {
|
---|
| 102 | utils.addSelectors(prevSelectors, selectors);
|
---|
| 103 | block.children = new List().fromArray(diff.ne1);
|
---|
| 104 | }
|
---|
| 105 | } else if (!diff.ne1.length && diff.ne2.length) {
|
---|
| 106 | // node is subset of prevBlock
|
---|
| 107 | var selectorLength = calcSelectorLength(prevSelectors);
|
---|
| 108 | var blockLength = calcDeclarationsLength(diff.eq); // declarations length
|
---|
| 109 |
|
---|
| 110 | if (allowMergeDown && selectorLength < blockLength) {
|
---|
| 111 | utils.addSelectors(selectors, prevSelectors);
|
---|
| 112 | prevBlock.children = new List().fromArray(diff.ne2);
|
---|
| 113 | }
|
---|
| 114 | } else {
|
---|
| 115 | // diff.ne1.length && diff.ne2.length
|
---|
| 116 | // extract equal block
|
---|
| 117 | var newSelector = {
|
---|
| 118 | type: 'SelectorList',
|
---|
| 119 | loc: null,
|
---|
| 120 | children: utils.addSelectors(prevSelectors.copy(), selectors)
|
---|
| 121 | };
|
---|
| 122 | var newBlockLength = calcSelectorLength(newSelector.children) + 2; // selectors length + curly braces length
|
---|
| 123 | var blockLength = calcDeclarationsLength(diff.eq); // declarations length
|
---|
| 124 |
|
---|
| 125 | // create new ruleset if declarations length greater than
|
---|
| 126 | // ruleset description overhead
|
---|
| 127 | if (blockLength >= newBlockLength) {
|
---|
| 128 | var newItem = list.createItem({
|
---|
| 129 | type: 'Rule',
|
---|
| 130 | loc: null,
|
---|
| 131 | prelude: newSelector,
|
---|
| 132 | block: {
|
---|
| 133 | type: 'Block',
|
---|
| 134 | loc: null,
|
---|
| 135 | children: new List().fromArray(diff.eq)
|
---|
| 136 | },
|
---|
| 137 | pseudoSignature: node.pseudoSignature
|
---|
| 138 | });
|
---|
| 139 |
|
---|
| 140 | block.children = new List().fromArray(diff.ne1);
|
---|
| 141 | prevBlock.children = new List().fromArray(diff.ne2overrided);
|
---|
| 142 |
|
---|
| 143 | if (allowMergeUp) {
|
---|
| 144 | list.insert(newItem, prevItem);
|
---|
| 145 | } else {
|
---|
| 146 | list.insert(newItem, item);
|
---|
| 147 | }
|
---|
| 148 |
|
---|
| 149 | return true;
|
---|
| 150 | }
|
---|
| 151 | }
|
---|
| 152 | }
|
---|
| 153 | }
|
---|
| 154 |
|
---|
| 155 | if (allowMergeUp) {
|
---|
| 156 | // TODO: disallow up merge only if any property interception only (i.e. diff.ne2overrided.length > 0);
|
---|
| 157 | // await property families to find property interception correctly
|
---|
| 158 | allowMergeUp = !prevSelectors.some(function(prevSelector) {
|
---|
| 159 | return selectors.some(function(selector) {
|
---|
| 160 | return selector.compareMarker === prevSelector.compareMarker;
|
---|
| 161 | });
|
---|
| 162 | });
|
---|
| 163 | }
|
---|
| 164 |
|
---|
| 165 | prevSelectors.each(function(data) {
|
---|
| 166 | disallowDownMarkers[data.compareMarker] = true;
|
---|
| 167 | });
|
---|
| 168 | });
|
---|
| 169 | }
|
---|
| 170 |
|
---|
| 171 | module.exports = function restructRule(ast) {
|
---|
| 172 | walk(ast, {
|
---|
| 173 | visit: 'Rule',
|
---|
| 174 | reverse: true,
|
---|
| 175 | enter: processRule
|
---|
| 176 | });
|
---|
| 177 | };
|
---|