postcss-merge-rules 4.0.0-rc.0 → 4.0.2
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/README.md +1 -1
- package/dist/index.js +69 -89
- package/dist/lib/ensureCompatibility.js +15 -17
- package/package.json +13 -13
- package/dist/lib/clone.js +0 -34
package/README.md
CHANGED
|
@@ -96,7 +96,7 @@ examples for your environment.
|
|
|
96
96
|
|
|
97
97
|
## Contributors
|
|
98
98
|
|
|
99
|
-
See [CONTRIBUTORS.md](https://github.com/
|
|
99
|
+
See [CONTRIBUTORS.md](https://github.com/cssnano/cssnano/blob/master/CONTRIBUTORS.md).
|
|
100
100
|
|
|
101
101
|
## License
|
|
102
102
|
|
package/dist/index.js
CHANGED
|
@@ -26,81 +26,57 @@ var _ensureCompatibility2 = _interopRequireDefault(_ensureCompatibility);
|
|
|
26
26
|
|
|
27
27
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
return `-${v}-`;
|
|
31
|
-
});
|
|
29
|
+
const prefixes = _vendors2.default.map(v => `-${v}-`);
|
|
32
30
|
|
|
33
31
|
function intersect(a, b, not) {
|
|
34
|
-
return a.filter(
|
|
35
|
-
|
|
32
|
+
return a.filter(c => {
|
|
33
|
+
const index = ~b.indexOf(c);
|
|
36
34
|
return not ? !index : index;
|
|
37
35
|
});
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
};
|
|
38
|
+
// Internet Explorer use :-ms-input-placeholder.
|
|
39
|
+
// Microsoft Edge use ::-ms-input-placeholder.
|
|
40
|
+
const findMsInputPlaceholder = selector => ~selector.search(/-ms-input-placeholder/i);
|
|
41
|
+
const different = (a, b) => intersect(a, b, true).concat(intersect(b, a, true));
|
|
42
|
+
const filterPrefixes = selector => intersect(prefixes, selector);
|
|
46
43
|
|
|
47
44
|
function sameVendor(selectorsA, selectorsB) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return same(selectorsA) === same(selectorsB);
|
|
45
|
+
let same = selectors => selectors.map(filterPrefixes).join();
|
|
46
|
+
let findMsVendor = selectors => selectors.find(findMsInputPlaceholder);
|
|
47
|
+
return same(selectorsA) === same(selectorsB) && !(findMsVendor(selectorsA) && findMsVendor(selectorsB));
|
|
52
48
|
}
|
|
53
49
|
|
|
54
|
-
|
|
55
|
-
return !filterPrefixes(selector).length;
|
|
56
|
-
};
|
|
50
|
+
const noVendor = selector => !filterPrefixes(selector).length;
|
|
57
51
|
|
|
58
52
|
function canMerge(ruleA, ruleB, browsers, compatibilityCache) {
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
const a = ruleA.selectors;
|
|
54
|
+
const b = ruleB.selectors;
|
|
61
55
|
|
|
62
|
-
|
|
56
|
+
const selectors = a.concat(b);
|
|
63
57
|
|
|
64
58
|
if (!(0, _ensureCompatibility2.default)(selectors, browsers, compatibilityCache)) {
|
|
65
59
|
return false;
|
|
66
60
|
}
|
|
67
61
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
62
|
+
const parent = (0, _cssnanoUtilSameParent2.default)(ruleA, ruleB);
|
|
63
|
+
const { name } = ruleA.parent;
|
|
71
64
|
if (parent && name && ~name.indexOf('keyframes')) {
|
|
72
65
|
return false;
|
|
73
66
|
}
|
|
74
67
|
return parent && (selectors.every(noVendor) || sameVendor(a, b));
|
|
75
68
|
}
|
|
76
69
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
};
|
|
80
|
-
var joinSelectors = function joinSelectors() {
|
|
81
|
-
for (var _len = arguments.length, rules = Array(_len), _key = 0; _key < _len; _key++) {
|
|
82
|
-
rules[_key] = arguments[_key];
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return rules.map(function (s) {
|
|
86
|
-
return s.selector;
|
|
87
|
-
}).join();
|
|
88
|
-
};
|
|
70
|
+
const getDecls = rule => rule.nodes && rule.nodes.map(String);
|
|
71
|
+
const joinSelectors = (...rules) => rules.map(s => s.selector).join();
|
|
89
72
|
|
|
90
|
-
function ruleLength() {
|
|
91
|
-
|
|
92
|
-
rules[_key2] = arguments[_key2];
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return rules.map(function (r) {
|
|
96
|
-
return r.nodes.length ? String(r) : '';
|
|
97
|
-
}).join('').length;
|
|
73
|
+
function ruleLength(...rules) {
|
|
74
|
+
return rules.map(r => r.nodes.length ? String(r) : '').join('').length;
|
|
98
75
|
}
|
|
99
76
|
|
|
100
77
|
function splitProp(prop) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
rest = void 0;
|
|
78
|
+
const parts = prop.split('-');
|
|
79
|
+
let base, rest;
|
|
104
80
|
// Treat vendor prefixed properties as if they were unprefixed;
|
|
105
81
|
// moving them when combined with non-prefixed properties can
|
|
106
82
|
// cause issues. e.g. moving -webkit-background-clip when there
|
|
@@ -119,45 +95,38 @@ function isConflictingProp(propA, propB) {
|
|
|
119
95
|
if (propA === propB) {
|
|
120
96
|
return true;
|
|
121
97
|
}
|
|
122
|
-
|
|
123
|
-
|
|
98
|
+
const a = splitProp(propA);
|
|
99
|
+
const b = splitProp(propB);
|
|
124
100
|
return a[0] === b[0] && a[1].length !== b[1].length;
|
|
125
101
|
}
|
|
126
102
|
|
|
127
103
|
function hasConflicts(declProp, notMoved) {
|
|
128
|
-
return notMoved.some(
|
|
129
|
-
return isConflictingProp(prop, declProp);
|
|
130
|
-
});
|
|
104
|
+
return notMoved.some(prop => isConflictingProp(prop, declProp));
|
|
131
105
|
}
|
|
132
106
|
|
|
133
107
|
function partialMerge(first, second) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
var intersection = intersect(getDecls(first), getDecls(second));
|
|
108
|
+
let intersection = intersect(getDecls(first), getDecls(second));
|
|
137
109
|
if (!intersection.length) {
|
|
138
110
|
return second;
|
|
139
111
|
}
|
|
140
|
-
|
|
112
|
+
let nextRule = second.next();
|
|
141
113
|
if (nextRule && nextRule.type === 'rule' && canMerge(second, nextRule)) {
|
|
142
|
-
|
|
114
|
+
let nextIntersection = intersect(getDecls(second), getDecls(nextRule));
|
|
143
115
|
if (nextIntersection.length > intersection.length) {
|
|
144
116
|
first = second;second = nextRule;intersection = nextIntersection;
|
|
145
117
|
}
|
|
146
118
|
}
|
|
147
|
-
|
|
119
|
+
const recievingBlock = second.clone();
|
|
148
120
|
recievingBlock.selector = joinSelectors(first, second);
|
|
149
121
|
recievingBlock.nodes = [];
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
var canMove = difference.every(function (d) {
|
|
159
|
-
return d.split(':')[0] !== base;
|
|
160
|
-
});
|
|
122
|
+
const difference = different(getDecls(first), getDecls(second));
|
|
123
|
+
const filterConflicts = (decls, intersectn) => {
|
|
124
|
+
let willNotMove = [];
|
|
125
|
+
return decls.reduce((willMove, decl) => {
|
|
126
|
+
let intersects = ~intersectn.indexOf(decl);
|
|
127
|
+
let prop = decl.split(':')[0];
|
|
128
|
+
let base = prop.split('-')[0];
|
|
129
|
+
let canMove = difference.every(d => d.split(':')[0] !== base);
|
|
161
130
|
if (intersects && canMove && !hasConflicts(prop, willNotMove)) {
|
|
162
131
|
willMove.push(decl);
|
|
163
132
|
} else {
|
|
@@ -166,30 +135,41 @@ function partialMerge(first, second) {
|
|
|
166
135
|
return willMove;
|
|
167
136
|
}, []);
|
|
168
137
|
};
|
|
138
|
+
const containsAllDeclaration = intersectionList => {
|
|
139
|
+
return intersectionList.some(declaration => {
|
|
140
|
+
return declaration.split(':')[0].toLowerCase() === 'all';
|
|
141
|
+
});
|
|
142
|
+
};
|
|
169
143
|
intersection = filterConflicts(getDecls(first).reverse(), intersection);
|
|
170
144
|
intersection = filterConflicts(getDecls(second), intersection);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
145
|
+
|
|
146
|
+
// Rules with "all" declarations must be on top
|
|
147
|
+
if (containsAllDeclaration(intersection)) {
|
|
148
|
+
second.parent.insertBefore(first, recievingBlock);
|
|
149
|
+
} else {
|
|
150
|
+
second.parent.insertBefore(second, recievingBlock);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const firstClone = first.clone();
|
|
154
|
+
const secondClone = second.clone();
|
|
155
|
+
const moveDecl = callback => {
|
|
156
|
+
return decl => {
|
|
175
157
|
if (~intersection.indexOf(String(decl))) {
|
|
176
|
-
callback.call(
|
|
158
|
+
callback.call(this, decl);
|
|
177
159
|
}
|
|
178
160
|
};
|
|
179
161
|
};
|
|
180
|
-
firstClone.walkDecls(moveDecl(
|
|
162
|
+
firstClone.walkDecls(moveDecl(decl => {
|
|
181
163
|
decl.remove();
|
|
182
164
|
recievingBlock.append(decl);
|
|
183
165
|
}));
|
|
184
|
-
secondClone.walkDecls(moveDecl(
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
var merged = ruleLength(firstClone, recievingBlock, secondClone);
|
|
188
|
-
var original = ruleLength(first, second);
|
|
166
|
+
secondClone.walkDecls(moveDecl(decl => decl.remove()));
|
|
167
|
+
const merged = ruleLength(firstClone, recievingBlock, secondClone);
|
|
168
|
+
const original = ruleLength(first, second);
|
|
189
169
|
if (merged < original) {
|
|
190
170
|
first.replaceWith(firstClone);
|
|
191
171
|
second.replaceWith(secondClone);
|
|
192
|
-
[firstClone, recievingBlock, secondClone].forEach(
|
|
172
|
+
[firstClone, recievingBlock, secondClone].forEach(r => {
|
|
193
173
|
if (!r.nodes.length) {
|
|
194
174
|
r.remove();
|
|
195
175
|
}
|
|
@@ -205,7 +185,7 @@ function partialMerge(first, second) {
|
|
|
205
185
|
}
|
|
206
186
|
|
|
207
187
|
function selectorMerger(browsers, compatibilityCache) {
|
|
208
|
-
|
|
188
|
+
let cache = null;
|
|
209
189
|
return function (rule) {
|
|
210
190
|
// Prime the cache with the first rule, or alternately ensure that it is
|
|
211
191
|
// safe to merge both declarations before continuing
|
|
@@ -230,8 +210,8 @@ function selectorMerger(browsers, compatibilityCache) {
|
|
|
230
210
|
// Merge when both selectors are exactly equal
|
|
231
211
|
// e.g. a { color: blue } a { font-weight: bold }
|
|
232
212
|
if (cache.selector === rule.selector) {
|
|
233
|
-
|
|
234
|
-
rule.walk(
|
|
213
|
+
const cached = getDecls(cache);
|
|
214
|
+
rule.walk(decl => {
|
|
235
215
|
if (~cached.indexOf(String(decl))) {
|
|
236
216
|
return decl.remove();
|
|
237
217
|
}
|
|
@@ -246,15 +226,15 @@ function selectorMerger(browsers, compatibilityCache) {
|
|
|
246
226
|
};
|
|
247
227
|
}
|
|
248
228
|
|
|
249
|
-
exports.default = _postcss2.default.plugin('postcss-merge-rules',
|
|
250
|
-
return
|
|
251
|
-
|
|
252
|
-
|
|
229
|
+
exports.default = _postcss2.default.plugin('postcss-merge-rules', () => {
|
|
230
|
+
return (css, result) => {
|
|
231
|
+
const resultOpts = result.opts || {};
|
|
232
|
+
const browsers = (0, _browserslist2.default)(null, {
|
|
253
233
|
stats: resultOpts.stats,
|
|
254
234
|
path: __dirname,
|
|
255
235
|
env: resultOpts.env
|
|
256
236
|
});
|
|
257
|
-
|
|
237
|
+
const compatibilityCache = {};
|
|
258
238
|
css.walkRules(selectorMerger(browsers, compatibilityCache));
|
|
259
239
|
};
|
|
260
240
|
});
|
|
@@ -14,16 +14,16 @@ var _postcssSelectorParser2 = _interopRequireDefault(_postcssSelectorParser);
|
|
|
14
14
|
|
|
15
15
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
const simpleSelectorRe = /^#?[-._a-z0-9 ]+$/i;
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
const cssSel2 = 'css-sel2';
|
|
20
|
+
const cssSel3 = 'css-sel3';
|
|
21
|
+
const cssGencontent = 'css-gencontent';
|
|
22
|
+
const cssFirstLetter = 'css-first-letter';
|
|
23
|
+
const cssFirstLine = 'css-first-line';
|
|
24
|
+
const cssInOutOfRange = 'css-in-out-of-range';
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
const pseudoElements = exports.pseudoElements = {
|
|
27
27
|
':active': cssSel2,
|
|
28
28
|
':after': cssGencontent,
|
|
29
29
|
':before': cssGencontent,
|
|
@@ -78,21 +78,19 @@ function ensureCompatibility(selectors, browsers, compatibilityCache) {
|
|
|
78
78
|
if (selectors.some(isCssMixin)) {
|
|
79
79
|
return false;
|
|
80
80
|
}
|
|
81
|
-
return selectors.every(
|
|
81
|
+
return selectors.every(selector => {
|
|
82
82
|
if (simpleSelectorRe.test(selector)) {
|
|
83
83
|
return true;
|
|
84
84
|
}
|
|
85
85
|
if (compatibilityCache && selector in compatibilityCache) {
|
|
86
86
|
return compatibilityCache[selector];
|
|
87
87
|
}
|
|
88
|
-
|
|
89
|
-
(0, _postcssSelectorParser2.default)(
|
|
90
|
-
ast.walk(
|
|
91
|
-
|
|
92
|
-
value = node.value;
|
|
93
|
-
|
|
88
|
+
let compatible = true;
|
|
89
|
+
(0, _postcssSelectorParser2.default)(ast => {
|
|
90
|
+
ast.walk(node => {
|
|
91
|
+
const { type, value } = node;
|
|
94
92
|
if (type === 'pseudo') {
|
|
95
|
-
|
|
93
|
+
const entry = pseudoElements[value];
|
|
96
94
|
if (entry && compatible) {
|
|
97
95
|
compatible = (0, _caniuseApi.isSupported)(entry, browsers);
|
|
98
96
|
}
|
|
@@ -133,7 +131,7 @@ function ensureCompatibility(selectors, browsers, compatibilityCache) {
|
|
|
133
131
|
return false;
|
|
134
132
|
}
|
|
135
133
|
});
|
|
136
|
-
}).
|
|
134
|
+
}).processSync(selector);
|
|
137
135
|
if (compatibilityCache) {
|
|
138
136
|
compatibilityCache[selector] = compatible;
|
|
139
137
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postcss-merge-rules",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.2",
|
|
4
4
|
"description": "Merge CSS rules with PostCSS.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"files": [
|
|
@@ -19,29 +19,29 @@
|
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"babel-cli": "^6.0.0",
|
|
22
|
-
"cross-env": "^
|
|
23
|
-
"postcss-discard-comments": "^4.0.0
|
|
24
|
-
"postcss-simple-vars": "^
|
|
22
|
+
"cross-env": "^5.0.0",
|
|
23
|
+
"postcss-discard-comments": "^4.0.0",
|
|
24
|
+
"postcss-simple-vars": "^5.0.1"
|
|
25
25
|
},
|
|
26
|
-
"homepage": "https://github.com/
|
|
26
|
+
"homepage": "https://github.com/cssnano/cssnano",
|
|
27
27
|
"author": {
|
|
28
28
|
"name": "Ben Briggs",
|
|
29
29
|
"email": "beneb.info@gmail.com",
|
|
30
30
|
"url": "http://beneb.info"
|
|
31
31
|
},
|
|
32
|
-
"repository": "
|
|
32
|
+
"repository": "cssnano/cssnano",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"browserslist": "^
|
|
35
|
-
"caniuse-api": "^
|
|
36
|
-
"cssnano-util-same-parent": "^4.0.0
|
|
37
|
-
"postcss": "^
|
|
38
|
-
"postcss-selector-parser": "^
|
|
34
|
+
"browserslist": "^4.0.0",
|
|
35
|
+
"caniuse-api": "^3.0.0",
|
|
36
|
+
"cssnano-util-same-parent": "^4.0.0",
|
|
37
|
+
"postcss": "^7.0.0",
|
|
38
|
+
"postcss-selector-parser": "^3.0.0",
|
|
39
39
|
"vendors": "^1.0.0"
|
|
40
40
|
},
|
|
41
41
|
"bugs": {
|
|
42
|
-
"url": "https://github.com/
|
|
42
|
+
"url": "https://github.com/cssnano/cssnano/issues"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
|
45
|
-
"node": ">=
|
|
45
|
+
"node": ">=6.9.0"
|
|
46
46
|
}
|
|
47
47
|
}
|
package/dist/lib/clone.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
var clone = function clone(obj, parent) {
|
|
7
|
-
if (typeof obj !== 'object' || obj === null) {
|
|
8
|
-
return obj;
|
|
9
|
-
}
|
|
10
|
-
var cloned = new obj.constructor();
|
|
11
|
-
for (var i in obj) {
|
|
12
|
-
if (!{}.hasOwnProperty.call(obj, i)) {
|
|
13
|
-
continue;
|
|
14
|
-
}
|
|
15
|
-
var value = obj[i];
|
|
16
|
-
if (i === 'parent' && typeof value === 'object') {
|
|
17
|
-
if (parent) {
|
|
18
|
-
cloned[i] = parent;
|
|
19
|
-
}
|
|
20
|
-
} else if (i === 'source') {
|
|
21
|
-
cloned[i] = value;
|
|
22
|
-
} else if (value instanceof Array) {
|
|
23
|
-
cloned[i] = value.map(function (j) {
|
|
24
|
-
return clone(j, cloned);
|
|
25
|
-
});
|
|
26
|
-
} else {
|
|
27
|
-
cloned[i] = clone(value, cloned);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return cloned;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
exports.default = clone;
|
|
34
|
-
module.exports = exports['default'];
|