mergeRules.js
2.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
'use strict';
const hasAllProps = require('./hasAllProps.js');
const getDecls = require('./getDecls.js');
const getRules = require('./getRules.js');
/**
* @param {import('postcss').Declaration} propA
* @param {import('postcss').Declaration} propB
* @return {boolean}
*/
function isConflictingProp(propA, propB) {
if (
!propB.prop ||
propB.important !== propA.important ||
propA.prop === propB.prop
) {
return false;
}
const partsA = propA.prop.split('-');
const partsB = propB.prop.split('-');
/* Be safe: check that the first part matches. So we don't try to
* combine e.g. border-color and color.
*/
if (partsA[0] !== partsB[0]) {
return false;
}
const partsASet = new Set(partsA);
return partsB.every((partB) => partsASet.has(partB));
}
/**
* @param {import('postcss').Declaration[]} match
* @param {import('postcss').Declaration[]} nodes
* @return {boolean}
*/
function hasConflicts(match, nodes) {
const firstNode = Math.min(...match.map((n) => nodes.indexOf(n)));
const lastNode = Math.max(...match.map((n) => nodes.indexOf(n)));
const between = nodes.slice(firstNode + 1, lastNode);
return match.some((a) => between.some((b) => isConflictingProp(a, b)));
}
/**
* @param {import('postcss').Rule} rule
* @param {string[]} properties
* @param {(rules: import('postcss').Declaration[], last: import('postcss').Declaration, props: import('postcss').Declaration[]) => boolean} callback
* @return {void}
*/
module.exports = function mergeRules(rule, properties, callback) {
let decls = getDecls(rule, properties);
while (decls.length) {
const last = decls[decls.length - 1];
const props = decls.filter((node) => node.important === last.important);
const rules = getRules(props, properties);
if (
hasAllProps(rules, ...properties) &&
!hasConflicts(
rules,
/** @type import('postcss').Declaration[]*/ (rule.nodes)
)
) {
if (callback(rules, last, props)) {
decls = decls.filter((node) => !rules.includes(node));
}
}
decls = decls.filter((node) => node !== last);
}
};