Spaces:
Configuration error
Configuration error
; | |
const cssTree = require('css-tree'); | |
const utils = require('./utils.cjs'); | |
function calcSelectorLength(list) { | |
return list.reduce((res, data) => res + data.id.length + 1, 0) - 1; | |
} | |
function calcDeclarationsLength(tokens) { | |
let length = 0; | |
for (const token of tokens) { | |
length += token.length; | |
} | |
return ( | |
length + // declarations | |
tokens.length - 1 // delimeters | |
); | |
} | |
function processRule(node, item, list) { | |
const avoidRulesMerge = this.block !== null ? this.block.avoidRulesMerge : false; | |
const selectors = node.prelude.children; | |
const block = node.block; | |
const disallowDownMarkers = Object.create(null); | |
let allowMergeUp = true; | |
let allowMergeDown = true; | |
list.prevUntil(item.prev, function(prev, prevItem) { | |
const prevBlock = prev.block; | |
const prevType = prev.type; | |
if (prevType !== 'Rule') { | |
const unsafe = utils.unsafeToSkipNode.call(selectors, prev); | |
if (!unsafe && prevType === 'Atrule' && prevBlock) { | |
cssTree.walk(prevBlock, { | |
visit: 'Rule', | |
enter(node) { | |
node.prelude.children.forEach((data) => { | |
disallowDownMarkers[data.compareMarker] = true; | |
}); | |
} | |
}); | |
} | |
return unsafe; | |
} | |
if (node.pseudoSignature !== prev.pseudoSignature) { | |
return true; | |
} | |
const prevSelectors = prev.prelude.children; | |
allowMergeDown = !prevSelectors.some((selector) => | |
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 | |
const 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 | |
const selectorLength = calcSelectorLength(selectors); | |
const blockLength = calcDeclarationsLength(diff.eq); // declarations length | |
if (allowMergeUp && selectorLength < blockLength) { | |
utils.addSelectors(prevSelectors, selectors); | |
block.children.fromArray(diff.ne1); | |
} | |
} else if (!diff.ne1.length && diff.ne2.length) { | |
// node is subset of prevBlock | |
const selectorLength = calcSelectorLength(prevSelectors); | |
const blockLength = calcDeclarationsLength(diff.eq); // declarations length | |
if (allowMergeDown && selectorLength < blockLength) { | |
utils.addSelectors(selectors, prevSelectors); | |
prevBlock.children.fromArray(diff.ne2); | |
} | |
} else { | |
// diff.ne1.length && diff.ne2.length | |
// extract equal block | |
const newSelector = { | |
type: 'SelectorList', | |
loc: null, | |
children: utils.addSelectors(prevSelectors.copy(), selectors) | |
}; | |
const newBlockLength = calcSelectorLength(newSelector.children) + 2; // selectors length + curly braces length | |
const blockLength = calcDeclarationsLength(diff.eq); // declarations length | |
// create new ruleset if declarations length greater than | |
// ruleset description overhead | |
if (blockLength >= newBlockLength) { | |
const newItem = list.createItem({ | |
type: 'Rule', | |
loc: null, | |
prelude: newSelector, | |
block: { | |
type: 'Block', | |
loc: null, | |
children: new cssTree.List().fromArray(diff.eq) | |
}, | |
pseudoSignature: node.pseudoSignature | |
}); | |
block.children.fromArray(diff.ne1); | |
prevBlock.children.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((prevSelector) => | |
selectors.some((selector) => | |
selector.compareMarker === prevSelector.compareMarker | |
) | |
); | |
} | |
prevSelectors.forEach((data) => { | |
disallowDownMarkers[data.compareMarker] = true; | |
}); | |
}); | |
} | |
function restructRule(ast) { | |
cssTree.walk(ast, { | |
visit: 'Rule', | |
reverse: true, | |
enter: processRule | |
}); | |
} | |
module.exports = restructRule; | |