diff options
Diffstat (limited to 'node_modules/csso/lib/compressor/restructure/8-restructRuleset.js')
| -rw-r--r-- | node_modules/csso/lib/compressor/restructure/8-restructRuleset.js | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/node_modules/csso/lib/compressor/restructure/8-restructRuleset.js b/node_modules/csso/lib/compressor/restructure/8-restructRuleset.js new file mode 100644 index 00000000..9a9e545f --- /dev/null +++ b/node_modules/csso/lib/compressor/restructure/8-restructRuleset.js @@ -0,0 +1,157 @@ +var List = require('../../utils/list.js'); +var utils = require('./utils.js'); +var walkRulesRight = require('../../utils/walk.js').rulesRight; + +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 processRuleset(node, item, list) { + var avoidRulesMerge = this.stylesheet.avoidRulesMerge; + var selectors = node.selector.selectors; + 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 !== 'Ruleset') { + return utils.unsafeToSkipNode.call(selectors, prev); + } + + var prevSelectors = prev.selector.selectors; + 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.isEqualLists(prevSelectors, selectors)) { + prevBlock.declarations.appendList(block.declarations); + list.remove(item); + return true; + } + + // try to join by properties + var diff = utils.compareDeclarations(block.declarations, prevBlock.declarations); + + // 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.declarations = new List(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.declarations = new List(diff.ne2); + } + } else { + // diff.ne1.length && diff.ne2.length + // extract equal block + var newSelector = { + type: 'Selector', + info: {}, + selectors: utils.addSelectors(prevSelectors.copy(), selectors) + }; + var newBlockLength = calcSelectorLength(newSelector.selectors) + 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 newRuleset = { + type: 'Ruleset', + info: {}, + pseudoSignature: node.pseudoSignature, + selector: newSelector, + block: { + type: 'Block', + info: {}, + declarations: new List(diff.eq) + } + }; + + block.declarations = new List(diff.ne1); + prevBlock.declarations = new List(diff.ne2.concat(diff.ne2overrided)); + list.insert(list.createItem(newRuleset), 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 restructRuleset(ast) { + walkRulesRight(ast, function(node, item, list) { + if (node.type === 'Ruleset') { + processRuleset.call(this, node, item, list); + } + }); +}; |
