postcss-merge-rules 4.0.3 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +75 -0
- package/dist/index.js +387 -183
- package/dist/lib/ensureCompatibility.js +139 -124
- package/package.json +17 -15
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
# [5.0.0](https://github.com/cssnano/cssnano/compare/postcss-merge-rules@5.0.0-rc.2...postcss-merge-rules@5.0.0) (2021-04-06)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package postcss-merge-rules
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# [5.0.0-rc.2](https://github.com/cssnano/cssnano/compare/postcss-merge-rules@5.0.0-rc.1...postcss-merge-rules@5.0.0-rc.2) (2021-03-15)
|
|
15
|
+
|
|
16
|
+
**Note:** Version bump only for package postcss-merge-rules
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# [5.0.0-rc.1](https://github.com/cssnano/cssnano/compare/postcss-merge-rules@5.0.0-rc.0...postcss-merge-rules@5.0.0-rc.1) (2021-03-04)
|
|
23
|
+
|
|
24
|
+
**Note:** Version bump only for package postcss-merge-rules
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# 5.0.0-rc.0 (2021-02-19)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
### Bug Fixes
|
|
34
|
+
|
|
35
|
+
* don't unsafe merge 'all' declaration ([#872](https://github.com/cssnano/cssnano/issues/872)) ([6ea9e5d](https://github.com/cssnano/cssnano/commit/6ea9e5dcad2d8ea22be7209332ee29d352c807de))
|
|
36
|
+
* focus-visible issue ([#882](https://github.com/cssnano/cssnano/issues/882)) ([4cfcaaf](https://github.com/cssnano/cssnano/commit/4cfcaaf25b162ec2b0308907a408d7dba6a354c3))
|
|
37
|
+
* **merge-rules, merge-idents:** add support for nested at-rules ([#719](https://github.com/cssnano/cssnano/issues/719)) ([cdedda7](https://github.com/cssnano/cssnano/commit/cdedda7f9d67873d872add044ad34c91616579f3))
|
|
38
|
+
* **postcss-merge-rules:** don't change specificity of prefixed properties ([#723](https://github.com/cssnano/cssnano/issues/723)) ([863cf2b](https://github.com/cssnano/cssnano/commit/863cf2b3470d3172523a3165dc368abcfa18809c))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### chore
|
|
42
|
+
|
|
43
|
+
* minimum require version of node is 10.13 ([#871](https://github.com/cssnano/cssnano/issues/871)) ([28bda24](https://github.com/cssnano/cssnano/commit/28bda243e32ce3ba89b3c358a5f78727b3732f11))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
### Features
|
|
47
|
+
|
|
48
|
+
* migarete to PostCSS 8 ([#975](https://github.com/cssnano/cssnano/issues/975)) ([40b82dc](https://github.com/cssnano/cssnano/commit/40b82dca7f53ac02cd4fe62846dec79b898ccb49))
|
|
49
|
+
* **postcss-merge-rules:** merge at-rules ([#722](https://github.com/cssnano/cssnano/issues/722)) ([8d4610a](https://github.com/cssnano/cssnano/commit/8d4610a6391ddab29bcb08ef0522d0b7ce2d6582))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
### BREAKING CHANGES
|
|
53
|
+
|
|
54
|
+
* minimum supported `postcss` version is `8.2.1`
|
|
55
|
+
* minimum require version of node is 10.13
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
## 4.1.9 (2019-02-12)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
### Performance Improvements
|
|
63
|
+
|
|
64
|
+
* **postcss-merge-rules:** increase perf ([#681](https://github.com/cssnano/cssnano/issues/681)) ([35bad2b](https://github.com/cssnano/cssnano/commit/35bad2b70fca5390c88eaabc24c25bb8d28b2f95))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## 4.1.1 (2018-09-24)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
### Bug Fixes
|
|
72
|
+
|
|
73
|
+
* handle uppercase `all` property in merge rules ([#611](https://github.com/cssnano/cssnano/issues/611)) ([0dfe335](https://github.com/cssnano/cssnano/commit/0dfe3355951fa4a080a04dca34c6d99420def7ac))
|
|
74
|
+
* merge same atrules with difference case ([#605](https://github.com/cssnano/cssnano/issues/605)) ([ca350fd](https://github.com/cssnano/cssnano/commit/ca350fda779bab5ca2eadf70299d92f8e495a273))
|
|
75
|
+
* **postcss-merge-longhand:** not mangle border output ([#555](https://github.com/cssnano/cssnano/issues/555)) ([9a70605](https://github.com/cssnano/cssnano/commit/9a706050b621e7795a9bf74eb7110b5c81804ffe)), closes [#553](https://github.com/cssnano/cssnano/issues/553) [#554](https://github.com/cssnano/cssnano/issues/554)
|
package/dist/index.js
CHANGED
|
@@ -1,241 +1,445 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
|
|
4
|
+
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.default = void 0;
|
|
6
7
|
|
|
7
|
-
var _browserslist = require(
|
|
8
|
+
var _browserslist = _interopRequireDefault(require("browserslist"));
|
|
8
9
|
|
|
9
|
-
var
|
|
10
|
+
var _vendors = _interopRequireDefault(require("vendors"));
|
|
10
11
|
|
|
11
|
-
var
|
|
12
|
+
var _cssnanoUtils = require("cssnano-utils");
|
|
12
13
|
|
|
13
|
-
var
|
|
14
|
+
var _ensureCompatibility = _interopRequireDefault(require("./lib/ensureCompatibility"));
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
var _vendors2 = _interopRequireDefault(_vendors);
|
|
16
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
/** @type {string[]} */
|
|
19
|
+
const prefixes = _vendors.default.map(v => `-${v}-`);
|
|
20
|
+
/**
|
|
21
|
+
* @param {postcss.Declaration} a
|
|
22
|
+
* @param {postcss.Declaration} b
|
|
23
|
+
* @return {boolean}
|
|
24
|
+
*/
|
|
20
25
|
|
|
21
|
-
var _cssnanoUtilSameParent2 = _interopRequireDefault(_cssnanoUtilSameParent);
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
function declarationIsEqual(a, b) {
|
|
28
|
+
return a.important === b.important && a.prop === b.prop && a.value === b.value;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* @param {postcss.Declaration[]} array
|
|
32
|
+
* @param {postcss.Declaration} decl
|
|
33
|
+
* @return {number}
|
|
34
|
+
*/
|
|
24
35
|
|
|
25
|
-
var _ensureCompatibility2 = _interopRequireDefault(_ensureCompatibility);
|
|
26
36
|
|
|
27
|
-
function
|
|
37
|
+
function indexOfDeclaration(array, decl) {
|
|
38
|
+
return array.findIndex(d => declarationIsEqual(d, decl));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns filtered array of matched or unmatched declarations
|
|
42
|
+
* @param {postcss.Declaration[]} a
|
|
43
|
+
* @param {postcss.Declaration[]} b
|
|
44
|
+
* @param {boolean} [not=false]
|
|
45
|
+
* @return {postcss.Declaration[]}
|
|
46
|
+
*/
|
|
28
47
|
|
|
29
|
-
const prefixes = _vendors2.default.map(v => `-${v}-`);
|
|
30
48
|
|
|
31
49
|
function intersect(a, b, not) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
50
|
+
return a.filter(c => {
|
|
51
|
+
const index = ~indexOfDeclaration(b, c);
|
|
52
|
+
return not ? !index : index;
|
|
53
|
+
});
|
|
36
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* @param {postcss.Declaration[]} a
|
|
57
|
+
* @param {postcss.Declaration[]} b
|
|
58
|
+
* @return {boolean}
|
|
59
|
+
*/
|
|
60
|
+
|
|
37
61
|
|
|
38
|
-
|
|
62
|
+
function sameDeclarationsAndOrder(a, b) {
|
|
63
|
+
if (a.length !== b.length) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return a.every((d, index) => declarationIsEqual(d, b[index]));
|
|
68
|
+
} // Internet Explorer use :-ms-input-placeholder.
|
|
39
69
|
// Microsoft Edge use ::-ms-input-placeholder.
|
|
70
|
+
|
|
71
|
+
|
|
40
72
|
const findMsInputPlaceholder = selector => ~selector.search(/-ms-input-placeholder/i);
|
|
41
|
-
|
|
42
|
-
|
|
73
|
+
/**
|
|
74
|
+
* @param {string} selector
|
|
75
|
+
* @return {string[]}
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
function filterPrefixes(selector) {
|
|
80
|
+
return prefixes.filter(prefix => selector.indexOf(prefix) !== -1);
|
|
81
|
+
}
|
|
43
82
|
|
|
44
83
|
function sameVendor(selectorsA, selectorsB) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
84
|
+
let same = selectors => selectors.map(filterPrefixes).join();
|
|
85
|
+
|
|
86
|
+
let findMsVendor = selectors => selectors.find(findMsInputPlaceholder);
|
|
87
|
+
|
|
88
|
+
return same(selectorsA) === same(selectorsB) && !(findMsVendor(selectorsA) && findMsVendor(selectorsB));
|
|
48
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* @param {string} selector
|
|
92
|
+
* @return {boolean}
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
function noVendor(selector) {
|
|
97
|
+
return !filterPrefixes(selector).length;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* @param {postcss.Rule} ruleA
|
|
101
|
+
* @param {postcss.Rule} ruleB
|
|
102
|
+
* @param {string[]=} browsers
|
|
103
|
+
* @param {Object.<string, boolean>=} compatibilityCache
|
|
104
|
+
* @return {boolean}
|
|
105
|
+
*/
|
|
49
106
|
|
|
50
|
-
const noVendor = selector => !filterPrefixes(selector).length;
|
|
51
107
|
|
|
52
108
|
function canMerge(ruleA, ruleB, browsers, compatibilityCache) {
|
|
53
|
-
|
|
54
|
-
|
|
109
|
+
const a = ruleA.selectors;
|
|
110
|
+
const b = ruleB.selectors;
|
|
111
|
+
const selectors = a.concat(b);
|
|
55
112
|
|
|
56
|
-
|
|
113
|
+
if (!(0, _ensureCompatibility.default)(selectors, browsers, compatibilityCache)) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
57
116
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
117
|
+
const parent = (0, _cssnanoUtils.sameParent)(ruleA, ruleB);
|
|
118
|
+
const {
|
|
119
|
+
name
|
|
120
|
+
} = ruleA.parent;
|
|
61
121
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
122
|
+
if (parent && name && ~name.indexOf('keyframes')) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return parent && (selectors.every(noVendor) || sameVendor(a, b));
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* @param {postcss.Rule} rule
|
|
130
|
+
* @return {postcss.Declaration[]}
|
|
131
|
+
*/
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
function getDecls(rule) {
|
|
135
|
+
return rule.nodes.filter(node => node.type === 'decl');
|
|
68
136
|
}
|
|
69
137
|
|
|
70
|
-
const getDecls = rule => rule.nodes && rule.nodes.map(String);
|
|
71
138
|
const joinSelectors = (...rules) => rules.map(s => s.selector).join();
|
|
72
139
|
|
|
73
140
|
function ruleLength(...rules) {
|
|
74
|
-
|
|
141
|
+
return rules.map(r => r.nodes.length ? String(r) : '').join('').length;
|
|
75
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* @param {string} prop
|
|
145
|
+
* @return {{prefix: string, base:string, rest:string[]}}
|
|
146
|
+
*/
|
|
147
|
+
|
|
76
148
|
|
|
77
149
|
function splitProp(prop) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
150
|
+
// Treat vendor prefixed properties as if they were unprefixed;
|
|
151
|
+
// moving them when combined with non-prefixed properties can
|
|
152
|
+
// cause issues. e.g. moving -webkit-background-clip when there
|
|
153
|
+
// is a background shorthand definition.
|
|
154
|
+
const parts = prop.split('-');
|
|
155
|
+
|
|
156
|
+
if (prop[0] !== '-') {
|
|
157
|
+
return {
|
|
158
|
+
prefix: '',
|
|
159
|
+
base: parts[0],
|
|
160
|
+
rest: parts.slice(1)
|
|
161
|
+
};
|
|
162
|
+
} // Don't split css variables
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
if (prop[1] === '-') {
|
|
166
|
+
return {
|
|
167
|
+
prefix: null,
|
|
168
|
+
base: null,
|
|
169
|
+
rest: [prop]
|
|
170
|
+
};
|
|
171
|
+
} // Found prefix
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
prefix: parts[1],
|
|
176
|
+
base: parts[2],
|
|
177
|
+
rest: parts.slice(3)
|
|
178
|
+
};
|
|
92
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* @param {string} propA
|
|
182
|
+
* @param {string} propB
|
|
183
|
+
*/
|
|
184
|
+
|
|
93
185
|
|
|
94
186
|
function isConflictingProp(propA, propB) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
187
|
+
if (propA === propB) {
|
|
188
|
+
// Same specificity
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const a = splitProp(propA);
|
|
193
|
+
const b = splitProp(propB); // Don't resort css variables
|
|
194
|
+
|
|
195
|
+
if (!a.base && !b.base) {
|
|
196
|
+
return true;
|
|
197
|
+
} // Different base;
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
if (a.base !== b.base) {
|
|
201
|
+
return false;
|
|
202
|
+
} // Conflict if rest-count mismatches
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
if (a.rest.length !== b.rest.length) {
|
|
206
|
+
return true;
|
|
207
|
+
} // Conflict if rest parameters are equal (same but unprefixed)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
return a.rest.every((s, index) => b.rest[index] === s);
|
|
101
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* @param {postcss.Rule} first
|
|
214
|
+
* @param {postcss.Rule} second
|
|
215
|
+
* @return {boolean} merged
|
|
216
|
+
*/
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
function mergeParents(first, second) {
|
|
220
|
+
// Null check for detached rules
|
|
221
|
+
if (!first.parent || !second.parent) {
|
|
222
|
+
return false;
|
|
223
|
+
} // Check if parents share node
|
|
224
|
+
|
|
102
225
|
|
|
103
|
-
|
|
104
|
-
return
|
|
226
|
+
if (first.parent === second.parent) {
|
|
227
|
+
return false;
|
|
228
|
+
} // sameParent() already called by canMerge()
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
second.remove();
|
|
232
|
+
first.parent.append(second);
|
|
233
|
+
return true;
|
|
105
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* @param {postcss.Rule} first
|
|
237
|
+
* @param {postcss.Rule} second
|
|
238
|
+
* @return {postcss.Rule} mergedRule
|
|
239
|
+
*/
|
|
240
|
+
|
|
106
241
|
|
|
107
242
|
function partialMerge(first, second) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
243
|
+
let intersection = intersect(getDecls(first), getDecls(second));
|
|
244
|
+
|
|
245
|
+
if (!intersection.length) {
|
|
246
|
+
return second;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
let nextRule = second.next();
|
|
250
|
+
|
|
251
|
+
if (!nextRule) {
|
|
252
|
+
// Grab next cousin
|
|
253
|
+
const parentSibling = second.parent.next();
|
|
254
|
+
nextRule = parentSibling && parentSibling.nodes && parentSibling.nodes[0];
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (nextRule && nextRule.type === 'rule' && canMerge(second, nextRule)) {
|
|
258
|
+
let nextIntersection = intersect(getDecls(second), getDecls(nextRule));
|
|
259
|
+
|
|
260
|
+
if (nextIntersection.length > intersection.length) {
|
|
261
|
+
mergeParents(second, nextRule);
|
|
262
|
+
first = second;
|
|
263
|
+
second = nextRule;
|
|
264
|
+
intersection = nextIntersection;
|
|
111
265
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const firstDecls = getDecls(first); // Filter out intersections with later conflicts in First
|
|
269
|
+
|
|
270
|
+
intersection = intersection.filter((decl, intersectIndex) => {
|
|
271
|
+
const index = indexOfDeclaration(firstDecls, decl);
|
|
272
|
+
const nextConflictInFirst = firstDecls.slice(index + 1).find(d => isConflictingProp(d.prop, decl.prop));
|
|
273
|
+
|
|
274
|
+
if (!nextConflictInFirst) {
|
|
275
|
+
return true;
|
|
118
276
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (containsAllDeclaration(intersection)) {
|
|
148
|
-
second.parent.insertBefore(first, recievingBlock);
|
|
149
|
-
} else {
|
|
150
|
-
second.parent.insertBefore(second, recievingBlock);
|
|
277
|
+
|
|
278
|
+
const nextConflictInIntersection = intersection.slice(intersectIndex + 1).find(d => isConflictingProp(d.prop, decl.prop));
|
|
279
|
+
|
|
280
|
+
if (!nextConflictInIntersection) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (declarationIsEqual(nextConflictInFirst, nextConflictInIntersection)) {
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return false;
|
|
289
|
+
}); // Filter out intersections with previous conflicts in Second
|
|
290
|
+
|
|
291
|
+
const secondDecls = getDecls(second);
|
|
292
|
+
intersection = intersection.filter(decl => {
|
|
293
|
+
const nextConflictIndex = secondDecls.findIndex(d => isConflictingProp(d.prop, decl.prop));
|
|
294
|
+
|
|
295
|
+
if (nextConflictIndex === -1) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (!declarationIsEqual(secondDecls[nextConflictIndex], decl)) {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (decl.prop.toLowerCase() !== 'direction' && decl.prop.toLowerCase() !== 'unicode-bidi' && secondDecls.some(declaration => declaration.prop.toLowerCase() === 'all')) {
|
|
304
|
+
return false;
|
|
151
305
|
}
|
|
152
306
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
307
|
+
secondDecls.splice(nextConflictIndex, 1);
|
|
308
|
+
return true;
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
if (!intersection.length) {
|
|
312
|
+
// Nothing to merge
|
|
313
|
+
return second;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const receivingBlock = second.clone();
|
|
317
|
+
receivingBlock.selector = joinSelectors(first, second);
|
|
318
|
+
receivingBlock.nodes = [];
|
|
319
|
+
second.parent.insertBefore(second, receivingBlock);
|
|
320
|
+
const firstClone = first.clone();
|
|
321
|
+
const secondClone = second.clone();
|
|
322
|
+
/**
|
|
323
|
+
* @param {function(postcss.Declaration):void} callback
|
|
324
|
+
* @return {function(postcss.Declaration)}
|
|
325
|
+
*/
|
|
326
|
+
|
|
327
|
+
function moveDecl(callback) {
|
|
328
|
+
return decl => {
|
|
329
|
+
if (~indexOfDeclaration(intersection, decl)) {
|
|
330
|
+
callback.call(this, decl);
|
|
331
|
+
}
|
|
161
332
|
};
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return second;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
firstClone.walkDecls(moveDecl(decl => {
|
|
336
|
+
decl.remove();
|
|
337
|
+
receivingBlock.append(decl);
|
|
338
|
+
}));
|
|
339
|
+
secondClone.walkDecls(moveDecl(decl => decl.remove()));
|
|
340
|
+
const merged = ruleLength(firstClone, receivingBlock, secondClone);
|
|
341
|
+
const original = ruleLength(first, second);
|
|
342
|
+
|
|
343
|
+
if (merged < original) {
|
|
344
|
+
first.replaceWith(firstClone);
|
|
345
|
+
second.replaceWith(secondClone);
|
|
346
|
+
[firstClone, receivingBlock, secondClone].forEach(r => {
|
|
347
|
+
if (!r.nodes.length) {
|
|
348
|
+
r.remove();
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
if (!secondClone.parent) {
|
|
353
|
+
return receivingBlock;
|
|
184
354
|
}
|
|
355
|
+
|
|
356
|
+
return secondClone;
|
|
357
|
+
} else {
|
|
358
|
+
receivingBlock.remove();
|
|
359
|
+
return second;
|
|
360
|
+
}
|
|
185
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* @param {string[]} browsers
|
|
364
|
+
* @param {Object.<string, boolean>} compatibilityCache
|
|
365
|
+
* @return {function(postcss.Rule)}
|
|
366
|
+
*/
|
|
367
|
+
|
|
186
368
|
|
|
187
369
|
function selectorMerger(browsers, compatibilityCache) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
370
|
+
/** @type {postcss.Rule} */
|
|
371
|
+
let cache = null;
|
|
372
|
+
return function (rule) {
|
|
373
|
+
// Prime the cache with the first rule, or alternately ensure that it is
|
|
374
|
+
// safe to merge both declarations before continuing
|
|
375
|
+
if (!cache || !canMerge(rule, cache, browsers, compatibilityCache)) {
|
|
376
|
+
cache = rule;
|
|
377
|
+
return;
|
|
378
|
+
} // Ensure that we don't deduplicate the same rule; this is sometimes
|
|
379
|
+
// caused by a partial merge
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
if (cache === rule) {
|
|
383
|
+
cache = rule;
|
|
384
|
+
return;
|
|
385
|
+
} // Parents merge: check if the rules have same parents, but not same parent nodes
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
mergeParents(cache, rule); // Merge when declarations are exactly equal
|
|
389
|
+
// e.g. h1 { color: red } h2 { color: red }
|
|
390
|
+
|
|
391
|
+
if (sameDeclarationsAndOrder(getDecls(rule), getDecls(cache))) {
|
|
392
|
+
rule.selector = joinSelectors(cache, rule);
|
|
393
|
+
cache.remove();
|
|
394
|
+
cache = rule;
|
|
395
|
+
return;
|
|
396
|
+
} // Merge when both selectors are exactly equal
|
|
397
|
+
// e.g. a { color: blue } a { font-weight: bold }
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
if (cache.selector === rule.selector) {
|
|
401
|
+
const cached = getDecls(cache);
|
|
402
|
+
rule.walk(decl => {
|
|
403
|
+
if (~indexOfDeclaration(cached, decl)) {
|
|
404
|
+
return decl.remove();
|
|
209
405
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
406
|
+
|
|
407
|
+
cache.append(decl);
|
|
408
|
+
});
|
|
409
|
+
rule.remove();
|
|
410
|
+
return;
|
|
411
|
+
} // Partial merge: check if the rule contains a subset of the last; if
|
|
412
|
+
// so create a joined selector with the subset, if smaller.
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
cache = partialMerge(cache, rule);
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function pluginCreator() {
|
|
420
|
+
return {
|
|
421
|
+
postcssPlugin: 'postcss-merge-rules',
|
|
422
|
+
|
|
423
|
+
prepare(result) {
|
|
424
|
+
const resultOpts = result.opts || {};
|
|
425
|
+
const browsers = (0, _browserslist.default)(null, {
|
|
426
|
+
stats: resultOpts.stats,
|
|
427
|
+
path: __dirname,
|
|
428
|
+
env: resultOpts.env
|
|
429
|
+
});
|
|
430
|
+
const compatibilityCache = {};
|
|
431
|
+
return {
|
|
432
|
+
OnceExit(css) {
|
|
433
|
+
css.walkRules(selectorMerger(browsers, compatibilityCache));
|
|
222
434
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
435
|
+
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
};
|
|
227
440
|
}
|
|
228
441
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
stats: resultOpts.stats,
|
|
234
|
-
path: __dirname,
|
|
235
|
-
env: resultOpts.env
|
|
236
|
-
});
|
|
237
|
-
const compatibilityCache = {};
|
|
238
|
-
css.walkRules(selectorMerger(browsers, compatibilityCache));
|
|
239
|
-
};
|
|
240
|
-
});
|
|
241
|
-
module.exports = exports['default'];
|
|
442
|
+
pluginCreator.postcss = true;
|
|
443
|
+
var _default = pluginCreator;
|
|
444
|
+
exports.default = _default;
|
|
445
|
+
module.exports = exports.default;
|
|
@@ -1,155 +1,170 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
|
|
4
|
+
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.pseudoElements = undefined;
|
|
7
6
|
exports.default = ensureCompatibility;
|
|
7
|
+
exports.pseudoElements = void 0;
|
|
8
8
|
|
|
9
|
-
var _caniuseApi = require(
|
|
9
|
+
var _caniuseApi = require("caniuse-api");
|
|
10
10
|
|
|
11
|
-
var _postcssSelectorParser = require(
|
|
12
|
-
|
|
13
|
-
var _postcssSelectorParser2 = _interopRequireDefault(_postcssSelectorParser);
|
|
11
|
+
var _postcssSelectorParser = _interopRequireDefault(require("postcss-selector-parser"));
|
|
14
12
|
|
|
15
13
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
16
14
|
|
|
17
15
|
const simpleSelectorRe = /^#?[-._a-z0-9 ]+$/i;
|
|
18
|
-
|
|
19
16
|
const cssSel2 = 'css-sel2';
|
|
20
17
|
const cssSel3 = 'css-sel3';
|
|
21
18
|
const cssGencontent = 'css-gencontent';
|
|
22
19
|
const cssFirstLetter = 'css-first-letter';
|
|
23
20
|
const cssFirstLine = 'css-first-line';
|
|
24
21
|
const cssInOutOfRange = 'css-in-out-of-range';
|
|
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
|
-
|
|
22
|
+
const pseudoElements = {
|
|
23
|
+
':active': cssSel2,
|
|
24
|
+
':after': cssGencontent,
|
|
25
|
+
':before': cssGencontent,
|
|
26
|
+
':checked': cssSel3,
|
|
27
|
+
':default': 'css-default-pseudo',
|
|
28
|
+
':dir': 'css-dir-pseudo',
|
|
29
|
+
':disabled': cssSel3,
|
|
30
|
+
':empty': cssSel3,
|
|
31
|
+
':enabled': cssSel3,
|
|
32
|
+
':first-child': cssSel2,
|
|
33
|
+
':first-letter': cssFirstLetter,
|
|
34
|
+
':first-line': cssFirstLine,
|
|
35
|
+
':first-of-type': cssSel3,
|
|
36
|
+
':focus': cssSel2,
|
|
37
|
+
':focus-within': 'css-focus-within',
|
|
38
|
+
':focus-visible': 'css-focus-visible',
|
|
39
|
+
':has': 'css-has',
|
|
40
|
+
':hover': cssSel2,
|
|
41
|
+
':in-range': cssInOutOfRange,
|
|
42
|
+
':indeterminate': 'css-indeterminate-pseudo',
|
|
43
|
+
':lang': cssSel2,
|
|
44
|
+
':last-child': cssSel3,
|
|
45
|
+
':last-of-type': cssSel3,
|
|
46
|
+
':matches': 'css-matches-pseudo',
|
|
47
|
+
':not': cssSel3,
|
|
48
|
+
':nth-child': cssSel3,
|
|
49
|
+
':nth-last-child': cssSel3,
|
|
50
|
+
':nth-last-of-type': cssSel3,
|
|
51
|
+
':nth-of-type': cssSel3,
|
|
52
|
+
':only-child': cssSel3,
|
|
53
|
+
':only-of-type': cssSel3,
|
|
54
|
+
':optional': 'css-optional-pseudo',
|
|
55
|
+
':out-of-range': cssInOutOfRange,
|
|
56
|
+
':placeholder-shown': 'css-placeholder-shown',
|
|
57
|
+
':root': cssSel3,
|
|
58
|
+
':target': cssSel3,
|
|
59
|
+
'::after': cssGencontent,
|
|
60
|
+
'::backdrop': 'dialog',
|
|
61
|
+
'::before': cssGencontent,
|
|
62
|
+
'::first-letter': cssFirstLetter,
|
|
63
|
+
'::first-line': cssFirstLine,
|
|
64
|
+
'::marker': 'css-marker-pseudo',
|
|
65
|
+
'::placeholder': 'css-placeholder',
|
|
66
|
+
'::selection': 'css-selection'
|
|
70
67
|
};
|
|
68
|
+
exports.pseudoElements = pseudoElements;
|
|
71
69
|
|
|
72
70
|
function isCssMixin(selector) {
|
|
73
|
-
|
|
71
|
+
return selector[selector.length - 1] === ':';
|
|
74
72
|
}
|
|
75
73
|
|
|
76
|
-
const isSupportedCache = {};
|
|
74
|
+
const isSupportedCache = {}; // Move to util in future
|
|
77
75
|
|
|
78
|
-
// Move to util in future
|
|
79
76
|
function isSupportedCached(feature, browsers) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
77
|
+
const key = JSON.stringify({
|
|
78
|
+
feature,
|
|
79
|
+
browsers
|
|
80
|
+
});
|
|
81
|
+
let result = isSupportedCache[key];
|
|
82
|
+
|
|
83
|
+
if (!result) {
|
|
84
|
+
result = (0, _caniuseApi.isSupported)(feature, browsers);
|
|
85
|
+
isSupportedCache[key] = result;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return result;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
function ensureCompatibility(selectors, browsers, compatibilityCache) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
// Should not merge mixins
|
|
93
|
+
if (selectors.some(isCssMixin)) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return selectors.every(selector => {
|
|
98
|
+
if (simpleSelectorRe.test(selector)) {
|
|
99
|
+
return true;
|
|
95
100
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
101
|
+
|
|
102
|
+
if (compatibilityCache && selector in compatibilityCache) {
|
|
103
|
+
return compatibilityCache[selector];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let compatible = true;
|
|
107
|
+
(0, _postcssSelectorParser.default)(ast => {
|
|
108
|
+
ast.walk(node => {
|
|
109
|
+
const {
|
|
110
|
+
type,
|
|
111
|
+
value
|
|
112
|
+
} = node;
|
|
113
|
+
|
|
114
|
+
if (type === 'pseudo') {
|
|
115
|
+
const entry = pseudoElements[value];
|
|
116
|
+
|
|
117
|
+
if (entry && compatible) {
|
|
118
|
+
compatible = isSupportedCached(entry, browsers);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (type === 'combinator') {
|
|
123
|
+
if (~value.indexOf('~')) {
|
|
124
|
+
compatible = isSupportedCached(cssSel3, browsers);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (~value.indexOf('>') || ~value.indexOf('+')) {
|
|
128
|
+
compatible = isSupportedCached(cssSel2, browsers);
|
|
129
|
+
}
|
|
99
130
|
}
|
|
100
|
-
|
|
101
|
-
|
|
131
|
+
|
|
132
|
+
if (type === 'attribute' && node.attribute) {
|
|
133
|
+
// [foo]
|
|
134
|
+
if (!node.operator) {
|
|
135
|
+
compatible = isSupportedCached(cssSel2, browsers);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (value) {
|
|
139
|
+
// [foo="bar"], [foo~="bar"], [foo|="bar"]
|
|
140
|
+
if (~['=', '~=', '|='].indexOf(node.operator)) {
|
|
141
|
+
compatible = isSupportedCached(cssSel2, browsers);
|
|
142
|
+
} // [foo^="bar"], [foo$="bar"], [foo*="bar"]
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
if (~['^=', '$=', '*='].indexOf(node.operator)) {
|
|
146
|
+
compatible = isSupportedCached(cssSel3, browsers);
|
|
147
|
+
}
|
|
148
|
+
} // [foo="bar" i]
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
if (node.insensitive) {
|
|
152
|
+
compatible = isSupportedCached('css-case-insensitive', browsers);
|
|
153
|
+
}
|
|
102
154
|
}
|
|
103
|
-
|
|
104
|
-
(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const entry = pseudoElements[value];
|
|
109
|
-
if (entry && compatible) {
|
|
110
|
-
compatible = isSupportedCached(entry, browsers);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
if (type === 'combinator') {
|
|
114
|
-
if (~value.indexOf('~')) {
|
|
115
|
-
compatible = isSupportedCached(cssSel3, browsers);
|
|
116
|
-
}
|
|
117
|
-
if (~value.indexOf('>') || ~value.indexOf('+')) {
|
|
118
|
-
compatible = isSupportedCached(cssSel2, browsers);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
if (type === 'attribute' && node.attribute) {
|
|
122
|
-
// [foo]
|
|
123
|
-
if (!node.operator) {
|
|
124
|
-
compatible = isSupportedCached(cssSel2, browsers);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (value) {
|
|
128
|
-
// [foo="bar"], [foo~="bar"], [foo|="bar"]
|
|
129
|
-
if (~['=', '~=', '|='].indexOf(node.operator)) {
|
|
130
|
-
compatible = isSupportedCached(cssSel2, browsers);
|
|
131
|
-
}
|
|
132
|
-
// [foo^="bar"], [foo$="bar"], [foo*="bar"]
|
|
133
|
-
if (~['^=', '$=', '*='].indexOf(node.operator)) {
|
|
134
|
-
compatible = isSupportedCached(cssSel3, browsers);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// [foo="bar" i]
|
|
139
|
-
if (node.insensitive) {
|
|
140
|
-
compatible = isSupportedCached('css-case-insensitive', browsers);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
if (!compatible) {
|
|
144
|
-
// If this node was not compatible,
|
|
145
|
-
// break out early from walking the rest
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
}).processSync(selector);
|
|
150
|
-
if (compatibilityCache) {
|
|
151
|
-
compatibilityCache[selector] = compatible;
|
|
155
|
+
|
|
156
|
+
if (!compatible) {
|
|
157
|
+
// If this node was not compatible,
|
|
158
|
+
// break out early from walking the rest
|
|
159
|
+
return false;
|
|
152
160
|
}
|
|
153
|
-
|
|
154
|
-
});
|
|
161
|
+
});
|
|
162
|
+
}).processSync(selector);
|
|
163
|
+
|
|
164
|
+
if (compatibilityCache) {
|
|
165
|
+
compatibilityCache[selector] = compatible;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return compatible;
|
|
169
|
+
});
|
|
155
170
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postcss-merge-rules",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "Merge CSS rules with PostCSS.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"files": [
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
"dist"
|
|
9
9
|
],
|
|
10
10
|
"scripts": {
|
|
11
|
-
"
|
|
11
|
+
"prebuild": "del-cli dist",
|
|
12
|
+
"build": "cross-env BABEL_ENV=publish babel src --config-file ../../babel.config.js --out-dir dist --ignore \"**/__tests__/\"",
|
|
13
|
+
"prepublish": "yarn build"
|
|
12
14
|
},
|
|
13
15
|
"keywords": [
|
|
14
16
|
"css",
|
|
@@ -17,12 +19,6 @@
|
|
|
17
19
|
"postcss-plugin"
|
|
18
20
|
],
|
|
19
21
|
"license": "MIT",
|
|
20
|
-
"devDependencies": {
|
|
21
|
-
"babel-cli": "^6.0.0",
|
|
22
|
-
"cross-env": "^5.0.0",
|
|
23
|
-
"postcss-discard-comments": "^4.0.0",
|
|
24
|
-
"postcss-simple-vars": "^5.0.1"
|
|
25
|
-
},
|
|
26
22
|
"homepage": "https://github.com/cssnano/cssnano",
|
|
27
23
|
"author": {
|
|
28
24
|
"name": "Ben Briggs",
|
|
@@ -31,17 +27,23 @@
|
|
|
31
27
|
},
|
|
32
28
|
"repository": "cssnano/cssnano",
|
|
33
29
|
"dependencies": {
|
|
34
|
-
"browserslist": "^4.
|
|
30
|
+
"browserslist": "^4.16.0",
|
|
35
31
|
"caniuse-api": "^3.0.0",
|
|
36
|
-
"cssnano-
|
|
37
|
-
"postcss": "^
|
|
38
|
-
"
|
|
39
|
-
"vendors": "^1.0.0"
|
|
32
|
+
"cssnano-utils": "^2.0.0",
|
|
33
|
+
"postcss-selector-parser": "^6.0.4",
|
|
34
|
+
"vendors": "^1.0.3"
|
|
40
35
|
},
|
|
41
36
|
"bugs": {
|
|
42
37
|
"url": "https://github.com/cssnano/cssnano/issues"
|
|
43
38
|
},
|
|
44
39
|
"engines": {
|
|
45
|
-
"node": ">=
|
|
46
|
-
}
|
|
40
|
+
"node": "^10 || ^12 || >=14.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"postcss": "^8.2.1"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"postcss": "^8.2.1"
|
|
47
|
+
},
|
|
48
|
+
"gitHead": "0e2c3bf5835bafcdc8783bef66f730a24194c8f3"
|
|
47
49
|
}
|