postcss-sort-media-queries 5.2.0 → 6.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/README.md +44 -50
- package/build/index.cjs +311 -0
- package/package.json +30 -57
- package/src/index.js +122 -0
- package/browser.js +0 -54
- package/index.js +0 -75
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
[PostCSS] plugin for sorting and combining CSS media queries with **mobile first** / **desktop first** methodologies.
|
|
17
17
|
|
|
18
|
-
> From
|
|
18
|
+
> From v6.0.0 plugin supported nested media queries and ESM usage
|
|
19
19
|
|
|
20
20
|
## Table of Contents
|
|
21
21
|
|
|
@@ -43,52 +43,49 @@ And here is the [online demo](https://postcss-sort-media-queries.github.io)
|
|
|
43
43
|
|
|
44
44
|
### Mobile first sorting
|
|
45
45
|
|
|
46
|
-
**
|
|
46
|
+
**Before**
|
|
47
47
|
|
|
48
48
|
```css
|
|
49
|
-
@media
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
@
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
@media
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
@media
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
@media screen and (max-width: 640px) {
|
|
65
|
-
.footer { color: #cfcfcf }
|
|
49
|
+
@media (min-width: 1400px) {}
|
|
50
|
+
@media (min-width: 1200px) {}
|
|
51
|
+
|
|
52
|
+
@layer reset {
|
|
53
|
+
|
|
54
|
+
@media (min-width: 1200px) {
|
|
55
|
+
@media (min-width: 992px) {}
|
|
56
|
+
@media (min-width: 768px) {}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@media (min-width: 768px) {
|
|
60
|
+
@media (min-width: 640px) {}
|
|
61
|
+
@media (min-width: 320px) {}
|
|
62
|
+
}
|
|
66
63
|
}
|
|
67
64
|
```
|
|
68
65
|
|
|
69
|
-
**
|
|
66
|
+
**After**
|
|
70
67
|
|
|
71
68
|
```css
|
|
72
|
-
@
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
@media
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
@media
|
|
82
|
-
|
|
83
|
-
.head { color: #cfcfcf }
|
|
84
|
-
.main { color: #cfcfcf }
|
|
85
|
-
.footer { color: #cfcfcf }
|
|
69
|
+
@layer reset {
|
|
70
|
+
|
|
71
|
+
@media (min-width: 768px) {
|
|
72
|
+
@media (min-width: 320px) {}
|
|
73
|
+
@media (min-width: 640px) {}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@media (min-width: 1200px) {
|
|
77
|
+
@media (min-width: 768px) {}
|
|
78
|
+
@media (min-width: 992px) {}
|
|
79
|
+
}
|
|
86
80
|
}
|
|
81
|
+
|
|
82
|
+
@media (min-width: 1200px) {}
|
|
83
|
+
@media (min-width: 1400px) {}
|
|
87
84
|
```
|
|
88
85
|
|
|
89
86
|
### Desktop first sorting
|
|
90
87
|
|
|
91
|
-
**
|
|
88
|
+
**Before**
|
|
92
89
|
```css
|
|
93
90
|
@media screen and (width < 640px) {
|
|
94
91
|
.header { color: #cdcdcd }
|
|
@@ -110,7 +107,7 @@ And here is the [online demo](https://postcss-sort-media-queries.github.io)
|
|
|
110
107
|
}
|
|
111
108
|
```
|
|
112
109
|
|
|
113
|
-
**
|
|
110
|
+
**After**
|
|
114
111
|
|
|
115
112
|
```css
|
|
116
113
|
@media screen and (max-width: 760px) {
|
|
@@ -144,6 +141,15 @@ Check you project for existed PostCSS config: `postcss.config.js`
|
|
|
144
141
|
in the project root, `"postcss"` section in `package.json`
|
|
145
142
|
or `postcss` in bundle config.
|
|
146
143
|
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
// CJS
|
|
147
|
+
let sortCssMq = require('postcss-sort-media-queries');
|
|
148
|
+
|
|
149
|
+
// ESM
|
|
150
|
+
import sortCssMq from 'postcss-sort-media-queries';
|
|
151
|
+
```
|
|
152
|
+
|
|
147
153
|
If you already use PostCSS, add the plugin to plugins list:
|
|
148
154
|
|
|
149
155
|
```diff
|
|
@@ -176,7 +182,7 @@ and set this plugin in settings.
|
|
|
176
182
|
|
|
177
183
|
## Options
|
|
178
184
|
|
|
179
|
-
> Sorting works based on [
|
|
185
|
+
> Sorting works based on [OlehDutchenko/sort-css-media-queries](https://github.com/OlehDutchenko/sort-css-media-queries) function.
|
|
180
186
|
|
|
181
187
|
### sort
|
|
182
188
|
|
|
@@ -237,18 +243,6 @@ postcss([
|
|
|
237
243
|
|
|
238
244
|
Or alternatively create a `sort-css-mq.config.json` file in the root of your project. Or add property `sortCssMQ: {}` in your `package.json`.
|
|
239
245
|
|
|
240
|
-
### Only Top Level
|
|
241
|
-
|
|
242
|
-
Sort only top level media queries to prevent eject nested media queries from parent node
|
|
243
|
-
|
|
244
|
-
```js
|
|
245
|
-
postcss([
|
|
246
|
-
sortMediaQueries({
|
|
247
|
-
onlyTopLevel: true,
|
|
248
|
-
})
|
|
249
|
-
]).process(css);
|
|
250
|
-
```
|
|
251
|
-
|
|
252
246
|
---
|
|
253
247
|
|
|
254
248
|
## Changelog
|
|
@@ -266,7 +260,7 @@ See [Releases history]
|
|
|
266
260
|
## Thanks
|
|
267
261
|
|
|
268
262
|
- Andrey Sitnik [@ai](https://github.com/ai)
|
|
269
|
-
- Oleh Dutchenko [@dutchenkoOleg](https://github.com/
|
|
263
|
+
- Oleh Dutchenko [@dutchenkoOleg](https://github.com/OlehDutchenko)
|
|
270
264
|
- Jakub Caban [@Lustmored](https://github.com/Lustmored)
|
|
271
265
|
- Dmytro Symonov [@Kassaila](https://github.com/Kassaila)
|
|
272
266
|
- Kai Falkowski [@SassNinja](https://github.com/SassNinja)
|
package/build/index.cjs
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/index.js
|
|
20
|
+
var index_exports = {};
|
|
21
|
+
__export(index_exports, {
|
|
22
|
+
default: () => index_default
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(index_exports);
|
|
25
|
+
|
|
26
|
+
// node_modules/sort-css-media-queries/lib/create-sort.js
|
|
27
|
+
var minMaxWidth = /(!?\(\s*min(-device)?-width)(.|\n)+\(\s*max(-device)?-width|\(\s*width\s*>(=)?(.|\n)+\(\s*width\s*<(=)?|(!?\(.*<(=)?\s*width\s*<(=)?)/i;
|
|
28
|
+
var minWidth = /\(\s*min(-device)?-width|\(\s*width\s*>(=)?/i;
|
|
29
|
+
var maxMinWidth = /(!?\(\s*max(-device)?-width)(.|\n)+\(\s*min(-device)?-width|\(\s*width\s*<(=)?(.|\n)+\(\s*width\s*>(=)?|(!?\(.*>(=)?\s*width\s*>(=)?)/i;
|
|
30
|
+
var maxWidth = /\(\s*max(-device)?-width|\(\s*width\s*<(=)?/i;
|
|
31
|
+
var isMinWidth = _testQuery(minMaxWidth, maxMinWidth, minWidth);
|
|
32
|
+
var isMaxWidth = _testQuery(maxMinWidth, minMaxWidth, maxWidth);
|
|
33
|
+
var minMaxHeight = /(!?\(\s*min(-device)?-height)(.|\n)+\(\s*max(-device)?-height|\(\s*height\s*>(=)?(.|\n)+\(\s*height\s*<(=)?|(!?\(.*<(=)?\s*height\s*<(=)?)/i;
|
|
34
|
+
var minHeight = /\(\s*min(-device)?-height|\(\s*height\s*>(=)?/i;
|
|
35
|
+
var maxMinHeight = /(!?\(\s*max(-device)?-height)(.|\n)+\(\s*min(-device)?-height|\(\s*height\s*<(=)?(.|\n)+\(\s*height\s*>(=)?|(!?\(.*>(=)?\s*height\s*>(=)?)/i;
|
|
36
|
+
var maxHeight = /\(\s*max(-device)?-height|\(\s*height\s*<(=)?/i;
|
|
37
|
+
var isMinHeight = _testQuery(minMaxHeight, maxMinHeight, minHeight);
|
|
38
|
+
var isMaxHeight = _testQuery(maxMinHeight, minMaxHeight, maxHeight);
|
|
39
|
+
var lessThan = /<(?!=)/;
|
|
40
|
+
var grtrThan = />(?!=)/;
|
|
41
|
+
var isPrint = /print/i;
|
|
42
|
+
var isPrintOnly = /^print$/i;
|
|
43
|
+
var maxValue = Number.MAX_VALUE;
|
|
44
|
+
function _getQueryLength(query) {
|
|
45
|
+
let length = /(-?\d*\.?\d+)(ch|em|ex|px|rem)/.exec(query);
|
|
46
|
+
if (length === null && (isMinWidth(query) || isMinHeight(query))) {
|
|
47
|
+
length = /(\d)/.exec(query);
|
|
48
|
+
}
|
|
49
|
+
if (length === "0") {
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
if (length === null) {
|
|
53
|
+
return maxValue;
|
|
54
|
+
}
|
|
55
|
+
let number = length[1];
|
|
56
|
+
const unit = length[2];
|
|
57
|
+
switch (unit) {
|
|
58
|
+
case "ch":
|
|
59
|
+
number = parseFloat(number) * 8.8984375;
|
|
60
|
+
break;
|
|
61
|
+
case "em":
|
|
62
|
+
case "rem":
|
|
63
|
+
number = parseFloat(number) * 16;
|
|
64
|
+
break;
|
|
65
|
+
case "ex":
|
|
66
|
+
number = parseFloat(number) * 8.296875;
|
|
67
|
+
break;
|
|
68
|
+
case "px":
|
|
69
|
+
number = parseFloat(number);
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
return +number;
|
|
73
|
+
}
|
|
74
|
+
function _testQuery(doubleTestTrue, doubleTestFalse, singleTest) {
|
|
75
|
+
return function(query) {
|
|
76
|
+
let result;
|
|
77
|
+
if (doubleTestTrue.test(query)) result = true;
|
|
78
|
+
else if (doubleTestFalse.test(query)) result = false;
|
|
79
|
+
else result = singleTest.test(query);
|
|
80
|
+
return query.includes("not") ? !result : result;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function _testIsPrint(a, b) {
|
|
84
|
+
const isPrintA = isPrint.test(a);
|
|
85
|
+
const isPrintOnlyA = isPrintOnly.test(a);
|
|
86
|
+
const isPrintB = isPrint.test(b);
|
|
87
|
+
const isPrintOnlyB = isPrintOnly.test(b);
|
|
88
|
+
if (isPrintA && isPrintB) {
|
|
89
|
+
if (!isPrintOnlyA && isPrintOnlyB) {
|
|
90
|
+
return 1;
|
|
91
|
+
}
|
|
92
|
+
if (isPrintOnlyA && !isPrintOnlyB) {
|
|
93
|
+
return -1;
|
|
94
|
+
}
|
|
95
|
+
return a.localeCompare(b);
|
|
96
|
+
}
|
|
97
|
+
if (isPrintA) {
|
|
98
|
+
return 1;
|
|
99
|
+
}
|
|
100
|
+
if (isPrintB) {
|
|
101
|
+
return -1;
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
function createSort(configuration) {
|
|
106
|
+
const config = configuration || {};
|
|
107
|
+
const UNITLESS_MQ_ALWAYS_FIRST = config.unitlessMqAlwaysFirst;
|
|
108
|
+
function sortCSSmq(a, b) {
|
|
109
|
+
const testIsPrint = _testIsPrint(a, b);
|
|
110
|
+
if (testIsPrint !== null) {
|
|
111
|
+
return testIsPrint;
|
|
112
|
+
}
|
|
113
|
+
const minA = isMinWidth(a) || isMinHeight(a);
|
|
114
|
+
const maxA = isMaxWidth(a) || isMaxHeight(a);
|
|
115
|
+
const minB = isMinWidth(b) || isMinHeight(b);
|
|
116
|
+
const maxB = isMaxWidth(b) || isMaxHeight(b);
|
|
117
|
+
if (UNITLESS_MQ_ALWAYS_FIRST && (!minA && !maxA || !minB && !maxB)) {
|
|
118
|
+
if (!minA && !maxA && !minB && !maxB) {
|
|
119
|
+
return a.localeCompare(b);
|
|
120
|
+
}
|
|
121
|
+
return !minB && !maxB ? 1 : -1;
|
|
122
|
+
} else {
|
|
123
|
+
if (minA && maxB) {
|
|
124
|
+
return -1;
|
|
125
|
+
}
|
|
126
|
+
if (maxA && minB) {
|
|
127
|
+
return 1;
|
|
128
|
+
}
|
|
129
|
+
const lengthA = _getQueryLength(a);
|
|
130
|
+
const lengthB = _getQueryLength(b);
|
|
131
|
+
if (lengthA === maxValue && lengthB === maxValue) {
|
|
132
|
+
return a.localeCompare(b);
|
|
133
|
+
} else if (lengthA === maxValue) {
|
|
134
|
+
return 1;
|
|
135
|
+
} else if (lengthB === maxValue) {
|
|
136
|
+
return -1;
|
|
137
|
+
}
|
|
138
|
+
if (lengthA > lengthB) {
|
|
139
|
+
if (maxA) {
|
|
140
|
+
return -1;
|
|
141
|
+
}
|
|
142
|
+
return 1;
|
|
143
|
+
}
|
|
144
|
+
if (lengthA < lengthB) {
|
|
145
|
+
if (maxA) {
|
|
146
|
+
return 1;
|
|
147
|
+
}
|
|
148
|
+
return -1;
|
|
149
|
+
}
|
|
150
|
+
if (lengthA === lengthB) {
|
|
151
|
+
if (maxA && maxB) {
|
|
152
|
+
if (lessThan.test(a) && !lessThan.test(b)) {
|
|
153
|
+
return 1;
|
|
154
|
+
}
|
|
155
|
+
if (!lessThan.test(a) && lessThan.test(b)) {
|
|
156
|
+
return -1;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (minA && minB) {
|
|
160
|
+
if (grtrThan.test(a) && !grtrThan.test(b)) {
|
|
161
|
+
return 1;
|
|
162
|
+
}
|
|
163
|
+
if (!grtrThan.test(a) && grtrThan.test(b)) {
|
|
164
|
+
return -1;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return a.localeCompare(b);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
sortCSSmq.desktopFirst = function(a, b) {
|
|
172
|
+
const testIsPrint = _testIsPrint(a, b);
|
|
173
|
+
if (testIsPrint !== null) {
|
|
174
|
+
return testIsPrint;
|
|
175
|
+
}
|
|
176
|
+
const minA = isMinWidth(a) || isMinHeight(a);
|
|
177
|
+
const maxA = isMaxWidth(a) || isMaxHeight(a);
|
|
178
|
+
const minB = isMinWidth(b) || isMinHeight(b);
|
|
179
|
+
const maxB = isMaxWidth(b) || isMaxHeight(b);
|
|
180
|
+
if (UNITLESS_MQ_ALWAYS_FIRST && (!minA && !maxA || !minB && !maxB)) {
|
|
181
|
+
if (!minA && !maxA && !minB && !maxB) {
|
|
182
|
+
return a.localeCompare(b);
|
|
183
|
+
}
|
|
184
|
+
return !minB && !maxB ? 1 : -1;
|
|
185
|
+
} else {
|
|
186
|
+
if (minA && maxB) {
|
|
187
|
+
return 1;
|
|
188
|
+
}
|
|
189
|
+
if (maxA && minB) {
|
|
190
|
+
return -1;
|
|
191
|
+
}
|
|
192
|
+
const lengthA = _getQueryLength(a);
|
|
193
|
+
const lengthB = _getQueryLength(b);
|
|
194
|
+
if (lengthA === maxValue && lengthB === maxValue) {
|
|
195
|
+
return a.localeCompare(b);
|
|
196
|
+
} else if (lengthA === maxValue) {
|
|
197
|
+
return 1;
|
|
198
|
+
} else if (lengthB === maxValue) {
|
|
199
|
+
return -1;
|
|
200
|
+
}
|
|
201
|
+
if (lengthA > lengthB) {
|
|
202
|
+
if (maxA) {
|
|
203
|
+
return -1;
|
|
204
|
+
}
|
|
205
|
+
return 1;
|
|
206
|
+
}
|
|
207
|
+
if (lengthA < lengthB) {
|
|
208
|
+
if (maxA) {
|
|
209
|
+
return 1;
|
|
210
|
+
}
|
|
211
|
+
return -1;
|
|
212
|
+
}
|
|
213
|
+
if (lengthA === lengthB) {
|
|
214
|
+
if (maxA && maxB) {
|
|
215
|
+
if (lessThan.test(a) && !lessThan.test(b)) {
|
|
216
|
+
return 1;
|
|
217
|
+
}
|
|
218
|
+
if (!lessThan.test(a) && lessThan.test(b)) {
|
|
219
|
+
return -1;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (minA && minB) {
|
|
223
|
+
if (grtrThan.test(a) && !grtrThan.test(b)) {
|
|
224
|
+
return 1;
|
|
225
|
+
}
|
|
226
|
+
if (!grtrThan.test(a) && grtrThan.test(b)) {
|
|
227
|
+
return -1;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return -a.localeCompare(b);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
return sortCSSmq;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/index.js
|
|
238
|
+
function sortAtRules(queries, options, sortCSSmq) {
|
|
239
|
+
if (typeof options.sort !== "function") {
|
|
240
|
+
options.sort = options.sort === "desktop-first" ? sortCSSmq.desktopFirst : sortCSSmq;
|
|
241
|
+
}
|
|
242
|
+
return queries.sort(options.sort);
|
|
243
|
+
}
|
|
244
|
+
function plugin(options = {}) {
|
|
245
|
+
options = Object.assign(
|
|
246
|
+
{
|
|
247
|
+
sort: "mobile-first",
|
|
248
|
+
configuration: false
|
|
249
|
+
},
|
|
250
|
+
options
|
|
251
|
+
);
|
|
252
|
+
const sortCSSmq = createSort(options.configuration);
|
|
253
|
+
return {
|
|
254
|
+
postcssPlugin: "postcss-sort-media-queries",
|
|
255
|
+
OnceExit(root, { AtRule }) {
|
|
256
|
+
let parents = {
|
|
257
|
+
root: [],
|
|
258
|
+
nested: []
|
|
259
|
+
};
|
|
260
|
+
let processed = /* @__PURE__ */ Symbol("processed");
|
|
261
|
+
root.walkAtRules("media", (atRule) => {
|
|
262
|
+
if (atRule.parent[processed]) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (atRule.parent.type === "root") {
|
|
266
|
+
parents.root.push(atRule.parent);
|
|
267
|
+
}
|
|
268
|
+
if (atRule.parent.type !== "root") {
|
|
269
|
+
parents.nested.push(atRule.parent);
|
|
270
|
+
}
|
|
271
|
+
atRule.parent[processed] = true;
|
|
272
|
+
return;
|
|
273
|
+
});
|
|
274
|
+
Object.keys(parents).forEach((type) => {
|
|
275
|
+
if (!parents[type].length) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
parents[type].forEach((parent) => {
|
|
279
|
+
let media = parent.nodes.filter(
|
|
280
|
+
(n) => n.type === "atrule" && n.name === "media"
|
|
281
|
+
);
|
|
282
|
+
if (!media) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
let atRules = [];
|
|
286
|
+
media.forEach((atRule) => {
|
|
287
|
+
let query = atRule.params;
|
|
288
|
+
if (!atRules[query]) {
|
|
289
|
+
atRules[query] = new AtRule({
|
|
290
|
+
name: atRule.name,
|
|
291
|
+
params: atRule.params,
|
|
292
|
+
source: atRule.source
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
atRule.nodes.forEach((node) => {
|
|
296
|
+
atRules[query].append(node.clone());
|
|
297
|
+
});
|
|
298
|
+
atRule.remove();
|
|
299
|
+
});
|
|
300
|
+
if (atRules) {
|
|
301
|
+
sortAtRules(Object.keys(atRules), options, sortCSSmq).forEach((query) => {
|
|
302
|
+
parent.append(atRules[query]);
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
plugin.postcss = true;
|
|
311
|
+
var index_default = plugin;
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postcss-sort-media-queries",
|
|
3
|
-
"version": "5.2.0",
|
|
4
3
|
"description": "PostCSS plugin for sorting and combining CSS media queries with mobile first / **desktop first methodologies",
|
|
4
|
+
"version": "6.0.0",
|
|
5
|
+
"engines": {
|
|
6
|
+
"node": ">=22.0.0"
|
|
7
|
+
},
|
|
5
8
|
"keywords": [
|
|
6
9
|
"postcss",
|
|
7
10
|
"postcss-plugin",
|
|
@@ -29,67 +32,37 @@
|
|
|
29
32
|
"url": "https://github.com/yunusga/postcss-sort-media-queries/issues"
|
|
30
33
|
},
|
|
31
34
|
"homepage": "https://github.com/yunusga/postcss-sort-media-queries",
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
"type": "module",
|
|
36
|
+
"main": "src/index.js",
|
|
37
|
+
"module": "src/index.js",
|
|
38
|
+
"exports": {
|
|
39
|
+
".": {
|
|
40
|
+
"require": "./src/index.cjs",
|
|
41
|
+
"import": "./src/index.js",
|
|
42
|
+
"default": "./src/index.js"
|
|
43
|
+
}
|
|
38
44
|
},
|
|
39
|
-
"
|
|
40
|
-
"
|
|
45
|
+
"files": [
|
|
46
|
+
"build"
|
|
47
|
+
],
|
|
48
|
+
"scripts": {
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"test:watch": "vitest --watch",
|
|
51
|
+
"coverage": "vitest --coverage",
|
|
52
|
+
"build": "npm run build:cjs",
|
|
53
|
+
"build:cjs": "esbuild src/index.js --outfile=build/index.cjs --format=cjs --bundle --platform=node --external:postcss"
|
|
41
54
|
},
|
|
42
55
|
"devDependencies": {
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"jest": "^27.3.1",
|
|
49
|
-
"jest-ci": "^0.1.1",
|
|
50
|
-
"jest-cli": "^27.3.1",
|
|
51
|
-
"lint-staged": "^13.2.1",
|
|
52
|
-
"postcss": "^8.4.23",
|
|
53
|
-
"postcss-flexbugs-fixes": "^5.0.2",
|
|
54
|
-
"postcss-media-minmax": "^5.0.0",
|
|
55
|
-
"postcss-nested": "^5.0.6"
|
|
56
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
57
|
+
"esbuild": "^0.27.3",
|
|
58
|
+
"postcss": "^8.5.6",
|
|
59
|
+
"prettier": "3.8.1",
|
|
60
|
+
"vitest": "^4.0.18"
|
|
56
61
|
},
|
|
57
62
|
"peerDependencies": {
|
|
58
|
-
"postcss": "^8.
|
|
63
|
+
"postcss": "^8.5.6"
|
|
59
64
|
},
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"pre-commit": "lint-staged"
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
"lint-staged": {
|
|
66
|
-
"*.js": "eslint --fix"
|
|
67
|
-
},
|
|
68
|
-
"eslintConfig": {
|
|
69
|
-
"parserOptions": {
|
|
70
|
-
"ecmaVersion": 2017
|
|
71
|
-
},
|
|
72
|
-
"env": {
|
|
73
|
-
"node": true,
|
|
74
|
-
"es6": true
|
|
75
|
-
},
|
|
76
|
-
"extends": [
|
|
77
|
-
"eslint:recommended",
|
|
78
|
-
"plugin:jest/recommended"
|
|
79
|
-
],
|
|
80
|
-
"rules": {
|
|
81
|
-
"jest/expect-expect": "off"
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
"jest": {
|
|
85
|
-
"testEnvironment": "node",
|
|
86
|
-
"coverageThreshold": {
|
|
87
|
-
"global": {
|
|
88
|
-
"statements": 100
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
"sortCssMQ": {
|
|
93
|
-
"unitlessMqAlwaysFirst": false
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"sort-css-media-queries": "^3.0.1"
|
|
94
67
|
}
|
|
95
68
|
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import createSort from 'sort-css-media-queries/create-sort';
|
|
2
|
+
|
|
3
|
+
// PostCSS plugin to sort CSS @media rules according to a configurable order.
|
|
4
|
+
// The plugin groups top-level and nested media at-rules, merges rules
|
|
5
|
+
// with identical queries, and re-inserts them in the desired order.
|
|
6
|
+
|
|
7
|
+
// Helper that ensures `options.sort` is a function and sorts queries.
|
|
8
|
+
function sortAtRules(queries, options, sortCSSmq) {
|
|
9
|
+
if (typeof options.sort !== 'function') {
|
|
10
|
+
options.sort = options.sort === 'desktop-first' ? sortCSSmq.desktopFirst : sortCSSmq;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return queries.sort(options.sort);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function plugin(options = {}) {
|
|
17
|
+
|
|
18
|
+
// Set default options and allow user overrides
|
|
19
|
+
options = Object.assign(
|
|
20
|
+
{
|
|
21
|
+
sort: 'mobile-first',
|
|
22
|
+
configuration: false,
|
|
23
|
+
},
|
|
24
|
+
options
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// Create a sorter based on configuration (from sort-css-media-queries)
|
|
28
|
+
const sortCSSmq = createSort(options.configuration);
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
postcssPlugin: 'postcss-sort-media-queries',
|
|
32
|
+
|
|
33
|
+
// Execute once after the entire tree has been parsed
|
|
34
|
+
OnceExit(root, { AtRule }) {
|
|
35
|
+
// Collect parent nodes that contain media at-rules. We separate
|
|
36
|
+
// top-level (`root`) parents from nested parents so ordering
|
|
37
|
+
// semantics can be preserved independently.
|
|
38
|
+
let parents = {
|
|
39
|
+
root: [],
|
|
40
|
+
nested: [],
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Symbol used to mark parents that we've already collected
|
|
44
|
+
let processed = Symbol('processed');
|
|
45
|
+
|
|
46
|
+
// Walk all @media at-rules and group their parents
|
|
47
|
+
root.walkAtRules('media', (atRule) => {
|
|
48
|
+
if (atRule.parent[processed]) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// If the parent is the root of the document, add to root list
|
|
53
|
+
if (atRule.parent.type === 'root') {
|
|
54
|
+
parents.root.push(atRule.parent);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Otherwise treat it as a nested parent
|
|
58
|
+
if (atRule.parent.type !== 'root') {
|
|
59
|
+
parents.nested.push(atRule.parent);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Mark this parent so we don't collect it twice
|
|
63
|
+
atRule.parent[processed] = true;
|
|
64
|
+
|
|
65
|
+
return;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// For each parent group, merge and sort its media at-rules
|
|
69
|
+
Object.keys(parents).forEach((type) => {
|
|
70
|
+
if (!parents[type].length) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
parents[type].forEach((parent) => {
|
|
75
|
+
// Filter only @media nodes from the parent's children
|
|
76
|
+
let media = parent.nodes.filter(
|
|
77
|
+
n => n.type === 'atrule' && n.name === 'media'
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
if (!media) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Combine at-rules with identical query params into a single
|
|
85
|
+
// AtRule instance, cloning their children to preserve content.
|
|
86
|
+
let atRules = [];
|
|
87
|
+
|
|
88
|
+
media.forEach((atRule) => {
|
|
89
|
+
let query = atRule.params;
|
|
90
|
+
|
|
91
|
+
if (!atRules[query]) {
|
|
92
|
+
atRules[query] = new AtRule({
|
|
93
|
+
name: atRule.name,
|
|
94
|
+
params: atRule.params,
|
|
95
|
+
source: atRule.source
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
atRule.nodes.forEach((node) => {
|
|
100
|
+
atRules[query].append(node.clone());
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Remove the original at-rule since its contents have been
|
|
104
|
+
// merged into `atRules[query]`.
|
|
105
|
+
atRule.remove();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Sort query keys and append merged at-rules back to the parent
|
|
109
|
+
if (atRules) {
|
|
110
|
+
sortAtRules(Object.keys(atRules), options, sortCSSmq).forEach((query) => {
|
|
111
|
+
parent.append(atRules[query]);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
plugin.postcss = true;
|
|
121
|
+
|
|
122
|
+
export default plugin;
|
package/browser.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
function sortAtRules(queries, sort, sortCSSmq) {
|
|
2
|
-
if (typeof sort !== 'function') {
|
|
3
|
-
sort = sort === 'desktop-first' ? sortCSSmq.desktopFirst : sortCSSmq
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
return queries.sort(sort)
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
module.exports = (opts = {}) => {
|
|
10
|
-
|
|
11
|
-
opts = Object.assign(
|
|
12
|
-
{
|
|
13
|
-
sort: 'mobile-first',
|
|
14
|
-
configuration: {
|
|
15
|
-
unitlessMqAlwaysFirst: false,
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
opts
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
const createSort = require('sort-css-media-queries/lib/create-sort');
|
|
22
|
-
const sortCSSmq = createSort(opts.configuration);
|
|
23
|
-
|
|
24
|
-
return {
|
|
25
|
-
postcssPlugin: 'postcss-sort-media-queries',
|
|
26
|
-
OnceExit(root, { AtRule }) {
|
|
27
|
-
|
|
28
|
-
let atRules = [];
|
|
29
|
-
|
|
30
|
-
root.walkAtRules('media', atRule => {
|
|
31
|
-
let query = atRule.params
|
|
32
|
-
|
|
33
|
-
if (!atRules[query]) {
|
|
34
|
-
atRules[query] = new AtRule({
|
|
35
|
-
name: atRule.name,
|
|
36
|
-
params: atRule.params,
|
|
37
|
-
source: atRule.source
|
|
38
|
-
})
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
atRule.nodes.forEach(node => {
|
|
42
|
-
atRules[query].append(node.clone())
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
atRule.remove()
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
sortAtRules(Object.keys(atRules), opts.sort, sortCSSmq).forEach(query => {
|
|
49
|
-
root.append(atRules[query])
|
|
50
|
-
})
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
module.exports.postcss = true
|
package/index.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
function sortAtRules(queries, sort, sortCSSmq) {
|
|
2
|
-
if (typeof sort !== 'function') {
|
|
3
|
-
sort = sort === 'desktop-first' ? sortCSSmq.desktopFirst : sortCSSmq
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
return queries.sort(sort)
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
module.exports = (opts = {}) => {
|
|
10
|
-
|
|
11
|
-
opts = Object.assign(
|
|
12
|
-
{
|
|
13
|
-
sort: 'mobile-first',
|
|
14
|
-
configuration: false,
|
|
15
|
-
onlyTopLevel: false,
|
|
16
|
-
},
|
|
17
|
-
opts
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
const createSort = require('sort-css-media-queries/lib/create-sort');
|
|
21
|
-
const sortCSSmq = opts.configuration ? createSort(opts.configuration) : require('sort-css-media-queries');
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
postcssPlugin: 'postcss-sort-media-queries',
|
|
25
|
-
OnceExit (root, { AtRule }) {
|
|
26
|
-
|
|
27
|
-
let atRules = [];
|
|
28
|
-
|
|
29
|
-
root.walkAtRules('media', atRule => {
|
|
30
|
-
if (opts.onlyTopLevel && atRule.parent.type === 'root') {
|
|
31
|
-
let query = atRule.params
|
|
32
|
-
|
|
33
|
-
if (!atRules[query]) {
|
|
34
|
-
atRules[query] = new AtRule({
|
|
35
|
-
name: atRule.name,
|
|
36
|
-
params: atRule.params,
|
|
37
|
-
source: atRule.source
|
|
38
|
-
})
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
atRule.nodes.forEach(node => {
|
|
42
|
-
atRules[query].append(node.clone())
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
atRule.remove()
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!opts.onlyTopLevel) {
|
|
49
|
-
let query = atRule.params
|
|
50
|
-
|
|
51
|
-
if (!atRules[query]) {
|
|
52
|
-
atRules[query] = new AtRule({
|
|
53
|
-
name: atRule.name,
|
|
54
|
-
params: atRule.params,
|
|
55
|
-
source: atRule.source
|
|
56
|
-
})
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
atRule.nodes.forEach(node => {
|
|
60
|
-
atRules[query].append(node.clone())
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
atRule.remove()
|
|
64
|
-
}
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
if (atRules) {
|
|
68
|
-
sortAtRules(Object.keys(atRules), opts.sort, sortCSSmq).forEach(query => {
|
|
69
|
-
root.append(atRules[query])
|
|
70
|
-
})
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
module.exports.postcss = true
|