| import { walk } from 'css-tree'; |
| import { unsafeToSkipNode, isEqualDeclarations} from './utils.js'; |
| |
| /* |
| At this step all rules has single simple selector. We try to join by equal |
| declaration blocks to first rule, e.g. |
| |
| .a { color: red } |
| b { ... } |
| .b { color: red } |
| -> |
| .a, .b { color: red } |
| b { ... } |
| */ |
| |
| function processRule(node, item, list) { |
| const selectors = node.prelude.children; |
| const declarations = node.block.children; |
| const nodeCompareMarker = selectors.first.compareMarker; |
| const skippedCompareMarkers = {}; |
| |
| list.nextUntil(item.next, function(next, nextItem) { |
| // skip non-ruleset node if safe |
| if (next.type !== 'Rule') { |
| return unsafeToSkipNode.call(selectors, next); |
| } |
| |
| if (node.pseudoSignature !== next.pseudoSignature) { |
| return true; |
| } |
| |
| const nextFirstSelector = next.prelude.children.head; |
| const nextDeclarations = next.block.children; |
| const nextCompareMarker = nextFirstSelector.data.compareMarker; |
| |
| // if next ruleset has same marked as one of skipped then stop joining |
| if (nextCompareMarker in skippedCompareMarkers) { |
| return true; |
| } |
| |
| // try to join by selectors |
| if (selectors.head === selectors.tail) { |
| if (selectors.first.id === nextFirstSelector.data.id) { |
| declarations.appendList(nextDeclarations); |
| list.remove(nextItem); |
| return; |
| } |
| } |
| |
| // try to join by properties |
| if (isEqualDeclarations(declarations, nextDeclarations)) { |
| const nextStr = nextFirstSelector.data.id; |
| |
| selectors.some((data, item) => { |
| const curStr = data.id; |
| |
| if (nextStr < curStr) { |
| selectors.insert(nextFirstSelector, item); |
| return true; |
| } |
| |
| if (!item.next) { |
| selectors.insert(nextFirstSelector); |
| return true; |
| } |
| }); |
| |
| list.remove(nextItem); |
| return; |
| } |
| |
| // go to next ruleset if current one can be skipped (has no equal specificity nor element selector) |
| if (nextCompareMarker === nodeCompareMarker) { |
| return true; |
| } |
| |
| skippedCompareMarkers[nextCompareMarker] = true; |
| }); |
| } |
| |
| export default function mergeRule(ast) { |
| walk(ast, { |
| visit: 'Rule', |
| enter: processRule |
| }); |
| }; |