let comparisonMap = { | |
atrule: ['name', 'params'], | |
rule: ['selector'], | |
} | |
let types = new Set(Object.keys(comparisonMap)) | |
export default function collapseAdjacentRules() { | |
function collapseRulesIn(root) { | |
let currentRule = null | |
root.each((node) => { | |
if (!types.has(node.type)) { | |
currentRule = null | |
return | |
} | |
if (currentRule === null) { | |
currentRule = node | |
return | |
} | |
let properties = comparisonMap[node.type] | |
if (node.type === 'atrule' && node.name === 'font-face') { | |
currentRule = node | |
} else if ( | |
properties.every( | |
(property) => | |
(node[property] ?? '').replace(/\s+/g, ' ') === | |
(currentRule[property] ?? '').replace(/\s+/g, ' ') | |
) | |
) { | |
// An AtRule may not have children (for example if we encounter duplicate @import url(…) rules) | |
if (node.nodes) { | |
currentRule.append(node.nodes) | |
} | |
node.remove() | |
} else { | |
currentRule = node | |
} | |
}) | |
// After we've collapsed adjacent rules & at-rules, we need to collapse | |
// adjacent rules & at-rules that are children of at-rules. | |
// We do not care about nesting rules because Tailwind CSS | |
// explicitly does not handle rule nesting on its own as | |
// the user is expected to use a nesting plugin | |
root.each((node) => { | |
if (node.type === 'atrule') { | |
collapseRulesIn(node) | |
} | |
}) | |
} | |
return (root) => { | |
collapseRulesIn(root) | |
} | |
} | |